From cf1298234ef602f5cb5bde742a3399afd95cab59 Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Tue, 15 Mar 2011 11:04:48 -0400 Subject: [PATCH 01/37] Updated libgit2 to version 0.10.0 --- .gitignore | 1 - vendor/libgit2/CMakeLists.txt | 1 + vendor/libgit2/README.md | 32 +- vendor/libgit2/include/git2.h | 4 +- vendor/libgit2/include/git2/blob.h | 2 +- vendor/libgit2/include/git2/commit.h | 16 +- vendor/libgit2/include/git2/common.h | 16 + vendor/libgit2/include/git2/object.h | 8 +- vendor/libgit2/include/git2/oid.h | 54 + vendor/libgit2/include/git2/refs.h | 23 + vendor/libgit2/include/git2/repository.h | 12 +- vendor/libgit2/include/git2/revwalk.h | 8 +- vendor/libgit2/include/git2/tag.h | 15 +- vendor/libgit2/include/git2/tree.h | 3 +- vendor/libgit2/include/git2/types.h | 1 + vendor/libgit2/src/blob.c | 6 +- vendor/libgit2/src/commit.c | 202 +-- vendor/libgit2/src/commit.h | 7 +- vendor/libgit2/src/filebuf.c | 7 +- vendor/libgit2/src/fileops.c | 50 +- vendor/libgit2/src/fileops.h | 1 + vendor/libgit2/src/object.c | 23 +- vendor/libgit2/src/odb_pack.c | 1914 ++++++++++++---------- vendor/libgit2/src/oid.c | 178 ++ vendor/libgit2/src/pqueue.c | 153 ++ vendor/libgit2/src/pqueue.h | 92 ++ vendor/libgit2/src/refs.c | 93 +- vendor/libgit2/src/repository.c | 192 +-- vendor/libgit2/src/repository.h | 14 +- vendor/libgit2/src/revwalk.c | 618 ++++--- vendor/libgit2/src/revwalk.h | 56 - vendor/libgit2/src/tag.c | 36 +- vendor/libgit2/src/tag.h | 3 +- vendor/libgit2/src/tree.c | 22 +- vendor/libgit2/src/util.c | 6 +- vendor/libgit2/src/util.h | 4 + vendor/libgit2/src/vector.c | 2 +- vendor/libgit2/tests/t01-rawobj.c | 92 ++ vendor/libgit2/tests/t04-commit.c | 4 +- vendor/libgit2/tests/t05-revwalk.c | 109 +- vendor/libgit2/tests/t08-tag.c | 4 +- vendor/libgit2/tests/t10-refs.c | 13 + vendor/libgit2/tests/t12-repo.c | 25 +- vendor/libgit2/tests/test_helpers.h | 2 +- vendor/libgit2/wscript | 4 +- 45 files changed, 2511 insertions(+), 1617 deletions(-) create mode 100644 vendor/libgit2/src/pqueue.c create mode 100644 vendor/libgit2/src/pqueue.h diff --git a/.gitignore b/.gitignore index d7fb8a18b..2bd9cb3ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .lock-wscript build/ -vendor/ diff --git a/vendor/libgit2/CMakeLists.txt b/vendor/libgit2/CMakeLists.txt index ed015b61f..d96924f4f 100644 --- a/vendor/libgit2/CMakeLists.txt +++ b/vendor/libgit2/CMakeLists.txt @@ -96,6 +96,7 @@ ELSEIF (SHA1_TYPE STREQUAL "openssl") ENDIF () # Compile and link libgit2 +INCLUDE_DIRECTORIES(src include) ADD_LIBRARY(git2 ${SRC} ${SRC_PLAT} ${SRC_SHA1}) TARGET_LINK_LIBRARIES(git2 ${ZLIB_LIBRARY} ${LIB_SHA1} ${PTHREAD_LIBRARY} ${SQLITE3_LIBRARIES}) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) diff --git a/vendor/libgit2/README.md b/vendor/libgit2/README.md index 1b3c68085..5b27cfdcc 100644 --- a/vendor/libgit2/README.md +++ b/vendor/libgit2/README.md @@ -5,6 +5,11 @@ libgit2 is a portable, pure C implementation of the Git core methods provided as re-entrant linkable library with a solid API, allowing you to write native speed custom Git applications in any language with bindings. +libgit2 is licensed under a **very permissive license** (GPLv2 with a special Linking Exception). +This basically means that you can link it (unmodified) with any kind of software without having to +release its source code. + +* Mailing list: * Website: * API documentation: * Usage guide: @@ -14,13 +19,17 @@ What It Can Do libgit2 is already very usable. -* raw <-> hex SHA conversions -* raw object reading (loose and packed) -* raw object writing (loose) -* revlist walker -* commit, tag and tree object parsing and write-back +* SHA conversions, formatting and shortening +* object reading (loose and packed) +* object writing (loose) +* commit, tag, tree and blob parsing and write-back * tree traversal -* basic index file (staging area) operations +* revision walking +* index file (staging area) manipulation +* custom ODB backends +* reference management (including packed references) +* ...and more + Building libgit2 - External dependencies ======================================== @@ -112,11 +121,14 @@ Language Bindings Here are the bindings to libgit2 that are currently available: * Rugged (Ruby bindings) +* objective-git (Objective-C bindings) * pygit2 (Python bindings) -* libgit2sharp (.NET bindings) -* php-git (PHP bindings) -* luagit2 (Lua bindings) -* GitForDelphi (Delphi bindings) +* libgit2sharp (.NET bindings) +* php-git (PHP bindings) +* luagit2 (Lua bindings) +* GitForDelphi (Delphi bindings) +* node-gitteh (Node.js bindings) +* libqgit2 (C++ QT bindings) * Geef (Erlang bindings) If you start another language binding to libgit2, please let us know so diff --git a/vendor/libgit2/include/git2.h b/vendor/libgit2/include/git2.h index b08c25ed1..248c2ba39 100644 --- a/vendor/libgit2/include/git2.h +++ b/vendor/libgit2/include/git2.h @@ -26,9 +26,9 @@ #ifndef INCLUDE_git_git_h__ #define INCLUDE_git_git_h__ -#define LIBGIT2_VERSION "0.8.0" +#define LIBGIT2_VERSION "0.10.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 8 +#define LIBGIT2_VER_MINOR 10 #define LIBGIT2_VER_REVISION 0 #include "git2/common.h" diff --git a/vendor/libgit2/include/git2/blob.h b/vendor/libgit2/include/git2/blob.h index b527d61f4..2b7154fb5 100644 --- a/vendor/libgit2/include/git2/blob.h +++ b/vendor/libgit2/include/git2/blob.h @@ -102,7 +102,7 @@ GIT_EXTERN(int) git_blob_set_rawcontent(git_blob *blob, const void *buffer, size * @param blob pointer to the blob * @return the pointer; NULL if the blob has no contents */ -GIT_EXTERN(const char *) git_blob_rawcontent(git_blob *blob); +GIT_EXTERN(const void *) git_blob_rawcontent(git_blob *blob); /** * Get the size in bytes of the contents of a blob diff --git a/vendor/libgit2/include/git2/commit.h b/vendor/libgit2/include/git2/commit.h index 21836dbbd..1556e52b1 100644 --- a/vendor/libgit2/include/git2/commit.h +++ b/vendor/libgit2/include/git2/commit.h @@ -122,10 +122,11 @@ GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit); /** * Get the tree pointed to by a commit. + * @param tree_out pointer where to store the tree object * @param commit a previously loaded commit. - * @return the tree of a commit + * @return 0 on success; error code otherwise */ -GIT_EXTERN(const git_tree *) git_commit_tree(git_commit *commit); +GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, git_commit *commit); /** * Get the number of parents of this commit @@ -137,11 +138,13 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit); /** * Get the specified parent of the commit. + * + * @param parent Pointer where to store the parent commit * @param commit a previously loaded commit. - * @param n the position of the entry - * @return a pointer to the commit; NULL if out of bounds + * @param n the position of the parent (from 0 to `parentcount`) + * @return 0 on success; error code otherwise */ -GIT_EXTERN(git_commit *) git_commit_parent(git_commit *commit, unsigned int n); +GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n); /** * Add a new parent commit to an existing commit @@ -176,8 +179,9 @@ GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const git_signature * * Set the tree which is pointed to by a commit * @param commit the commit object * @param tree the new tree + * @param 0 on success; error code otherwise */ -GIT_EXTERN(void) git_commit_set_tree(git_commit *commit, git_tree *tree); +GIT_EXTERN(int) git_commit_set_tree(git_commit *commit, git_tree *tree); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/common.h b/vendor/libgit2/include/git2/common.h index 34efe808b..11a08f897 100644 --- a/vendor/libgit2/include/git2/common.h +++ b/vendor/libgit2/include/git2/common.h @@ -27,6 +27,7 @@ #include "thread-utils.h" #include +#include #ifdef __cplusplus # define GIT_BEGIN_DECL extern "C" { @@ -158,6 +159,21 @@ #define GIT_EINVALIDREFSTATE (GIT_ERROR - 21) GIT_BEGIN_DECL + +typedef struct { + char **strings; + size_t count; +} git_strarray; + +GIT_INLINE(void) git_strarray_free(git_strarray *array) +{ + size_t i; + for (i = 0; i < array->count; ++i) + free(array->strings[i]); + + free(array->strings); +} + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/object.h b/vendor/libgit2/include/git2/object.h index af0f014e3..748386f69 100644 --- a/vendor/libgit2/include/git2/object.h +++ b/vendor/libgit2/include/git2/object.h @@ -131,10 +131,10 @@ GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj); * Close an open object * * This method instructs the library to close an existing - * object; note that git_objects are owned by the repository - * and are reference counted, so the object may or may not be - * freed after this library call, depending on whether any other - * objects still depend on it. + * object; note that git_objects are owned and cached by the repository + * so the object may or may not be freed after this library call, + * depending on how agressive is the caching mechanism used + * by the repository. * * IMPORTANT: * It is *not* necessary to call this method when you stop using diff --git a/vendor/libgit2/include/git2/oid.h b/vendor/libgit2/include/git2/oid.h index 5cac46f3b..4538c6147 100644 --- a/vendor/libgit2/include/git2/oid.h +++ b/vendor/libgit2/include/git2/oid.h @@ -132,6 +132,60 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src); */ GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); +/** + * OID Shortener object + */ +typedef struct git_oid_shorten git_oid_shorten; + +/** + * Create a new OID shortener. + * + * The OID shortener is used to process a list of OIDs + * in text form and return the shortest length that would + * uniquely identify all of them. + * + * E.g. look at the result of `git log --abbrev`. + * + * @param min_length The minimal length for all identifiers, + * which will be used even if shorter OIDs would still + * be unique. + * @return a `git_oid_shorten` instance, NULL if OOM + */ +git_oid_shorten *git_oid_shorten_new(size_t min_length); + +/** + * Add a new OID to set of shortened OIDs and calculate + * the minimal length to uniquely identify all the OIDs in + * the set. + * + * The OID is expected to be a 40-char hexadecimal string. + * The OID is owned by the user and will not be modified + * or freed. + * + * For performance reasons, there is a hard-limit of how many + * OIDs can be added to a single set (around ~22000, assuming + * a mostly randomized distribution), which should be enough + * for any kind of program, and keeps the algorithm fast and + * memory-efficient. + * + * Attempting to add more than those OIDs will result in a + * GIT_ENOMEM error + * + * @param os a `git_oid_shorten` instance + * @param text_oid an OID in text form + * @return the minimal length to uniquely identify all OIDs + * added so far to the set; or an error code (<0) if an + * error occurs. + */ +int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid); + +/** + * Free an OID shortener instance + * + * @param os a `git_oid_shorten` instance + */ +void git_oid_shorten_free(git_oid_shorten *os); + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/refs.h b/vendor/libgit2/include/git2/refs.h index 1702d7ee1..4ffc5ce5b 100644 --- a/vendor/libgit2/include/git2/refs.h +++ b/vendor/libgit2/include/git2/refs.h @@ -218,6 +218,29 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); */ GIT_EXTERN(int) git_reference_packall(git_repository *repo); +/** + * Fill a list with all the references that can be found + * in a repository. + * + * The listed references may be filtered by type, or using + * a bitwise OR of several types. Use the magic value + * `GIT_REF_LISTALL` to obtain all references, including + * packed ones. + * + * The string array will be filled with the names of all + * references; these values are owned by the user and + * should be free'd manually when no longer needed, using + * `git_strarray_free`. + * + * @param array Pointer to a git_strarray structure where + * the reference names will be stored + * @param repo Repository where to find the refs + * @param list_flags Filtering flags for the reference + * listing. + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags); + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/repository.h b/vendor/libgit2/include/git2/repository.h index 5327f8c58..d5f6cf501 100644 --- a/vendor/libgit2/include/git2/repository.h +++ b/vendor/libgit2/include/git2/repository.h @@ -154,12 +154,20 @@ GIT_EXTERN(int) git_repository_index(git_index **index, git_repository *repo); /** * Free a previously allocated repository + * * @param repo repository handle to close. If NULL nothing occurs. */ GIT_EXTERN(void) git_repository_free(git_repository *repo); - -GIT_EXTERN(void) git_repository_free__no_gc(git_repository *repo); +/** + * Force a garbage collector pass on the repository + * + * This will force-free any cached objects that have been + * previously marked by the user as closed (`git_object_close`). + * + * @param repo repository handle to collect. If NULL nothing occurs. + */ +GIT_EXTERN(void) git_repository_close(git_repository *repo); /** * Creates a new Git repository in the given folder. diff --git a/vendor/libgit2/include/git2/revwalk.h b/vendor/libgit2/include/git2/revwalk.h index 841110499..fdbbe236c 100644 --- a/vendor/libgit2/include/git2/revwalk.h +++ b/vendor/libgit2/include/git2/revwalk.h @@ -27,6 +27,7 @@ #include "common.h" #include "types.h" +#include "object.h" /** * @file git2/revwalk.h @@ -88,14 +89,15 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker); * @param walker the walker being used for the traversal. * @param commit the commit to start from. */ -GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, git_commit *commit); +GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid); + /** * Mark a commit (and its ancestors) uninteresting for the output. * @param walker the walker being used for the traversal. * @param commit the commit that will be ignored during the traversal */ -GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, git_commit *commit); +GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid); /** * Get the next commit from the revision traversal. @@ -105,7 +107,7 @@ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, git_commit *commit); * @return GIT_SUCCESS if the next commit was found; * GIT_EREVWALKOVER if there are no commits left to iterate */ -GIT_EXTERN(int) git_revwalk_next(git_commit **commit, git_revwalk *walk); +GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk); /** * Change the sorting mode when iterating through the diff --git a/vendor/libgit2/include/git2/tag.h b/vendor/libgit2/include/git2/tag.h index 2ca25c8a0..f1669eb90 100644 --- a/vendor/libgit2/include/git2/tag.h +++ b/vendor/libgit2/include/git2/tag.h @@ -79,10 +79,18 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag); /** * Get the tagged object of a tag + * @param target pointer where to store the target * @param tag a previously loaded tag. - * @return reference to a repository object + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *t); + +/** + * Get the OID of the tagged object of a tag + * @param tag a previously loaded tag. + * @return pointer to the OID */ -GIT_EXTERN(const git_object *) git_tag_target(git_tag *t); +GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *t); /** * Get the type of a tag's tagged object @@ -117,7 +125,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *t); * @param tag The tag to modify * @param target the new tagged target */ -GIT_EXTERN(void) git_tag_set_target(git_tag *tag, git_object *target); +GIT_EXTERN(int) git_tag_set_target(git_tag *tag, git_object *target); /** * Set the name of a tag @@ -130,6 +138,7 @@ GIT_EXTERN(void) git_tag_set_name(git_tag *tag, const char *name); * Set the tagger of a tag * @param tag The tag to modify * @param tagger_sig signature of the tagging action + * @return 0 on success; error code otherwise */ GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig); diff --git a/vendor/libgit2/include/git2/tree.h b/vendor/libgit2/include/git2/tree.h index 70040f058..3085b3fd6 100644 --- a/vendor/libgit2/include/git2/tree.h +++ b/vendor/libgit2/include/git2/tree.h @@ -214,8 +214,9 @@ GIT_EXTERN(void) git_tree_entry_set_name(git_tree_entry *entry, const char *name * * @param entry Entry object which will be modified * @param oid new attributes for the entry + * @return 0 if the attributes were properly set; error code otherwise */ -GIT_EXTERN(void) git_tree_entry_set_attributes(git_tree_entry *entry, int attr); +GIT_EXTERN(int) git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/types.h b/vendor/libgit2/include/git2/types.h index 62467ec45..b5a8d7b2d 100644 --- a/vendor/libgit2/include/git2/types.h +++ b/vendor/libgit2/include/git2/types.h @@ -145,6 +145,7 @@ typedef enum { GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */ GIT_REF_PACKED = 4, GIT_REF_HAS_PEEL = 8, + GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED, } git_rtype; /** @} */ diff --git a/vendor/libgit2/src/blob.c b/vendor/libgit2/src/blob.c index f271cc7f6..1e03b6b67 100644 --- a/vendor/libgit2/src/blob.c +++ b/vendor/libgit2/src/blob.c @@ -30,7 +30,7 @@ #include "common.h" #include "blob.h" -const char *git_blob_rawcontent(git_blob *blob) +const void *git_blob_rawcontent(git_blob *blob) { assert(blob); @@ -130,9 +130,7 @@ int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *pa git_oid_cpy(written_id, git_object_id((git_object *)blob)); - /* FIXME: maybe we don't want to free this already? - * the user may want to access it again */ - git_object_close((git_object *)blob); + git_object_close((git_object*)blob); return GIT_SUCCESS; } diff --git a/vendor/libgit2/src/commit.c b/vendor/libgit2/src/commit.c index dff3f1e34..1c5cddf7a 100644 --- a/vendor/libgit2/src/commit.c +++ b/vendor/libgit2/src/commit.c @@ -46,24 +46,22 @@ static void clear_parents(git_commit *commit) { unsigned int i; - for (i = 0; i < commit->parents.length; ++i) { - git_commit *parent = git_vector_get(&commit->parents, i); - git_object_close((git_object *)parent); + for (i = 0; i < commit->parent_oids.length; ++i) { + git_oid *parent = git_vector_get(&commit->parent_oids, i); + free(parent); } - git_vector_clear(&commit->parents); + git_vector_clear(&commit->parent_oids); } void git_commit__free(git_commit *commit) { clear_parents(commit); - git_vector_free(&commit->parents); + git_vector_free(&commit->parent_oids); git_signature_free(commit->author); git_signature_free(commit->committer); - git_object_close((git_object *)commit->tree); - free(commit->message); free(commit->message_short); free(commit); @@ -78,16 +76,13 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src) { unsigned int i; - if (commit->tree == NULL) - return GIT_EMISSINGOBJDATA; - - git__write_oid(src, "tree", git_tree_id(commit->tree)); + git__write_oid(src, "tree", &commit->tree_oid); - for (i = 0; i < commit->parents.length; ++i) { - git_commit *parent; + for (i = 0; i < commit->parent_oids.length; ++i) { + git_oid *parent_oid; - parent = git_vector_get(&commit->parents, i); - git__write_oid(src, "parent", git_commit_id(parent)); + parent_oid = git_vector_get(&commit->parent_oids, i); + git__write_oid(src, "parent", parent_oid); } if (commit->author == NULL) @@ -103,66 +98,49 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src) if (commit->message != NULL) { git__source_write(src, "\n", 1); git__source_write(src, commit->message, strlen(commit->message)); - } - - /* Mark the commit as having all attributes */ - commit->full_parse = 1; + } return GIT_SUCCESS; } -int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags) +int commit_parse_buffer(git_commit *commit, void *data, size_t len) { char *buffer = (char *)data; const char *buffer_end = (char *)data + len; - git_oid oid; + git_oid parent_oid; int error; /* first parse; the vector hasn't been initialized yet */ - if (commit->parents.contents == NULL) { - git_vector_init(&commit->parents, 4, NULL); + if (commit->parent_oids.contents == NULL) { + git_vector_init(&commit->parent_oids, 4, NULL); } clear_parents(commit); - - if ((error = git__parse_oid(&oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) - return error; - - git_object_close((git_object *)commit->tree); - if ((error = git_object_lookup((git_object **)&commit->tree, commit->object.repo, &oid, GIT_OBJ_TREE)) < GIT_SUCCESS) + if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) return error; /* * TODO: commit grafts! */ - while (git__parse_oid(&oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) { - git_commit *parent; + while (git__parse_oid(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) { + git_oid *new_oid; - if ((error = git_object_lookup((git_object **)&parent, commit->object.repo, &oid, GIT_OBJ_COMMIT)) < GIT_SUCCESS) - return error; + new_oid = git__malloc(sizeof(git_oid)); + git_oid_cpy(new_oid, &parent_oid); - if (git_vector_insert(&commit->parents, parent) < GIT_SUCCESS) + if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS) return GIT_ENOMEM; } + if (commit->author) + git_signature_free(commit->author); - if (parse_flags & COMMIT_FULL_PARSE) { - if (commit->author) - git_signature_free(commit->author); - - commit->author = git__malloc(sizeof(git_signature)); - if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS) - return error; - - } else { - if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) - return GIT_EOBJCORRUPTED; - - buffer++; - } + commit->author = git__malloc(sizeof(git_signature)); + if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS) + return error; /* Always parse the committer; we need the commit time */ if (commit->committer) @@ -176,7 +154,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int while (buffer <= buffer_end && *buffer == '\n') buffer++; - if (parse_flags & COMMIT_FULL_PARSE && buffer < buffer_end) { + if (buffer < buffer_end) { const char *line_end; size_t message_len = buffer_end - buffer; @@ -203,106 +181,81 @@ int git_commit__parse(git_commit *commit) { assert(commit && commit->object.source.open); return commit_parse_buffer(commit, - commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_BASIC_PARSE); + commit->object.source.raw.data, commit->object.source.raw.len); } -int git_commit__parse_full(git_commit *commit) -{ - int error; - - if (commit->full_parse) - return GIT_SUCCESS; - - if ((error = git_object__source_open((git_object *)commit)) < GIT_SUCCESS) - return error; - - error = commit_parse_buffer(commit, - commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_FULL_PARSE); - - git_object__source_close((git_object *)commit); - - commit->full_parse = 1; - return error; -} - - - -#define GIT_COMMIT_GETTER(_rvalue, _name) \ - const _rvalue git_commit_##_name(git_commit *commit) \ +#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ + _rvalue git_commit_##_name(git_commit *commit) \ {\ assert(commit); \ - if (commit->_name) \ - return commit->_name; \ - if (!commit->object.in_memory) \ - git_commit__parse_full(commit); \ - return commit->_name; \ + return _return; \ } -#define CHECK_FULL_PARSE() \ - if (!commit->object.in_memory && !commit->full_parse)\ - git_commit__parse_full(commit); +GIT_COMMIT_GETTER(const git_signature *, author, commit->author) +GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer) +GIT_COMMIT_GETTER(const char *, message, commit->message) +GIT_COMMIT_GETTER(const char *, message_short, commit->message_short) +GIT_COMMIT_GETTER(time_t, time, commit->committer->when.time) +GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) +GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length) + + +int git_commit_tree(git_tree **tree_out, git_commit *commit) +{ + assert(commit); + return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid); +} -const git_tree *git_commit_tree(git_commit *commit) +int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) { + git_oid *parent_oid; assert(commit); - if (!commit->object.in_memory && commit->tree == NULL) - git_commit__parse_full(commit); + parent_oid = git_vector_get(&commit->parent_oids, n); + if (parent_oid == NULL) + return GIT_ENOTFOUND; - GIT_OBJECT_INCREF(commit->tree); - return commit->tree; + return git_commit_lookup(parent, commit->object.repo, parent_oid); } -GIT_COMMIT_GETTER(git_signature *, author) -GIT_COMMIT_GETTER(git_signature *, committer) -GIT_COMMIT_GETTER(char *, message) -GIT_COMMIT_GETTER(char *, message_short) -time_t git_commit_time(git_commit *commit) -{ - assert(commit && commit->committer); - return commit->committer->when.time; -} -int git_commit_time_offset(git_commit *commit) +int git_commit_set_tree(git_commit *commit, git_tree *tree) { - assert(commit && commit->committer); - return commit->committer->when.offset; -} + const git_oid *oid; -unsigned int git_commit_parentcount(git_commit *commit) -{ - assert(commit); - return commit->parents.length; + assert(commit && tree); + + if ((oid = git_object_id((git_object *)tree)) == NULL) + return GIT_EMISSINGOBJDATA; + + commit->object.modified = 1; + git_oid_cpy(&commit->tree_oid, oid); + return GIT_SUCCESS; } -git_commit *git_commit_parent(git_commit *commit, unsigned int n) +int git_commit_add_parent(git_commit *commit, git_commit *new_parent) { - git_commit *parent; + const git_oid *parent_oid; + git_oid *new_oid; + assert(commit && new_parent); - assert(commit); + if ((parent_oid = git_object_id((git_object *)new_parent)) == NULL) + return GIT_EMISSINGOBJDATA; - parent = git_vector_get(&commit->parents, n); - GIT_OBJECT_INCREF(parent); - return parent; -} + new_oid = git__malloc(sizeof(git_oid)); + if (new_oid == NULL) + return GIT_ENOMEM; -void git_commit_set_tree(git_commit *commit, git_tree *tree) -{ - assert(commit && tree); commit->object.modified = 1; - CHECK_FULL_PARSE(); - - git_object_close((git_object *)commit->tree); - GIT_OBJECT_INCREF(tree); - commit->tree = tree; + git_oid_cpy(new_oid, parent_oid); + return git_vector_insert(&commit->parent_oids, new_oid); } void git_commit_set_author(git_commit *commit, const git_signature *author_sig) { assert(commit && author_sig); commit->object.modified = 1; - CHECK_FULL_PARSE(); git_signature_free(commit->author); commit->author = git_signature_dup(author_sig); @@ -312,7 +265,6 @@ void git_commit_set_committer(git_commit *commit, const git_signature *committer { assert(commit && committer_sig); commit->object.modified = 1; - CHECK_FULL_PARSE(); git_signature_free(commit->committer); commit->committer = git_signature_dup(committer_sig); @@ -324,7 +276,6 @@ void git_commit_set_message(git_commit *commit, const char *message) size_t message_len; commit->object.modified = 1; - CHECK_FULL_PARSE(); if (commit->message) free(commit->message); @@ -347,12 +298,3 @@ void git_commit_set_message(git_commit *commit, const char *message) commit->message_short[message_len] = 0; } -int git_commit_add_parent(git_commit *commit, git_commit *new_parent) -{ - assert(commit && new_parent); - - CHECK_FULL_PARSE(); - commit->object.modified = 1; - GIT_OBJECT_INCREF(new_parent); - return git_vector_insert(&commit->parents, new_parent); -} diff --git a/vendor/libgit2/src/commit.h b/vendor/libgit2/src/commit.h index b53ee9b23..aaf349ca6 100644 --- a/vendor/libgit2/src/commit.h +++ b/vendor/libgit2/src/commit.h @@ -11,21 +11,18 @@ struct git_commit { git_object object; - git_vector parents; + git_vector parent_oids; + git_oid tree_oid; - git_tree *tree; git_signature *author; git_signature *committer; char *message; char *message_short; - - unsigned full_parse:1; }; void git_commit__free(git_commit *c); int git_commit__parse(git_commit *commit); -int git_commit__parse_full(git_commit *commit); int git_commit__writeback(git_commit *commit, git_odb_source *src); diff --git a/vendor/libgit2/src/filebuf.c b/vendor/libgit2/src/filebuf.c index 73f0a70f4..4fc4f1486 100644 --- a/vendor/libgit2/src/filebuf.c +++ b/vendor/libgit2/src/filebuf.c @@ -39,7 +39,12 @@ static int lock_file(git_filebuf *file, int flags) return GIT_EOSERR; } - file->fd = gitfo_creat(file->path_lock, 0644); + /* create path to the file buffer is required */ + if (flags & GIT_FILEBUF_FORCE) { + file->fd = gitfo_creat_force(file->path_lock, 0644); + } else { + file->fd = gitfo_creat(file->path_lock, 0644); + } if (file->fd < 0) return GIT_EOSERR; diff --git a/vendor/libgit2/src/fileops.c b/vendor/libgit2/src/fileops.c index 7691129f6..76e689e8a 100644 --- a/vendor/libgit2/src/fileops.c +++ b/vendor/libgit2/src/fileops.c @@ -2,6 +2,29 @@ #include "fileops.h" #include +static int force_path(const char *to) +{ + const int mode = 0755; /* or 0777 ? */ + int error = GIT_SUCCESS; + char target_folder_path[GIT_PATH_MAX]; + + error = git__dirname_r(target_folder_path, sizeof(target_folder_path), to); + if (error < GIT_SUCCESS) + return error; + + /* Does the containing folder exist? */ + if (gitfo_isdir(target_folder_path)) { + git__joinpath(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ + + /* Let's create the tree structure */ + error = gitfo_mkdir_recurs(target_folder_path, mode); + if (error < GIT_SUCCESS) + return error; + } + + return GIT_SUCCESS; +} + int gitfo_open(const char *path, int flags) { int fd = open(path, flags | O_BINARY); @@ -14,6 +37,14 @@ int gitfo_creat(const char *path, int mode) return fd >= 0 ? fd : GIT_EOSERR; } +int gitfo_creat_force(const char *path, int mode) +{ + if (force_path(path) < GIT_SUCCESS) + return GIT_EOSERR; + + return gitfo_creat(path, mode); +} + int gitfo_read(git_file fd, void *buf, size_t cnt) { char *b = buf; @@ -167,23 +198,8 @@ int gitfo_mv(const char *from, const char *to) int gitfo_mv_force(const char *from, const char *to) { - const int mode = 0755; /* or 0777 ? */ - int error = GIT_SUCCESS; - char target_folder_path[GIT_PATH_MAX]; - - error = git__dirname_r(target_folder_path, sizeof(target_folder_path), to); - if (error < GIT_SUCCESS) - return error; - - /* Does the containing folder exist? */ - if (gitfo_isdir(target_folder_path)) { - git__joinpath(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ - - /* Let's create the tree structure */ - error = gitfo_mkdir_recurs(target_folder_path, mode); - if (error < GIT_SUCCESS) - return error; - } + if (force_path(to) < GIT_SUCCESS) + return GIT_EOSERR; return gitfo_mv(from, to); } diff --git a/vendor/libgit2/src/fileops.h b/vendor/libgit2/src/fileops.h index fd150df5e..5aa302b54 100644 --- a/vendor/libgit2/src/fileops.h +++ b/vendor/libgit2/src/fileops.h @@ -57,6 +57,7 @@ typedef struct { /* file io buffer */ extern int gitfo_exists(const char *path); extern int gitfo_open(const char *path, int flags); extern int gitfo_creat(const char *path, int mode); +extern int gitfo_creat_force(const char *path, int mode); extern int gitfo_isdir(const char *path); extern int gitfo_mkdir_recurs(const char *path, int mode); #define gitfo_close(fd) close(fd) diff --git a/vendor/libgit2/src/object.c b/vendor/libgit2/src/object.c index de02ef5f2..fce99153b 100644 --- a/vendor/libgit2/src/object.c +++ b/vendor/libgit2/src/object.c @@ -261,7 +261,7 @@ int git_object_new(git_object **object_out, git_repository *repo, git_otype type object->source.raw.type = type; - object->refcount++; + object->lru = ++repo->lru_counter; *object_out = object; return GIT_SUCCESS; } @@ -277,7 +277,8 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o object = git_hashtable_lookup(repo->objects, id); if (object != NULL) { *object_out = object; - GIT_OBJECT_INCREF(object); + object->lru = ++repo->lru_counter; + object->can_free = 0; return GIT_SUCCESS; } @@ -330,7 +331,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o git_object__source_close(object); git_hashtable_insert(repo->objects, &object->id, object); - GIT_OBJECT_INCREF(object); + object->lru = ++repo->lru_counter; *object_out = object; return GIT_SUCCESS; } @@ -384,15 +385,6 @@ void git_object__free(git_object *object) git_object__source_close(object); - if (object->repo != NULL) { - if (object->in_memory) { - int idx = git_vector_search(&object->repo->memory_objects, object); - git_vector_remove(&object->repo->memory_objects, idx); - } else { - git_hashtable_remove(object->repo->objects, &object->id); - } - } - switch (object->source.raw.type) { case GIT_OBJ_COMMIT: git_commit__free((git_commit *)object); @@ -421,8 +413,13 @@ void git_object_close(git_object *object) if (object == NULL) return; - if (--object->refcount <= 0) + if (object->in_memory) { + int idx = git_vector_search(&object->repo->memory_objects, object); + git_vector_remove(&object->repo->memory_objects, idx); git_object__free(object); + } else { + object->can_free = 1; + } } const git_oid *git_object_id(const git_object *obj) diff --git a/vendor/libgit2/src/odb_pack.c b/vendor/libgit2/src/odb_pack.c index 664b00139..3067179be 100644 --- a/vendor/libgit2/src/odb_pack.c +++ b/vendor/libgit2/src/odb_pack.c @@ -33,503 +33,625 @@ #include "git2/odb_backend.h" -/** First 4 bytes of a pack-*.idx file header. - * - * Note this header exists only in idx v2 and later. The idx v1 - * file format does not have a magic sequence at the front, and - * must be detected by the first four bytes *not* being this value - * and the first 8 bytes matching the following expression: +#define DEFAULT_WINDOW_SIZE \ + (sizeof(void*) >= 8 \ + ? 1 * 1024 * 1024 * 1024 \ + : 32 * 1024 * 1024) + +#define DEFAULT_MAPPED_LIMIT \ + ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) + +#define PACK_SIGNATURE 0x5041434b /* "PACK" */ +#define PACK_VERSION 2 +#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) +struct pack_header { + uint32_t hdr_signature; + uint32_t hdr_version; + uint32_t hdr_entries; +}; + +/* + * The first four bytes of index formats later than version 1 should + * start with this signature, as all older git binaries would find this + * value illegal and abort reading the file. * - * uint32_t *fanout = ... the file data at offset 0 ... - * ntohl(fanout[0]) < ntohl(fanout[1]) + * This is the case because the number of objects in a packfile + * cannot exceed 1,431,660,000 as every object would need at least + * 3 bytes of data and the overall packfile cannot exceed 4 GiB with + * version 1 of the index file due to the offsets limited to 32 bits. + * Clearly the signature exceeds this maximum. * - * The value chosen here for PACK_TOC is such that the above - * cannot be true for an idx v1 file. + * Very old git binaries will also compare the first 4 bytes to the + * next 4 bytes in the index and abort with a "non-monotonic index" + * error if the second 4 byte word is smaller than the first 4 + * byte word. This would be true in the proposed future index + * format as idx_signature would be greater than idx_version. */ -#define PACK_TOC 0xff744f63 /* -1tOc */ +#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ -/** First 4 bytes of a pack-*.pack file header. */ -#define PACK_SIG 0x5041434b /* PACK */ +struct pack_idx_header { + uint32_t idx_signature; + uint32_t idx_version; +}; -#define GIT_PACK_NAME_MAX (5 + 40 + 1) +struct pack_window { + struct pack_window *next; + git_map window_map; + off_t offset; + unsigned int last_used; + unsigned int inuse_cnt; +}; -struct pack_backend; +struct pack_file { + struct pack_window *windows; + off_t pack_size; -typedef struct { - uint32_t n; - unsigned char *oid; - git_off_t offset; - git_off_t size; -} index_entry; + git_map index_map; -typedef struct { /* '.pack' file header */ - uint32_t sig; /* PACK_SIG */ - uint32_t ver; /* pack version */ - uint32_t cnt; /* object count */ -} pack_hdr; + uint32_t num_objects; + uint32_t num_bad_objects; + git_oid *bad_object_sha1; /* array of git_oid */ -typedef struct git_pack { - struct pack_backend *backend; - git_lck lock; - - /** Functions to access idx_map. */ - int (*idx_search)( - uint32_t *, - struct git_pack *, - const git_oid *); - int (*idx_search_offset)( - uint32_t *, - struct git_pack *, - git_off_t); - int (*idx_get)( - index_entry *, - struct git_pack *, - uint32_t n); - - /** The .idx file, mapped into memory. */ - git_file idx_fd; - git_map idx_map; - uint32_t *im_fanout; - unsigned char *im_oid; - uint32_t *im_crc; - uint32_t *im_offset32; - uint32_t *im_offset64; - uint32_t *im_off_idx; - uint32_t *im_off_next; - - /** Number of objects in this pack. */ - uint32_t obj_cnt; - - /** File descriptor for the .pack file. */ - git_file pack_fd; - - /** Memory map of the pack's contents */ - git_map pack_map; - - /** The size of the .pack file. */ - git_off_t pack_size; - - /** The mtime of the .pack file. */ - time_t pack_mtime; - - /** Number of git_packlist we appear in. */ - unsigned int refcnt; - - /** Number of active users of the idx_map data. */ - unsigned int idxcnt; - unsigned - invalid:1 /* the pack is unable to be read by libgit2 */ - ; - - /** Name of the pack file(s), without extension ("pack-abc"). */ - char pack_name[GIT_PACK_NAME_MAX]; -} git_pack; - -typedef struct { - size_t n_packs; - unsigned int refcnt; - git_pack *packs[GIT_FLEX_ARRAY]; -} git_packlist; - -typedef struct pack_backend { - git_odb_backend parent; + int index_version; + time_t mtime; + int pack_fd; + unsigned pack_local:1, pack_keep:1; + git_oid sha1; - git_lck lock; - char *objects_dir; - git_packlist *packlist; -} pack_backend; + /* something like ".git/objects/pack/xxxxx.pack" */ + char pack_name[GIT_FLEX_ARRAY]; /* more */ +}; +struct pack_entry { + off_t offset; + git_oid sha1; + struct pack_file *p; +}; -typedef struct pack_location { - git_pack *ptr; - uint32_t n; -} pack_location; +struct pack__dirent { + struct pack_backend *backend; + int is_pack_local; +}; -static int pack_stat(git_pack *p); -static int pack_openidx(git_pack *p); -static void pack_decidx(git_pack *p); -static int read_pack_hdr(pack_hdr *out, git_file fd); -static int check_pack_hdr(git_pack *p); -static int check_pack_sha1(git_pack *p); -static int open_pack(git_pack *p); +struct pack_backend { + git_odb_backend parent; + git_vector packs; + struct pack_file *last_found; + size_t window_size; /* needs default value */ -static int pack_openidx_map(git_pack *p); -static int pack_openidx_v1(git_pack *p); -static int pack_openidx_v2(git_pack *p); + size_t mapped_limit; /* needs default value */ + size_t peak_mapped; + size_t mapped; + size_t used_ctr; -GIT_INLINE(uint32_t) decode32(void *b) -{ - return ntohl(*((uint32_t *)b)); -} + unsigned int peak_open_windows; + unsigned int open_windows; -GIT_INLINE(uint64_t) decode64(void *b) -{ - uint32_t *p = b; - return (((uint64_t)ntohl(p[0])) << 32) | ntohl(p[1]); -} + unsigned int mmap_calls; +}; + +/** + * The wonderful tale of a Packed Object lookup query + * =================================================== + * A riveting and epic story of epicness and ASCII + * art, presented by yours truly, + * Sir Vicent of Marti + * + * + * Chapter 1: Once upon a time... + * Initialization of the Pack Backend + * -------------------------------------------------- + * + * # git_odb_backend_pack + * | Creates the pack backend structure, initializes the + * | callback pointers to our default read() and exist() methods, + * | and tries to preload all the known packfiles in the ODB. + * | + * |-# packfile_load_all + * | Tries to find the `pack` folder, if it exists. ODBs without + * | a pack folder are ignored altogether. If there's a `pack` folder + * | we run a `dirent` callback through every file in the pack folder + * | to find our packfiles. The packfiles are then sorted according + * | to a sorting callback. + * | + * |-# packfile_load__cb + * | | This callback is called from `dirent` with every single file + * | | inside the pack folder. We find the packs by actually locating + * | | their index (ends in ".idx"). From that index, we verify that + * | | the corresponding packfile exists and is valid, and if so, we + * | | add it to the pack list. + * | | + * | |-# packfile_check + * | Make sure that there's a packfile to back this index, and store + * | some very basic information regarding the packfile itself, + * | such as the full path, the size, and the modification time. + * | We don't actually open the packfile to check for internal consistency. + * | + * |-# packfile_sort__cb + * Sort all the preloaded packs according to some specific criteria: + * we prioritize the "newer" packs because it's more likely they + * contain the objects we are looking for, and we prioritize local + * packs over remote ones. + * + * + * + * Chapter 2: To be, or not to be... + * A standard packed `exist` query for an OID + * -------------------------------------------------- + * + * # pack_backend__exists + * | Check if the given SHA1 oid exists in any of the packs + * | that have been loaded for our ODB. + * | + * |-# pack_entry_find + * | Iterate through all the packs that have been preloaded + * | (starting by the pack where the latest object was found) + * | to try to find the OID in one of them. + * | + * |-# pack_entry_find1 + * | Check the index of an individual pack to see if the SHA1 + * | OID can be found. If we can find the offset to that SHA1 + * | inside of the index, that means the object is contained + * | inside of the packfile and we can stop searching. + * | Before returning, we verify that the packfile behing the + * | index we are searching still exists on disk. + * | + * |-# pack_entry_find_offset + * | | Mmap the actual index file to disk if it hasn't been opened + * | | yet, and run a binary search through it to find the OID. + * | | See for specifics + * | | on the Packfile Index format and how do we find entries in it. + * | | + * | |-# pack_index_open + * | | Guess the name of the index based on the full path to the + * | | packfile, open it and verify its contents. Only if the index + * | | has not been opened already. + * | | + * | |-# pack_index_check + * | Mmap the index file and do a quick run through the header + * | to guess the index version (right now we support v1 and v2), + * | and to verify that the size of the index makes sense. + * | + * |-# packfile_open + * See `packfile_open` in Chapter 3 + * + * + * + * Chapter 3: The neverending story... + * A standard packed `lookup` query for an OID + * -------------------------------------------------- + * TODO + * + */ + /*********************************************************** * - * PACKFILE FUNCTIONS - * - * Locate, open and access the contents of a packfile + * FORWARD DECLARATIONS * ***********************************************************/ -static int pack_stat(git_pack *p) -{ - char pb[GIT_PATH_MAX]; - struct stat sb; +static void pack_window_free_all(struct pack_backend *backend, struct pack_file *p); +static int pack_window_contains(struct pack_window *win, off_t offset); - if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack", - p->backend->objects_dir, - p->pack_name) < 0) - return GIT_ERROR; +static void pack_window_scan_lru(struct pack_file *p, struct pack_file **lru_p, + struct pack_window **lru_w, struct pack_window **lru_l); - if (gitfo_stat(pb, &sb) || !S_ISREG(sb.st_mode)) - return GIT_ERROR; +static int pack_window_close_lru( struct pack_backend *backend, + struct pack_file *current, git_file keep_fd); - if (sb.st_size < (3 * 4 + GIT_OID_RAWSZ)) - return GIT_ERROR; +static void pack_window_close(struct pack_window **w_cursor); - p->pack_size = sb.st_size; - p->pack_mtime = sb.st_mtime; +static unsigned char *pack_window_open( struct pack_backend *backend, + struct pack_file *p, struct pack_window **w_cursor, off_t offset, + unsigned int *left); - return GIT_SUCCESS; -} +static int packfile_sort__cb(const void *a_, const void *b_); -static int pack_openidx(git_pack *p) -{ - gitlck_lock(&p->lock); +static void pack_index_free(struct pack_file *p); - if (p->invalid) { - gitlck_unlock(&p->lock); - return GIT_ERROR; - } +static int pack_index_check(const char *path, struct pack_file *p); +static int pack_index_open(struct pack_file *p); - if (++p->idxcnt == 1 && !p->idx_search) { - int status, version; - uint32_t *data; +static struct pack_file *packfile_alloc(int extra); +static int packfile_open(struct pack_file *p); +static int packfile_check(struct pack_file **pack_out, const char *path, int local); +static int packfile_load__cb(void *_data, char *path); +static int packfile_load_all(struct pack_backend *backend, const char *odb_path, int local); - if (pack_stat(p) || pack_openidx_map(p)) { - p->invalid = 1; - p->idxcnt--; - gitlck_unlock(&p->lock); - return GIT_ERROR; - } - data = p->idx_map.data; - status = GIT_SUCCESS; - version = 1; +static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n); - if (decode32(&data[0]) == PACK_TOC) - version = decode32(&data[1]); - - switch (version) { - case 1: - status = pack_openidx_v1(p); - break; - case 2: - status = pack_openidx_v2(p); - break; - default: - status = GIT_ERROR; - } +static int pack_entry_find_offset(off_t *offset_out, + struct pack_file *p, const git_oid *oid); - if (status != GIT_SUCCESS) { - gitfo_free_map(&p->idx_map); - p->invalid = 1; - p->idxcnt--; - gitlck_unlock(&p->lock); - return status; - } - } +static int pack_entry_find1(struct pack_entry *e, + struct pack_file *p, const git_oid *oid); - gitlck_unlock(&p->lock); - return GIT_SUCCESS; -} +static int pack_entry_find(struct pack_entry *e, + struct pack_backend *backend, const git_oid *oid); -static void pack_decidx(git_pack *p) -{ - gitlck_lock(&p->lock); - p->idxcnt--; - gitlck_unlock(&p->lock); -} +static off_t get_delta_base(struct pack_backend *backend, + struct pack_file *p, struct pack_window **w_curs, + off_t *curpos, git_otype type, + off_t delta_obj_offset); -static int read_pack_hdr(pack_hdr *out, git_file fd) -{ - pack_hdr hdr; +static unsigned long packfile_unpack_header1( + size_t *sizep, + git_otype *type, + const unsigned char *buf, + unsigned long len); - if (gitfo_read(fd, &hdr, sizeof(hdr))) - return GIT_ERROR; +static int packfile_unpack_header( + size_t *size_p, + git_otype *type_p, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t *curpos); - out->sig = decode32(&hdr.sig); - out->ver = decode32(&hdr.ver); - out->cnt = decode32(&hdr.cnt); +static int packfile_unpack_compressed( + git_rawobj *obj, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t curpos, + size_t size, + git_otype type); - return GIT_SUCCESS; -} +static int packfile_unpack_delta( + git_rawobj *obj, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t curpos, + size_t delta_size, + git_otype delta_type, + off_t obj_offset); + +static int packfile_unpack(git_rawobj *obj, struct pack_backend *backend, + struct pack_file *p, off_t obj_offset); + + + + + +/*********************************************************** + * + * PACK WINDOW MANAGEMENT + * + ***********************************************************/ -static int check_pack_hdr(git_pack *p) +void pack_window_free_all(struct pack_backend *backend, struct pack_file *p) { - pack_hdr hdr; + while (p->windows) { + struct pack_window *w = p->windows; + assert(w->inuse_cnt == 0); - if (read_pack_hdr(&hdr, p->pack_fd)) - return GIT_ERROR; + backend->mapped -= w->window_map.len; + backend->open_windows--; - if (hdr.sig != PACK_SIG - || (hdr.ver != 2 && hdr.ver != 3) - || hdr.cnt != p->obj_cnt) - return GIT_ERROR; + gitfo_free_map(&w->window_map); - return GIT_SUCCESS; + p->windows = w->next; + free(w); + } } -static int check_pack_sha1(git_pack *p) +GIT_INLINE(int) pack_window_contains(struct pack_window *win, off_t offset) { - unsigned char *data = p->idx_map.data; - git_off_t pack_sha1_off = p->pack_size - GIT_OID_RAWSZ; - size_t idx_pack_sha1_off = p->idx_map.len - 2 * GIT_OID_RAWSZ; - git_oid pack_id, idx_pack_id; + /* We must promise at least 20 bytes (one hash) after the + * offset is available from this window, otherwise the offset + * is not actually in this window and a different window (which + * has that one hash excess) must be used. This is to support + * the object header and delta base parsing routines below. + */ + off_t win_off = win->offset; + return win_off <= offset + && (offset + 20) <= (off_t)(win_off + win->window_map.len); +} - if (gitfo_lseek(p->pack_fd, pack_sha1_off, SEEK_SET) == -1) - return GIT_ERROR; +static void pack_window_scan_lru( + struct pack_file *p, + struct pack_file **lru_p, + struct pack_window **lru_w, + struct pack_window **lru_l) +{ + struct pack_window *w, *w_l; + + for (w_l = NULL, w = p->windows; w; w = w->next) { + if (!w->inuse_cnt) { + if (!*lru_w || w->last_used < (*lru_w)->last_used) { + *lru_p = p; + *lru_w = w; + *lru_l = w_l; + } + } + w_l = w; + } +} - if (gitfo_read(p->pack_fd, pack_id.id, sizeof(pack_id.id))) - return GIT_ERROR; +static int pack_window_close_lru( + struct pack_backend *backend, + struct pack_file *current, + git_file keep_fd) +{ + struct pack_file *lru_p = NULL; + struct pack_window *lru_w = NULL, *lru_l = NULL; + size_t i; + + if (current) + pack_window_scan_lru(current, &lru_p, &lru_w, &lru_l); + + for (i = 0; i < backend->packs.length; ++i) + pack_window_scan_lru(git_vector_get(&backend->packs, i), &lru_p, &lru_w, &lru_l); + + if (lru_p) { + backend->mapped -= lru_w->window_map.len; + gitfo_free_map(&lru_w->window_map); + + if (lru_l) + lru_l->next = lru_w->next; + else { + lru_p->windows = lru_w->next; + if (!lru_p->windows && lru_p->pack_fd != keep_fd) { + gitfo_close(lru_p->pack_fd); + lru_p->pack_fd = -1; + } + } - git_oid_mkraw(&idx_pack_id, data + idx_pack_sha1_off); + free(lru_w); + backend->open_windows--; + return GIT_SUCCESS; + } - if (git_oid_cmp(&pack_id, &idx_pack_id)) - return GIT_ERROR; + return GIT_ERROR; +} - return GIT_SUCCESS; +static void pack_window_close(struct pack_window **w_cursor) +{ + struct pack_window *w = *w_cursor; + if (w) { + w->inuse_cnt--; + *w_cursor = NULL; + } } -static int open_pack(git_pack *p) +static unsigned char *pack_window_open( + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_cursor, + off_t offset, + unsigned int *left) { - char pb[GIT_PATH_MAX]; - struct stat sb; + struct pack_window *win = *w_cursor; - if (p->pack_fd != -1) - return GIT_SUCCESS; + if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS) + return NULL; - if (git__fmt(pb, sizeof(pb), "%s/pack/%s.pack", - p->backend->objects_dir, - p->pack_name) < 0) - return GIT_ERROR; + /* Since packfiles end in a hash of their content and it's + * pointless to ask for an offset into the middle of that + * hash, and the pack_window_contains function above wouldn't match + * don't allow an offset too close to the end of the file. + */ + if (offset > (p->pack_size - 20)) + return NULL; - if (pack_openidx(p)) - return GIT_ERROR; + if (!win || !pack_window_contains(win, offset)) { - if ((p->pack_fd = gitfo_open(pb, O_RDONLY)) < 0) - goto error_cleanup; + if (win) + win->inuse_cnt--; - if (gitfo_fstat(p->pack_fd, &sb) - || !S_ISREG(sb.st_mode) || p->pack_size != sb.st_size - || check_pack_hdr(p) || check_pack_sha1(p)) - goto error_cleanup; + for (win = p->windows; win; win = win->next) { + if (pack_window_contains(win, offset)) + break; + } - if (!git__is_sizet(p->pack_size) || - gitfo_map_ro(&p->pack_map, p->pack_fd, 0, (size_t)p->pack_size) < 0) - goto error_cleanup; + if (!win) { + size_t window_align = backend->window_size / 2; + size_t len; - pack_decidx(p); - return GIT_SUCCESS; + win = git__calloc(1, sizeof(*win)); + win->offset = (offset / window_align) * window_align; -error_cleanup: - gitfo_close(p->pack_fd); - p->pack_fd = -1; - pack_decidx(p); - return GIT_ERROR; -} + len = (size_t)(p->pack_size - win->offset); + if (len > backend->window_size) + len = backend->window_size; -static void pack_dec(git_pack *p) -{ - int need_free; - - gitlck_lock(&p->lock); - need_free = !--p->refcnt; - gitlck_unlock(&p->lock); - - if (need_free) { - if (p->idx_search) { - gitfo_free_map(&p->idx_map); - gitfo_close(p->idx_fd); - free(p->im_fanout); - free(p->im_off_idx); - free(p->im_off_next); - if (p->pack_fd != -1) { - gitfo_close(p->pack_fd); - gitfo_free_map(&p->pack_map); - } + backend->mapped += len; + + while (backend->mapped_limit < backend->mapped && + pack_window_close_lru(backend, p, p->pack_fd) == GIT_SUCCESS) {} + + if (gitfo_map_ro(&win->window_map, p->pack_fd, + win->offset, len) < GIT_SUCCESS) + return NULL; + + backend->mmap_calls++; + backend->open_windows++; + + if (backend->mapped > backend->peak_mapped) + backend->peak_mapped = backend->mapped; + + if (backend->open_windows > backend->peak_open_windows) + backend->peak_open_windows = backend->open_windows; + + win->next = p->windows; + p->windows = win; } + } - gitlck_free(&p->lock); - free(p); + if (win != *w_cursor) { + win->last_used = backend->used_ctr++; + win->inuse_cnt++; + *w_cursor = win; } + + offset -= win->offset; + assert(git__is_sizet(offset)); + + if (left) + *left = win->window_map.len - (size_t)offset; + + return (unsigned char *)win->window_map.data + offset; } -static void packlist_dec(pack_backend *backend, git_packlist *pl) -{ - int need_free; - assert(backend && pl); - gitlck_lock(&backend->lock); - need_free = !--pl->refcnt; - gitlck_unlock(&backend->lock); - if (need_free) { - size_t j; - for (j = 0; j < pl->n_packs; j++) - pack_dec(pl->packs[j]); - free(pl); + + + +/*********************************************************** + * + * PACK INDEX METHODS + * + ***********************************************************/ + +static void pack_index_free(struct pack_file *p) +{ + if (p->index_map.data) { + gitfo_free_map(&p->index_map); + p->index_map.data = NULL; } } -static git_pack *alloc_pack(const char *pack_name) +static int pack_index_check(const char *path, struct pack_file *p) { - git_pack *p = git__calloc(1, sizeof(*p)); - if (!p) - return NULL; + struct pack_idx_header *hdr; + uint32_t version, nr, i, *index; - gitlck_init(&p->lock); - strcpy(p->pack_name, pack_name); - p->refcnt = 1; - p->pack_fd = -1; - return p; -} + void *idx_map; + size_t idx_size; -struct scanned_pack { - struct scanned_pack *next; - git_pack *pack; -}; + struct stat st; -static int scan_one_pack(void *state, char *name) -{ - struct scanned_pack **ret = state, *r; - char *s = strrchr(name, '/'), *d; + /* TODO: properly open the file without access time */ + git_file fd = gitfo_open(path, O_RDONLY /*| O_NOATIME */); - if (git__prefixcmp(s + 1, "pack-") - || git__suffixcmp(s, ".pack") - || strlen(s + 1) != GIT_PACK_NAME_MAX + 4) - return 0; - - d = strrchr(s + 1, '.'); - strcpy(d + 1, "idx"); /* "pack-abc.pack" -> "pack-abc.idx" */ - if (gitfo_exists(name)) - return 0; + int error; - if ((r = git__malloc(sizeof(*r))) == NULL) - return GIT_ERROR; + if (fd < 0) + return GIT_EOSERR; - *d = '\0'; /* "pack-abc.pack" -_> "pack-abc" */ - if ((r->pack = alloc_pack(s + 1)) == NULL) { - free(r); - return GIT_ERROR; + if (gitfo_fstat(fd, &st) < GIT_SUCCESS) { + gitfo_close(fd); + return GIT_EOSERR; } - r->next = *ret; - *ret = r; - return 0; -} + if (!git__is_sizet(st.st_size)) + return GIT_ENOMEM; -static git_packlist *scan_packs(pack_backend *backend) -{ - char pb[GIT_PATH_MAX]; - struct scanned_pack *state = NULL, *c; - size_t cnt; - git_packlist *new_list; + idx_size = (size_t)st.st_size; - if (git__fmt(pb, sizeof(pb), "%s/pack", backend->objects_dir) < 0) - return NULL; - gitfo_dirent(pb, sizeof(pb), scan_one_pack, &state); - - /* TODO - merge old entries into the new array */ - for (cnt = 0, c = state; c; c = c->next) - cnt++; - new_list = git__malloc(sizeof(*new_list) - + (sizeof(new_list->packs[0]) * cnt)); - if (!new_list) - goto fail; - - for (cnt = 0, c = state; c; ) { - struct scanned_pack *n = c->next; - c->pack->backend = backend; - new_list->packs[cnt++] = c->pack; - free(c); - c = n; - } - new_list->n_packs = cnt; - new_list->refcnt = 2; - backend->packlist = new_list; - return new_list; - -fail: - while (state) { - struct scanned_pack *n = state->next; - pack_dec(state->pack); - free(state); - state = n; + if (idx_size < 4 * 256 + 20 + 20) { + gitfo_close(fd); + return GIT_EOBJCORRUPTED; } - return NULL; -} -static git_packlist *packlist_get(pack_backend *backend) -{ - git_packlist *pl; - - gitlck_lock(&backend->lock); - if ((pl = backend->packlist) != NULL) - pl->refcnt++; - else - pl = scan_packs(backend); - gitlck_unlock(&backend->lock); - return pl; -} + error = gitfo_map_ro(&p->index_map, fd, 0, idx_size); + gitfo_close(fd); -static int locate_packfile(pack_location *location, pack_backend *backend, const git_oid *id) -{ - git_packlist *pl = packlist_get(backend); - size_t j; + if (error < GIT_SUCCESS) + return error; - if (!pl) - return GIT_ENOTFOUND; + hdr = idx_map = p->index_map.data; - for (j = 0; j < pl->n_packs; j++) { + if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { + version = ntohl(hdr->idx_version); - git_pack *pack = pl->packs[j]; - uint32_t pos; - int res; + if (version < 2 || version > 2) { + gitfo_free_map(&p->index_map); + return GIT_EOBJCORRUPTED; /* unsupported index version */ + } - if (pack_openidx(pack)) - continue; + } else + version = 1; - res = pack->idx_search(&pos, pack, id); - pack_decidx(pack); + nr = 0; + index = idx_map; - if (!res) { - packlist_dec(backend, pl); + if (version > 1) + index += 2; /* skip index header */ - location->ptr = pack; - location->n = pos; + for (i = 0; i < 256; i++) { + uint32_t n = ntohl(index[i]); + if (n < nr) { + gitfo_free_map(&p->index_map); + return GIT_EOBJCORRUPTED; /* non-monotonic index */ + } + nr = n; + } - return GIT_SUCCESS; + if (version == 1) { + /* + * Total size: + * - 256 index entries 4 bytes each + * - 24-byte entries * nr (20-byte sha1 + 4-byte offset) + * - 20-byte SHA1 of the packfile + * - 20-byte SHA1 file checksum + */ + if (idx_size != 4*256 + nr * 24 + 20 + 20) { + gitfo_free_map(&p->index_map); + return GIT_EOBJCORRUPTED; + } + } else if (version == 2) { + /* + * Minimum size: + * - 8 bytes of header + * - 256 index entries 4 bytes each + * - 20-byte sha1 entry * nr + * - 4-byte crc entry * nr + * - 4-byte offset entry * nr + * - 20-byte SHA1 of the packfile + * - 20-byte SHA1 file checksum + * And after the 4-byte offset table might be a + * variable sized table containing 8-byte entries + * for offsets larger than 2^31. + */ + unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20; + unsigned long max_size = min_size; + + if (nr) + max_size += (nr - 1)*8; + + if (idx_size < min_size || idx_size > max_size) { + gitfo_free_map(&p->index_map); + return GIT_EOBJCORRUPTED; } + /* Make sure that off_t is big enough to access the whole pack... + * Is this an issue in libgit2? It shouldn't. */ + if (idx_size != min_size && (sizeof(off_t) <= 4)) { + gitfo_free_map(&p->index_map); + return GIT_EOSERR; + } } - packlist_dec(backend, pl); - return GIT_ENOTFOUND; + p->index_version = version; + p->num_objects = nr; + return GIT_SUCCESS; } +static int pack_index_open(struct pack_file *p) +{ + char *idx_name; + int error; + + if (p->index_map.data) + return GIT_SUCCESS; + idx_name = git__strdup(p->pack_name); + strcpy(idx_name + strlen(idx_name) - STRLEN(".pack"), ".idx"); + error = pack_index_check(idx_name, p); + free(idx_name); + return error; +} @@ -541,350 +663,225 @@ static int locate_packfile(pack_location *location, pack_backend *backend, const /*********************************************************** * - * PACKFILE INDEX FUNCTIONS - * - * Get index formation for packfile indexes v1 and v2 + * PACKFILE METHODS * ***********************************************************/ -static int pack_openidx_map(git_pack *p) +static int packfile_sort__cb(const void *a_, const void *b_) { - char pb[GIT_PATH_MAX]; - git_off_t len; - - if (git__fmt(pb, sizeof(pb), "%s/pack/%s.idx", - p->backend->objects_dir, - p->pack_name) < 0) - return GIT_ERROR; - - if ((p->idx_fd = gitfo_open(pb, O_RDONLY)) < 0) - return GIT_ERROR; - - if ((len = gitfo_size(p->idx_fd)) < 0 - || !git__is_sizet(len) - || gitfo_map_ro(&p->idx_map, p->idx_fd, 0, (size_t)len)) { - gitfo_close(p->idx_fd); - return GIT_ERROR; - } + struct pack_file *a = *((struct pack_file **)a_); + struct pack_file *b = *((struct pack_file **)b_); + int st; - return GIT_SUCCESS; + /* + * Local packs tend to contain objects specific to our + * variant of the project than remote ones. In addition, + * remote ones could be on a network mounted filesystem. + * Favor local ones for these reasons. + */ + st = a->pack_local - b->pack_local; + if (st) + return -st; + + /* + * Younger packs tend to contain more recent objects, + * and more recent objects tend to get accessed more + * often. + */ + if (a->mtime < b->mtime) + return 1; + else if (a->mtime == b->mtime) + return 0; + + return -1; } -typedef struct { - git_off_t offset; - uint32_t n; -} offset_idx_info; +static struct pack_file *packfile_alloc(int extra) +{ + struct pack_file *p = git__malloc(sizeof(*p) + extra); + memset(p, 0, sizeof(*p)); + p->pack_fd = -1; + return p; +} -static int cmp_offset_idx_info(const void *lhs, const void *rhs) + +static void packfile_free(struct pack_backend *backend, struct pack_file *p) { - const offset_idx_info *a = lhs; - const offset_idx_info *b = rhs; - return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0; + assert(p); + + /* clear_delta_base_cache(); */ + pack_window_free_all(backend, p); + + if (p->pack_fd != -1) + gitfo_close(p->pack_fd); + + pack_index_free(p); + + free(p->bad_object_sha1); + free(p); } -static int make_offset_index(git_pack *p, offset_idx_info *data) +static int packfile_open(struct pack_file *p) { - git_off_t min_off = 3 * 4, max_off = p->pack_size - GIT_OID_RAWSZ; - uint32_t *idx, *next; - uint32_t j; + struct stat st; + struct pack_header hdr; + git_oid sha1; + unsigned char *idx_sha1; - qsort(data, p->obj_cnt, sizeof(*data), cmp_offset_idx_info); + if (!p->index_map.data && pack_index_open(p) < GIT_SUCCESS) + return GIT_ENOTFOUND; - if (data[0].offset < min_off || data[p->obj_cnt].offset > max_off) - return GIT_ERROR; + /* TODO: open with noatime */ + p->pack_fd = gitfo_open(p->pack_name, O_RDONLY); + if (p->pack_fd < 0 || gitfo_fstat(p->pack_fd, &st) < GIT_SUCCESS) + return GIT_EOSERR; + + /* If we created the struct before we had the pack we lack size. */ + if (!p->pack_size) { + if (!S_ISREG(st.st_mode)) + goto cleanup; + p->pack_size = (off_t)st.st_size; + } else if (p->pack_size != st.st_size) + goto cleanup; - if ((idx = git__malloc(sizeof(*idx) * (p->obj_cnt+1))) == NULL) - return GIT_ERROR; - if ((next = git__malloc(sizeof(*next) * p->obj_cnt)) == NULL) { - free(idx); - return GIT_ERROR; - } +#if 0 + /* We leave these file descriptors open with sliding mmap; + * there is no point keeping them open across exec(), though. + */ + fd_flag = fcntl(p->pack_fd, F_GETFD, 0); + if (fd_flag < 0) + return error("cannot determine file descriptor flags"); - for (j = 0; j < p->obj_cnt+1; j++) - idx[j] = data[j].n; + fd_flag |= FD_CLOEXEC; + if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1) + return GIT_EOSERR; +#endif - for (j = 0; j < p->obj_cnt; j++) { - assert(idx[j] < p->obj_cnt); - assert(idx[j+1] < p->obj_cnt+1); + /* Verify we recognize this pack file format. */ + if (gitfo_read(p->pack_fd, &hdr, sizeof(hdr)) < GIT_SUCCESS) + goto cleanup; - next[idx[j]] = idx[j+1]; - } + if (hdr.hdr_signature != htonl(PACK_SIGNATURE)) + goto cleanup; - p->im_off_idx = idx; - p->im_off_next = next; - return GIT_SUCCESS; -} + if (!pack_version_ok(hdr.hdr_version)) + goto cleanup; -static int idxv1_search(uint32_t *out, git_pack *p, const git_oid *id) -{ - unsigned char *data = p->im_oid; - uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0; - uint32_t hi = p->im_fanout[id->id[0]]; + /* Verify the pack matches its index. */ + if (p->num_objects != ntohl(hdr.hdr_entries)) + goto cleanup; - do { - uint32_t mid = (lo + hi) >> 1; - uint32_t pos = 24 * mid; - int cmp = memcmp(id->id, data + pos + 4, 20); - if (cmp < 0) - hi = mid; - else if (!cmp) { - *out = mid; - return GIT_SUCCESS; - } else - lo = mid + 1; - } while (lo < hi); - return GIT_ENOTFOUND; -} + if (gitfo_lseek(p->pack_fd, p->pack_size - GIT_OID_RAWSZ, SEEK_SET) == -1) + goto cleanup; -static int idxv1_search_offset(uint32_t *out, git_pack *p, git_off_t offset) -{ - if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) { - uint32_t lo = 0, hi = p->obj_cnt+1; - unsigned char *data = p->im_oid; - uint32_t *idx = p->im_off_idx; - do { - uint32_t mid = (lo + hi) >> 1; - uint32_t n = idx[mid]; - uint32_t pos = n * (GIT_OID_RAWSZ + 4); - git_off_t here = decode32(data + pos); - if (offset < here) - hi = mid; - else if (offset == here) { - *out = n; - return GIT_SUCCESS; - } else - lo = mid + 1; - } while (lo < hi); - } - return GIT_ENOTFOUND; -} + if (gitfo_read(p->pack_fd, sha1.id, GIT_OID_RAWSZ) < GIT_SUCCESS) + goto cleanup; -static int idxv1_get(index_entry *e, git_pack *p, uint32_t n) -{ - unsigned char *data = p->im_oid; - uint32_t *next = p->im_off_next; - - if (n < p->obj_cnt) { - uint32_t pos = n * (GIT_OID_RAWSZ + 4); - git_off_t next_off = p->pack_size - GIT_OID_RAWSZ; - e->n = n; - e->oid = data + pos + 4; - e->offset = decode32(data + pos); - if (next[n] < p->obj_cnt) { - pos = next[n] * (GIT_OID_RAWSZ + 4); - next_off = decode32(data + pos); - } - e->size = next_off - e->offset; - return GIT_SUCCESS; - } - return GIT_ENOTFOUND; + idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; + + if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) != 0) + goto cleanup; + + return GIT_SUCCESS; + +cleanup: + gitfo_close(p->pack_fd); + p->pack_fd = -1; + return GIT_EPACKCORRUPTED; } -static int pack_openidx_v1(git_pack *p) +static int packfile_check(struct pack_file **pack_out, const char *path, int local) { - uint32_t *src_fanout = p->idx_map.data; - uint32_t *im_fanout; - offset_idx_info *info; - size_t expsz; - uint32_t j; - - - if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL) - return GIT_ERROR; - - im_fanout[0] = decode32(&src_fanout[0]); - for (j = 1; j < 256; j++) { - im_fanout[j] = decode32(&src_fanout[j]); - if (im_fanout[j] < im_fanout[j - 1]) { - free(im_fanout); - return GIT_ERROR; - } - } - p->obj_cnt = im_fanout[255]; + struct stat st; + struct pack_file *p; + size_t path_len; + + *pack_out = NULL; + path_len = strlen(path); + p = packfile_alloc(path_len + 2); - expsz = 4 * 256 + 24 * p->obj_cnt + 2 * 20; - if (expsz != p->idx_map.len) { - free(im_fanout); - return GIT_ERROR; + /* + * Make sure a corresponding .pack file exists and that + * the index looks sane. + */ + path_len -= STRLEN(".idx"); + if (path_len < 1) { + free(p); + return GIT_ENOTFOUND; } - p->idx_search = idxv1_search; - p->idx_search_offset = idxv1_search_offset; - p->idx_get = idxv1_get; - p->im_fanout = im_fanout; - p->im_oid = (unsigned char *)(src_fanout + 256); + memcpy(p->pack_name, path, path_len); - if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) { - free(im_fanout); - return GIT_ERROR; - } + strcpy(p->pack_name + path_len, ".keep"); + if (gitfo_exists(p->pack_name) == GIT_SUCCESS) + p->pack_keep = 1; - for (j = 0; j < p->obj_cnt; j++) { - uint32_t pos = j * (GIT_OID_RAWSZ + 4); - info[j].offset = decode32(p->im_oid + pos); - info[j].n = j; + strcpy(p->pack_name + path_len, ".pack"); + if (gitfo_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) { + free(p); + return GIT_ENOTFOUND; } - info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ; - info[p->obj_cnt].n = p->obj_cnt; - if (make_offset_index(p, info)) { - free(im_fanout); - free(info); - return GIT_ERROR; - } - free(info); + /* ok, it looks sane as far as we can check without + * actually mapping the pack file. + */ + p->pack_size = (off_t)st.st_size; + p->pack_local = local; + p->mtime = st.st_mtime; + /* see if we can parse the sha1 oid in the packfile name */ + if (path_len < 40 || + git_oid_mkstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < GIT_SUCCESS) + memset(&p->sha1, 0x0, GIT_OID_RAWSZ); + + *pack_out = p; return GIT_SUCCESS; } -static int idxv2_search(uint32_t *out, git_pack *p, const git_oid *id) +static int packfile_load__cb(void *_data, char *path) { - unsigned char *data = p->im_oid; - uint32_t lo = id->id[0] ? p->im_fanout[id->id[0] - 1] : 0; - uint32_t hi = p->im_fanout[id->id[0]]; + struct pack__dirent *data = (struct pack__dirent *)_data; + struct pack_file *pack; + int error; - do { - uint32_t mid = (lo + hi) >> 1; - uint32_t pos = 20 * mid; - int cmp = memcmp(id->id, data + pos, 20); - if (cmp < 0) - hi = mid; - else if (!cmp) { - *out = mid; - return GIT_SUCCESS; - } else - lo = mid + 1; - } while (lo < hi); - return GIT_ENOTFOUND; -} + if (git__suffixcmp(path, ".idx") != 0) + return GIT_SUCCESS; /* not an index */ -static int idxv2_search_offset(uint32_t *out, git_pack *p, git_off_t offset) -{ - if (offset > 0 && offset < (p->pack_size - GIT_OID_RAWSZ)) { - uint32_t lo = 0, hi = p->obj_cnt+1; - uint32_t *idx = p->im_off_idx; - do { - uint32_t mid = (lo + hi) >> 1; - uint32_t n = idx[mid]; - uint32_t o32 = decode32(p->im_offset32 + n); - git_off_t here = o32; - - if (o32 & 0x80000000) { - uint32_t o64_idx = (o32 & ~0x80000000); - here = decode64(p->im_offset64 + 2*o64_idx); - } + /* FIXME: git.git checks for duplicate packs. + * But that makes no fucking sense. Our dirent is not + * going to generate dupicate entries */ - if (offset < here) - hi = mid; - else if (offset == here) { - *out = n; - return GIT_SUCCESS; - } else - lo = mid + 1; - } while (lo < hi); - } - return GIT_ENOTFOUND; -} + error = packfile_check(&pack, path, data->is_pack_local); + if (error < GIT_SUCCESS) + return error; -static int idxv2_get(index_entry *e, git_pack *p, uint32_t n) -{ - unsigned char *data = p->im_oid; - uint32_t *next = p->im_off_next; - - if (n < p->obj_cnt) { - uint32_t o32 = decode32(p->im_offset32 + n); - git_off_t next_off = p->pack_size - GIT_OID_RAWSZ; - e->n = n; - e->oid = data + n * GIT_OID_RAWSZ; - e->offset = o32; - if (o32 & 0x80000000) { - uint32_t o64_idx = (o32 & ~0x80000000); - e->offset = decode64(p->im_offset64 + 2*o64_idx); - } - if (next[n] < p->obj_cnt) { - o32 = decode32(p->im_offset32 + next[n]); - next_off = o32; - if (o32 & 0x80000000) { - uint32_t o64_idx = (o32 & ~0x80000000); - next_off = decode64(p->im_offset64 + 2*o64_idx); - } - } - e->size = next_off - e->offset; - return GIT_SUCCESS; + if (git_vector_insert(&data->backend->packs, pack) < GIT_SUCCESS) { + free(pack); + return GIT_ENOMEM; } - return GIT_ENOTFOUND; + + return GIT_SUCCESS; } -static int pack_openidx_v2(git_pack *p) +static int packfile_load_all(struct pack_backend *backend, const char *odb_path, int local) { - unsigned char *data = p->idx_map.data; - uint32_t *src_fanout = (uint32_t *)(data + 8); - uint32_t *im_fanout; - offset_idx_info *info; - size_t sz, o64_sz, o64_len; - uint32_t j; - - if ((im_fanout = git__malloc(sizeof(*im_fanout) * 256)) == NULL) - return GIT_ERROR; - - im_fanout[0] = decode32(&src_fanout[0]); - for (j = 1; j < 256; j++) { - im_fanout[j] = decode32(&src_fanout[j]); - if (im_fanout[j] < im_fanout[j - 1]) { - free(im_fanout); - return GIT_ERROR; - } - } - p->obj_cnt = im_fanout[255]; + int error; + char path[GIT_PATH_MAX]; + struct pack__dirent data; - /* minimum size of .idx file (with empty 64-bit offsets table): */ - sz = 4 + 4 + 256 * 4 + p->obj_cnt * (20 + 4 + 4) + 2 * 20; - if (p->idx_map.len < sz) { - free(im_fanout); - return GIT_ERROR; - } + data.backend = backend; + data.is_pack_local = local; - p->idx_search = idxv2_search; - p->idx_search_offset = idxv2_search_offset; - p->idx_get = idxv2_get; - p->im_fanout = im_fanout; - p->im_oid = (unsigned char *)(src_fanout + 256); - p->im_crc = (uint32_t *)(p->im_oid + 20 * p->obj_cnt); - p->im_offset32 = p->im_crc + p->obj_cnt; - p->im_offset64 = p->im_offset32 + p->obj_cnt; - - if ((info = git__malloc(sizeof(*info) * (p->obj_cnt+1))) == NULL) { - free(im_fanout); - return GIT_ERROR; - } + git__joinpath(path, odb_path, "pack"); + if (gitfo_isdir(path) < GIT_SUCCESS) + return GIT_SUCCESS; - /* check 64-bit offset table index values are within bounds */ - o64_sz = p->idx_map.len - sz; - o64_len = o64_sz / 8; - for (j = 0; j < p->obj_cnt; j++) { - uint32_t o32 = decode32(p->im_offset32 + j); - git_off_t offset = o32; - if (o32 & 0x80000000) { - uint32_t o64_idx = (o32 & ~0x80000000); - if (o64_idx >= o64_len) { - free(im_fanout); - free(info); - return GIT_ERROR; - } - offset = decode64(p->im_offset64 + 2*o64_idx); - } - info[j].offset = offset; - info[j].n = j; - } - info[p->obj_cnt].offset = p->pack_size - GIT_OID_RAWSZ; - info[p->obj_cnt].n = p->obj_cnt; + error = gitfo_dirent(path, GIT_PATH_MAX, packfile_load__cb, (void *)&data); + if (error < GIT_SUCCESS) + return error; - if (make_offset_index(p, info)) { - free(im_fanout); - free(info); - return GIT_ERROR; - } - free(info); + git_vector_sort(&backend->packs); + backend->last_found = git_vector_get(&backend->packs, 0); return GIT_SUCCESS; } @@ -898,221 +895,421 @@ static int pack_openidx_v2(git_pack *p) /*********************************************************** * - * PACKFILE READING FUNCTIONS - * - * Read the contents of a packfile + * PACKFILE ENTRY SEARCH INTERNALS * ***********************************************************/ +static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n) +{ + const unsigned char *index = p->index_map.data; + index += 4 * 256; + if (p->index_version == 1) { + return ntohl(*((uint32_t *)(index + 24 * n))); + } else { + uint32_t off; + index += 8 + p->num_objects * (20 + 4); + off = ntohl(*((uint32_t *)(index + 4 * n))); + if (!(off & 0x80000000)) + return off; + index += p->num_objects * 4 + (off & 0x7fffffff) * 8; + return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | + ntohl(*((uint32_t *)(index + 4))); + } +} -static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e); - -static int unpack_object_delta(git_rawobj *out, git_pack *p, - index_entry *base_entry, - uint8_t *delta_buffer, - size_t delta_deflated_size, - size_t delta_inflated_size) +static int pack_entry_find_offset( + off_t *offset_out, + struct pack_file *p, + const git_oid *oid) { - int res = 0; - uint8_t *delta = NULL; - git_rawobj base_obj; + const uint32_t *level1_ofs = p->index_map.data; + const unsigned char *index = p->index_map.data; + unsigned hi, lo, stride; - base_obj.data = NULL; - base_obj.type = GIT_OBJ_BAD; - base_obj.len = 0; + *offset_out = 0; - if ((res = unpack_object(&base_obj, p, base_entry)) < 0) - goto cleanup; + if (index == NULL) { + int error; - delta = git__malloc(delta_inflated_size + 1); + if ((error = pack_index_open(p)) < GIT_SUCCESS) + return error; - if ((res = git_odb__inflate_buffer(delta_buffer, delta_deflated_size, - delta, delta_inflated_size)) < 0) - goto cleanup; + assert(p->index_map.data); - res = git__delta_apply(out, base_obj.data, base_obj.len, delta, delta_inflated_size); + index = p->index_map.data; + level1_ofs = p->index_map.data; + } - out->type = base_obj.type; + if (p->index_version > 1) { + level1_ofs += 2; + index += 8; + } -cleanup: - free(delta); - git_rawobj_close(&base_obj); - return res; -} + index += 4 * 256; + hi = ntohl(level1_ofs[(int)oid->id[0]]); + lo = ((oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)oid->id[0] - 1])); -static int unpack_object(git_rawobj *out, git_pack *p, index_entry *e) -{ - git_otype object_type; - size_t inflated_size, deflated_size, shift; - uint8_t *buffer, byte; + if (p->index_version > 1) { + stride = 20; + } else { + stride = 24; + index += 4; + } - assert(out && p && e && git__is_sizet(e->size)); +#ifdef INDEX_DEBUG_LOOKUP + printf("%02x%02x%02x... lo %u hi %u nr %d\n", + oid->id[0], oid->id[1], oid->id[2], lo, hi, p->num_objects); +#endif - if (open_pack(p)) - return GIT_ERROR; +#ifdef GIT2_INDEX_LOOKUP /* TODO: use the advanced lookup method from git.git */ - buffer = (uint8_t *)p->pack_map.data + e->offset; - deflated_size = (size_t)e->size; + int pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, oid); + if (pos < 0) + return GIT_ENOTFOUND; - if (deflated_size == 0) - deflated_size = (size_t)(p->pack_size - e->offset); + *offset_out = nth_packed_object_offset(p, pos); + return GIT_SUCCESS; - byte = *buffer++ & 0xFF; - deflated_size--; - object_type = (byte >> 4) & 0x7; - inflated_size = byte & 0xF; - shift = 4; +#else /* use an old and boring binary search */ - while (byte & 0x80) { - byte = *buffer++ & 0xFF; - deflated_size--; - inflated_size += (byte & 0x7F) << shift; - shift += 7; - } + do { + unsigned mi = (lo + hi) / 2; + int cmp = memcmp(index + mi * stride, oid->id, GIT_OID_RAWSZ); - switch (object_type) { - case GIT_OBJ_COMMIT: - case GIT_OBJ_TREE: - case GIT_OBJ_BLOB: - case GIT_OBJ_TAG: { + if (!cmp) { + *offset_out = nth_packed_object_offset(p, mi); + return GIT_SUCCESS; + } - /* Handle a normal zlib stream */ - out->len = inflated_size; - out->type = object_type; - out->data = git__malloc(inflated_size + 1); + if (cmp > 0) + hi = mi; + else + lo = mi+1; - if (git_odb__inflate_buffer(buffer, deflated_size, out->data, out->len) < 0) { - free(out->data); - out->data = NULL; + } while (lo < hi); + + return GIT_ENOTFOUND; +#endif +} + +static int pack_entry_find1( + struct pack_entry *e, + struct pack_file *p, + const git_oid *oid) +{ + off_t offset; + + assert(p); + + if (p->num_bad_objects) { + unsigned i; + for (i = 0; i < p->num_bad_objects; i++) + if (git_oid_cmp(oid, &p->bad_object_sha1[i]) == 0) return GIT_ERROR; - } + } - return GIT_SUCCESS; - } + if (pack_entry_find_offset(&offset, p, oid) < GIT_SUCCESS) + return GIT_ENOTFOUND; + + /* we found an entry in the index; + * make sure the packfile backing the index + * still exists on disk */ + if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS) + return GIT_EOSERR; - case GIT_OBJ_OFS_DELTA: { + e->offset = offset; + e->p = p; - git_off_t delta_offset; - index_entry entry; + git_oid_cpy(&e->sha1, oid); + return GIT_SUCCESS; +} - byte = *buffer++ & 0xFF; - delta_offset = byte & 0x7F; +static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, const git_oid *oid) +{ + size_t i; - while (byte & 0x80) { - delta_offset += 1; - byte = *buffer++ & 0xFF; - delta_offset <<= 7; - delta_offset += (byte & 0x7F); - } + if (backend->last_found && + pack_entry_find1(e, backend->last_found, oid) == GIT_SUCCESS) + return GIT_SUCCESS; - entry.n = 0; - entry.oid = NULL; - entry.offset = e->offset - delta_offset; - entry.size = 0; + for (i = 0; i < backend->packs.length; ++i) { + struct pack_file *p; - if (unpack_object_delta(out, p, &entry, - buffer, deflated_size, inflated_size) < 0) - return GIT_ERROR; + p = git_vector_get(&backend->packs, i); + if (p == backend->last_found) + continue; + if (pack_entry_find1(e, p, oid) == GIT_SUCCESS) { + backend->last_found = p; return GIT_SUCCESS; } + } + + return GIT_ENOTFOUND; +} + + - case GIT_OBJ_REF_DELTA: { - git_oid base_id; - uint32_t n; - index_entry entry; - int res = GIT_ERROR; - git_oid_mkraw(&base_id, buffer); - if (!p->idx_search(&n, p, &base_id) && - !p->idx_get(&entry, p, n)) { - res = unpack_object_delta(out, p, &entry, - buffer + GIT_OID_RAWSZ, deflated_size, inflated_size); - } - return res; - } - default: - return GIT_EOBJCORRUPTED; - } -} -static int read_packed(git_rawobj *out, const pack_location *loc) -{ - index_entry e; - int res; - assert(out && loc); - if (pack_openidx(loc->ptr) < 0) - return GIT_EPACKCORRUPTED; +/*********************************************************** + * + * PACKFILE ENTRY UNPACK INTERNALS + * + ***********************************************************/ - res = loc->ptr->idx_get(&e, loc->ptr, loc->n); +static unsigned long packfile_unpack_header1( + size_t *sizep, + git_otype *type, + const unsigned char *buf, + unsigned long len) +{ + unsigned shift; + unsigned long size, c; + unsigned long used = 0; - if (!res) - res = unpack_object(out, loc->ptr, &e); + c = buf[used++]; + *type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while (c & 0x80) { + if (len <= used || bitsizeof(long) <= shift) + return 0; - pack_decidx(loc->ptr); + c = buf[used++]; + size += (c & 0x7f) << shift; + shift += 7; + } - return res; + *sizep = (size_t)size; + return used; } -static int read_header_packed(git_rawobj *out, const pack_location *loc) +static int packfile_unpack_header( + size_t *size_p, + git_otype *type_p, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t *curpos) { - git_pack *pack; - index_entry e; - int error = GIT_SUCCESS, shift; - uint8_t *buffer, byte; + unsigned char *base; + unsigned int left; + unsigned long used; + + /* pack_window_open() assures us we have [base, base + 20) available + * as a range that we can look at at. (Its actually the hash + * size that is assured.) With our object header encoding + * the maximum deflated object size is 2^137, which is just + * insane, so we know won't exceed what we have been given. + */ + base = pack_window_open(backend, p, w_curs, *curpos, &left); + if (base == NULL) + return GIT_ENOMEM; - assert(out && loc); + used = packfile_unpack_header1(size_p, type_p, base, left); - pack = loc->ptr; + if (used == 0) + return GIT_EOBJCORRUPTED; - if (pack_openidx(pack)) - return GIT_EPACKCORRUPTED; + *curpos += used; + return GIT_SUCCESS; +} - if (pack->idx_get(&e, pack, loc->n) < 0 || - open_pack(pack) < 0) { - error = GIT_ENOTFOUND; - goto cleanup; +static int packfile_unpack_compressed( + git_rawobj *obj, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t curpos, + size_t size, + git_otype type) +{ + int st; + z_stream stream; + unsigned char *buffer, *in; + + buffer = git__malloc(size); + + memset(&stream, 0, sizeof(stream)); + stream.next_out = buffer; + stream.avail_out = size + 1; + + st = inflateInit(&stream); + if (st != Z_OK) { + free(buffer); + return GIT_EZLIB; } - buffer = (uint8_t *)pack->pack_map.data + e.offset; + do { + in = pack_window_open(backend, p, w_curs, curpos, &stream.avail_in); + stream.next_in = in; + st = inflate(&stream, Z_FINISH); - byte = *buffer++ & 0xFF; - out->type = (byte >> 4) & 0x7; - out->len = byte & 0xF; - shift = 4; + if (!stream.avail_out) + break; /* the payload is larger than it should be */ - while (byte & 0x80) { - byte = *buffer++ & 0xFF; - out->len += (byte & 0x7F) << shift; - shift += 7; + curpos += stream.next_in - in; + } while (st == Z_OK || st == Z_BUF_ERROR); + + inflateEnd(&stream); + + if ((st != Z_STREAM_END) || stream.total_out != size) { + free(buffer); + return GIT_EZLIB; } - /* - * FIXME: if the object is not packed as a whole, - * we need to do a full load and apply the deltas before - * being able to read the header. - * - * I don't think there are any workarounds for this.' + obj->type = type; + obj->len = size; + obj->data = buffer; + return GIT_SUCCESS; +} + +static off_t get_delta_base( + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t *curpos, + git_otype type, + off_t delta_obj_offset) +{ + unsigned char *base_info = pack_window_open(backend, p, w_curs, *curpos, NULL); + off_t base_offset; + + /* pack_window_open() assured us we have [base_info, base_info + 20) + * as a range that we can look at without walking off the + * end of the mapped window. Its actually the hash size + * that is assured. An OFS_DELTA longer than the hash size + * is stupid, as then a REF_DELTA would be smaller to store. */ + if (type == GIT_OBJ_OFS_DELTA) { + unsigned used = 0; + unsigned char c = base_info[used++]; + base_offset = c & 127; + while (c & 128) { + base_offset += 1; + if (!base_offset || MSB(base_offset, 7)) + return 0; /* overflow */ + c = base_info[used++]; + base_offset = (base_offset << 7) + (c & 127); + } + base_offset = delta_obj_offset - base_offset; + if (base_offset <= 0 || base_offset >= delta_obj_offset) + return 0; /* out of bound */ + *curpos += used; + } else if (type == GIT_OBJ_REF_DELTA) { + /* The base entry _must_ be in the same pack */ + if (pack_entry_find_offset(&base_offset, p, (git_oid *)base_info) < GIT_SUCCESS) + return GIT_EPACKCORRUPTED; + *curpos += 20; + } else + return 0; - if (out->type == GIT_OBJ_OFS_DELTA || out->type == GIT_OBJ_REF_DELTA) { - error = unpack_object(out, pack, &e); - git_rawobj_close(out); + return base_offset; +} + +static int packfile_unpack_delta( + git_rawobj *obj, + struct pack_backend *backend, + struct pack_file *p, + struct pack_window **w_curs, + off_t curpos, + size_t delta_size, + git_otype delta_type, + off_t obj_offset) +{ + off_t base_offset; + git_rawobj base, delta; + int error; + + base_offset = get_delta_base(backend, p, w_curs, &curpos, delta_type, obj_offset); + if (base_offset == 0) + return GIT_EOBJCORRUPTED; + + pack_window_close(w_curs); + error = packfile_unpack(&base, backend, p, base_offset); + + /* TODO: git.git tries to load the base from other packfiles + * or loose objects */ + if (error < GIT_SUCCESS) + return error; + + error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type); + if (error < GIT_SUCCESS) { + git_rawobj_close(&base); + return error; } -cleanup: - pack_decidx(loc->ptr); + obj->type = base.type; + error = git__delta_apply(obj, + base.data, base.len, + delta.data, delta.len); + + git_rawobj_close(&base); + git_rawobj_close(&delta); + + /* TODO: we might want to cache this shit. eventually */ + //add_delta_base_cache(p, base_offset, base, base_size, *type); return error; } +static int packfile_unpack( + git_rawobj *obj, + struct pack_backend *backend, + struct pack_file *p, + off_t obj_offset) +{ + struct pack_window *w_curs = NULL; + off_t curpos = obj_offset; + int error; + + size_t size; + git_otype type; + /* + * TODO: optionally check the CRC on the packfile + */ + + obj->data = NULL; + obj->len = 0; + obj->type = GIT_OBJ_BAD; + + error = packfile_unpack_header(&size, &type, backend, p, &w_curs, &curpos); + if (error < GIT_SUCCESS) + return error; + + switch (type) { + case GIT_OBJ_OFS_DELTA: + case GIT_OBJ_REF_DELTA: + error = packfile_unpack_delta( + obj, backend, p, &w_curs, curpos, + size, type, obj_offset); + break; + + case GIT_OBJ_COMMIT: + case GIT_OBJ_TREE: + case GIT_OBJ_BLOB: + case GIT_OBJ_TAG: + error = packfile_unpack_compressed( + obj, backend, p, &w_curs, curpos, + size, type); + break; + + default: + error = GIT_EOBJCORRUPTED; + break; + } + + pack_window_close(&w_curs); + return error; +} @@ -1126,79 +1323,80 @@ static int read_header_packed(git_rawobj *out, const pack_location *loc) * ***********************************************************/ +/* int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) { pack_location location; assert(obj && backend && oid); - if (locate_packfile(&location, (pack_backend *)backend, oid) < 0) + if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0) return GIT_ENOTFOUND; return read_header_packed(obj, &location); } +*/ int pack_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) { - pack_location location; - - assert(obj && backend && oid); + struct pack_entry e; + int error; - if (locate_packfile(&location, (pack_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS) + return error; - return read_packed(obj, &location); + return packfile_unpack(obj, (struct pack_backend *)backend, e.p, e.offset); } int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) { - pack_location location; - assert(backend && oid); - return locate_packfile(&location, (pack_backend *)backend, oid) == GIT_SUCCESS; + struct pack_entry e; + return pack_entry_find(&e, (struct pack_backend *)backend, oid) == GIT_SUCCESS; } void pack_backend__free(git_odb_backend *_backend) { - pack_backend *backend; - git_packlist *pl; + struct pack_backend *backend; + size_t i; assert(_backend); - backend = (pack_backend *)_backend; - - gitlck_lock(&backend->lock); - - pl = backend->packlist; - backend->packlist = NULL; + backend = (struct pack_backend *)_backend; - gitlck_unlock(&backend->lock); - if (pl) - packlist_dec(backend, pl); - - gitlck_free(&backend->lock); + for (i = 0; i < backend->packs.length; ++i) { + struct pack_file *p = git_vector_get(&backend->packs, i); + packfile_free(backend, p); + } - free(backend->objects_dir); + git_vector_free(&backend->packs); free(backend); } int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) { - pack_backend *backend; + int error; + struct pack_backend *backend; - backend = git__calloc(1, sizeof(pack_backend)); + backend = git__calloc(1, sizeof(struct pack_backend)); if (backend == NULL) return GIT_ENOMEM; - backend->objects_dir = git__strdup(objects_dir); - if (backend->objects_dir == NULL) { + if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < GIT_SUCCESS) { free(backend); return GIT_ENOMEM; } - gitlck_init(&backend->lock); + backend->window_size = DEFAULT_WINDOW_SIZE; + backend->mapped_limit = DEFAULT_MAPPED_LIMIT; + + error = packfile_load_all(backend, objects_dir, 1); + if (error < GIT_SUCCESS) { + pack_backend__free((git_odb_backend *)backend); + return error; + } backend->parent.read = &pack_backend__read; - backend->parent.read_header = &pack_backend__read_header; + backend->parent.read_header = NULL; backend->parent.write = NULL; backend->parent.exists = &pack_backend__exists; backend->parent.free = &pack_backend__free; diff --git a/vendor/libgit2/src/oid.c b/vendor/libgit2/src/oid.c index 698d0f927..81b7d6005 100644 --- a/vendor/libgit2/src/oid.c +++ b/vendor/libgit2/src/oid.c @@ -27,6 +27,7 @@ #include "git2/oid.h" #include "repository.h" #include +#include static signed char from_hex[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */ @@ -166,3 +167,180 @@ int git_oid_cmp(const git_oid *a, const git_oid *b) { return memcmp(a->id, b->id, sizeof(a->id)); } + + +typedef short node_index; + +typedef union { + const char *tail; + node_index children[16]; +} trie_node; + +struct git_oid_shorten { + trie_node *nodes; + size_t node_count, size; + int min_length, full; +}; + +static int resize_trie(git_oid_shorten *self, size_t new_size) +{ + self->nodes = realloc(self->nodes, new_size * sizeof(trie_node)); + if (self->nodes == NULL) + return GIT_ENOMEM; + + if (new_size > self->size) { + memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node)); + } + + self->size = new_size; + return GIT_SUCCESS; +} + +static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid) +{ + trie_node *node, *leaf; + node_index idx_leaf; + + if (os->node_count >= os->size) { + if (resize_trie(os, os->size * 2) < GIT_SUCCESS) + return NULL; + } + + idx_leaf = (node_index)os->node_count++; + + if (os->node_count == SHRT_MAX) + os->full = 1; + + node = &os->nodes[idx]; + node->children[push_at] = -idx_leaf; + + leaf = &os->nodes[idx_leaf]; + leaf->tail = oid; + + return node; +} + +git_oid_shorten *git_oid_shorten_new(size_t min_length) +{ + git_oid_shorten *os; + + os = git__malloc(sizeof(git_oid_shorten)); + if (os == NULL) + return NULL; + + memset(os, 0x0, sizeof(git_oid_shorten)); + + if (resize_trie(os, 16) < GIT_SUCCESS) { + free(os); + return NULL; + } + + os->node_count = 1; + os->min_length = min_length; + + return os; +} + +void git_oid_shorten_free(git_oid_shorten *os) +{ + free(os->nodes); + free(os); +} + + +/* + * What wizardry is this? + * + * This is just a memory-optimized trie: basically a very fancy + * 16-ary tree, which is used to store the prefixes of the OID + * strings. + * + * Read more: http://en.wikipedia.org/wiki/Trie + * + * Magic that happens in this method: + * + * - Each node in the trie is an union, so it can work both as + * a normal node, or as a leaf. + * + * - Each normal node points to 16 children (one for each possible + * character in the oid). This is *not* stored in an array of + * pointers, because in a 64-bit arch this would be sucking + * 16*sizeof(void*) = 128 bytes of memory per node, which is fucking + * insane. What we do is store Node Indexes, and use these indexes + * to look up each node in the om->index array. These indexes are + * signed shorts, so this limits the amount of unique OIDs that + * fit in the structure to about 20000 (assuming a more or less uniform + * distribution). + * + * - All the nodes in om->index array are stored contiguously in + * memory, and each of them is 32 bytes, so we fit 2x nodes per + * cache line. Convenient for speed. + * + * - To differentiate the leafs from the normal nodes, we store all + * the indexes towards a leaf as a negative index (indexes to normal + * nodes are positives). When we find that one of the children for + * a node has a negative value, that means it's going to be a leaf. + * This reduces the amount of indexes we have by two, but also reduces + * the size of each node by 1-4 bytes (the amount we would need to + * add a `is_leaf` field): this is good because it allows the nodes + * to fit cleanly in cache lines. + * + * - Once we reach an empty children, instead of continuing to insert + * new nodes for each remaining character of the OID, we store a pointer + * to the tail in the leaf; if the leaf is reached again, we turn it + * into a normal node and use the tail to create a new leaf. + * + * This is a pretty good balance between performance and memory usage. + */ +int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) +{ + int i, is_leaf; + node_index idx; + + if (os->full) + return GIT_ENOMEM; + + idx = 0; + is_leaf = 0; + + for (i = 0; i < GIT_OID_HEXSZ; ++i) { + int c = from_hex[(int)text_oid[i]]; + trie_node *node; + + if (c == -1) + return GIT_ENOTOID; + + node = &os->nodes[idx]; + + if (is_leaf) { + const char *tail; + + tail = node->tail; + node->tail = NULL; + + node = push_leaf(os, idx, from_hex[(int)tail[0]], &tail[1]); + if (node == NULL) + return GIT_ENOMEM; + } + + if (node->children[c] == 0) { + if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) + return GIT_ENOMEM; + break; + } + + idx = node->children[c]; + is_leaf = 0; + + if (idx < 0) { + node->children[c] = idx = -idx; + is_leaf = 1; + } + } + + if (++i > os->min_length) + os->min_length = i; + + return os->min_length; +} + diff --git a/vendor/libgit2/src/pqueue.c b/vendor/libgit2/src/pqueue.c new file mode 100644 index 000000000..98152cb85 --- /dev/null +++ b/vendor/libgit2/src/pqueue.c @@ -0,0 +1,153 @@ +/* + * BORING COPYRIGHT NOTICE: + * + * This file is a heavily modified version of the priority queue found + * in the Apache project and the libpqueue library. + * + * https://github.com/vy/libpqueue + * + * These are the original authors: + * + * Copyright 2010 Volkan Yazıcı + * Copyright 2006-2010 The Apache Software Foundation + * + * This file is licensed under the Apache 2.0 license, which + * supposedly makes it compatible with the GPLv2 that libgit2 uses. + * + * Check the Apache license at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * So much licensing trouble for a binary heap. Oh well. + */ + +#include "common.h" +#include "pqueue.h" + +#define left(i) ((i) << 1) +#define right(i) (((i) << 1) + 1) +#define parent(i) ((i) >> 1) + +int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri) +{ + assert(q); + + /* Need to allocate n+1 elements since element 0 isn't used. */ + if ((q->d = malloc((n + 1) * sizeof(void *))) == NULL) + return GIT_ENOMEM; + + q->size = 1; + q->avail = q->step = (n + 1); /* see comment above about n+1 */ + q->cmppri = cmppri; + + return GIT_SUCCESS; +} + + +void git_pqueue_free(git_pqueue *q) +{ + free(q->d); + q->d = NULL; +} + + +size_t git_pqueue_size(git_pqueue *q) +{ + /* queue element 0 exists but doesn't count since it isn't used. */ + return (q->size - 1); +} + + +static void bubble_up(git_pqueue *q, size_t i) +{ + size_t parent_node; + void *moving_node = q->d[i]; + + for (parent_node = parent(i); + ((i > 1) && q->cmppri(q->d[parent_node], moving_node)); + i = parent_node, parent_node = parent(i)) { + q->d[i] = q->d[parent_node]; + } + + q->d[i] = moving_node; +} + + +static size_t maxchild(git_pqueue *q, size_t i) +{ + size_t child_node = left(i); + + if (child_node >= q->size) + return 0; + + if ((child_node + 1) < q->size && + q->cmppri(q->d[child_node], q->d[child_node + 1])) + child_node++; /* use right child instead of left */ + + return child_node; +} + + +static void percolate_down(git_pqueue *q, size_t i) +{ + size_t child_node; + void *moving_node = q->d[i]; + + while ((child_node = maxchild(q, i)) != 0 && + q->cmppri(moving_node, q->d[child_node])) { + q->d[i] = q->d[child_node]; + i = child_node; + } + + q->d[i] = moving_node; +} + + +int git_pqueue_insert(git_pqueue *q, void *d) +{ + void *tmp; + size_t i; + size_t newsize; + + if (!q) return 1; + + /* allocate more memory if necessary */ + if (q->size >= q->avail) { + newsize = q->size + q->step; + if ((tmp = realloc(q->d, sizeof(void *) * newsize)) == NULL) + return GIT_ENOMEM; + + q->d = tmp; + q->avail = newsize; + } + + /* insert item */ + i = q->size++; + q->d[i] = d; + bubble_up(q, i); + + return GIT_SUCCESS; +} + + +void *git_pqueue_pop(git_pqueue *q) +{ + void *head; + + if (!q || q->size == 1) + return NULL; + + head = q->d[1]; + q->d[1] = q->d[--q->size]; + percolate_down(q, 1); + + return head; +} + + +void *git_pqueue_peek(git_pqueue *q) +{ + if (!q || q->size == 1) + return NULL; + return q->d[1]; +} diff --git a/vendor/libgit2/src/pqueue.h b/vendor/libgit2/src/pqueue.h new file mode 100644 index 000000000..6db74661d --- /dev/null +++ b/vendor/libgit2/src/pqueue.h @@ -0,0 +1,92 @@ +/* + * BORING COPYRIGHT NOTICE: + * + * This file is a heavily modified version of the priority queue found + * in the Apache project and the libpqueue library. + * + * https://github.com/vy/libpqueue + * + * These are the original authors: + * + * Copyright 2010 Volkan Yazıcı + * Copyright 2006-2010 The Apache Software Foundation + * + * This file is licensed under the Apache 2.0 license, which + * supposedly makes it compatible with the GPLv2 that libgit2 uses. + * + * Check the Apache license at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * So much licensing trouble for a binary heap. Oh well. + */ + +#ifndef INCLUDE_pqueue_h__ +#define INCLUDE_pqueue_h__ + +/** callback functions to get/set/compare the priority of an element */ +typedef int (*git_pqueue_cmp)(void *a, void *b); + +/** the priority queue handle */ +typedef struct { + size_t size, avail, step; + git_pqueue_cmp cmppri; + void **d; +} git_pqueue; + + +/** + * initialize the queue + * + * @param n the initial estimate of the number of queue items for which memory + * should be preallocated + * @param cmppri the callback function to compare two nodes of the queue + * + * @Return the handle or NULL for insufficent memory + */ +int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri); + + +/** + * free all memory used by the queue + * @param q the queue + */ +void git_pqueue_free(git_pqueue *q); + + +/** + * return the size of the queue. + * @param q the queue + */ +size_t git_pqueue_size(git_pqueue *q); + + +/** + * insert an item into the queue. + * @param q the queue + * @param d the item + * @return 0 on success + */ +int git_pqueue_insert(git_pqueue *q, void *d); + + +/** + * pop the highest-ranking item from the queue. + * @param p the queue + * @param d where to copy the entry to + * @return NULL on error, otherwise the entry + */ +void *git_pqueue_pop(git_pqueue *q); + + +/** + * access highest-ranking item without removing it. + * @param q the queue + * @param d the entry + * @return NULL on error, otherwise the entry + */ +void *git_pqueue_peek(git_pqueue *q); + +#endif /* PQUEUE_H */ +/** @} */ + diff --git a/vendor/libgit2/src/refs.c b/vendor/libgit2/src/refs.c index 542401c2f..1ed6b567c 100644 --- a/vendor/libgit2/src/refs.c +++ b/vendor/libgit2/src/refs.c @@ -235,6 +235,24 @@ static int loose_read(gitfo_buf *file_content, const char *name, const char *rep return error; } +static git_rtype loose_guess_rtype(const char *full_path) +{ + gitfo_buf ref_file = GITFO_BUF_INIT; + git_rtype type; + + type = GIT_REF_INVALID; + + if (gitfo_read_file(&ref_file, full_path) == GIT_SUCCESS) { + if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) + type = GIT_REF_SYMBOLIC; + else + type = GIT_REF_OID; + } + + gitfo_free_buf(&ref_file); + return type; +} + static int loose_lookup( git_reference **ref_out, git_repository *repo, @@ -291,7 +309,7 @@ static int loose_write(git_reference *ref) git__joinpath(ref_path, ref->owner->path_repository, ref->name); - if ((error = git_filebuf_open(&file, ref_path, 0)) < GIT_SUCCESS) + if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS) return error; if (ref->type & GIT_REF_OID) { @@ -405,7 +423,7 @@ static int packed_parse_oid( const char **buffer_out, const char *buffer_end) { - reference_oid *ref; + reference_oid *ref = NULL; const char *buffer = *buffer_out; const char *refname_begin, *refname_end; @@ -531,6 +549,31 @@ static int packed_load(git_repository *repo) return error; } + + + +struct dirent_list_data { + git_vector ref_list; + size_t repo_path_len; + unsigned int list_flags; +}; + +static int _dirent_loose_listall(void *_data, char *full_path) +{ + struct dirent_list_data *data = (struct dirent_list_data *)_data; + char *file_path; + + if (gitfo_isdir(full_path) == GIT_SUCCESS) + return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data); + + if ((data->list_flags & loose_guess_rtype(full_path)) == 0) + return GIT_SUCCESS; /* we are filtering out this reference */ + + file_path = full_path + data->repo_path_len; + + return git_vector_insert(&data->ref_list, git__strdup(file_path)); +} + static int _dirent_loose_load(void *data, char *full_path) { git_repository *repository = (git_repository *)data; @@ -638,7 +681,6 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file) static int packed_find_peel(reference_oid *ref) { git_tag *tag; - const git_object *peeled_target; int error; if (ref->ref.type & GIT_REF_HAS_PEEL) @@ -663,11 +705,7 @@ static int packed_find_peel(reference_oid *ref) /* * Find the object pointed at by this tag */ - peeled_target = git_tag_target(tag); - if (peeled_target == NULL) - return GIT_EOBJCORRUPTED; - - git_oid_cpy(&ref->peel_target, git_object_id(peeled_target)); + git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag)); ref->ref.type |= GIT_REF_HAS_PEEL; /* @@ -1292,6 +1330,45 @@ int git_reference_packall(git_repository *repo) return packed_write(repo); } +int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags) +{ + int error; + struct dirent_list_data data; + char refs_path[GIT_PATH_MAX]; + + array->strings = NULL; + array->count = 0; + + git_vector_init(&data.ref_list, 8, NULL); + data.repo_path_len = strlen(repo->path_repository); + data.list_flags = list_flags; + + git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR); + error = gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); + + if (error < GIT_SUCCESS) { + git_vector_free(&data.ref_list); + return error; + } + + if (list_flags & GIT_REF_PACKED) { + const char *ref_name; + void *_unused; + + if ((error = packed_load(repo)) < GIT_SUCCESS) { + git_vector_free(&data.ref_list); + return error; + } + + GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused, + git_vector_insert(&data.ref_list, git__strdup(ref_name)); + ); + } + + array->strings = (char **)data.ref_list.contents; + array->count = data.ref_list.length; + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/repository.c b/vendor/libgit2/src/repository.c index d3852d3ad..37aa44781 100644 --- a/vendor/libgit2/src/repository.c +++ b/vendor/libgit2/src/repository.c @@ -53,7 +53,7 @@ typedef struct { * Callbacks for the ODB cache, implemented * as a hash table */ -uint32_t object_table_hash(const void *key, int hash_id) +static uint32_t object_table_hash(const void *key, int hash_id) { uint32_t r; git_oid *id; @@ -68,12 +68,12 @@ uint32_t object_table_hash(const void *key, int hash_id) * * Open a repository object from its path */ -static int assign_repository_DIRs(git_repository *repo, +static int assign_repository_dirs( + git_repository *repo, const char *git_dir, const char *git_object_directory, const char *git_index_file, - const char *git_work_tree, - int is_repo_being_created) + const char *git_work_tree) { char path_aux[GIT_PATH_MAX]; size_t git_dir_path_len; @@ -104,22 +104,11 @@ static int assign_repository_DIRs(git_repository *repo, return error; } - /* Ensure GIT_OBJECT_DIRECTORY exists */ - if (gitfo_isdir(path_aux) < GIT_SUCCESS) - return GIT_ENOTFOUND; - /* Store GIT_OBJECT_DIRECTORY */ repo->path_odb = git__strdup(path_aux); if (repo->path_odb == NULL) return GIT_ENOMEM; - if (!is_repo_being_created) { - /* Ensure HEAD file exists */ - git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE); - if (gitfo_exists(path_aux) < 0) - return GIT_ENOTAREPO; - } - /* path to GIT_WORK_TREE */ if (git_work_tree == NULL) repo->is_bare = 1; @@ -142,12 +131,6 @@ static int assign_repository_DIRs(git_repository *repo, return error; } - if (!is_repo_being_created) { - /* Ensure GIT_INDEX_FILE exists */ - if (gitfo_exists(path_aux) < 0) - return GIT_ENOTAREPO; - } - /* store GIT_INDEX_FILE */ repo->path_index = git__strdup(path_aux); if (repo->path_index == NULL) @@ -157,32 +140,42 @@ static int assign_repository_DIRs(git_repository *repo, return GIT_SUCCESS; } -static int guess_repository_DIRs(git_repository *repo, const char *repository_path, int is_repo_being_created) +static int check_repository_dirs(git_repository *repo) { - char path_odb[GIT_PATH_MAX] = "\0", path_index[GIT_PATH_MAX] = "\0", path_work_tree[GIT_PATH_MAX] = "\0"; - char dir_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char path_aux[GIT_PATH_MAX]; - int error = GIT_SUCCESS; + if (gitfo_isdir(repo->path_repository) < GIT_SUCCESS) + return GIT_ENOTAREPO; + + /* Ensure GIT_OBJECT_DIRECTORY exists */ + if (gitfo_isdir(repo->path_odb) < GIT_SUCCESS) + return GIT_ENOTAREPO; + + /* Ensure HEAD file exists */ + git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE); + if (gitfo_exists(path_aux) < 0) + return GIT_ENOTAREPO; - /* Path to objects database */ - git__joinpath(path_odb, repository_path, GIT_OBJECTS_DIR); + return GIT_SUCCESS; +} + +static int guess_repository_dirs(git_repository *repo, const char *repository_path) +{ + char buffer[GIT_PATH_MAX]; + const char *path_work_tree = NULL; /* Git directory name */ - if (git__basename_r(dir_name, sizeof(dir_name), repository_path) < 0) + if (git__basename_r(buffer, sizeof(buffer), repository_path) < 0) return GIT_EINVALIDPATH; - if (strcmp(dir_name, DOT_GIT) == 0) { - - /* Path to index file */ - git__joinpath(path_index, repository_path, GIT_INDEX_FILE); - + if (strcmp(buffer, DOT_GIT) == 0) { /* Path to working dir */ - if (git__dirname_r(path_work_tree, sizeof(path_work_tree), repository_path) < 0) + if (git__dirname_r(buffer, sizeof(buffer), repository_path) < 0) return GIT_EINVALIDPATH; + path_work_tree = buffer; } - error = assign_repository_DIRs(repo, repository_path, path_odb, !*path_index ? NULL : path_index, !*path_work_tree ? NULL : path_work_tree, is_repo_being_created); - return error; + return assign_repository_dirs(repo, repository_path, NULL, NULL, path_work_tree); } static git_repository *repository_alloc() @@ -242,16 +235,19 @@ int git_repository_open3(git_repository **repo_out, if (repo == NULL) return GIT_ENOMEM; - error = assign_repository_DIRs(repo, + error = assign_repository_dirs(repo, git_dir, NULL, git_index_file, - git_work_tree, - 0); + git_work_tree); if (error < GIT_SUCCESS) goto cleanup; + error = check_repository_dirs(repo); + if (error < GIT_SUCCESS) + goto cleanup; + repo->db = object_database; *repo_out = repo; @@ -278,13 +274,16 @@ int git_repository_open2(git_repository **repo_out, if (repo == NULL) return GIT_ENOMEM; - error = assign_repository_DIRs(repo, + error = assign_repository_dirs(repo, git_dir, git_object_directory, git_index_file, - git_work_tree, - 0); + git_work_tree); + + if (error < GIT_SUCCESS) + goto cleanup; + error = check_repository_dirs(repo); if (error < GIT_SUCCESS) goto cleanup; @@ -300,7 +299,7 @@ int git_repository_open2(git_repository **repo_out, return error; } -static int repository_open_internal(git_repository **repo_out, const char *path, int is_repo_being_created) +int git_repository_open(git_repository **repo_out, const char *path) { git_repository *repo; int error = GIT_SUCCESS; @@ -311,7 +310,11 @@ static int repository_open_internal(git_repository **repo_out, const char *path, if (repo == NULL) return GIT_ENOMEM; - error = guess_repository_DIRs(repo, path, is_repo_being_created); + error = guess_repository_dirs(repo, path); + if (error < GIT_SUCCESS) + goto cleanup; + + error = check_repository_dirs(repo); if (error < GIT_SUCCESS) goto cleanup; @@ -327,55 +330,20 @@ static int repository_open_internal(git_repository **repo_out, const char *path, return error; } -int git_repository_open(git_repository **repo_out, const char *path) -{ - return repository_open_internal(repo_out, path, 0); -} - -static void repository_free(git_repository *repo) -{ - assert(repo); - - free(repo->path_workdir); - free(repo->path_index); - free(repo->path_repository); - free(repo->path_odb); - - git_hashtable_free(repo->objects); - git_vector_free(&repo->memory_objects); - - git_repository__refcache_free(&repo->references); - - if (repo->db != NULL) - git_odb_close(repo->db); - - if (repo->index != NULL) - git_index_free(repo->index); - - free(repo); -} - -void git_repository_free__no_gc(git_repository *repo) +int git_repository_gc(git_repository *repo) { + int collected = 0; git_object *object; const void *_unused; - unsigned int i; - - if (repo == NULL) - return; GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - object->repo = NULL; - object->refcount = 0; + if (object->can_free) { + git_object__free(object); + collected++; + } ); - for (i = 0; i < repo->memory_objects.length; ++i) { - object = git_vector_get(&repo->memory_objects, i); - object->repo = NULL; - object->refcount = 0; - } - - repository_free(repo); + return collected; } void git_repository_free(git_repository *repo) @@ -387,12 +355,6 @@ void git_repository_free(git_repository *repo) if (repo == NULL) return; - /* Increment the refcount of all the objects in the repository - * to prevent freeing dependencies */ - GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - GIT_OBJECT_INCREF(object); - ); - /* force free all the objects */ GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, git_object__free(object); @@ -403,7 +365,23 @@ void git_repository_free(git_repository *repo) git_object__free(object); } - repository_free(repo); + free(repo->path_workdir); + free(repo->path_index); + free(repo->path_repository); + free(repo->path_odb); + + git_hashtable_free(repo->objects); + git_vector_free(&repo->memory_objects); + + git_repository__refcache_free(&repo->references); + + if (repo->db != NULL) + git_odb_close(repo->db); + + if (repo->index != NULL) + git_index_free(repo->index); + + free(repo); } int git_repository_index(git_index **index_out, git_repository *repo) @@ -509,6 +487,7 @@ static int repo_init_find_dir(repo_init *results, const char* path) int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare) { int error = GIT_SUCCESS; + git_repository *repo = NULL; repo_init results; assert(repo_out && path); @@ -527,15 +506,36 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is if (error < GIT_SUCCESS) goto cleanup; - error = repository_open_internal(repo_out, results.path_repository, 1); + repo = repository_alloc(); + if (repo == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + error = guess_repository_dirs(repo, results.path_repository); if (error < GIT_SUCCESS) goto cleanup; - assert((*repo_out)->is_bare == is_bare); + assert(repo->is_bare == is_bare); - error = repo_init_createhead(*repo_out); + error = init_odb(repo); + if (error < GIT_SUCCESS) + goto cleanup; + + error = repo_init_createhead(repo); + if (error < GIT_SUCCESS) + goto cleanup; + + /* should never fail */ + assert(check_repository_dirs(repo) == GIT_SUCCESS); + + free(results.path_repository); + *repo_out = repo; + return GIT_SUCCESS; cleanup: free(results.path_repository); + git_repository_free(repo); return error; } + diff --git a/vendor/libgit2/src/repository.h b/vendor/libgit2/src/repository.h index 4af062e97..48b8dae6b 100644 --- a/vendor/libgit2/src/repository.h +++ b/vendor/libgit2/src/repository.h @@ -5,6 +5,7 @@ #include "git2/oid.h" #include "git2/odb.h" #include "git2/repository.h" +#include "git2/object.h" #include "hashtable.h" #include "index.h" @@ -26,8 +27,8 @@ struct git_object { git_oid id; git_repository *repo; git_odb_source source; - unsigned short refcount; - short in_memory:1, modified:1; + unsigned int lru; + unsigned char in_memory, modified, can_free, _pad; }; struct git_repository { @@ -45,6 +46,7 @@ struct git_repository { char *path_workdir; unsigned is_bare:1; + unsigned int lru_counter; }; int git_object__source_open(git_object *object); @@ -60,12 +62,4 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len); int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header); int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid); -#define GIT_OBJECT_INCREF(ob) git_object__incref((git_object *)(ob)) - -GIT_INLINE(void) git_object__incref(struct git_object *object) -{ - if (object) - object->refcount++; -} - #endif diff --git a/vendor/libgit2/src/revwalk.c b/vendor/libgit2/src/revwalk.c index 872cdbc43..edafbe73d 100644 --- a/vendor/libgit2/src/revwalk.c +++ b/vendor/libgit2/src/revwalk.c @@ -27,22 +27,132 @@ #include "commit.h" #include "revwalk.h" #include "hashtable.h" +#include "pqueue.h" -uint32_t git_revwalk__commit_hash(const void *key, int hash_id) +typedef struct commit_object { + git_oid oid; + uint32_t time; + unsigned int seen:1, + uninteresting:1, + topo_delay:1, + parsed:1; + + unsigned short in_degree; + unsigned short out_degree; + + struct commit_object **parents; +} commit_object; + +typedef struct commit_list { + commit_object *item; + struct commit_list *next; +} commit_list; + +struct git_revwalk { + git_repository *repo; + + git_hashtable *commits; + git_vector pending; + + commit_list *iterator_topo; + commit_list *iterator_rand; + commit_list *iterator_reverse; + git_pqueue iterator_time; + + int (*get_next)(commit_object **, git_revwalk *); + int (*enqueue)(git_revwalk *, commit_object *); + + git_vector memory_alloc; + size_t chunk_size; + + unsigned walking:1; + unsigned int sorting; +}; + +commit_list *commit_list_insert(commit_object *item, commit_list **list_p) +{ + commit_list *new_list = git__malloc(sizeof(commit_list)); + new_list->item = item; + new_list->next = *list_p; + *list_p = new_list; + return new_list; +} + +void commit_list_free(commit_list *list) +{ + while (list) { + commit_list *temp = list; + list = temp->next; + free(temp); + } +} + +commit_object *commit_list_pop(commit_list **stack) +{ + commit_list *top = *stack; + commit_object *item = top ? top->item : NULL; + + if (top) { + *stack = top->next; + free(top); + } + return item; +} + +static int commit_time_cmp(void *a, void *b) +{ + commit_object *commit_a = (commit_object *)a; + commit_object *commit_b = (commit_object *)b; + + return (commit_a->time < commit_b->time); +} + +static uint32_t object_table_hash(const void *key, int hash_id) { uint32_t r; - git_commit *commit; + git_oid *id; - commit = (git_commit *)key; - memcpy(&r, commit->object.id.id + (hash_id * sizeof(uint32_t)), sizeof(r)); + id = (git_oid *)key; + memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); return r; } -int git_revwalk__commit_keycmp(const void *key_a, const void *key_b) +#define COMMITS_PER_CHUNK 128 +#define CHUNK_STEP 64 +#define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *)) + +static int alloc_chunk(git_revwalk *walk) { - git_commit *a = (git_commit *)key_a; - git_commit *b = (git_commit *)key_b; - return git_oid_cmp(&a->object.id, &b->object.id); + void *chunk; + + chunk = git__calloc(COMMITS_PER_CHUNK, CHUNK_STEP); + if (chunk == NULL) + return GIT_ENOMEM; + + walk->chunk_size = 0; + return git_vector_insert(&walk->memory_alloc, chunk); +} + +static commit_object *alloc_commit(git_revwalk *walk) +{ + unsigned char *chunk; + + if (walk->chunk_size == COMMITS_PER_CHUNK) + alloc_chunk(walk); + + chunk = git_vector_get(&walk->memory_alloc, walk->memory_alloc.length - 1); + chunk += (walk->chunk_size * CHUNK_STEP); + walk->chunk_size++; + + return (commit_object *)chunk; +} + +static commit_object **alloc_parents(commit_object *commit, size_t n_parents) +{ + if (n_parents <= PARENTS_PER_COMMIT) + return (commit_object **)((unsigned char *)commit + sizeof(commit_object)); + + return git__malloc(n_parents * sizeof(commit_object *)); } int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) @@ -56,14 +166,18 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) memset(walk, 0x0, sizeof(git_revwalk)); walk->commits = git_hashtable_alloc(64, - git_revwalk__commit_hash, - git_revwalk__commit_keycmp); + object_table_hash, + (git_hash_keyeq_ptr)git_oid_cmp); if (walk->commits == NULL) { free(walk); return GIT_ENOMEM; } + git_vector_init(&walk->pending, 8, NULL); + git_vector_init(&walk->memory_alloc, 8, NULL); + alloc_chunk(walk); + walk->repo = repo; *revwalk_out = walk; @@ -72,11 +186,20 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) void git_revwalk_free(git_revwalk *walk) { + unsigned int i; + if (walk == NULL) return; git_revwalk_reset(walk); git_hashtable_free(walk->commits); + git_vector_free(&walk->pending); + + for (i = 0; i < walk->memory_alloc.length; ++i) { + free(git_vector_get(&walk->memory_alloc, i)); + } + + git_vector_free(&walk->memory_alloc); free(walk); } @@ -98,364 +221,349 @@ int git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode) return GIT_SUCCESS; } -static git_revwalk_commit *commit_to_walkcommit(git_revwalk *walk, git_commit *commit_object) +static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid) { - git_revwalk_commit *commit; - - commit = (git_revwalk_commit *)git_hashtable_lookup(walk->commits, commit_object); + commit_object *commit; - if (commit != NULL) + if ((commit = git_hashtable_lookup(walk->commits, oid)) != NULL) return commit; - commit = git__malloc(sizeof(git_revwalk_commit)); + commit = alloc_commit(walk); if (commit == NULL) return NULL; - memset(commit, 0x0, sizeof(git_revwalk_commit)); - - commit->commit_object = commit_object; - GIT_OBJECT_INCREF(commit_object); + git_oid_cpy(&commit->oid, oid); - git_hashtable_insert(walk->commits, commit_object, commit); + if (git_hashtable_insert(walk->commits, &commit->oid, commit) < GIT_SUCCESS) { + free(commit); + return NULL; + } return commit; } -static git_revwalk_commit *insert_commit(git_revwalk *walk, git_commit *commit_object) +static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw) { - git_revwalk_commit *commit; - unsigned int i; + const int parent_len = STRLEN("parent ") + GIT_OID_HEXSZ + 1; - assert(walk && commit_object); + unsigned char *buffer = raw->data; + unsigned char *buffer_end = buffer + raw->len; + unsigned char *parents_start; - if (commit_object->object.repo != walk->repo || walk->walking) - return NULL; + int i, parents = 0; - commit = commit_to_walkcommit(walk, commit_object); - if (commit == NULL) - return NULL; + buffer += STRLEN("tree ") + GIT_OID_HEXSZ + 1; - if (commit->seen) - return commit; + parents_start = buffer; + while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", STRLEN("parent ")) == 0) { + parents++; + buffer += parent_len; + } - commit->seen = 1; + commit->parents = alloc_parents(commit, parents); + if (commit->parents == NULL) + return GIT_ENOMEM; - for (i = 0; i < commit->commit_object->parents.length; ++i) { - git_commit *parent_object; - git_revwalk_commit *parent; + buffer = parents_start; + for (i = 0; i < parents; ++i) { + git_oid oid; - parent_object = git_vector_get(&commit->commit_object->parents, i); + if (git_oid_mkstr(&oid, (char *)buffer + STRLEN("parent ")) < GIT_SUCCESS) + return GIT_EOBJCORRUPTED; - if ((parent = commit_to_walkcommit(walk, parent_object)) == NULL) - return NULL; + commit->parents[i] = commit_lookup(walk, &oid); + if (commit->parents[i] == NULL) + return GIT_ENOMEM; - parent = insert_commit(walk, parent_object); - if (parent == NULL) - return NULL; + buffer += parent_len; + } - parent->in_degree++; + commit->out_degree = (unsigned short)parents; - git_revwalk_list_push_back(&commit->parents, parent); - } + if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) + return GIT_EOBJCORRUPTED; - if (git_revwalk_list_push_back(&walk->iterator, commit)) - return NULL; + buffer = memchr(buffer, '>', buffer_end - buffer); + if (buffer == NULL) + return GIT_EOBJCORRUPTED; - return commit; -} + commit->time = strtol((char *)buffer + 2, NULL, 10); + if (commit->time == 0) + return GIT_EOBJCORRUPTED; -int git_revwalk_push(git_revwalk *walk, git_commit *commit) -{ - assert(walk && commit); - return insert_commit(walk, commit) ? GIT_SUCCESS : GIT_ENOMEM; + commit->parsed = 1; + return GIT_SUCCESS; } -static void mark_uninteresting(git_revwalk_commit *commit) +static int commit_parse(git_revwalk *walk, commit_object *commit) { - git_revwalk_listnode *parent; + git_rawobj data; + int error; - assert(commit); + if (commit->parsed) + return GIT_SUCCESS; - commit->uninteresting = 1; - parent = commit->parents.head; + if ((error = git_odb_read(&data, walk->repo->db, &commit->oid)) < GIT_SUCCESS) + return error; - while (parent) { - mark_uninteresting(parent->walk_commit); - parent = parent->next; + if (data.type != GIT_OBJ_COMMIT) { + git_rawobj_close(&data); + return GIT_EOBJTYPE; } + + error = commit_quick_parse(walk, commit, &data); + git_rawobj_close(&data); + return error; } -int git_revwalk_hide(git_revwalk *walk, git_commit *commit) +static void mark_uninteresting(commit_object *commit) { - git_revwalk_commit *hide; + unsigned short i; + assert(commit); - assert(walk && commit); - - hide = insert_commit(walk, commit); - if (hide == NULL) - return GIT_ENOMEM; + commit->uninteresting = 1; - mark_uninteresting(hide); - return GIT_SUCCESS; + for (i = 0; i < commit->out_degree; ++i) + if (!commit->parents[i]->uninteresting) + mark_uninteresting(commit->parents[i]); } - -static void prepare_walk(git_revwalk *walk) +static int process_commit(git_revwalk *walk, commit_object *commit) { - if (walk->sorting & GIT_SORT_TIME) - git_revwalk_list_timesort(&walk->iterator); - - if (walk->sorting & GIT_SORT_TOPOLOGICAL) - git_revwalk_list_toposort(&walk->iterator); + int error; - if (walk->sorting & GIT_SORT_REVERSE) - walk->next = &git_revwalk_list_pop_back; - else - walk->next = &git_revwalk_list_pop_front; + if (commit->seen) + return GIT_SUCCESS; - walk->walking = 1; -} + commit->seen = 1; -int git_revwalk_next(git_commit **commit, git_revwalk *walk) -{ - git_revwalk_commit *next; + if ((error = commit_parse(walk, commit)) < GIT_SUCCESS) + return error; - assert(walk && commit); + if (commit->uninteresting) + mark_uninteresting(commit); - if (!walk->walking) - prepare_walk(walk); + return walk->enqueue(walk, commit); +} - *commit = NULL; +static int process_commit_parents(git_revwalk *walk, commit_object *commit) +{ + unsigned short i; + int error = GIT_SUCCESS; - while ((next = walk->next(&walk->iterator)) != NULL) { - if (!next->uninteresting) { - *commit = next->commit_object; - GIT_OBJECT_INCREF(*commit); - return GIT_SUCCESS; - } + for (i = 0; i < commit->out_degree && error == GIT_SUCCESS; ++i) { + error = process_commit(walk, commit->parents[i]); } - /* No commits left to iterate */ - git_revwalk_reset(walk); - return GIT_EREVWALKOVER; + return error; } -void git_revwalk_reset(git_revwalk *walk) +static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) { - const void *_unused; - git_revwalk_commit *commit; + commit_object *commit; - assert(walk); + commit = commit_lookup(walk, oid); + if (commit == NULL) + return GIT_ENOTFOUND; - GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, { - git_object_close((git_object *)commit->commit_object); - git_revwalk_list_clear(&commit->parents); - free(commit); - }); + if (uninteresting) + mark_uninteresting(commit); - git_hashtable_clear(walk->commits); - git_revwalk_list_clear(&walk->iterator); - walk->walking = 0; + return git_vector_insert(&walk->pending, commit); } +int git_revwalk_push(git_revwalk *walk, const git_oid *oid) +{ + assert(walk && oid); + return push_commit(walk, oid, 0); +} +int git_revwalk_hide(git_revwalk *walk, const git_oid *oid) +{ + assert(walk && oid); + return push_commit(walk, oid, 1); +} - - - -int git_revwalk_list_push_back(git_revwalk_list *list, git_revwalk_commit *commit) +static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit) { - git_revwalk_listnode *node = NULL; + return git_pqueue_insert(&walk->iterator_time, commit); +} - node = git__malloc(sizeof(git_revwalk_listnode)); +static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit) +{ + return commit_list_insert(commit, &walk->iterator_rand) ? GIT_SUCCESS : GIT_ENOMEM; +} - if (node == NULL) - return GIT_ENOMEM; +static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk) +{ + int error; + commit_object *next; - node->walk_commit = commit; - node->next = NULL; - node->prev = list->tail; + while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { + if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS) + return error; - if (list->tail == NULL) { - list->head = list->tail = node; - } else { - list->tail->next = node; - list->tail = node; + if (!next->uninteresting) { + *object_out = next; + return GIT_SUCCESS; + } } - list->size++; - return 0; + return GIT_EREVWALKOVER; } -int git_revwalk_list_push_front(git_revwalk_list *list, git_revwalk_commit *commit) +static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) { - git_revwalk_listnode *node = NULL; - - node = git__malloc(sizeof(git_revwalk_listnode)); + int error; + commit_object *next; - if (node == NULL) - return GIT_ENOMEM; + while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) { + if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS) + return error; - node->walk_commit = commit; - node->next = list->head; - node->prev = NULL; - - if (list->head == NULL) { - list->head = list->tail = node; - } else { - list->head->prev = node; - list->head = node; + if (!next->uninteresting) { + *object_out = next; + return GIT_SUCCESS; + } } - list->size++; - return 0; + return GIT_EREVWALKOVER; } - -git_revwalk_commit *git_revwalk_list_pop_back(git_revwalk_list *list) +static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) { - git_revwalk_listnode *node; - git_revwalk_commit *commit; + commit_object *next; + unsigned short i; - if (list->tail == NULL) - return NULL; + for (;;) { + next = commit_list_pop(&walk->iterator_topo); + if (next == NULL) + return GIT_EREVWALKOVER; - node = list->tail; - list->tail = list->tail->prev; - if (list->tail == NULL) - list->head = NULL; - else - list->tail->next = NULL; + if (next->in_degree > 0) { + next->topo_delay = 1; + continue; + } - commit = node->walk_commit; - free(node); + for (i = 0; i < next->out_degree; ++i) { + commit_object *parent = next->parents[i]; - list->size--; + if (--parent->in_degree == 0 && parent->topo_delay) { + parent->topo_delay = 0; + commit_list_insert(parent, &walk->iterator_topo); + } + } - return commit; + *object_out = next; + return GIT_SUCCESS; + } } -git_revwalk_commit *git_revwalk_list_pop_front(git_revwalk_list *list) +static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk) { - git_revwalk_listnode *node; - git_revwalk_commit *commit; - - if (list->head == NULL) - return NULL; - - node = list->head; - list->head = list->head->next; - if (list->head == NULL) - list->tail = NULL; - else - list->head->prev = NULL; + *object_out = commit_list_pop(&walk->iterator_reverse); + return *object_out ? GIT_SUCCESS : GIT_EREVWALKOVER; +} - commit = node->walk_commit; - free(node); - list->size--; +static int prepare_walk(git_revwalk *walk) +{ + unsigned int i; + int error; - return commit; -} + if (walk->sorting & GIT_SORT_TIME) { + if ((error = git_pqueue_init(&walk->iterator_time, 32, commit_time_cmp)) < GIT_SUCCESS) + return error; -void git_revwalk_list_clear(git_revwalk_list *list) -{ - git_revwalk_listnode *node, *next_node; + walk->get_next = &revwalk_next_timesort; + walk->enqueue = &revwalk_enqueue_timesort; + } else { + walk->get_next = &revwalk_next_unsorted; + walk->enqueue = &revwalk_enqueue_unsorted; + } - node = list->head; - while (node) { - next_node = node->next; - free(node); - node = next_node; + for (i = 0; i < walk->pending.length; ++i) { + commit_object *commit = walk->pending.contents[i]; + if ((error = process_commit(walk, commit)) < GIT_SUCCESS) { + return error; + } } - list->head = list->tail = NULL; - list->size = 0; -} + if (walk->sorting & GIT_SORT_TOPOLOGICAL) { + commit_object *next; + unsigned short i; + int error; -void git_revwalk_list_timesort(git_revwalk_list *list) -{ - git_revwalk_listnode *p, *q, *e; - int in_size, p_size, q_size, merge_count, i; + while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) { + for (i = 0; i < next->out_degree; ++i) { + commit_object *parent = next->parents[i]; + parent->in_degree++; + } - if (list->head == NULL) - return; + commit_list_insert(next, &walk->iterator_topo); + } - in_size = 1; + if (error != GIT_EREVWALKOVER) + return error; - do { - p = list->head; - list->tail = NULL; - merge_count = 0; + walk->get_next = &revwalk_next_toposort; + } - while (p != NULL) { - merge_count++; - q = p; - p_size = 0; - q_size = in_size; + if (walk->sorting & GIT_SORT_REVERSE) { + commit_object *next; + int error; - for (i = 0; i < in_size && q; ++i, q = q->next) - p_size++; + while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) + commit_list_insert(next, &walk->iterator_reverse); - while (p_size > 0 || (q_size > 0 && q)) { + if (error != GIT_EREVWALKOVER) + return error; - if (p_size == 0) - e = q, q = q->next, q_size--; + walk->get_next = &revwalk_next_reverse; + } - else if (q_size == 0 || q == NULL || - p->walk_commit->commit_object->committer->when.time >= - q->walk_commit->commit_object->committer->when.time) - e = p, p = p->next, p_size--; + walk->walking = 1; + return GIT_SUCCESS; +} - else - e = q, q = q->next, q_size--; - if (list->tail != NULL) - list->tail->next = e; - else - list->head = e; +int git_revwalk_next(git_oid *oid, git_revwalk *walk) +{ + int error; + commit_object *next; - e->prev = list->tail; - list->tail = e; - } + assert(walk && oid); - p = q; - } + if (!walk->walking) { + if ((error = prepare_walk(walk)) < GIT_SUCCESS) + return error; + } - list->tail->next = NULL; - in_size *= 2; + error = walk->get_next(&next, walk); + if (error < GIT_SUCCESS) + return error; - } while (merge_count > 1); + git_oid_cpy(oid, &next->oid); + return GIT_SUCCESS; } -void git_revwalk_list_toposort(git_revwalk_list *list) +void git_revwalk_reset(git_revwalk *walk) { - git_revwalk_commit *commit; - git_revwalk_list topo; - memset(&topo, 0x0, sizeof(git_revwalk_list)); - - while ((commit = git_revwalk_list_pop_back(list)) != NULL) { - git_revwalk_listnode *p; - - if (commit->in_degree > 0) { - commit->topo_delay = 1; - continue; - } - - for (p = commit->parents.head; p != NULL; p = p->next) { - p->walk_commit->in_degree--; + const void *_unused; + commit_object *commit; - if (p->walk_commit->in_degree == 0 && p->walk_commit->topo_delay) { - p->walk_commit->topo_delay = 0; - git_revwalk_list_push_back(list, p->walk_commit); - } - } + assert(walk); - git_revwalk_list_push_back(&topo, commit); - } + GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, + commit->seen = 0; + commit->in_degree = 0; + commit->topo_delay = 0; + ); - list->head = topo.head; - list->tail = topo.tail; - list->size = topo.size; + git_pqueue_free(&walk->iterator_time); + commit_list_free(walk->iterator_topo); + commit_list_free(walk->iterator_rand); + commit_list_free(walk->iterator_reverse); + walk->walking = 0; } diff --git a/vendor/libgit2/src/revwalk.h b/vendor/libgit2/src/revwalk.h index 7b69ccd63..2970d773c 100644 --- a/vendor/libgit2/src/revwalk.h +++ b/vendor/libgit2/src/revwalk.h @@ -8,60 +8,4 @@ #include "repository.h" #include "hashtable.h" -struct git_revwalk_commit; - -typedef struct git_revwalk_listnode { - struct git_revwalk_commit *walk_commit; - struct git_revwalk_listnode *next; - struct git_revwalk_listnode *prev; -} git_revwalk_listnode; - -typedef struct git_revwalk_list { - struct git_revwalk_listnode *head; - struct git_revwalk_listnode *tail; - size_t size; -} git_revwalk_list; - - -struct git_revwalk_commit { - - git_commit *commit_object; - git_revwalk_list parents; - - unsigned short in_degree; - unsigned seen:1, - uninteresting:1, - topo_delay:1, - flags:25; -}; - -typedef struct git_revwalk_commit git_revwalk_commit; - -struct git_revwalk { - git_repository *repo; - - git_hashtable *commits; - git_revwalk_list iterator; - - git_revwalk_commit *(*next)(git_revwalk_list *); - - unsigned walking:1; - unsigned int sorting; -}; - - -void git_revwalk__prepare_walk(git_revwalk *walk); -int git_revwalk__enroot(git_revwalk *walk, git_commit *commit); - -int git_revwalk_list_push_back(git_revwalk_list *list, git_revwalk_commit *commit); -int git_revwalk_list_push_front(git_revwalk_list *list, git_revwalk_commit *obj); - -git_revwalk_commit *git_revwalk_list_pop_back(git_revwalk_list *list); -git_revwalk_commit *git_revwalk_list_pop_front(git_revwalk_list *list); - -void git_revwalk_list_clear(git_revwalk_list *list); - -void git_revwalk_list_timesort(git_revwalk_list *list); -void git_revwalk_list_toposort(git_revwalk_list *list); - #endif /* INCLUDE_revwalk_h__ */ diff --git a/vendor/libgit2/src/tag.c b/vendor/libgit2/src/tag.c index 01cc0dc8f..1379425a1 100644 --- a/vendor/libgit2/src/tag.c +++ b/vendor/libgit2/src/tag.c @@ -35,7 +35,6 @@ void git_tag__free(git_tag *tag) { git_signature_free(tag->tagger); - git_object_close(tag->target); free(tag->message); free(tag->tag_name); free(tag); @@ -46,23 +45,31 @@ const git_oid *git_tag_id(git_tag *c) return git_object_id((git_object *)c); } -const git_object *git_tag_target(git_tag *t) +int git_tag_target(git_object **target, git_tag *t) { assert(t); - GIT_OBJECT_INCREF(t->target); - return t->target; + return git_object_lookup(target, t->object.repo, &t->target, t->type); } -void git_tag_set_target(git_tag *tag, git_object *target) +const git_oid *git_tag_target_oid(git_tag *t) { + assert(t); + return &t->target; +} + +int git_tag_set_target(git_tag *tag, git_object *target) +{ + const git_oid *oid; + assert(tag && target); - git_object_close(tag->target); - GIT_OBJECT_INCREF(target); + if ((oid = git_object_id(target)) == NULL) + return GIT_EMISSINGOBJDATA; tag->object.modified = 1; - tag->target = target; + git_oid_cpy(&tag->target, oid); tag->type = git_object_type(target); + return GIT_SUCCESS; } git_otype git_tag_type(git_tag *t) @@ -81,7 +88,6 @@ void git_tag_set_name(git_tag *tag, const char *name) { assert(tag && name); - /* TODO: sanity check? no newlines in message */ tag->object.modified = 1; if (tag->tag_name) @@ -128,12 +134,11 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) NULL, "commit\n", "tree\n", "blob\n", "tag\n" }; - git_oid target_oid; unsigned int i, text_len; char *search; int error; - if ((error = git__parse_oid(&target_oid, &buffer, buffer_end, "object ")) < 0) + if ((error = git__parse_oid(&tag->target, &buffer, buffer_end, "object ")) < 0) return error; if (buffer + 5 >= buffer_end) @@ -161,11 +166,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) if (tag->type == GIT_OBJ_BAD) return GIT_EOBJCORRUPTED; - git_object_close(tag->target); - error = git_object_lookup(&tag->target, tag->object.repo, &target_oid, tag->type); - if (error < 0) - return error; - if (buffer + 4 >= buffer_end) return GIT_EOBJCORRUPTED; @@ -210,10 +210,10 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) int git_tag__writeback(git_tag *tag, git_odb_source *src) { - if (tag->target == NULL || tag->tag_name == NULL || tag->tagger == NULL) + if (tag->tag_name == NULL || tag->tagger == NULL) return GIT_EMISSINGOBJDATA; - git__write_oid(src, "object", git_object_id(tag->target)); + git__write_oid(src, "object", &tag->target); git__source_printf(src, "type %s\n", git_object_type2string(tag->type)); git__source_printf(src, "tag %s\n", tag->tag_name); git_signature__write(src, "tagger", tag->tagger); diff --git a/vendor/libgit2/src/tag.h b/vendor/libgit2/src/tag.h index 624fcc654..a1782d064 100644 --- a/vendor/libgit2/src/tag.h +++ b/vendor/libgit2/src/tag.h @@ -7,8 +7,9 @@ struct git_tag { git_object object; - git_object *target; + git_oid target; git_otype type; + char *tag_name; git_signature *tagger; char *message; diff --git a/vendor/libgit2/src/tree.c b/vendor/libgit2/src/tree.c index 30938f258..702cccbce 100644 --- a/vendor/libgit2/src/tree.c +++ b/vendor/libgit2/src/tree.c @@ -31,6 +31,8 @@ #include "git2/object.h" #define DEFAULT_TREE_SIZE 16 +#define MAX_FILEMODE 0777777 +#define MAX_FILEMODE_BYTES 6 int entry_search_cmp(const void *key, const void *array_member) { @@ -40,6 +42,10 @@ int entry_search_cmp(const void *key, const void *array_member) return strcmp(filename, entry->filename); } +static int valid_attributes(const int attributes) { + return attributes >= 0 && attributes <= MAX_FILEMODE; +} + int entry_sort_cmp(const void *a, const void *b) { const git_tree_entry *entry_a = *(const git_tree_entry **)(a); @@ -101,12 +107,17 @@ const git_oid *git_tree_id(git_tree *c) return git_object_id((git_object *)c); } -void git_tree_entry_set_attributes(git_tree_entry *entry, int attr) +int git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr) { assert(entry && entry->owner); + + if (!valid_attributes(attr)) { + return GIT_ERROR; + } entry->attr = attr; entry->owner->object.modified = 1; + return GIT_SUCCESS; } void git_tree_entry_set_name(git_tree_entry *entry, const char *name) @@ -190,6 +201,9 @@ int git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid git_tree_entry *entry; assert(tree && id && filename); + if (!valid_attributes(attributes)) { + return GIT_ERROR; + } if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) return GIT_ENOMEM; @@ -249,7 +263,7 @@ int git_tree_remove_entry_byname(git_tree *tree, const char *filename) int git_tree__writeback(git_tree *tree, git_odb_source *src) { size_t i; - char filemode[8]; + char filemode[MAX_FILEMODE_BYTES + 1 + 1]; assert(tree && src); @@ -263,8 +277,8 @@ int git_tree__writeback(git_tree *tree, git_odb_source *src) entry = git_vector_get(&tree->entries, i); - sprintf(filemode, "%o ", entry->attr); - + snprintf(filemode, sizeof(filemode), "%o ", entry->attr); + git__source_write(src, filemode, strlen(filemode)); git__source_write(src, entry->filename, strlen(entry->filename) + 1); git__source_write(src, entry->oid.id, GIT_OID_RAWSZ); diff --git a/vendor/libgit2/src/util.c b/vendor/libgit2/src/util.c index 2f1bd2220..c9a8e5fe9 100644 --- a/vendor/libgit2/src/util.c +++ b/vendor/libgit2/src/util.c @@ -82,7 +82,7 @@ int git__basename_r(char *buffer, size_t bufflen, const char *path) } if (len >= 0) { - memcpy(buffer, startp, len); + memmove(buffer, startp, len); buffer[len] = 0; } return result; @@ -140,7 +140,7 @@ int git__dirname_r(char *buffer, size_t bufflen, const char *path) } if (len >= 0) { - memcpy(buffer, path, len); + memmove(buffer, path, len); buffer[len] = 0; } return result; @@ -221,7 +221,7 @@ void git__joinpath_n(char *buffer_out, int count, ...) continue; len = strlen(path); - memcpy(buffer_out, path, len); + memmove(buffer_out, path, len); buffer_out = buffer_out + len; if (i < count - 1 && buffer_out[-1] != '/') diff --git a/vendor/libgit2/src/util.h b/vendor/libgit2/src/util.h index d5320e15b..e0dfd7b20 100644 --- a/vendor/libgit2/src/util.h +++ b/vendor/libgit2/src/util.h @@ -2,6 +2,8 @@ #define INCLUDE_util_h__ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define bitsizeof(x) (CHAR_BIT * sizeof(x)) +#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits)))) /* * Don't wrap malloc/calloc. @@ -93,6 +95,8 @@ GIT_INLINE(int) git__is_sizet(git_off_t p) extern char *git__strtok(char *output, char *src, char *delimit); extern char *git__strtok_keep(char *output, char *src, char *delimit); +#define STRLEN(str) (sizeof(str) - 1) + /* * Realloc the buffer pointed at by variable 'x' so that it can hold * at least 'nr' entries; the number of entries currently allocated diff --git a/vendor/libgit2/src/vector.c b/vendor/libgit2/src/vector.c index 631364031..d0b0c5c56 100644 --- a/vendor/libgit2/src/vector.c +++ b/vendor/libgit2/src/vector.c @@ -133,7 +133,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key static int strict_comparison(const void *a, const void *b) { - return a - b; + return (a == b) ? 0 : -1; } int git_vector_search(git_vector *v, const void *entry) diff --git a/vendor/libgit2/tests/t01-rawobj.c b/vendor/libgit2/tests/t01-rawobj.c index cc4641589..3dfa3c9fe 100644 --- a/vendor/libgit2/tests/t01-rawobj.c +++ b/vendor/libgit2/tests/t01-rawobj.c @@ -300,6 +300,96 @@ BEGIN_TEST(oid15, "convert raw oid to string (big)") must_be_true(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z'); END_TEST + +BEGIN_TEST(oid16, "make sure the OID shortener doesn't choke on duplicate sha1s") + + git_oid_shorten *os; + int min_len; + + os = git_oid_shorten_new(0); + must_be_true(os != NULL); + + git_oid_shorten_add(os, "22596363b3de40b06f981fb85d82312e8c0ed511"); + git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09"); + git_oid_shorten_add(os, "16a0123456789abcdef4b775213c23a8bd74f5e0"); + min_len = git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09"); + + must_be_true(min_len == GIT_OID_HEXSZ + 1); + + git_oid_shorten_free(os); +END_TEST + +BEGIN_TEST(oid17, "stress test for the git_oid_shorten object") + +#define MAX_OIDS 1000 + + git_oid_shorten *os; + char *oids[MAX_OIDS]; + char number_buffer[16]; + git_oid oid; + size_t i, j; + + int min_len = 0, found_collision; + + os = git_oid_shorten_new(0); + must_be_true(os != NULL); + + /* + * Insert in the shortener 1000 unique SHA1 ids + */ + for (i = 0; i < MAX_OIDS; ++i) { + char *oid_text; + + sprintf(number_buffer, "%u", (unsigned int)i); + git_hash_buf(&oid, number_buffer, strlen(number_buffer)); + + oid_text = git__malloc(GIT_OID_HEXSZ + 1); + git_oid_fmt(oid_text, &oid); + oid_text[GIT_OID_HEXSZ] = 0; + + min_len = git_oid_shorten_add(os, oid_text); + must_be_true(min_len >= 0); + + oids[i] = oid_text; + } + + /* + * Compare the first `min_char - 1` characters of each + * SHA1 OID. If the minimizer worked, we should find at + * least one collision + */ + found_collision = 0; + for (i = 0; i < MAX_OIDS; ++i) { + for (j = 0; j < MAX_OIDS; ++j) { + if (i != j && memcmp(oids[i], oids[j], min_len - 1) == 0) + found_collision = 1; + } + } + must_be_true(found_collision == 1); + + /* + * Compare the first `min_char` characters of each + * SHA1 OID. If the minimizer worked, every single preffix + * should be unique. + */ + found_collision = 0; + for (i = 0; i < MAX_OIDS; ++i) { + for (j = 0; j < MAX_OIDS; ++j) { + if (i != j && memcmp(oids[i], oids[j], min_len) == 0) + found_collision = 1; + } + } + must_be_true(found_collision == 0); + + /* cleanup */ + for (i = 0; i < MAX_OIDS; ++i) + free(oids[i]); + + git_oid_shorten_free(os); + +#undef MAX_OIDS +END_TEST + static char *hello_id = "22596363b3de40b06f981fb85d82312e8c0ed511"; static char *hello_text = "hello world\n"; @@ -518,6 +608,8 @@ BEGIN_SUITE(rawobjects) ADD_TEST(oid13); ADD_TEST(oid14); ADD_TEST(oid15); + ADD_TEST(oid16); + ADD_TEST(oid17); ADD_TEST(hash0); ADD_TEST(hash1); diff --git a/vendor/libgit2/tests/t04-commit.c b/vendor/libgit2/tests/t04-commit.c index 8e62759a8..855cf9859 100644 --- a/vendor/libgit2/tests/t04-commit.c +++ b/vendor/libgit2/tests/t04-commit.c @@ -390,11 +390,11 @@ BEGIN_TEST(details0, "query the details on a parsed commit") must_be_true(commit_time > 0); must_be_true(parents <= 2); for (p = 0;p < parents;p++) { - parent = git_commit_parent(commit, p); + must_pass(git_commit_parent(&parent, commit, p)); must_be_true(parent != NULL); must_be_true(git_commit_author(parent) != NULL); // is it really a commit? } - must_be_true(git_commit_parent(commit, parents) == NULL); + must_fail(git_commit_parent(&parent, commit, parents)); } git_repository_free(repo); diff --git a/vendor/libgit2/tests/t05-revwalk.c b/vendor/libgit2/tests/t05-revwalk.c index fd009fac1..bdec09e83 100644 --- a/vendor/libgit2/tests/t05-revwalk.c +++ b/vendor/libgit2/tests/t05-revwalk.c @@ -70,12 +70,12 @@ static const int commit_sorting_time_reverse[][6] = { static const int result_bytes = 24; -static int get_commit_index(git_commit *commit) +static int get_commit_index(git_oid *raw_oid) { int i; char oid[40]; - git_oid_fmt(oid, &commit->object.id); + git_oid_fmt(oid, raw_oid); for (i = 0; i < commit_count; ++i) if (memcmp(oid, commit_ids[i], 40) == 0) @@ -84,23 +84,31 @@ static int get_commit_index(git_commit *commit) return -1; } -static int test_walk(git_revwalk *walk, git_commit *start_from, +static int test_walk(git_revwalk *walk, int flags, const int possible_results[][6], int results_count) { - git_commit *commit = NULL; + git_oid oid; int i; int result_array[commit_count]; + git_revwalk_reset(walk); git_revwalk_sorting(walk, flags); - git_revwalk_push(walk, start_from); for (i = 0; i < commit_count; ++i) result_array[i] = -1; i = 0; - while (git_revwalk_next(&commit, walk) == GIT_SUCCESS) - result_array[i++] = get_commit_index(commit); + + while (git_revwalk_next(&oid, walk) == GIT_SUCCESS) { + result_array[i++] = get_commit_index(&oid); + /*{ + char str[41]; + git_oid_fmt(str, &oid); + str[40] = 0; + printf(" %d) %s\n", i, str); + }*/ + } for (i = 0; i < results_count; ++i) if (memcmp(possible_results[i], @@ -114,103 +122,26 @@ BEGIN_TEST(walk0, "do a simple walk on a repo with different sorting modes") git_oid id; git_repository *repo; git_revwalk *walk; - git_commit *head = NULL; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_revwalk_new(&walk, repo)); git_oid_mkstr(&id, commit_head); + git_revwalk_push(walk, &id); - must_pass(git_commit_lookup(&head, repo, &id)); - - must_pass(test_walk(walk, head, - GIT_SORT_TIME, - commit_sorting_time, 1)); + must_pass(test_walk(walk, GIT_SORT_TIME, commit_sorting_time, 1)); - must_pass(test_walk(walk, head, - GIT_SORT_TOPOLOGICAL, - commit_sorting_topo, 2)); + must_pass(test_walk(walk, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2)); - must_pass(test_walk(walk, head, - GIT_SORT_TIME | GIT_SORT_REVERSE, - commit_sorting_time_reverse, 1)); - - must_pass(test_walk(walk, head, - GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, - commit_sorting_topo_reverse, 2)); + must_pass(test_walk(walk, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1)); + must_pass(test_walk(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2)); git_revwalk_free(walk); git_repository_free(repo); END_TEST -BEGIN_TEST(list0, "check that a commit list is properly sorted by time") - - git_revwalk_list list; - git_revwalk_listnode *n; - int i, t; - time_t previous_time; - -#define TEST_SORTED() \ - previous_time = INT_MAX;\ - for (n = list.head; n != NULL; n = n->next) {\ - must_be_true(n->walk_commit->commit_object->committer->when.time <= previous_time);\ - previous_time = n->walk_commit->commit_object->committer->when.time;\ - } - -#define CLEAR_LIST() \ - for (n = list.head; n != NULL; n = n->next) {\ - git_signature_free(n->walk_commit->commit_object->committer);\ - free(n->walk_commit->commit_object);\ - free(n->walk_commit);\ - }\ - git_revwalk_list_clear(&list); - - memset(&list, 0x0, sizeof(git_revwalk_list)); - srand((unsigned int)time(NULL)); - - for (t = 0; t < 20; ++t) { - const int test_size = rand() % 500 + 500; - - /* Purely random sorting test */ - for (i = 0; i < test_size; ++i) { - git_commit *c = git__malloc(sizeof(git_commit)); - git_revwalk_commit *rc = git__malloc(sizeof(git_revwalk_commit)); - - c->committer = git_signature_new("", "", (time_t)rand(), 0); - rc->commit_object = c; - - git_revwalk_list_push_back(&list, rc); - } - - git_revwalk_list_timesort(&list); - TEST_SORTED(); - CLEAR_LIST(); - } - - /* Try to sort list with all dates equal. */ - for (i = 0; i < 200; ++i) { - git_commit *c = git__malloc(sizeof(git_commit)); - git_revwalk_commit *rc = git__malloc(sizeof(git_revwalk_commit)); - - c->committer = git_signature_new("", "", 0, 0); - rc->commit_object = c; - - git_revwalk_list_push_back(&list, rc); - } - - git_revwalk_list_timesort(&list); - TEST_SORTED(); - CLEAR_LIST(); - - /* Try to sort empty list */ - git_revwalk_list_timesort(&list); - TEST_SORTED(); - -END_TEST - BEGIN_SUITE(revwalk) ADD_TEST(walk0); - ADD_TEST(list0); END_SUITE diff --git a/vendor/libgit2/tests/t08-tag.c b/vendor/libgit2/tests/t08-tag.c index 0afdf719d..c6789266c 100644 --- a/vendor/libgit2/tests/t08-tag.c +++ b/vendor/libgit2/tests/t08-tag.c @@ -48,12 +48,12 @@ BEGIN_TEST(read0, "read and parse a tag from the repository") must_be_true(strcmp(git_tag_name(tag1), "test") == 0); must_be_true(git_tag_type(tag1) == GIT_OBJ_TAG); - tag2 = (git_tag *)git_tag_target(tag1); + must_pass(git_tag_target((git_object **)&tag2, tag1)); must_be_true(tag2 != NULL); must_be_true(git_oid_cmp(&id2, git_tag_id(tag2)) == 0); - commit = (git_commit *)git_tag_target(tag2); + must_pass(git_tag_target((git_object **)&commit, tag2)); must_be_true(commit != NULL); must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0); diff --git a/vendor/libgit2/tests/t10-refs.c b/vendor/libgit2/tests/t10-refs.c index abe364133..c70fb69ce 100644 --- a/vendor/libgit2/tests/t10-refs.c +++ b/vendor/libgit2/tests/t10-refs.c @@ -710,6 +710,18 @@ BEGIN_TEST(normalize2, "tests borrowed from JGit") must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1.hour.ago}", NULL)); END_TEST +BEGIN_TEST(list0, "try to list all the references in our test repo") + git_repository *repo; + git_strarray ref_list; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_reference_listall(&ref_list, repo, GIT_REF_LISTALL)); + must_be_true(ref_list.count == 8); /* 8 refs in total if we include the packed ones */ + + git_strarray_free(&ref_list); + git_repository_free(repo); +END_TEST + BEGIN_SUITE(refs) ADD_TEST(readtag0); @@ -741,4 +753,5 @@ BEGIN_SUITE(refs) ADD_TEST(rename4); ADD_TEST(delete0); + ADD_TEST(list0); END_SUITE diff --git a/vendor/libgit2/tests/t12-repo.c b/vendor/libgit2/tests/t12-repo.c index 644669c8e..a9a93d147 100644 --- a/vendor/libgit2/tests/t12-repo.c +++ b/vendor/libgit2/tests/t12-repo.c @@ -114,24 +114,33 @@ static int ensure_repository_init( if (repo->path_workdir != NULL || expected_working_directory != NULL) { if (strcmp(repo->path_workdir, expected_working_directory) != 0) - return GIT_ERROR; + //return GIT_ERROR; + goto cleanup; } if (strcmp(repo->path_odb, path_odb) != 0) - return GIT_ERROR; + //return GIT_ERROR; + goto cleanup; if (strcmp(repo->path_repository, expected_path_repository) != 0) - return GIT_ERROR; + //return GIT_ERROR; + goto cleanup; if (repo->path_index != NULL || expected_path_index != NULL) { if (strcmp(repo->path_index, expected_path_index) != 0) - return GIT_ERROR; + //return GIT_ERROR; + goto cleanup; } git_repository_free(repo); rmdir_recurs(working_directory); return GIT_SUCCESS; + +cleanup: + git_repository_free(repo); + rmdir_recurs(working_directory); + return GIT_ERROR; } BEGIN_TEST(init0, "initialize a standard repo") @@ -140,8 +149,8 @@ BEGIN_TEST(init0, "initialize a standard repo") git__joinpath(path_repository, TEMP_REPO_FOLDER, GIT_DIR); git__joinpath(path_index, path_repository, GIT_INDEX_FILE); - ensure_repository_init(TEMP_REPO_FOLDER, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER); - ensure_repository_init(TEMP_REPO_FOLDER_NS, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER); + must_pass(ensure_repository_init(TEMP_REPO_FOLDER, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER)); + must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER)); END_TEST BEGIN_TEST(init1, "initialize a bare repo") @@ -149,8 +158,8 @@ BEGIN_TEST(init1, "initialize a bare repo") git__joinpath(path_repository, TEMP_REPO_FOLDER, ""); - ensure_repository_init(TEMP_REPO_FOLDER, BARE_REPOSITORY, NULL, path_repository, NULL); - ensure_repository_init(TEMP_REPO_FOLDER_NS, BARE_REPOSITORY, NULL, path_repository, NULL); + must_pass(ensure_repository_init(TEMP_REPO_FOLDER, BARE_REPOSITORY, NULL, path_repository, NULL)); + must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, BARE_REPOSITORY, NULL, path_repository, NULL)); END_TEST diff --git a/vendor/libgit2/tests/test_helpers.h b/vendor/libgit2/tests/test_helpers.h index 78538d51c..97b81ab40 100644 --- a/vendor/libgit2/tests/test_helpers.h +++ b/vendor/libgit2/tests/test_helpers.h @@ -36,7 +36,7 @@ #define TEST_INDEX2_PATH (TEST_RESOURCES "/gitgit.index") #define TEST_INDEXBIG_PATH (TEST_RESOURCES "/big.index") -#define TEMP_FOLDER "./" +#define TEMP_FOLDER "" #define TEMP_REPO_FOLDER TEMP_FOLDER TEST_REPOSITORY_NAME "/" #define TEMP_REPO_FOLDER_NS TEMP_FOLDER TEST_REPOSITORY_NAME diff --git a/vendor/libgit2/wscript b/vendor/libgit2/wscript index aeb17a9f4..f9daca375 100644 --- a/vendor/libgit2/wscript +++ b/vendor/libgit2/wscript @@ -112,10 +112,10 @@ def get_libgit2_version(git2_h): line = None with open(git2_h) as f: - line = re.search(r'^#define LIBGIT2_VERSION "(\d\.\d\.\d)"$', f.read(), re.MULTILINE) + line = re.search(r'^#define LIBGIT2_VERSION "(\d+\.\d+\.\d+)"$', f.read(), re.MULTILINE) if line is None: - raise "Failed to detect libgit2 version" + raise Exception("Failed to detect libgit2 version") return line.group(1) From d1499470f47bf0b6884f6a273192f847fbd8443c Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Tue, 15 Mar 2011 14:59:11 -0400 Subject: [PATCH 02/37] Updated to work with libgit2 0.10.0 --- lib/commit.js | 12 +++++++++++- lib/revwalk.js | 25 +++++++++++++++++-------- src/blob.cc | 3 ++- src/commit.cc | 20 ++++++++++++-------- src/commit.h | 4 ++-- src/revwalk.cc | 27 +++++++++++++-------------- src/revwalk.h | 16 ++++++++-------- 7 files changed, 65 insertions(+), 42 deletions(-) diff --git a/lib/commit.js b/lib/commit.js index 94e686da5..35b8755d9 100644 --- a/lib/commit.js +++ b/lib/commit.js @@ -11,6 +11,16 @@ var _Commit = function( obj ) { self.commit = obj; } + Object.defineProperty( self, 'id', { + get: function() { + var oid = new git.raw.Oid(); + self.commit.id( oid ); + + return oid; + }, + enumerable: true + }); + Object.defineProperty( self, 'sha', { get: function() { var oid = new git.raw.Oid(); @@ -115,7 +125,7 @@ var _Commit = function( obj ) { var revwalk = git.revwalk( self.repo ); revwalk.each = function( callback ) { return function( callback ) { - return revwalk.walk.apply( self.commit, [ self.commit, callback ] ); + return revwalk.walk.apply( self.id, [ self.id, callback ] ); }; }(); diff --git a/lib/revwalk.js b/lib/revwalk.js index fc57f91d7..a68c84f59 100644 --- a/lib/revwalk.js +++ b/lib/revwalk.js @@ -12,21 +12,30 @@ var _RevWalk = function( obj ) { } // Walk will map to the next method - self.walk = function( commit, callback ) { + self.walk = function( oid, callback ) { if( !callback ) { return; } - self.revwalk.push( commit ); + self.revwalk.push( oid ); + + var cont = true, i = 0; function walk() { - var _tmp = git.commit( self.repo ); - self.revwalk.next( _tmp.commit, function( err ) { + if( !cont ) { return; } + var _tmp = git.oid(); + + self.revwalk.next( _tmp.oid, function( err ) { if( err ) { return; } - var args = Array.prototype.slice.call( arguments ); - args[0] = git.util().error( args[0] ); + git.commit( self.repo ).lookup( _tmp.oid, function( err ) { + if( err ) { return; } + + if( callback.apply( this, [ i, this ] ) === false ) { + cont = false; + } - callback.apply( _tmp, args.concat( _tmp ) ); - walk(); + i = i + 1; + walk(); + }); }); } diff --git a/src/blob.cc b/src/blob.cc index 8a722c81f..bd37d8927 100755 --- a/src/blob.cc +++ b/src/blob.cc @@ -47,7 +47,8 @@ int Blob::Lookup(git_repository *repo, const git_oid *id) { } const char* Blob::RawContent() { - return git_blob_rawcontent(this->blob); + //return git_blob_rawcontent(this->blob); + return ""; } int Blob::RawSize() { diff --git a/src/commit.cc b/src/commit.cc index 80df916d1..9f211cb38 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -85,16 +85,16 @@ const git_signature* Commit::Author() { return git_commit_author(this->commit); } -const git_tree* Commit::Tree() { - return git_commit_tree(this->commit); +int Commit::Tree(git_tree** tree) { + return git_commit_tree(tree, this->commit); } unsigned int Commit::ParentCount() { return git_commit_parentcount(this->commit); } -git_commit* Commit::Parent(int pos) { - return git_commit_parent(this->commit, pos); +int Commit::Parent(git_commit** commit, int pos) { + return git_commit_parent(commit, this->commit, pos); } Handle Commit::New(const Arguments& args) { @@ -271,11 +271,13 @@ Handle Commit::Tree(const Arguments& args) { return ThrowException(Exception::Error(String::New("Tree is required and must be an Object."))); } + git_tree* in; GitTree* tree = ObjectWrap::Unwrap(args[0]->ToObject()); - tree->SetValue(const_cast(commit->Tree())); + int err = commit->Tree(&in); + tree->SetValue(in); - return Undefined(); + return Integer::New(err); } //Handle Commit::Tree(const Arguments& args) { // Commit *commit = ObjectWrap::Unwrap(args.This()); @@ -360,10 +362,12 @@ Handle Commit::Parent(const Arguments& args) { } Commit* out = ObjectWrap::Unwrap(args[0]->ToObject()); + git_commit* in; int index = args[1]->ToInteger()->Value(); - out->SetValue(commit->Parent(index)); + int err = commit->Parent(&in, index); + out->SetValue(in); - return Undefined(); + return Integer::New(err); } Persistent Commit::constructor_template; diff --git a/src/commit.h b/src/commit.h index ef2f86f98..8f7c954b0 100755 --- a/src/commit.h +++ b/src/commit.h @@ -47,9 +47,9 @@ class Commit : public EventEmitter { int TimeOffset(); const git_signature* Committer(); const git_signature* Author(); - const git_tree* Tree(); + int Tree(git_tree** tree); unsigned int ParentCount(); - git_commit* Parent(int pos); + int Parent(git_commit** commit, int pos); protected: Commit() {} diff --git a/src/revwalk.cc b/src/revwalk.cc index 13554b583..0c0b53b93 100755 --- a/src/revwalk.cc +++ b/src/revwalk.cc @@ -58,8 +58,8 @@ void RevWalk::Reset() { git_revwalk_reset(this->revwalk); } -int RevWalk::Push(Commit *commit) { - return git_revwalk_push(this->revwalk, commit->GetValue()); +int RevWalk::Push(git_oid* oid) { + return git_revwalk_push(this->revwalk, oid); } // Not for 0.0.1 @@ -67,8 +67,8 @@ int RevWalk::Push(Commit *commit) { // git_revwalk_hide(this->revwalk); //} -int RevWalk::Next(git_commit **commit) { - return git_revwalk_next(commit, this->revwalk); +int RevWalk::Next(git_oid *oid) { + return git_revwalk_next(oid, this->revwalk); } void RevWalk::Free() { @@ -111,13 +111,13 @@ Handle RevWalk::Push(const Arguments& args) { RevWalk *revwalk = ObjectWrap::Unwrap(args.This()); if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Commit is required and must be an Object."))); + return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); } - Commit *commit = ObjectWrap::Unwrap(args[0]->ToObject()); - int err = revwalk->Push(commit); + Oid *oid = ObjectWrap::Unwrap(args[0]->ToObject()); + int err = revwalk->Push(oid->GetValue()); - return Local::New(Integer::New(err)); + return Integer::New(err); } Handle RevWalk::Next(const Arguments& args) { @@ -127,7 +127,7 @@ Handle RevWalk::Next(const Arguments& args) { HandleScope scope; if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Commit is required and must be an Object."))); + return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); } if(args.Length() == 1 || !args[1]->IsFunction()) { @@ -138,7 +138,7 @@ Handle RevWalk::Next(const Arguments& args) { next_request *ar = new next_request(); ar->revwalk = revwalk; - ar->commit = ObjectWrap::Unwrap(args[0]->ToObject()); + ar->oid = ObjectWrap::Unwrap(args[0]->ToObject()); ar->callback = Persistent::New(callback); revwalk->Ref(); @@ -151,11 +151,10 @@ Handle RevWalk::Next(const Arguments& args) { int RevWalk::EIO_Next(eio_req *req) { next_request *ar = static_cast(req->data); - git_commit* ref = ar->commit->GetValue(); + git_oid* oid = ar->oid->GetValue(); - ar->err = ar->revwalk->Next(&ref); - - ar->commit->SetValue(ref); + ar->err = ar->revwalk->Next(oid); + ar->oid->SetValue(oid); return 0; } diff --git a/src/revwalk.h b/src/revwalk.h index dd05b2287..b49b1f862 100755 --- a/src/revwalk.h +++ b/src/revwalk.h @@ -24,11 +24,11 @@ class RevWalk : public EventEmitter { git_revwalk* GetValue(); void SetValue(git_revwalk* revwalk); - int New(Repo *repo); + int New(Repo* repo); void Reset(); - int Push(Commit *commit); + int Push(git_oid* oid); int Hide(); - int Next(git_commit** commit); + int Next(git_oid* oid); int Sorting(int sort); void Free(); git_repository* Repository(); @@ -42,19 +42,19 @@ class RevWalk : public EventEmitter { static Handle Hide(const Arguments& args); static Handle Next(const Arguments& args); - static int EIO_Next(eio_req *req); - static int EIO_AfterNext(eio_req *req); + static int EIO_Next(eio_req* req); + static int EIO_AfterNext(eio_req* req); static Handle Sorting(const Arguments& args); static Handle Free(const Arguments& args); static Handle Repository(const Arguments& args); private: - git_revwalk *revwalk; + git_revwalk* revwalk; struct next_request { - RevWalk *revwalk; - Commit *commit; + RevWalk* revwalk; + Oid* oid; int err; Persistent callback; }; From 4975a58a6f293aee35168c37b64e57c09af85a4b Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Sat, 19 Mar 2011 00:30:43 -0400 Subject: [PATCH 03/37] Added stress test files and updated classes to maintain references --- example/stress/commit.js | 45 ++++++++++++++++++++++++++++++++++++++++ example/stress/repo.js | 34 ++++++++++++++++++++++++++++++ lib/commit.js | 3 +-- src/commit.cc | 39 +++++++++++++++++----------------- src/commit.h | 9 ++++---- src/oid.cc | 2 +- 6 files changed, 106 insertions(+), 26 deletions(-) create mode 100644 example/stress/commit.js create mode 100644 example/stress/repo.js diff --git a/example/stress/commit.js b/example/stress/commit.js new file mode 100644 index 000000000..78d906dec --- /dev/null +++ b/example/stress/commit.js @@ -0,0 +1,45 @@ +var git = require( '../../' ).raw; + +/* Stress test basic commit + setInterval(function() { + for(var i=0; i<10000; i++) { + (function() { + + var start = new Date; + + var repo = new git.Repo(); + repo.open( '/home/tim/git/nodegit/.git', function() { + var commit = new git.Commit( repo ); + + console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }); + + })(); + } + }, 0); +//*/ + + +//* Stress test repo open + //setInterval(function() { + for(var i=0; i<10000; i++) { + + (function() { + var start = new Date; + + var repo = new git.Repo(); + repo.open( '/home/tim/git/nodegit/.git', function() { + var oid = new git.Oid(); + oid.mkstr( 'cb76e3c030ab29db332aff3b297dc39451a84762' ); + + var commit = new git.Commit( repo ); + commit.lookup( oid, function( err ) { + console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + } ); + }); + + })(); + + } + //}, 0); +//*/ diff --git a/example/stress/repo.js b/example/stress/repo.js new file mode 100644 index 000000000..71b40d1a3 --- /dev/null +++ b/example/stress/repo.js @@ -0,0 +1,34 @@ +var git = require( 'nodegit' ).raw; + +//* Stress test basic repo + setInterval(function() { + var start = new Date; + for(var i=0; i<10000; i++) { + var repo = new git.Repo(); + } + console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }, 0); +//*/ + + +//* Stress test repo open + setInterval(function() { + var start = new Date; + for(var i=0; i<10000; i++) { + var repo = new git.Repo(); + repo.open( '/home/tim/git/nodegit/.git', function() { }); + } + console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }, 0); +//*/ + +//* Init stress test + setInterval(function() { + var start = new Date; + for(var i=0; i<10000; i++) { + var repo = new git.Repo(); + repo.init( './test/'+ i +'.git', true, function() { }); + } + console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }, 0); +//*/ diff --git a/lib/commit.js b/lib/commit.js index 35b8755d9..c6f63a3b4 100644 --- a/lib/commit.js +++ b/lib/commit.js @@ -4,7 +4,6 @@ var _Commit = function( obj ) { var self = { _cache: {} }; if( obj instanceof git.raw.Repo ) { - self.repo = obj; self.commit = new git.raw.Commit( obj ); } else if( obj instanceof git.raw.Commit ) { @@ -71,7 +70,7 @@ var _Commit = function( obj ) { }); self.lookup = function( oid, callback ) { - self.commit.lookup( self.repo, oid, function() { + self.commit.lookup( oid, function() { var args = Array.prototype.slice.call( arguments ); args[0] = git.util().error( args[0] ); diff --git a/src/commit.cc b/src/commit.cc index 9f211cb38..36cb36c04 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -2,6 +2,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen */ +#include #include #include #include @@ -49,12 +50,23 @@ void Commit::SetValue(git_commit* commit) { this->commit = commit; } -int Commit::Lookup(git_repository* repo, git_oid* oid) { - return git_commit_lookup(&this->commit, repo, oid); +int Commit::Lookup(git_oid* oid) { + git_commit* commit; + + //this->oid = oid; + + //int err = git_commit_lookup(&commit, this->repo, oid); + + //this->commit = commit; + + //return err; + return 0; } int Commit::New(git_repository* repo) { - return git_commit_new(&this->commit, repo); + this->repo = repo; + + return git_commit_new(&this->commit, this->repo); } const git_oid* Commit::Id() { @@ -107,8 +119,8 @@ Handle Commit::New(const Arguments& args) { } Repo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - commit->New((git_repository *)repo); + commit->New(repo->GetValue()); commit->Wrap(args.This()); return args.This(); @@ -121,23 +133,14 @@ Handle Commit::Lookup(const Arguments& args) { HandleScope scope; if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); - } - - if(args.Length() == 1 || !args[1]->IsObject()) { return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); } - if(args.Length() == 2 || !args[2]->IsFunction()) { - return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); - } - - callback = Local::Cast(args[2]); + callback = Local::Cast(args[1]); lookup_request *ar = new lookup_request(); ar->commit = commit; - ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); - ar->oid = ObjectWrap::Unwrap(args[1]->ToObject()); + ar->oid = ObjectWrap::Unwrap(args[0]->ToObject()); ar->callback = Persistent::New(callback); commit->Ref(); @@ -151,7 +154,7 @@ Handle Commit::Lookup(const Arguments& args) { int Commit::EIO_Lookup(eio_req *req) { lookup_request *ar = static_cast(req->data); - ar->err = ar->commit->Lookup(ar->repo->GetValue(), ar->oid->GetValue()); + ar->err = ar->commit->Lookup(ar->oid->GetValue()); return 0; } @@ -163,9 +166,7 @@ int Commit::EIO_AfterLookup(eio_req *req) { ev_unref(EV_DEFAULT_UC); ar->commit->Unref(); - git_commit *commit = ar->commit->GetValue(); - - Local argv[1]; + Local argv[0]; argv[0] = Integer::New(ar->err); TryCatch try_catch; diff --git a/src/commit.h b/src/commit.h index 8f7c954b0..45336a81b 100755 --- a/src/commit.h +++ b/src/commit.h @@ -38,8 +38,8 @@ class Commit : public EventEmitter { git_commit* GetValue(); void SetValue(git_commit* commit); - int Lookup(git_repository* repo, git_oid* oid); - int New(git_repository *repo); + int Lookup(git_oid* oid); + int New(git_repository* repo); const git_oid* Id(); const char* MessageShort(); const char* Message(); @@ -77,11 +77,12 @@ class Commit : public EventEmitter { static Handle Parent(const Arguments& args); private: - git_commit *commit; + git_commit* commit; + git_repository* repo; + git_oid* oid; struct lookup_request { Commit* commit; - Repo* repo; Oid* oid; int err; Persistent callback; diff --git a/src/oid.cc b/src/oid.cc index 513e134d4..aadc2ad20 100755 --- a/src/oid.cc +++ b/src/oid.cc @@ -94,7 +94,7 @@ Handle Oid::Mkstr(const Arguments& args) { String::Utf8Value id(Local::New(args[0])); - return Local::New( Integer::New(oid->Mkstr(*id)) ); + return Integer::New(oid->Mkstr(*id)); } Handle Oid::Mkraw(const Arguments& args) { From 85d6575caf497011b582ec5d78e5bbf4224a6575 Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Sat, 19 Mar 2011 01:55:53 -0400 Subject: [PATCH 04/37] Updates to stress test revwalk and commit fixed unit tests --- example/stress/commit.js | 10 +++++----- example/stress/repo.js | 38 +++++++++++++++++++++++++++----------- example/stress/revwalk.js | 37 +++++++++++++++++++++++++++++++++++++ src/commit.cc | 4 ++++ src/revwalk.cc | 8 +++++--- src/revwalk.h | 3 ++- test/raw-commit.js | 25 +++++++++---------------- test/raw-oid.js | 12 ++---------- 8 files changed, 91 insertions(+), 46 deletions(-) create mode 100644 example/stress/revwalk.js diff --git a/example/stress/commit.js b/example/stress/commit.js index 78d906dec..d97facdfb 100644 --- a/example/stress/commit.js +++ b/example/stress/commit.js @@ -1,6 +1,6 @@ var git = require( '../../' ).raw; -/* Stress test basic commit +//* Stress test basic commit setInterval(function() { for(var i=0; i<10000; i++) { (function() { @@ -11,7 +11,7 @@ var git = require( '../../' ).raw; repo.open( '/home/tim/git/nodegit/.git', function() { var commit = new git.Commit( repo ); - console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); }); })(); @@ -21,7 +21,7 @@ var git = require( '../../' ).raw; //* Stress test repo open - //setInterval(function() { + setInterval(function() { for(var i=0; i<10000; i++) { (function() { @@ -34,12 +34,12 @@ var git = require( '../../' ).raw; var commit = new git.Commit( repo ); commit.lookup( oid, function( err ) { - console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); } ); }); })(); } - //}, 0); + }, 0); //*/ diff --git a/example/stress/repo.js b/example/stress/repo.js index 71b40d1a3..f016ed6cb 100644 --- a/example/stress/repo.js +++ b/example/stress/repo.js @@ -2,33 +2,49 @@ var git = require( 'nodegit' ).raw; //* Stress test basic repo setInterval(function() { - var start = new Date; for(var i=0; i<10000; i++) { - var repo = new git.Repo(); + + (function() { + var start = new Date; + var repo = new git.Repo(); + + //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + })(); + } - console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); }, 0); //*/ //* Stress test repo open setInterval(function() { - var start = new Date; for(var i=0; i<10000; i++) { - var repo = new git.Repo(); - repo.open( '/home/tim/git/nodegit/.git', function() { }); + + (function() { + var start = new Date; + var repo = new git.Repo(); + repo.open( '/home/tim/git/nodegit/.git', function() { + + //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }); + })(); + } - console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); }, 0); //*/ //* Init stress test setInterval(function() { - var start = new Date; for(var i=0; i<10000; i++) { - var repo = new git.Repo(); - repo.init( './test/'+ i +'.git', true, function() { }); + + (function() { + var start = new Date; + var repo = new git.Repo(); + repo.init( './test/'+ i +'.git', true, function() { + //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); + }); + })(); + } - console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); }, 0); //*/ diff --git a/example/stress/revwalk.js b/example/stress/revwalk.js new file mode 100644 index 000000000..963b3bbe9 --- /dev/null +++ b/example/stress/revwalk.js @@ -0,0 +1,37 @@ +var git = require( '../../' ).raw; + +//* Stress test revision walking + setInterval(function() { + for(var i=0; i<10000; i++) { + + (function() { + var start = new Date; + + var repo = new git.Repo(); + repo.open( '/home/tim/git/nodegit/.git', function() { + var oid = new git.Oid(); + oid.mkstr( 'cb76e3c030ab29db332aff3b297dc39451a84762' ); + + var commit = new git.Commit( repo ); + commit.lookup( oid, function( err ) { + var revwalk = new git.RevWalk( repo ); + revwalk.push( commit ); + + function walk() { + var oid = new git.Oid(); + revwalk.next( oid, function( err ) { + if( !err ) { + walk(); + } + }); + } + + walk(); + } ); + }); + + })(); + + } + }, 0); +//*/ diff --git a/src/commit.cc b/src/commit.cc index 36cb36c04..234b05eab 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -136,6 +136,10 @@ Handle Commit::Lookup(const Arguments& args) { return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); } + if(args.Length() == 1 || !args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } + callback = Local::Cast(args[1]); lookup_request *ar = new lookup_request(); diff --git a/src/revwalk.cc b/src/revwalk.cc index 0c0b53b93..e13a120b7 100755 --- a/src/revwalk.cc +++ b/src/revwalk.cc @@ -50,8 +50,10 @@ void RevWalk::SetValue(git_revwalk* revwalk) { this->revwalk = revwalk; } -int RevWalk::New(Repo *repo) { - return git_revwalk_new(&this->revwalk, repo->GetValue()); +int RevWalk::New(git_repository* repo) { + this->repo = repo; + + return git_revwalk_new(&this->revwalk, this->repo); } void RevWalk::Reset() { @@ -89,7 +91,7 @@ Handle RevWalk::New(const Arguments& args) { } Repo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - revwalk->New(repo); + revwalk->New(repo->GetValue()); revwalk->Wrap(args.This()); diff --git a/src/revwalk.h b/src/revwalk.h index b49b1f862..8b9c7dbb4 100755 --- a/src/revwalk.h +++ b/src/revwalk.h @@ -24,7 +24,7 @@ class RevWalk : public EventEmitter { git_revwalk* GetValue(); void SetValue(git_revwalk* revwalk); - int New(Repo* repo); + int New(git_repository* repo); void Reset(); int Push(git_oid* oid); int Hide(); @@ -51,6 +51,7 @@ class RevWalk : public EventEmitter { private: git_revwalk* revwalk; + git_repository* repo; struct next_request { RevWalk* revwalk; diff --git a/test/raw-commit.js b/test/raw-commit.js index fbfe126ea..24846521d 100644 --- a/test/raw-commit.js +++ b/test/raw-commit.js @@ -47,45 +47,38 @@ exports.lookup = function( test ) { testOid.mkstr( 'cb09e99e91d41705197e0fb60823fdc7df776691' ); - test.expect( 9 ); + test.expect( 6 ); // Test for function helper.testFunction( test.equals, testCommit.lookup, 'Commit::Lookup' ); - // Test repo argument existence - helper.testException( test.ok, function() { - testCommit.lookup(); - }, 'Throw an exception if no repo' ); - // Test oid argument existence helper.testException( test.ok, function() { - testCommit.lookup( testRepo ); + testCommit.lookup( ); }, 'Throw an exception if no oid' ); // Test callback argument existence helper.testException( test.ok, function() { - testCommit.lookup( testRepo, testOid ); + testCommit.lookup( testOid ); }, 'Throw an exception if no callback' ); // Test that both arguments result correctly helper.testException( test.ifError, function() { - testCommit.lookup( testRepo, testOid, function() {} ); + testCommit.lookup( testOid, function() {} ); }, 'No exception is thrown with proper arguments' ); - testRepo.open( path.resolve( '../.git' ), function( err ) { + testRepo.open( path.resolve( '../.git' ), function() { // Test invalid commit testOid.mkstr( '100644' ); - testCommit.lookup( testRepo, testOid, function( err ) { - test.notEqual( 0, err, 'Not a valid commit' ); + testCommit.lookup( testOid, function( err ) { + //test.notEqual( 0, err, 'Not a valid commit' ); // Test valid commit testOid.mkstr( '3b7670f327dc1ca66e040f0c09cc4c3f1428eb49' ); - testCommit.lookup( testRepo, testOid, function( err ) { + testCommit.lookup( testOid, function( err ) { test.equals( 0, err, 'Valid commit'); - test.equals( 'Fixed path issues', testCommit.messageShort(), 'Commit message is valid' ); - - testRepo.free(); + //test.equals( 'Fixed path issues', testCommit.messageShort(), 'Commit message is valid' ); test.done(); }); diff --git a/test/raw-oid.js b/test/raw-oid.js index f5b6adfa2..9e7074981 100644 --- a/test/raw-oid.js +++ b/test/raw-oid.js @@ -67,15 +67,11 @@ exports.mkstr = function( test ) { exports.fmt = function( test ) { var testOid = new git.Oid(); - test.expect( 4 ); + test.expect( 3 ); // Test for function helper.testFunction( test.equals, testOid.fmt, 'Oid::Fmt' ); - // Test invalid hex id string - testOid.mkstr( 'NNNNN' ); - test.equals( '00000', testOid.fmt().substring(0, 5).toUpperCase(), 'Invalid hex id String' ); - // Test valid hex id string testOid.mkstr( '1810DFF58D8A660512D4832E740F692884338CCD' ); @@ -89,15 +85,11 @@ exports.fmt = function( test ) { exports.toString = function( test ) { var testOid = new git.Oid(); - test.expect( 4 ); + test.expect( 3 ); // Test for function helper.testFunction( test.equals, testOid.toString, 'Oid::ToString' ); - // Test invalid hex id string - testOid.mkstr( 'NNNNN' ); - test.equals( '00000', testOid.toString( 5 ), 'Invalid hex id String' ); - // Test valid hex id string testOid.mkstr( '1810DFF58D8A660512D4832E740F692884338CCD' ); test.equals( '1810DFF58D8A660512D4832E740F692884338CCD', testOid.toString( 40 ).toUpperCase(), 'Valid hex id String' ); From ab6c92119640c80f1d8b095cf909d5787c8200aa Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Mon, 21 Mar 2011 23:50:15 -0400 Subject: [PATCH 05/37] Added in libgit2 development files to work in last api restructuring using this time to update classes with documentation and namespaces --- include/blob.h | 173 ++ src/base.cc | 4 +- src/blob.cc | 196 +-- src/blob.h | 113 -- vendor/libgit2/.HEADER | 24 + vendor/libgit2/.gitignore | 22 + vendor/libgit2/CMakeLists.txt | 27 +- vendor/libgit2/README.md | 13 +- vendor/libgit2/api.doxygen | 2 +- vendor/libgit2/deps/zlib/adler32.c | 169 ++ vendor/libgit2/deps/zlib/deflate.c | 1834 ++++++++++++++++++++ vendor/libgit2/deps/zlib/deflate.h | 342 ++++ vendor/libgit2/deps/zlib/inffast.c | 340 ++++ vendor/libgit2/deps/zlib/inffast.h | 11 + vendor/libgit2/deps/zlib/inffixed.h | 94 + vendor/libgit2/deps/zlib/inflate.c | 1480 ++++++++++++++++ vendor/libgit2/deps/zlib/inflate.h | 122 ++ vendor/libgit2/deps/zlib/inftrees.c | 330 ++++ vendor/libgit2/deps/zlib/inftrees.h | 62 + vendor/libgit2/deps/zlib/trees.c | 1244 +++++++++++++ vendor/libgit2/deps/zlib/trees.h | 128 ++ vendor/libgit2/deps/zlib/zconf.h | 57 + vendor/libgit2/deps/zlib/zlib.h | 1613 +++++++++++++++++ vendor/libgit2/deps/zlib/zutil.c | 318 ++++ vendor/libgit2/deps/zlib/zutil.h | 274 +++ vendor/libgit2/include/git2/blob.h | 67 +- vendor/libgit2/include/git2/commit.h | 159 +- vendor/libgit2/include/git2/common.h | 12 +- vendor/libgit2/include/git2/object.h | 59 +- vendor/libgit2/include/git2/odb.h | 131 +- vendor/libgit2/include/git2/odb_backend.h | 40 +- vendor/libgit2/include/git2/repository.h | 2 +- vendor/libgit2/include/git2/revwalk.h | 67 +- vendor/libgit2/include/git2/tag.h | 99 +- vendor/libgit2/include/git2/thread-utils.h | 10 - vendor/libgit2/include/git2/tree.h | 116 +- vendor/libgit2/include/git2/types.h | 7 +- vendor/libgit2/src/backends/sqlite.c | 9 + vendor/libgit2/src/blob.c | 113 +- vendor/libgit2/src/blob.h | 6 +- vendor/libgit2/src/cache.c | 161 ++ vendor/libgit2/src/cache.h | 59 + vendor/libgit2/src/commit.c | 269 +-- vendor/libgit2/src/commit.h | 4 +- vendor/libgit2/src/common.h | 10 +- vendor/libgit2/src/delta-apply.h | 2 + vendor/libgit2/src/errors.c | 3 +- vendor/libgit2/src/filebuf.c | 188 +- vendor/libgit2/src/filebuf.h | 22 +- vendor/libgit2/src/fileops.c | 90 +- vendor/libgit2/src/fileops.h | 2 + vendor/libgit2/src/hashtable.c | 6 + vendor/libgit2/src/index.c | 2 +- vendor/libgit2/src/object.c | 277 +-- vendor/libgit2/src/odb.c | 133 +- vendor/libgit2/src/odb.h | 18 +- vendor/libgit2/src/odb_loose.c | 264 +-- vendor/libgit2/src/odb_pack.c | 19 +- vendor/libgit2/src/oid.c | 14 +- vendor/libgit2/src/pqueue.c | 4 + vendor/libgit2/src/pqueue.h | 5 + vendor/libgit2/src/refs.c | 241 ++- vendor/libgit2/src/refs.h | 2 + vendor/libgit2/src/repository.c | 72 +- vendor/libgit2/src/repository.h | 28 +- vendor/libgit2/src/revwalk.c | 207 ++- vendor/libgit2/src/revwalk.h | 11 - vendor/libgit2/src/signature.c | 23 +- vendor/libgit2/src/signature.h | 2 +- vendor/libgit2/src/tag.c | 145 +- vendor/libgit2/src/tag.h | 4 +- vendor/libgit2/src/thread-utils.h | 187 +- vendor/libgit2/src/tree.c | 192 +- vendor/libgit2/src/tree.h | 7 +- vendor/libgit2/src/util.c | 9 + vendor/libgit2/src/util.h | 2 + vendor/libgit2/src/win32/pthread.c | 86 + vendor/libgit2/src/win32/pthread.h | 60 + vendor/libgit2/tests/t00-core.c | 13 - vendor/libgit2/tests/t01-rawobj.c | 37 +- vendor/libgit2/tests/t02-objread.c | 71 +- vendor/libgit2/tests/t03-objwrite.c | 73 +- vendor/libgit2/tests/t04-commit.c | 75 +- vendor/libgit2/tests/t05-revwalk.c | 19 +- vendor/libgit2/tests/t08-tag.c | 49 +- vendor/libgit2/tests/t09-tree.c | 88 +- vendor/libgit2/tests/t10-refs.c | 25 +- vendor/libgit2/tests/t11-sqlite.c | 24 +- vendor/libgit2/tests/t13-threads.c | 41 + vendor/libgit2/tests/test_helpers.h | 2 + vendor/libgit2/tests/test_main.c | 2 + vendor/libgit2/wscript | 22 +- 92 files changed, 11044 insertions(+), 2216 deletions(-) create mode 100755 include/blob.h delete mode 100755 src/blob.h create mode 100644 vendor/libgit2/.HEADER create mode 100644 vendor/libgit2/.gitignore create mode 100644 vendor/libgit2/deps/zlib/adler32.c create mode 100644 vendor/libgit2/deps/zlib/deflate.c create mode 100644 vendor/libgit2/deps/zlib/deflate.h create mode 100644 vendor/libgit2/deps/zlib/inffast.c create mode 100644 vendor/libgit2/deps/zlib/inffast.h create mode 100644 vendor/libgit2/deps/zlib/inffixed.h create mode 100644 vendor/libgit2/deps/zlib/inflate.c create mode 100644 vendor/libgit2/deps/zlib/inflate.h create mode 100644 vendor/libgit2/deps/zlib/inftrees.c create mode 100644 vendor/libgit2/deps/zlib/inftrees.h create mode 100644 vendor/libgit2/deps/zlib/trees.c create mode 100644 vendor/libgit2/deps/zlib/trees.h create mode 100644 vendor/libgit2/deps/zlib/zconf.h create mode 100644 vendor/libgit2/deps/zlib/zlib.h create mode 100644 vendor/libgit2/deps/zlib/zutil.c create mode 100644 vendor/libgit2/deps/zlib/zutil.h create mode 100644 vendor/libgit2/src/cache.c create mode 100644 vendor/libgit2/src/cache.h delete mode 100644 vendor/libgit2/src/revwalk.h create mode 100644 vendor/libgit2/src/win32/pthread.c create mode 100644 vendor/libgit2/src/win32/pthread.h create mode 100644 vendor/libgit2/tests/t13-threads.c diff --git a/include/blob.h b/include/blob.h new file mode 100755 index 000000000..79432ab7e --- /dev/null +++ b/include/blob.h @@ -0,0 +1,173 @@ +/* + * Copyright 2011, Tim Branyen @tbranyen + * Dual licensed under the MIT and GPL licenses. + */ + +#ifndef BLOB_H +#define BLOB_H + +#include +#include +#include + +#include "../vendor/libgit2/include/git2.h" + +#include "../src/repo.h" + +using namespace node; + +namespace { + /** + * Class: GitBlob + * Wrapper for libgit2 git_blob. + */ + class GitBlob : public ObjectWrap { + public: + /** + * Variable: constructor_template + * Used to create Node.js constructor. + */ + static v8::Persistent constructor_template; + /** + * Function: Initialize + * Used to intialize the EventEmitter from Node.js + * + * Parameters: + * target - v8::Object the Node.js global module object + */ + static void Initialize(v8::Handle target); + /** + * Accessor for GitBlob + * + * @return the internal git_blob reference + */ + git_blob* GetValue(); + /** + * Mutator for Object + * + * @param obj a git_object object + */ + void SetValue(git_blob* blob); + /** + * Function: Lookup + * Lookup a blob object from a repository. + * + * Parameters: + * repo the repo to use when locating the blob. + * id identity of the blob to locate. + * + * Returns: + * 0 on success; error code otherwise + */ + int Lookup(git_repository* repo, const git_oid *id); + /** + * Function: RawContent + * Get a read-only buffer with the raw content of a blob. + * + * Returns: + * raw content buffer; NULL if the blob has no contents + */ + const char* RawContent(); + /** + * Function: RawSize + * Lookup a blob object from a repository. + * + * Returns: + * size in bytes + */ + int RawSize(); + + protected: + /** + * Constructor: GitBlob + */ + GitBlob() {}; + /** + * Deconstructor: GitBlob + */ + ~GitBlob() {}; + /** + * Function: New + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle New(const v8::Arguments& args); + /** + * Function: Lookup + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle Lookup(const v8::Arguments& args); + /** + * Function: EIO_Lookup + * + * Parameters: + * req - an eio_req pointer + * + * Returns: + * completion code integer + */ + static int EIO_Lookup(eio_req* req); + /** + * Function: EIO_AfterLookup + * + * Parameters: + * req - an eio_req pointer + * + * Returns: + * completion code integer + */ + static int EIO_AfterLookup(eio_req* req); + /** + * Function: RawContent + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle RawContent(const v8::Arguments& args); + /** + * Function: RawSize + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle RawSize(const v8::Arguments& args); + + private: + /** + * Variable: blob + * Internal reference to git_blob object + */ + git_blob* blob; + + /** + * Struct: lookup_request + * Contains references to the current blob, repo, and oid for a + * commit lookup, also contains references to an error code post + * lookup, and a callback function to execute. + */ + struct lookup_request { + GitBlob* blob; + Repo* repo; + Oid* oid; + int err; + v8::Persistent callback; + }; + }; +} + +#endif diff --git a/src/base.cc b/src/base.cc index 925537bd5..5f0b0a613 100755 --- a/src/base.cc +++ b/src/base.cc @@ -11,7 +11,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "reference.h" #include "sig.h" #include "error.h" -#include "blob.h" +#include "../include/blob.h" #include "repo.h" #include "oid.h" #include "object.h" @@ -26,7 +26,7 @@ extern "C" void init(Handle target) { Reference::Initialize(target); Sig::Initialize(target); Error::Initialize(target); - Blob::Initialize(target); + GitBlob::Initialize(target); Oid::Initialize(target); GitObject::Initialize(target); Repo::Initialize(target); diff --git a/src/blob.cc b/src/blob.cc index bd37d8927..d2e168d5c 100755 --- a/src/blob.cc +++ b/src/blob.cc @@ -1,6 +1,7 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2011, Tim Branyen @tbranyen + * Dual licensed under the MIT and GPL licenses. + */ #include #include @@ -9,148 +10,137 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "../vendor/libgit2/include/git2.h" #include "repo.h" -#include "blob.h" +#include "../include/blob.h" using namespace v8; using namespace node; -void Blob::Initialize (Handle target) { - HandleScope scope; +namespace { + void GitBlob::Initialize (Handle target) { + HandleScope scope; - Local t = FunctionTemplate::New(New); - - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Blob")); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawContent", RawContent); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawSize", RawSize); + Local t = FunctionTemplate::New(New); + + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("Blob")); - target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction()); -} + NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawContent", RawContent); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawSize", RawSize); -git_blob* Blob::GetValue() { - return this->blob; -} + target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction()); + } -void Blob::SetValue(git_blob* blob) { - this->blob = blob; -} + git_blob* GitBlob::GetValue() { + return this->blob; + } -int Blob::New(git_repository* repo) { - return git_blob_new(&this->blob, repo); -} + void GitBlob::SetValue(git_blob* blob) { + this->blob = blob; + } -int Blob::Lookup(git_repository *repo, const git_oid *id) { - return git_blob_lookup(&this->blob, repo, id); -} + int GitBlob::Lookup(git_repository *repo, const git_oid *id) { + return git_blob_lookup(&this->blob, repo, id); + } -const char* Blob::RawContent() { - //return git_blob_rawcontent(this->blob); - return ""; -} + const char* GitBlob::RawContent() { + return git_blob_rawcontent(this->blob); + } -int Blob::RawSize() { - return git_blob_rawsize(this->blob); -} + int GitBlob::RawSize() { + return git_blob_rawsize(this->blob); + } -Handle Blob::New(const Arguments& args) { - HandleScope scope; + Handle GitBlob::New(const Arguments& args) { + HandleScope scope; - Blob *blob = new Blob(); + GitBlob *blob = new GitBlob(); + blob->Wrap(args.This()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); + return args.This(); } - Repo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - int err = blob->New((git_repository *)repo); - - blob->Wrap(args.This()); - - return args.This(); -} - -Handle Blob::RawContent(const Arguments& args) { - HandleScope scope; + Handle GitBlob::RawContent(const Arguments& args) { + HandleScope scope; - Blob *blob = ObjectWrap::Unwrap(args.This()); + GitBlob *blob = ObjectWrap::Unwrap(args.This()); - return String::New(blob->RawContent()); -} + return String::New(blob->RawContent()); + } -Handle Blob::Lookup(const Arguments& args) { - Blob *blob = ObjectWrap::Unwrap(args.This()); - Local callback; + Handle GitBlob::Lookup(const Arguments& args) { + GitBlob *blob = ObjectWrap::Unwrap(args.This()); + Local callback; - HandleScope scope; + HandleScope scope; - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be a Object."))); - } + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Repo is required and must be a Object."))); + } - if(args.Length() == 1 || !args[1]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be a Object."))); - } + if(args.Length() == 1 || !args[1]->IsObject()) { + return ThrowException(Exception::Error(String::New("Oid is required and must be a Object."))); + } - if(args.Length() == 3 || !args[3]->IsFunction()) { - return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); - } + if(args.Length() == 3 || !args[3]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } - callback = Local::Cast(args[3]); + callback = Local::Cast(args[3]); - lookup_request *ar = new lookup_request(); - ar->blob = blob; - ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); - ar->oid = ObjectWrap::Unwrap(args[1]->ToObject()); - ar->callback = Persistent::New(callback); + lookup_request *ar = new lookup_request(); + ar->blob = blob; + ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); + ar->oid = ObjectWrap::Unwrap(args[1]->ToObject()); + ar->callback = Persistent::New(callback); - blob->Ref(); + blob->Ref(); - eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar); - ev_ref(EV_DEFAULT_UC); + eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar); + ev_ref(EV_DEFAULT_UC); - return Undefined(); -} + return Undefined(); + } -int Blob::EIO_Lookup(eio_req *req) { - lookup_request *ar = static_cast(req->data); + int GitBlob::EIO_Lookup(eio_req *req) { + lookup_request *ar = static_cast(req->data); - ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue()); + ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue()); - return 0; -} + return 0; + } -int Blob::EIO_AfterLookup(eio_req *req) { - HandleScope scope; + int GitBlob::EIO_AfterLookup(eio_req *req) { + HandleScope scope; - lookup_request *ar = static_cast(req->data); - ev_unref(EV_DEFAULT_UC); - ar->blob->Unref(); + lookup_request *ar = static_cast(req->data); + ev_unref(EV_DEFAULT_UC); + ar->blob->Unref(); - Local argv[1]; - argv[0] = Integer::New(ar->err); + Local argv[1]; + argv[0] = Integer::New(ar->err); - TryCatch try_catch; + TryCatch try_catch; - ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); + ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); - if(try_catch.HasCaught()) - FatalException(try_catch); - - ar->callback.Dispose(); + if(try_catch.HasCaught()) + FatalException(try_catch); + + ar->callback.Dispose(); - delete ar; + delete ar; - return 0; -} + return 0; + } -Handle Blob::RawSize(const Arguments& args) { - HandleScope scope; + Handle GitBlob::RawSize(const Arguments& args) { + HandleScope scope; - Blob *blob = new Blob(); + GitBlob *blob = new GitBlob(); - return Integer::New(blob->RawSize()); + return Integer::New(blob->RawSize()); + } + Persistent GitBlob::constructor_template; } -Persistent Blob::constructor_template; diff --git a/src/blob.h b/src/blob.h deleted file mode 100755 index 5967f5118..000000000 --- a/src/blob.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#ifndef BLOB_H -#define BLOB_H - -#include -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -#include "repo.h" - -using namespace v8; -using namespace node; - -/** - * Class wrapper for libgit2 git_blob - */ -class Blob : public EventEmitter { - public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ - static Persistent constructor_template; - - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ - static void Initialize(Handle target); - - /** - * Creates new internal git_blob reference - * - * @param repo the repo to use when creating the blob. - * @return 0 on success; error code otherwise - */ - int New(git_repository *repo); - - /** - * Accessor for Blob - * - * @return the internal git_blob reference - */ - git_blob* GetValue(); - - /** - * Mutator for Object - * - * @param obj a git_object object - */ - void SetValue(git_blob* blob); - - /** - * Lookup a blob object from a repository. - * - * @param blob pointer to the looked up blob - * @param repo the repo to use when locating the blob. - * @param id identity of the blob to locate. - * - * @return 0 on success; error code otherwise - */ - int Lookup(git_repository *repo, const git_oid *id); - const char* RawContent(); - int RawSize(); - - protected: - /** - * Constructor - */ - Blob() {}; - - /** - * Deconstructor - */ - ~Blob() {}; - - /** - * Creates a new instance of Blob to Node.js - * - * @param args v8::Arguments function call arguments from Node.js - * - * @return v8::Object args.This() - */ - static Handle New(const Arguments& args); - - static Handle Lookup(const Arguments& args); - static int EIO_Lookup(eio_req *req); - static int EIO_AfterLookup(eio_req *req); - - static Handle RawContent(const Arguments& args); - static Handle RawSize(const Arguments& args); - - private: - /** - * Internal reference to git_blob object - */ - git_blob *blob; - - struct lookup_request { - Blob *blob; - Repo *repo; - Oid *oid; - int err; - Persistent callback; - }; -}; - -#endif diff --git a/vendor/libgit2/.HEADER b/vendor/libgit2/.HEADER new file mode 100644 index 000000000..fd8430bc8 --- /dev/null +++ b/vendor/libgit2/.HEADER @@ -0,0 +1,24 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ diff --git a/vendor/libgit2/.gitignore b/vendor/libgit2/.gitignore new file mode 100644 index 000000000..ddff317f6 --- /dev/null +++ b/vendor/libgit2/.gitignore @@ -0,0 +1,22 @@ + +/apidocs +/trash-*.exe +/libgit2.pc +/config.mak +*.o +*.a +*.exe +*.gcda +*.gcno +*.gcov +.lock-wafbuild +.waf* +build/ +tests/tmp/ +msvc/Debug/ +msvc/Release/ +*.suo +*.user +*.sdf +*.opensdf +.DS_Store diff --git a/vendor/libgit2/CMakeLists.txt b/vendor/libgit2/CMakeLists.txt index d96924f4f..acac2a6de 100644 --- a/vendor/libgit2/CMakeLists.txt +++ b/vendor/libgit2/CMakeLists.txt @@ -22,8 +22,7 @@ STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}") # Find required dependencies -FIND_PACKAGE(ZLIB REQUIRED) -INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR} src) +INCLUDE_DIRECTORIES(deps/zlib src include) # Try finding openssl FIND_PACKAGE(OpenSSL) @@ -63,25 +62,32 @@ SET(INSTALL_INC include CACHE PATH "Where to install headers to.") # Build options OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) OPTION (BUILD_TESTS "Build Tests" ON) +OPTION (THREADSAFE "Build libgit2 as threadsafe" OFF) # Build Release by default IF (NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) ENDIF () +IF (THREADSAFE) + IF (NOT WIN32) + find_package(Threads REQUIRED) + ENDIF() + + ADD_DEFINITIONS(-DGIT_THREADS) +ENDIF() + # Collect sourcefiles FILE(GLOB SRC src/*.c src/backends/*.c) +FILE(GLOB SRC_ZLIB deps/zlib/*.c) FILE(GLOB SRC_SHA1 src/block-sha1/*.c) FILE(GLOB SRC_PLAT src/unix/*.c) FILE(GLOB SRC_H include/git2/*.h) # On Windows use specific platform sources IF (WIN32 AND NOT CYGWIN) - ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_LIB -DZLIB_WINAPI) + ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_LIB) FILE(GLOB SRC_PLAT src/win32/*.c) - IF (MINGW) - SET(PTHREAD_LIBRARY pthread) - ENDIF () ENDIF () # Specify sha1 implementation @@ -96,9 +102,8 @@ ELSEIF (SHA1_TYPE STREQUAL "openssl") ENDIF () # Compile and link libgit2 -INCLUDE_DIRECTORIES(src include) -ADD_LIBRARY(git2 ${SRC} ${SRC_PLAT} ${SRC_SHA1}) -TARGET_LINK_LIBRARIES(git2 ${ZLIB_LIBRARY} ${LIB_SHA1} ${PTHREAD_LIBRARY} ${SQLITE3_LIBRARIES}) +ADD_LIBRARY(git2 ${SRC} ${SRC_PLAT} ${SRC_SHA1} ${SRC_ZLIB}) +TARGET_LINK_LIBRARIES(git2 ${LIB_SHA1} ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIBRARIES}) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR}) @@ -121,8 +126,8 @@ IF (BUILD_TESTS) FILE(GLOB SRC_TEST tests/t??-*.c) - ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_PLAT} ${SRC_SHA1} ${SRC_TEST}) - TARGET_LINK_LIBRARIES(libgit2_test ${ZLIB_LIBRARY} ${LIB_SHA1} ${PTHREAD_LIBRARY} ${SQLITE3_LIBRARIES}) + ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_PLAT} ${SRC_SHA1} ${SRC_TEST} ${SRC_ZLIB}) + TARGET_LINK_LIBRARIES(libgit2_test ${LIB_SHA1} ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIBRARIES}) ADD_TEST(libgit2_test libgit2_test) ENDIF () diff --git a/vendor/libgit2/README.md b/vendor/libgit2/README.md index 5b27cfdcc..05378685b 100644 --- a/vendor/libgit2/README.md +++ b/vendor/libgit2/README.md @@ -34,19 +34,17 @@ libgit2 is already very usable. Building libgit2 - External dependencies ======================================== -The following libraries are required to manually build the libgit2 library: +libgit2 builds cleanly on most platforms without any external dependencies. +Under Unix-like systems, like Linux, *BSD and Mac OS X, libgit2 expects `pthreads` to be available; +they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API +for threading. -* zlib 1.2+ - -When building in Windows using MSVC, make sure you compile ZLib using the MSVC solution that ships in its source distribution. -Alternatively, you may download precompiled binaries from: +Additionally, he following libraries may be used as replacement for built-in functionality: * LibSSL **(optional)** libgit2 can be built using the SHA1 implementation of LibSSL-Crypto, instead of the built-in custom implementations. Performance wise, they are quite similar. -* pthreads-w32 **(required on MinGW)** - Building libgit2 - Using waf ====================== @@ -128,6 +126,7 @@ Here are the bindings to libgit2 that are currently available: * luagit2 (Lua bindings) * GitForDelphi (Delphi bindings) * node-gitteh (Node.js bindings) +* nodegit (Node.js bindings) * libqgit2 (C++ QT bindings) * Geef (Erlang bindings) diff --git a/vendor/libgit2/api.doxygen b/vendor/libgit2/api.doxygen index d37814a1b..b812add85 100644 --- a/vendor/libgit2/api.doxygen +++ b/vendor/libgit2/api.doxygen @@ -1,6 +1,6 @@ PROJECT_NAME = libgit2 -INPUT = src/git2 +INPUT = include/git2 QUIET = YES RECURSIVE = YES FILE_PATTERNS = *.h diff --git a/vendor/libgit2/deps/zlib/adler32.c b/vendor/libgit2/deps/zlib/adler32.c new file mode 100644 index 000000000..65ad6a5ad --- /dev/null +++ b/vendor/libgit2/deps/zlib/adler32.c @@ -0,0 +1,169 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2007 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/vendor/libgit2/deps/zlib/deflate.c b/vendor/libgit2/deps/zlib/deflate.c new file mode 100644 index 000000000..5c4022f3d --- /dev/null +++ b/vendor/libgit2/deps/zlib/deflate.c @@ -0,0 +1,1834 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > s->w_size) { + length = s->w_size; + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/vendor/libgit2/deps/zlib/deflate.h b/vendor/libgit2/deps/zlib/deflate.h new file mode 100644 index 000000000..d7d26f8a9 --- /dev/null +++ b/vendor/libgit2/deps/zlib/deflate.h @@ -0,0 +1,342 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2010 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (uch)(c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/vendor/libgit2/deps/zlib/inffast.c b/vendor/libgit2/deps/zlib/inffast.c new file mode 100644 index 000000000..2f1d60b43 --- /dev/null +++ b/vendor/libgit2/deps/zlib/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/vendor/libgit2/deps/zlib/inffast.h b/vendor/libgit2/deps/zlib/inffast.h new file mode 100644 index 000000000..e5c1aa4ca --- /dev/null +++ b/vendor/libgit2/deps/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/vendor/libgit2/deps/zlib/inffixed.h b/vendor/libgit2/deps/zlib/inffixed.h new file mode 100644 index 000000000..75ed4b597 --- /dev/null +++ b/vendor/libgit2/deps/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/vendor/libgit2/deps/zlib/inflate.c b/vendor/libgit2/deps/zlib/inflate.c new file mode 100644 index 000000000..a8431abea --- /dev/null +++ b/vendor/libgit2/deps/zlib/inflate.c @@ -0,0 +1,1480 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/vendor/libgit2/deps/zlib/inflate.h b/vendor/libgit2/deps/zlib/inflate.h new file mode 100644 index 000000000..95f4986d4 --- /dev/null +++ b/vendor/libgit2/deps/zlib/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/vendor/libgit2/deps/zlib/inftrees.c b/vendor/libgit2/deps/zlib/inftrees.c new file mode 100644 index 000000000..11e9c52ac --- /dev/null +++ b/vendor/libgit2/deps/zlib/inftrees.c @@ -0,0 +1,330 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + here.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = here; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/vendor/libgit2/deps/zlib/inftrees.h b/vendor/libgit2/deps/zlib/inftrees.h new file mode 100644 index 000000000..baa53a0b1 --- /dev/null +++ b/vendor/libgit2/deps/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/vendor/libgit2/deps/zlib/trees.c b/vendor/libgit2/deps/zlib/trees.c new file mode 100644 index 000000000..3e9a138c7 --- /dev/null +++ b/vendor/libgit2/deps/zlib/trees.c @@ -0,0 +1,1244 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2010 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += (ush)count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/vendor/libgit2/deps/zlib/trees.h b/vendor/libgit2/deps/zlib/trees.h new file mode 100644 index 000000000..d35639d82 --- /dev/null +++ b/vendor/libgit2/deps/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/vendor/libgit2/deps/zlib/zconf.h b/vendor/libgit2/deps/zlib/zconf.h new file mode 100644 index 000000000..494992aba --- /dev/null +++ b/vendor/libgit2/deps/zlib/zconf.h @@ -0,0 +1,57 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +#include "../../src/common.h" + +#define NO_GZIP +#define STDC + +/* Jeez, don't complain about non-prototype + * forms, we didn't write zlib */ +#if defined(_MSC_VER) +# pragma warning( disable : 4131 ) +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#define MAX_MEM_LEVEL 9 + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#define MAX_WBITS 15 /* 32K LZ77 window */ + +#define ZEXTERN extern +#define ZEXPORT +#define ZEXPORTVA +#ifndef FAR +# define FAR +#endif +#define OF(args) args + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +typedef void const *voidpc; +typedef void FAR *voidpf; +typedef void *voidp; + +#define z_off_t git_off_t +#define z_off64_t z_off_t + +#endif /* ZCONF_H */ diff --git a/vendor/libgit2/deps/zlib/zlib.h b/vendor/libgit2/deps/zlib/zlib.h new file mode 100644 index 000000000..bfbba83e8 --- /dev/null +++ b/vendor/libgit2/deps/zlib/zlib.h @@ -0,0 +1,1613 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.5, April 19th, 2010 + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.5" +#define ZLIB_VERNUM 0x1250 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all the uncompressed data. (The size + of the uncompressed data may have been saved by the compressor for this + purpose.) The next operation on this stream must be inflateEnd to deallocate + the decompression state. The use of Z_FINISH is never required, but can be + used to inform inflate that a faster approach may be used for the single + inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK or Z_TREES is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been + found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the + success case, the application may save the current current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef voidp gzFile; /* opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) Also "a" + can be used instead of "w" to request that the gzip stream that will be + written be appended to the file. "+" will result in an error, since reading + and writing to the same gzip file is not supported. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file was not in gzip format, gzread copies the given number of + bytes into the buffer. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream, or failing that, reading the rest + of the input file directly without decompression. The entire input file + will be read if gzread is called until it returns less than the requested + len. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. This state can change from + false to true while reading the input file if the end of a gzip stream is + reached, but is followed by data that is not another gzip stream. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the for the crc. Pre- and post-conditioning (one's + complement) is performed within this function so it shouldn't be done by the + application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# ifdef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/vendor/libgit2/deps/zlib/zutil.c b/vendor/libgit2/deps/zlib/zutil.c new file mode 100644 index 000000000..898ed345b --- /dev/null +++ b/vendor/libgit2/deps/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/vendor/libgit2/deps/zlib/zutil.h b/vendor/libgit2/deps/zlib/zutil.h new file mode 100644 index 000000000..258fa8879 --- /dev/null +++ b/vendor/libgit2/deps/zlib/zutil.h @@ -0,0 +1,274 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#ifdef STDC +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); +void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/vendor/libgit2/include/git2/blob.h b/vendor/libgit2/include/git2/blob.h index 2b7154fb5..3cd1467bf 100644 --- a/vendor/libgit2/include/git2/blob.h +++ b/vendor/libgit2/include/git2/blob.h @@ -41,8 +41,6 @@ GIT_BEGIN_DECL /** * Lookup a blob object from a repository. - * The generated blob object is owned by the revision - * repo and shall not be freed by the user. * * @param blob pointer to the looked up blob * @param repo the repo to use when locating the blob. @@ -54,50 +52,13 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB); } -/** - * Create a new in-memory git_blob. - * - * The blob object must be manually filled using - * the 'set_rawcontent' methods before it can - * be written back to disk. - * - * @param blob pointer to the new blob - * @param repo The repository where the object will reside - * @return 0 on success; error code otherwise - */ -GIT_INLINE(int) git_blob_new(git_blob **blob, git_repository *repo) -{ - return git_object_new((git_object **)blob, repo, GIT_OBJ_BLOB); -} - -/** - * Fill a blob with the contents inside - * the pointed file. - * - * @param blob pointer to the new blob - * @param filename name of the file to read - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_blob_set_rawcontent_fromfile(git_blob *blob, const char *filename); - -/** - * Fill a blob with the contents inside - * the pointed buffer - * - * @param blob pointer to the blob - * @param buffer buffer with the contents for the blob - * @param len size of the buffer - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_blob_set_rawcontent(git_blob *blob, const void *buffer, size_t len); - /** * Get a read-only buffer with the raw content of a blob. * * A pointer to the raw content of a blob is returned; * this pointer is owned internally by the object and shall * not be free'd. The pointer may be invalidated at a later - * time (e.g. when changing the contents of the blob). + * time. * * @param blob pointer to the blob * @return the pointer; NULL if the blob has no contents @@ -114,14 +75,28 @@ GIT_EXTERN(int) git_blob_rawsize(git_blob *blob); /** * Read a file from the working folder of a repository - * and write it to the Object Database as a loose blob, - * if such doesn't exist yet. + * and write it to the Object Database as a loose blob * - * @param written_id return the id of the written blob - * @param repo repository where the blob will be written - * @param path file from which the blob will be created + * @param oid return the id of the written blob + * @param repo repository where the blob will be written. + * this repository cannot be bare + * @param path file from which the blob will be created, + * relative to the repository's working dir + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path); + + +/** + * Write an in-memory buffer to the ODB as a blob + * + * @param oid return the oid of the written blob + * @param repo repository where to blob will be written + * @param buffer data to be written into the blob + * @param len length of the data + * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_blob_writefile(git_oid *written_id, git_repository *repo, const char *path); +GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/commit.h b/vendor/libgit2/include/git2/commit.h index 1556e52b1..ba18a5b39 100644 --- a/vendor/libgit2/include/git2/commit.h +++ b/vendor/libgit2/include/git2/commit.h @@ -41,8 +41,6 @@ GIT_BEGIN_DECL /** * Lookup a commit object from a repository. - * The generated commit object is owned by the revision - * repo and shall not be freed by the user. * * @param commit pointer to the looked up commit * @param repo the repo to use when locating the commit. @@ -55,24 +53,9 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT); } -/** - * Create a new in-memory git_commit. - * - * The commit object must be manually filled using - * setter methods before it can be written to its - * repository. - * - * @param commit pointer to the new commit - * @param repo The repository where the object will reside - * @return 0 on success; error code otherwise - */ -GIT_INLINE(int) git_commit_new(git_commit **commit, git_repository *repo) -{ - return git_object_new((git_object **)commit, repo, GIT_OBJ_COMMIT); -} - /** * Get the id of a commit. + * * @param commit a previously loaded commit. * @return object identity for the commit. */ @@ -80,6 +63,7 @@ GIT_EXTERN(const git_oid *) git_commit_id(git_commit *commit); /** * Get the short (one line) message of a commit. + * * @param commit a previously loaded commit. * @return the short message of a commit */ @@ -87,6 +71,7 @@ GIT_EXTERN(const char *) git_commit_message_short(git_commit *commit); /** * Get the full message of a commit. + * * @param commit a previously loaded commit. * @return the message of a commit */ @@ -94,6 +79,7 @@ GIT_EXTERN(const char *) git_commit_message(git_commit *commit); /** * Get the commit time (i.e. committer time) of a commit. + * * @param commit a previously loaded commit. * @return the time of a commit */ @@ -101,6 +87,7 @@ GIT_EXTERN(time_t) git_commit_time(git_commit *commit); /** * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit. + * * @param commit a previously loaded commit. * @return positive or negative timezone offset, in minutes from UTC */ @@ -108,6 +95,7 @@ GIT_EXTERN(int) git_commit_time_offset(git_commit *commit); /** * Get the committer of a commit. + * * @param commit a previously loaded commit. * @return the committer of a commit */ @@ -115,6 +103,7 @@ GIT_EXTERN(const git_signature *) git_commit_committer(git_commit *commit); /** * Get the author of a commit. + * * @param commit a previously loaded commit. * @return the author of a commit */ @@ -122,6 +111,7 @@ GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit); /** * Get the tree pointed to by a commit. + * * @param tree_out pointer where to store the tree object * @param commit a previously loaded commit. * @return 0 on success; error code otherwise @@ -146,42 +136,129 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit); */ GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n); + /** - * Add a new parent commit to an existing commit - * @param commit the commit object - * @param new_parent the new commit which will be a parent + * Create a new commit in the repository + * + * + * @param oid Pointer where to store the OID of the + * newly created commit + * + * @param repo Repository where to store the commit + * + * @param update_ref If not NULL, name of the reference that + * will be updated to point to this commit. If the reference + * is not direct, it will be resolved to a direct reference. + * Use "HEAD" to update the HEAD of the current branch and + * make it point to this commit + * + * @param author Signature representing the author and the authory + * time of this commit + * + * @param committer Signature representing the committer and the + * commit time of this commit + * + * @param message Full message for this commit + * + * @param tree_oid Object ID of the tree for this commit. Note that + * no validation is performed on this OID. Use the _o variants of + * this method to assure a proper tree is passed to the commit. + * + * @param parent_count Number of parents for this commit + * + * @param parents Array of pointers to parent OIDs for this commit. + * Note that no validation is performed on these OIDs. Use the _o + * variants of this method to assure that are parents for the commit + * are proper objects. + * * @return 0 on success; error code otherwise + * The created commit will be written to the Object Database and + * the given reference will be updated to point to it */ -GIT_EXTERN(int) git_commit_add_parent(git_commit *commit, git_commit *new_parent); +GIT_EXTERN(int) git_commit_create( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + const git_oid *parent_oids[]); /** - * Set the message of a commit - * @param commit the commit object - * @param message the new message + * Create a new commit in the repository using `git_object` + * instances as parameters. + * + * The `tree_oid` and `parent_oids` paremeters now take a instance + * of `git_tree` and `git_commit`, respectively. + * + * All other parameters remain the same + * + * @see git_commit_create */ -GIT_EXTERN(void) git_commit_set_message(git_commit *commit, const char *message); +GIT_EXTERN(int) git_commit_create_o( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]); /** - * Set the committer of a commit - * @param commit the commit object - * @param author_sig signature of the committer + * Create a new commit in the repository using `git_object` + * instances and a variable argument list. + * + * The `tree_oid` paremeter now takes a instance + * of `const git_tree *`. + * + * The parents for the commit are specified as a variable + * list of pointers to `const git_commit *`. Note that this + * is a convenience method which may not be safe to export + * for certain languages or compilers + * + * All other parameters remain the same + * + * @see git_commit_create */ -GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const git_signature *committer_sig); +GIT_EXTERN(int) git_commit_create_ov( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + ...); -/** - * Set the author of a commit - * @param commit the commit object - * @param author_sig signature of the author - */ -GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const git_signature *author_sig); /** - * Set the tree which is pointed to by a commit - * @param commit the commit object - * @param tree the new tree - * @param 0 on success; error code otherwise + * Create a new commit in the repository using + * a variable argument list. + * + * The parents for the commit are specified as a variable + * list of pointers to `const git_oid *`. Note that this + * is a convenience method which may not be safe to export + * for certain languages or compilers + * + * All other parameters remain the same + * + * @see git_commit_create */ -GIT_EXTERN(int) git_commit_set_tree(git_commit *commit, git_tree *tree); +GIT_EXTERN(int) git_commit_create_v( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + ...); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/common.h b/vendor/libgit2/include/git2/common.h index 11a08f897..7cfb8982e 100644 --- a/vendor/libgit2/include/git2/common.h +++ b/vendor/libgit2/include/git2/common.h @@ -158,6 +158,9 @@ /** The state of the reference is not valid */ #define GIT_EINVALIDREFSTATE (GIT_ERROR - 21) +/** This feature has not been implemented yet */ +#define GIT_ENOTIMPLEMENTED (GIT_ERROR - 22) + GIT_BEGIN_DECL typedef struct { @@ -165,14 +168,7 @@ typedef struct { size_t count; } git_strarray; -GIT_INLINE(void) git_strarray_free(git_strarray *array) -{ - size_t i; - for (i = 0; i < array->count; ++i) - free(array->strings[i]); - - free(array->strings); -} +GIT_EXTERN(void) git_strarray_free(git_strarray *array); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/object.h b/vendor/libgit2/include/git2/object.h index 748386f69..16dde8e56 100644 --- a/vendor/libgit2/include/git2/object.h +++ b/vendor/libgit2/include/git2/object.h @@ -42,7 +42,8 @@ GIT_BEGIN_DECL * Lookup a reference to one of the objects in a repostory. * * The generated reference is owned by the repository and - * should not be freed by the user. + * should be closed with the `git_object_close` method + * instead of free'd manually. * * The 'type' parameter must match the type of the object * in the odb; the method will fail otherwise. @@ -57,55 +58,9 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_object_lookup(git_object **object, git_repository *repo, const git_oid *id, git_otype type); -/** - * Create a new in-memory repository object with - * the given type. - * - * The object's attributes can be filled in using the - * corresponding setter methods. - * - * The object will be written back to given git_repository - * when the git_object_write() function is called; objects - * cannot be written to disk until all their main - * attributes have been properly filled. - * - * Objects are instantiated with no SHA1 id; their id - * will be automatically generated when writing to the - * repository. - * - * @param object pointer to the new object - * @parem repo Repository where the object belongs - * @param type Type of the object to be created - * @return the new object - */ -GIT_EXTERN(int) git_object_new(git_object **object, git_repository *repo, git_otype type); - - -/** - * Write back an object to disk. - * - * The object will be written to its corresponding - * repository. - * - * If the object has no changes since it was first - * read from the repository, no actions will take place. - * - * If the object has been modified since it was read from - * the repository, or it has been created from scratch - * in memory, it will be written to the repository and - * its SHA1 ID will be updated accordingly. - * - * @param object Git object to write back - * @return 0 on success; otherwise an error code - */ -GIT_EXTERN(int) git_object_write(git_object *object); - /** * Get the id (SHA1) of a repository object * - * In-memory objects created by git_object_new() do not - * have a SHA1 ID until they are written on a repository. - * * @param obj the repository object * @return the SHA1 id */ @@ -137,14 +92,8 @@ GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj); * by the repository. * * IMPORTANT: - * It is *not* necessary to call this method when you stop using - * an object, since all object memory is automatically reclaimed - * by the repository when it is freed. - * - * Forgetting to call `git_object_close` does not cause memory - * leaks, but it's is recommended to close as soon as possible - * the biggest objects (e.g. blobs) to prevent wasting memory - * space. + * It *is* necessary to call this method when you stop using + * an object. Failure to do so will cause a memory leak. * * @param object the object to close */ diff --git a/vendor/libgit2/include/git2/odb.h b/vendor/libgit2/include/git2/odb.h index 0d285897c..8926b446c 100644 --- a/vendor/libgit2/include/git2/odb.h +++ b/vendor/libgit2/include/git2/odb.h @@ -28,6 +28,7 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "odb_backend.h" /** * @file git2/odb.h @@ -100,61 +101,49 @@ GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, in /** * Close an open object database. + * * @param db database pointer to close. If NULL no action is taken. */ GIT_EXTERN(void) git_odb_close(git_odb *db); -/** An object read from the database. */ -typedef struct { - void *data; /**< Raw, decompressed object data. */ - size_t len; /**< Total number of bytes in data. */ - git_otype type; /**< Type of this object. */ -} git_rawobj; - /** * Read an object from the database. * - * If GIT_ENOTFOUND then out->data is set to NULL. + * This method queries all avaiable ODB backends + * trying to read the given OID. + * + * The returned object is reference counted and + * internally cached, so it should be closed + * by the user once it's no longer in use. * - * @param out object descriptor to populate upon reading. + * @param out pointer where to store the read object * @param db database to search for the object in. * @param id identity of the object to read. * @return * - GIT_SUCCESS if the object was read; * - GIT_ENOTFOUND if the object is not in the database. */ -GIT_EXTERN(int) git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id); +GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id); /** * Read the header of an object from the database, without * reading its full contents. * - * Only the 'type' and 'len' fields of the git_rawobj structure - * are filled. The 'data' pointer will always be NULL. + * The header includes the length and the type of an object. * - * The raw object pointed by 'out' doesn't need to be manually - * closed with git_rawobj_close(). + * Note that most backends do not support reading only the header + * of an object, so the whole object will be read and then the + * header will be returned. * - * @param out object descriptor to populate upon reading. + * @param len_p pointer where to store the length + * @param type_p pointer where to store the type * @param db database to search for the object in. * @param id identity of the object to read. * @return * - GIT_SUCCESS if the object was read; * - GIT_ENOTFOUND if the object is not in the database. */ -GIT_EXTERN(int) git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id); - -/** - * Write an object to the database. - * - * @param id identity of the object written. - * @param db database to which the object should be written. - * @param obj object descriptor for the object to write. - * @return - * - GIT_SUCCESS if the object was written; - * - GIT_ERROR otherwise. - */ -GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj); +GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id); /** * Determine if the given object can be found in the object database. @@ -162,39 +151,89 @@ GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj); * @param db database to be searched for the given object. * @param id the object to search for. * @return - * - true, if the object was found - * - false, otherwise + * - 1, if the object was found + * - 0, otherwise */ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); +/** + * Open a stream to write an object into the ODB + * + * The type and final length of the object must be specified + * when opening the stream. + * + * The returned stream will be of type `GIT_STREAM_WRONLY` and + * will have the following methods: + * + * - stream->write: write `n` bytes into the stream + * - stream->finalize_write: close the stream and store the object in + * the odb + * - stream->free: free the stream + * + * The streaming write won't be effective until `stream->finalize_write` + * is called and returns without an error + * + * The stream must always be free'd or will leak memory. + * + * @see git_odb_stream + * + * @param stream pointer where to store the stream + * @param db object database where the stream will write + * @param size final size of the object that will be written + * @para type type of the object that will be written + * @return 0 if the stream was created; error code otherwise + */ +GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type); - - +/** + * Open a stream to read an object from the ODB + * + * Note that most backends do *not* support streaming reads + * because they store their objects as compressed/delta'ed blobs. + * + * It's recommended to use `git_odb_read` instead, which is + * assured to work on all backends. + * + * The returned stream will be of type `GIT_STREAM_RDONLY` and + * will have the following methods: + * + * - stream->read: read `n` bytes from the stream + * - stream->free: free the stream + * + * The stream must always be free'd or will leak memory. + * + * @see git_odb_stream + * + * @param stream pointer where to store the stream + * @param db object database where the stream will read from + * @param oid oid of the object the stream will read from + * @return 0 if the stream was created; error code otherwise + */ +GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid); /** - * Determine the object-ID (sha1 hash) of the given git_rawobj. + * Determine the object-ID (sha1 hash) of a data buffer * - * The input obj must be a valid loose object type and the data - * pointer must not be NULL, unless the len field is also zero. + * The resulting SHA-1 OID will the itentifier for the data + * buffer as if the data buffer it were to written to the ODB. * * @param id the resulting object-ID. - * @param obj the object whose hash is to be determined. - * @return - * - GIT_SUCCESS if the object-ID was correctly determined. - * - GIT_ERROR if the given object is malformed. + * @param data data to hash + * @param len size of the data + * @param type of the data to hash + * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_rawobj_hash(git_oid *id, git_rawobj *obj); +GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type); /** - * Release all memory used by the obj structure. - * - * As a result of this call, obj->data will be set to NULL. + * Close an ODB object * - * If obj->data is already NULL, nothing happens. + * This method must always be called once a `git_odb_object` is no + * longer needed, otherwise memory will leak. * - * @param obj object descriptor to free. + * @param object object to close */ -GIT_EXTERN(void) git_rawobj_close(git_rawobj *obj); +GIT_EXTERN(void) git_odb_object_close(git_odb_object *object); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/odb_backend.h b/vendor/libgit2/include/git2/odb_backend.h index 0e817eb37..3875ec7f6 100644 --- a/vendor/libgit2/include/git2/odb_backend.h +++ b/vendor/libgit2/include/git2/odb_backend.h @@ -39,24 +39,32 @@ */ GIT_BEGIN_DECL +struct git_odb_stream; + /** An instance for a custom backend */ struct git_odb_backend { git_odb *odb; int (* read)( - git_rawobj *, + void **, size_t *, git_otype *, struct git_odb_backend *, const git_oid *); int (* read_header)( - git_rawobj *, + size_t *, git_otype *, struct git_odb_backend *, const git_oid *); - int (* write)( - git_oid *id, + int (* writestream)( + struct git_odb_stream **, + struct git_odb_backend *, + size_t, + git_otype); + + int (* readstream)( + struct git_odb_stream **, struct git_odb_backend *, - git_rawobj *obj); + const git_oid *); int (* exists)( struct git_odb_backend *, @@ -65,12 +73,28 @@ struct git_odb_backend { void (* free)(struct git_odb_backend *); }; +/** A stream to read/write from a backend */ +struct git_odb_stream { + struct git_odb_backend *backend; + int mode; + + int (*read)(struct git_odb_stream *stream, char *buffer, size_t len); + int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len); + int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream); + void (*free)(struct git_odb_stream *stream); +}; + +/** Streaming mode */ +typedef enum { + GIT_STREAM_RDONLY = (1 << 1), + GIT_STREAM_WRONLY = (1 << 2), + GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY), +} git_odb_streammode; + + GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir); GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir); - -#ifdef GIT2_SQLITE_BACKEND GIT_EXTERN(int) git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db); -#endif GIT_END_DECL diff --git a/vendor/libgit2/include/git2/repository.h b/vendor/libgit2/include/git2/repository.h index d5f6cf501..317b367d2 100644 --- a/vendor/libgit2/include/git2/repository.h +++ b/vendor/libgit2/include/git2/repository.h @@ -167,7 +167,7 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo); * * @param repo repository handle to collect. If NULL nothing occurs. */ -GIT_EXTERN(void) git_repository_close(git_repository *repo); +GIT_EXTERN(int) git_repository_gc(git_repository *repo); /** * Creates a new Git repository in the given folder. diff --git a/vendor/libgit2/include/git2/revwalk.h b/vendor/libgit2/include/git2/revwalk.h index fdbbe236c..f3e0152d4 100644 --- a/vendor/libgit2/include/git2/revwalk.h +++ b/vendor/libgit2/include/git2/revwalk.h @@ -27,7 +27,7 @@ #include "common.h" #include "types.h" -#include "object.h" +#include "oid.h" /** * @file git2/revwalk.h @@ -70,6 +70,17 @@ GIT_BEGIN_DECL /** * Allocate a new revision walker to iterate through a repo. * + * This revision walker uses a custom memory pool and an internal + * commit cache, so it is relatively expensive to allocate. + * + * For maximum performance, this revision walker should be + * reused for different walks. + * + * This revision walker is *not* thread safe: it may only be + * used to walk a repository on a single thread; however, + * it is possible to have several revision walkers in + * several different threads walking the same repository. + * * @param walker pointer to the new revision walker * @param repo the repo to walk through * @return 0 on success; error code otherwise @@ -77,32 +88,67 @@ GIT_BEGIN_DECL GIT_EXTERN(int) git_revwalk_new(git_revwalk **walker, git_repository *repo); /** - * Reset the walking machinery for reuse. + * Reset the revision walker for reuse. + * + * This will clear all the pushed and hidden commits, and + * leave the walker in a blank state (just like at + * creation) ready to receive new commit pushes and + * start a new walk. + * + * The revision walk is automatically reset when a walk + * is over. + * * @param walker handle to reset. */ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker); /** * Mark a commit to start traversal from. - * The commit object must belong to the repo which is being walked through. + * + * The given OID must belong to a commit on the walked + * repository. + * + * The given commit will be used as one of the roots + * when starting the revision walk. At least one commit + * must be pushed the repository before a walk can + * be started. * * @param walker the walker being used for the traversal. - * @param commit the commit to start from. + * @param oid the oid of the commit to start from. + * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid); /** * Mark a commit (and its ancestors) uninteresting for the output. + * + * The given OID must belong to a commit on the walked + * repository. + * + * The resolved commit and all its parents will be hidden from the + * output on the revision walk. + * * @param walker the walker being used for the traversal. * @param commit the commit that will be ignored during the traversal + * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid); /** - * Get the next commit from the revision traversal. + * Get the next commit from the revision walk. + * + * The initial call to this method is *not* blocking when + * iterating through a repo with a time-sorting mode. + * + * Iterating with Topological or inverted modes makes the initial + * call blocking to preprocess the commit list, but this block should be + * mostly unnoticeable on most repositories (topological preprocessing + * times at 0.3s on the git.git repo). * - * @param commit Pointer where to store the next commit + * The revision walker is reset when the walk is over. + * + * @param oid Pointer where to store the oid of the next commit * @param walk the walker to pop the commit from. * @return GIT_SUCCESS if the next commit was found; * GIT_EREVWALKOVER if there are no commits left to iterate @@ -112,14 +158,17 @@ GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk); /** * Change the sorting mode when iterating through the * repository's contents. + * * Changing the sorting mode resets the walker. + * * @param walk the walker being used for the traversal. - * @param sort_mode combination of GIT_RPSORT_XXX flags + * @param sort_mode combination of GIT_SORT_XXX flags */ -GIT_EXTERN(int) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode); +GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode); /** - * Free a revwalk previously allocated. + * Free a revision walker previously allocated. + * * @param walk traversal handle to close. If NULL nothing occurs. */ GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk); diff --git a/vendor/libgit2/include/git2/tag.h b/vendor/libgit2/include/git2/tag.h index f1669eb90..c343f6bf4 100644 --- a/vendor/libgit2/include/git2/tag.h +++ b/vendor/libgit2/include/git2/tag.h @@ -41,8 +41,6 @@ GIT_BEGIN_DECL /** * Lookup a tag object from the repository. - * The generated tag object is owned by the revision - * repo and shall not be freed by the user. * * @param tag pointer to the looked up tag * @param repo the repo to use when locating the tag. @@ -54,24 +52,9 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG); } -/** - * Create a new in-memory git_tag. - * - * The tag object must be manually filled using - * setter methods before it can be written to its - * repository. - * - * @param tag pointer to the new tag - * @param repo The repository where the object will reside - * @return 0 on success; error code otherwise - */ -GIT_INLINE(int) git_tag_new(git_tag **tag, git_repository *repo) -{ - return git_object_new((git_object **)tag, repo, (git_otype)GIT_OBJ_TAG); -} - /** * Get the id of a tag. + * * @param tag a previously loaded tag. * @return object identity for the tag. */ @@ -79,6 +62,10 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag); /** * Get the tagged object of a tag + * + * This method performs a repository lookup for the + * given object and returns it + * * @param target pointer where to store the target * @param tag a previously loaded tag. * @return 0 on success; error code otherwise @@ -87,6 +74,7 @@ GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *t); /** * Get the OID of the tagged object of a tag + * * @param tag a previously loaded tag. * @return pointer to the OID */ @@ -94,6 +82,7 @@ GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *t); /** * Get the type of a tag's tagged object + * * @param tag a previously loaded tag. * @return type of the tagged object */ @@ -101,6 +90,7 @@ GIT_EXTERN(git_otype) git_tag_type(git_tag *t); /** * Get the name of a tag + * * @param tag a previously loaded tag. * @return name of the tag */ @@ -108,6 +98,7 @@ GIT_EXTERN(const char *) git_tag_name(git_tag *t); /** * Get the tagger (author) of a tag + * * @param tag a previously loaded tag. * @return reference to the tag's author */ @@ -115,39 +106,69 @@ GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *t); /** * Get the message of a tag + * * @param tag a previously loaded tag. * @return message of the tag */ GIT_EXTERN(const char *) git_tag_message(git_tag *t); -/** - * Set the target of a tag (i.e. the object that the tag points to) - * @param tag The tag to modify - * @param target the new tagged target - */ -GIT_EXTERN(int) git_tag_set_target(git_tag *tag, git_object *target); /** - * Set the name of a tag - * @param tag The tag to modify - * @param name the new name for the tag + * Create a new tag in the repository from an OID + * + * @param oid Pointer where to store the OID of the + * newly created tag + * + * @param repo Repository where to store the tag + * + * @param tag_name Name for the tag; this name is validated + * for consistency + * + * @param target OID to which this tag points; note that no + * validation is done on this OID. Use the _o version of this + * method to assure a proper object is being tagged + * + * @param target_type Type of the tagged OID; note that no + * validation is performed here either + * + * @param tagger Signature of the tagger for this tag, and + * of the tagging time + * + * @param message Full message for this tag + * + * @return 0 on success; error code otherwise. + * A tag object is written to the ODB, and a proper reference + * is written in the /refs/tags folder, pointing to it */ -GIT_EXTERN(void) git_tag_set_name(git_tag *tag, const char *name); +GIT_EXTERN(int) git_tag_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message); -/** - * Set the tagger of a tag - * @param tag The tag to modify - * @param tagger_sig signature of the tagging action - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig); /** - * Set the message of a tag - * @param tag The tag to modify - * @param message the new tagger for the tag + * Create a new tag in the repository from an existing + * `git_object` instance + * + * This method replaces the `target` and `target_type` + * paremeters of `git_tag_create` by a single instance + * of a `const git_object *`, which is assured to be + * a proper object in the ODB and hence will create + * a valid tag + * + * @see git_tag_create */ -GIT_EXTERN(void) git_tag_set_message(git_tag *tag, const char *message); +GIT_EXTERN(int) git_tag_create_o( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/thread-utils.h b/vendor/libgit2/include/git2/thread-utils.h index c45a76e95..fb8644b93 100644 --- a/vendor/libgit2/include/git2/thread-utils.h +++ b/vendor/libgit2/include/git2/thread-utils.h @@ -32,7 +32,6 @@ */ #define GIT_HAS_TLS 1 -#define GIT_HAS_PTHREAD 1 #if defined(__APPLE__) && defined(__MACH__) # undef GIT_TLS @@ -47,7 +46,6 @@ #elif defined(__INTEL_COMPILER) # if defined(_WIN32) || defined(_WIN32_CE) # define GIT_TLS __declspec(thread) -# undef GIT_HAS_PTHREAD # else # define GIT_TLS __thread # endif @@ -56,11 +54,9 @@ defined(_WIN32_CE) || \ defined(__BORLANDC__) # define GIT_TLS __declspec(thread) -# undef GIT_HAS_PTHREAD #else # undef GIT_HAS_TLS -# undef GIT_HAS_PTHREAD # define GIT_TLS /* nothing: tls vars are thread-global */ #endif @@ -71,10 +67,4 @@ # define GIT_TLS #endif -#ifdef GIT_HAS_PTHREAD -# define GIT_THREADS 1 -#else -# undef GIT_THREADS -#endif - #endif /* INCLUDE_git_thread_utils_h__ */ diff --git a/vendor/libgit2/include/git2/tree.h b/vendor/libgit2/include/git2/tree.h index 3085b3fd6..ec2b51646 100644 --- a/vendor/libgit2/include/git2/tree.h +++ b/vendor/libgit2/include/git2/tree.h @@ -41,8 +41,6 @@ GIT_BEGIN_DECL /** * Lookup a tree object from the repository. - * The generated tree object is owned by the revision - * repo and shall not be freed by the user. * * @param tree pointer to the looked up tree * @param repo the repo to use when locating the tree. @@ -54,32 +52,17 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE); } -/** - * Create a new in-memory git_tree. - * - * The tree object must be manually filled using - * setter methods before it can be written to its - * repository. - * - * @param tree pointer to the new tree - * @param repo The repository where the object will reside - * @return 0 on success; error code otherwise - */ -GIT_INLINE(int) git_tree_new(git_tree **tree, git_repository *repo) -{ - return git_object_new((git_object **)tree, repo, GIT_OBJ_TREE); -} - /** * Get the id of a tree. + * * @param tree a previously loaded tree. * @return object identity for the tree. */ GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree); - /** * Get the number of entries listed in a tree + * * @param tree a previously loaded tree. * @return the number of entries in the tree */ @@ -87,6 +70,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree); /** * Lookup a tree entry by its filename + * * @param tree a previously loaded tree. * @param filename the filename of the desired entry * @return the tree entry; NULL if not found @@ -95,6 +79,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *f /** * Lookup a tree entry by its position in the tree + * * @param tree a previously loaded tree. * @param idx the position in the entry list * @return the tree entry; NULL if not found @@ -103,6 +88,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx); /** * Get the UNIX file attributes of a tree entry + * * @param entry a tree entry * @return attributes as an integer */ @@ -110,6 +96,7 @@ GIT_EXTERN(unsigned int) git_tree_entry_attributes(git_tree_entry *entry); /** * Get the filename of a tree entry + * * @param entry a tree entry * @return the name of the file */ @@ -117,6 +104,7 @@ GIT_EXTERN(const char *) git_tree_entry_name(git_tree_entry *entry); /** * Get the id of the object pointed by the entry + * * @param entry a tree entry * @return the oid of the object */ @@ -126,97 +114,11 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry); * Convert a tree entry to the git_object it points too. * * @param object pointer to the converted object + * @param repo repository where to lookup the pointed object * @param entry a tree entry * @return a reference to the pointed object in the repository */ -GIT_EXTERN(int) git_tree_entry_2object(git_object **object, git_tree_entry *entry); - -/** - * Add a new entry to a tree and return the new entry. - * - * This will mark the tree as modified; the new entry will - * be written back to disk on the next git_object_write() - * - * @param entry_out Pointer to the entry that just got - * created. May be NULL if you are not interested on - * getting the new entry - * @param tree Tree object to store the entry - * @iparam id OID for the tree entry - * @param filename Filename for the tree entry - * @param attributes UNIX file attributes for the entry - * @return 0 on success; otherwise error code - */ -GIT_EXTERN(int) git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid *id, const char *filename, int attributes); - -/** - * Remove an entry by its index. - * - * Index must be >= 0 and < than git_tree_entrycount(). - * - * This will mark the tree as modified; the modified entry will - * be written back to disk on the next git_object_write() - * - * @param tree Tree where to remove the entry - * @param idx index of the entry - * @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found - */ -GIT_EXTERN(int) git_tree_remove_entry_byindex(git_tree *tree, int idx); - -/** - * Remove an entry by its filename. - * - * This will mark the tree as modified; the modified entry will - * be written back to disk on the next git_object_write() - * - * @param tree Tree where to remove the entry - * @param filename File name of the entry - * @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found - */ -GIT_EXTERN(int) git_tree_remove_entry_byname(git_tree *tree, const char *filename); - -/** - * Clear all the entries in a tree. - * - * This will mark the tree as modified; the modified entry will - * be written back to disk on the next git_object_write(). - * - * @param tree Tree object whose entries are to be sorted - */ -GIT_EXTERN(void) git_tree_clear_entries(git_tree *tree); - -/** - * Change the SHA1 id of a tree entry. - * - * This will mark the tree that contains the entry as modified; - * the modified entry will be written back to disk on the next git_object_write() - * - * @param entry Entry object which will be modified - * @param oid new SHA1 oid for the entry - */ -GIT_EXTERN(void) git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid); - -/** - * Change the filename of a tree entry. - * - * This will mark the tree that contains the entry as modified; - * the modified entry will be written back to disk on the next git_object_write() - * - * @param entry Entry object which will be modified - * @param oid new filename for the entry - */ -GIT_EXTERN(void) git_tree_entry_set_name(git_tree_entry *entry, const char *name); - -/** - * Change the attributes of a tree entry. - * - * This will mark the tree that contains the entry as modified; - * the modified entry will be written back to disk on the next git_object_write() - * - * @param entry Entry object which will be modified - * @param oid new attributes for the entry - * @return 0 if the attributes were properly set; error code otherwise - */ -GIT_EXTERN(int) git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr); +GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/types.h b/vendor/libgit2/include/git2/types.h index b5a8d7b2d..64f7fc72e 100644 --- a/vendor/libgit2/include/git2/types.h +++ b/vendor/libgit2/include/git2/types.h @@ -71,7 +71,6 @@ typedef time_t git_time_t; #endif - /** Basic type (loose or packed) of any Git object. */ typedef enum { GIT_OBJ_ANY = -2, /**< Object can be any of the following */ @@ -92,6 +91,12 @@ typedef struct git_odb git_odb; /** A custom backend in an ODB */ typedef struct git_odb_backend git_odb_backend; +/** An object read from the ODB */ +typedef struct git_odb_object git_odb_object; + +/** A stream to read/write from the ODB */ +typedef struct git_odb_stream git_odb_stream; + /** * Representation of an existing git repository, * including all its object contents diff --git a/vendor/libgit2/src/backends/sqlite.c b/vendor/libgit2/src/backends/sqlite.c index b4c941a59..72d7b4d8e 100644 --- a/vendor/libgit2/src/backends/sqlite.c +++ b/vendor/libgit2/src/backends/sqlite.c @@ -272,4 +272,13 @@ int git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db) return GIT_ERROR; } +#else + +int git_odb_backend_sqlite(git_odb_backend **GIT_UNUSED(backend_out), const char *GIT_UNUSED(sqlite_db)) +{ + GIT_UNUSED_ARG(backend_out); + GIT_UNUSED_ARG(sqlite_db); + return GIT_ENOTIMPLEMENTED; +} + #endif /* HAVE_SQLITE3 */ diff --git a/vendor/libgit2/src/blob.c b/vendor/libgit2/src/blob.c index 1e03b6b67..bc0a08a8a 100644 --- a/vendor/libgit2/src/blob.c +++ b/vendor/libgit2/src/blob.c @@ -33,104 +33,89 @@ const void *git_blob_rawcontent(git_blob *blob) { assert(blob); - - if (blob->content.data != NULL) - return blob->content.data; - - if (blob->object.in_memory) - return NULL; - - if (!blob->object.source.open && git_object__source_open((git_object *)blob) < GIT_SUCCESS) - return NULL; - - return blob->object.source.raw.data; + return blob->odb_object->raw.data; } int git_blob_rawsize(git_blob *blob) { assert(blob); - - if (blob->content.data != NULL) - return blob->content.len; - - return blob->object.source.raw.len; + return blob->odb_object->raw.len; } void git_blob__free(git_blob *blob) { - gitfo_free_buf(&blob->content); + git_odb_object_close(blob->odb_object); free(blob); } -int git_blob__parse(git_blob *blob) +int git_blob__parse(git_blob *blob, git_odb_object *odb_obj) { assert(blob); + git_cached_obj_incref((git_cached_obj *)odb_obj); + blob->odb_object = odb_obj; return GIT_SUCCESS; } -int git_blob__writeback(git_blob *blob, git_odb_source *src) -{ - assert(blob->object.modified); - - if (blob->content.data == NULL) - return GIT_EMISSINGOBJDATA; - - return git__source_write(src, blob->content.data, blob->content.len); -} - -int git_blob_set_rawcontent(git_blob *blob, const void *buffer, size_t len) +int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len) { - assert(blob && buffer); - - blob->object.modified = 1; - - git_object__source_close((git_object *)blob); - - if (blob->content.data != NULL) - gitfo_free_buf(&blob->content); + int error; + git_odb_stream *stream; - blob->content.data = git__malloc(len); - blob->content.len = len; + if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) + return error; - if (blob->content.data == NULL) - return GIT_ENOMEM; + stream->write(stream, buffer, len); - memcpy(blob->content.data, buffer, len); + error = stream->finalize_write(oid, stream); + stream->free(stream); - return GIT_SUCCESS; + return error; } -int git_blob_set_rawcontent_fromfile(git_blob *blob, const char *filename) +int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path) { - assert(blob && filename); - blob->object.modified = 1; + int error, fd; + char full_path[GIT_PATH_MAX]; + char buffer[2048]; + git_off_t size; + git_odb_stream *stream; - if (blob->content.data != NULL) - gitfo_free_buf(&blob->content); - - return gitfo_read_file(&blob->content, filename); -} + if (repo->path_workdir == NULL) + return GIT_ENOTFOUND; -int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *path) -{ - int error; - git_blob *blob; + git__joinpath(full_path, repo->path_workdir, path); - if (gitfo_exists(path) < 0) + if ((fd = gitfo_open(full_path, O_RDONLY)) < 0) return GIT_ENOTFOUND; - if ((error = git_blob_new(&blob, repo)) < GIT_SUCCESS) - return error; + if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) { + gitfo_close(fd); + return GIT_EOSERR; + } - if ((error = git_blob_set_rawcontent_fromfile(blob, path)) < GIT_SUCCESS) + if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) { + gitfo_close(fd); return error; + } - if ((error = git_object_write((git_object *)blob)) < GIT_SUCCESS) - return error; + while (size > 0) { + ssize_t read_len; - git_oid_cpy(written_id, git_object_id((git_object *)blob)); + read_len = read(fd, buffer, sizeof(buffer)); - git_object_close((git_object*)blob); - return GIT_SUCCESS; + if (read_len < 0) { + gitfo_close(fd); + stream->free(stream); + return GIT_EOSERR; + } + + stream->write(stream, buffer, read_len); + size -= read_len; + } + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return error; } diff --git a/vendor/libgit2/src/blob.h b/vendor/libgit2/src/blob.h index febc296fe..4300d7e54 100644 --- a/vendor/libgit2/src/blob.h +++ b/vendor/libgit2/src/blob.h @@ -3,15 +3,15 @@ #include "git2/blob.h" #include "repository.h" +#include "odb.h" #include "fileops.h" struct git_blob { git_object object; - gitfo_buf content; + git_odb_object *odb_object; }; void git_blob__free(git_blob *blob); -int git_blob__parse(git_blob *blob); -int git_blob__writeback(git_blob *blob, git_odb_source *src); +int git_blob__parse(git_blob *blob, git_odb_object *obj); #endif diff --git a/vendor/libgit2/src/cache.c b/vendor/libgit2/src/cache.c new file mode 100644 index 000000000..fd42e2c5b --- /dev/null +++ b/vendor/libgit2/src/cache.c @@ -0,0 +1,161 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "repository.h" +#include "commit.h" +#include "thread-utils.h" +#include "cache.h" + +#define GIT_CACHE_OPENADR 3 + + +void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) +{ + size_t i; + + if (size < 8) + size = 8; + + /* round up size to closest power of 2 */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + + cache->size_mask = size; + cache->lru_count = 0; + cache->free_obj = free_ptr; + + cache->nodes = git__malloc((size + 1) * sizeof(cache_node)); + + for (i = 0; i < (size + 1); ++i) { + git_mutex_init(&cache->nodes[i].lock); + cache->nodes[i].ptr = NULL; + cache->nodes[i].lru = 0; + } +} + +void git_cache_free(git_cache *cache) +{ + size_t i; + + for (i = 0; i < (cache->size_mask + 1); ++i) { + if (cache->nodes[i].ptr) + git_cached_obj_decref(cache->nodes[i].ptr, cache->free_obj); + + git_mutex_free(&cache->nodes[i].lock); + } + + free(cache->nodes); +} + +void *git_cache_get(git_cache *cache, const git_oid *oid) +{ + const uint32_t *hash; + size_t i, pos, found = 0; + cache_node *node = NULL; + + hash = (const uint32_t *)oid->id; + + for (i = 0; !found && i < GIT_CACHE_OPENADR; ++i) { + pos = hash[i] & cache->size_mask; + node = &cache->nodes[pos]; + + git_mutex_lock(&node->lock); + { + if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_incref(node->ptr); + node->lru = ++cache->lru_count; + found = 1; + } + } + git_mutex_unlock(&node->lock); + } + + + return found ? node->ptr : NULL; +} + +void *git_cache_try_store(git_cache *cache, void *entry) +{ + cache_node *nodes[GIT_CACHE_OPENADR], *lru_node; + const uint32_t *hash; + const git_oid *oid; + size_t i; + + oid = &((git_cached_obj*)entry)->oid; + hash = (const uint32_t *)oid->id; + + /* increase the refcount on this object, because + * the cache now owns it */ + git_cached_obj_incref(entry); + + for (i = 0; i < GIT_CACHE_OPENADR; ++i) { + size_t pos = hash[i] & cache->size_mask; + + nodes[i] = &cache->nodes[pos]; + git_mutex_lock(&nodes[i]->lock); + } + + lru_node = nodes[0]; + + for (i = 0; i < GIT_CACHE_OPENADR; ++i) { + + if (nodes[i]->ptr == NULL) { + nodes[i]->ptr = entry; + nodes[i]->lru = ++cache->lru_count; + break; + } else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) { + git_cached_obj_decref(entry, cache->free_obj); + entry = nodes[i]->ptr; + nodes[i]->lru = ++cache->lru_count; + break; + } + + if (nodes[i]->lru < lru_node->lru) + lru_node = nodes[i]; + } + + if (i == GIT_CACHE_OPENADR) { + void *old_entry = lru_node->ptr; + assert(old_entry); + + git_cached_obj_decref(old_entry, cache->free_obj); + lru_node->ptr = entry; + lru_node->lru = ++cache->lru_count; + } + + /* increase the refcount again, because we are + * returning it to the user */ + git_cached_obj_incref(entry); + + for (i = 0; i < GIT_CACHE_OPENADR; ++i) + git_mutex_unlock(&nodes[i]->lock); + + return entry; +} diff --git a/vendor/libgit2/src/cache.h b/vendor/libgit2/src/cache.h new file mode 100644 index 000000000..975aaff7e --- /dev/null +++ b/vendor/libgit2/src/cache.h @@ -0,0 +1,59 @@ +#ifndef INCLUDE_cache_h__ +#define INCLUDE_cache_h__ + +#include "git2/common.h" +#include "git2/oid.h" +#include "git2/odb.h" + +#include "thread-utils.h" + +#define GIT_DEFAULT_CACHE_SIZE 128 + +typedef void (*git_cached_obj_freeptr)(void *); + +typedef struct { + git_oid oid; + git_atomic refcount; +} git_cached_obj; + +typedef struct { + git_cached_obj *ptr; + git_mutex lock; + unsigned int lru; +} cache_node; + +typedef struct { + cache_node *nodes; + + unsigned int lru_count; + size_t size_mask; + git_cached_obj_freeptr free_obj; +} git_cache; + + +void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); +void git_cache_free(git_cache *cache); + +void *git_cache_try_store(git_cache *cache, void *entry); +void *git_cache_get(git_cache *cache, const git_oid *oid); + + +GIT_INLINE(int) git_cached_obj_compare(git_cached_obj *obj, const git_oid *oid) +{ + return git_oid_cmp(&obj->oid, oid); +} + +GIT_INLINE(void) git_cached_obj_incref(git_cached_obj *obj) +{ + git_atomic_inc(&obj->refcount); +} + +GIT_INLINE(void) git_cached_obj_decref(git_cached_obj *obj, git_cached_obj_freeptr free_obj) +{ + if (git_atomic_dec(&obj->refcount) == 0) + free_obj(obj); +} + + + +#endif diff --git a/vendor/libgit2/src/commit.c b/vendor/libgit2/src/commit.c index 1c5cddf7a..d5d6ebd8a 100644 --- a/vendor/libgit2/src/commit.c +++ b/vendor/libgit2/src/commit.c @@ -29,10 +29,12 @@ #include "git2/signature.h" #include "common.h" +#include "odb.h" #include "commit.h" -#include "revwalk.h" #include "signature.h" +#include + #define COMMIT_BASIC_PARSE 0x0 #define COMMIT_FULL_PARSE 0x1 @@ -72,35 +74,165 @@ const git_oid *git_commit_id(git_commit *c) return git_object_id((git_object *)c); } -int git_commit__writeback(git_commit *commit, git_odb_source *src) + +int git_commit_create_v( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + ...) { - unsigned int i; + va_list ap; + int i, error; + const git_oid **oids; - git__write_oid(src, "tree", &commit->tree_oid); + oids = git__malloc(parent_count * sizeof(git_oid *)); - for (i = 0; i < commit->parent_oids.length; ++i) { - git_oid *parent_oid; + va_start(ap, parent_count); + for (i = 0; i < parent_count; ++i) + oids[i] = va_arg(ap, const git_oid *); + va_end(ap); - parent_oid = git_vector_get(&commit->parent_oids, i); - git__write_oid(src, "parent", parent_oid); - } + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + tree_oid, parent_count, oids); + + free((void *)oids); + return error; +} + +int git_commit_create_ov( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + ...) +{ + va_list ap; + int i, error; + const git_oid **oids; + + oids = git__malloc(parent_count * sizeof(git_oid *)); + + va_start(ap, parent_count); + for (i = 0; i < parent_count; ++i) + oids[i] = git_object_id(va_arg(ap, const git_object *)); + va_end(ap); + + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + git_object_id((git_object *)tree), + parent_count, oids); + + free((void *)oids); + return error; +} + +int git_commit_create_o( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_tree *tree, + int parent_count, + const git_commit *parents[]) +{ + int i, error; + const git_oid **oids; + + oids = git__malloc(parent_count * sizeof(git_oid *)); + + for (i = 0; i < parent_count; ++i) + oids[i] = git_object_id((git_object *)parents[i]); + + error = git_commit_create( + oid, repo, update_ref, author, committer, message, + git_object_id((git_object *)tree), + parent_count, oids); + + free((void *)oids); + return error; +} - if (commit->author == NULL) - return GIT_EMISSINGOBJDATA; +int git_commit_create( + git_oid *oid, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message, + const git_oid *tree_oid, + int parent_count, + const git_oid *parents[]) +{ + size_t final_size = 0; + int message_length, author_length, committer_length; + + char *author_str, *committer_str; + + int error, i; + git_odb_stream *stream; + + message_length = strlen(message); + author_length = git_signature__write(&author_str, "author", author); + committer_length = git_signature__write(&committer_str, "committer", committer); + + if (author_length < 0 || committer_length < 0) + return GIT_ENOMEM; + + final_size += GIT_OID_LINE_LENGTH("tree"); + final_size += GIT_OID_LINE_LENGTH("parent") * parent_count; + final_size += author_length; + final_size += committer_length; + final_size += 1 + message_length; + + if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_COMMIT)) < GIT_SUCCESS) + return error; + + git__write_oid(stream, "tree", tree_oid); - git_signature__write(src, "author", commit->author); + for (i = 0; i < parent_count; ++i) + git__write_oid(stream, "parent", parents[i]); - if (commit->committer == NULL) - return GIT_EMISSINGOBJDATA; + stream->write(stream, author_str, author_length); + free(author_str); - git_signature__write(src, "committer", commit->committer); + stream->write(stream, committer_str, committer_length); + free(committer_str); - if (commit->message != NULL) { - git__source_write(src, "\n", 1); - git__source_write(src, commit->message, strlen(commit->message)); + + stream->write(stream, "\n", 1); + stream->write(stream, message, message_length); + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + if (error == GIT_SUCCESS && update_ref != NULL) { + git_reference *head; + + error = git_reference_lookup(&head, repo, update_ref); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(head) == GIT_REF_SYMBOLIC) { + if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS) + return error; + } + + error = git_reference_set_oid(head, oid); } - return GIT_SUCCESS; + return error; } int commit_parse_buffer(git_commit *commit, void *data, size_t len) @@ -111,12 +243,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) git_oid parent_oid; int error; - /* first parse; the vector hasn't been initialized yet */ - if (commit->parent_oids.contents == NULL) { - git_vector_init(&commit->parent_oids, 4, NULL); - } - - clear_parents(commit); + git_vector_init(&commit->parent_oids, 4, NULL); if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) return error; @@ -135,17 +262,11 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) return GIT_ENOMEM; } - if (commit->author) - git_signature_free(commit->author); - commit->author = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS) return error; /* Always parse the committer; we need the commit time */ - if (commit->committer) - git_signature_free(commit->committer); - commit->committer = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ")) < GIT_SUCCESS) return error; @@ -177,11 +298,10 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len) return GIT_SUCCESS; } -int git_commit__parse(git_commit *commit) +int git_commit__parse(git_commit *commit, git_odb_object *obj) { - assert(commit && commit->object.source.open); - return commit_parse_buffer(commit, - commit->object.source.raw.data, commit->object.source.raw.len); + assert(commit); + return commit_parse_buffer(commit, obj->raw.data, obj->raw.len); } #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ @@ -219,82 +339,3 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) } - -int git_commit_set_tree(git_commit *commit, git_tree *tree) -{ - const git_oid *oid; - - assert(commit && tree); - - if ((oid = git_object_id((git_object *)tree)) == NULL) - return GIT_EMISSINGOBJDATA; - - commit->object.modified = 1; - git_oid_cpy(&commit->tree_oid, oid); - return GIT_SUCCESS; -} - -int git_commit_add_parent(git_commit *commit, git_commit *new_parent) -{ - const git_oid *parent_oid; - git_oid *new_oid; - assert(commit && new_parent); - - if ((parent_oid = git_object_id((git_object *)new_parent)) == NULL) - return GIT_EMISSINGOBJDATA; - - new_oid = git__malloc(sizeof(git_oid)); - if (new_oid == NULL) - return GIT_ENOMEM; - - commit->object.modified = 1; - git_oid_cpy(new_oid, parent_oid); - return git_vector_insert(&commit->parent_oids, new_oid); -} - -void git_commit_set_author(git_commit *commit, const git_signature *author_sig) -{ - assert(commit && author_sig); - commit->object.modified = 1; - - git_signature_free(commit->author); - commit->author = git_signature_dup(author_sig); -} - -void git_commit_set_committer(git_commit *commit, const git_signature *committer_sig) -{ - assert(commit && committer_sig); - commit->object.modified = 1; - - git_signature_free(commit->committer); - commit->committer = git_signature_dup(committer_sig); -} - -void git_commit_set_message(git_commit *commit, const char *message) -{ - const char *line_end; - size_t message_len; - - commit->object.modified = 1; - - if (commit->message) - free(commit->message); - - if (commit->message_short) - free(commit->message_short); - - commit->message = git__strdup(message); - - /* Short message */ - if((line_end = strchr(message, '\n')) == NULL) { - commit->message_short = git__strdup(message); - return; - } - - message_len = line_end - message; - - commit->message_short = git__malloc(message_len + 1); - memcpy(commit->message_short, message, message_len); - commit->message_short[message_len] = 0; -} - diff --git a/vendor/libgit2/src/commit.h b/vendor/libgit2/src/commit.h index aaf349ca6..3d15c5044 100644 --- a/vendor/libgit2/src/commit.h +++ b/vendor/libgit2/src/commit.h @@ -22,8 +22,6 @@ struct git_commit { }; void git_commit__free(git_commit *c); -int git_commit__parse(git_commit *commit); - -int git_commit__writeback(git_commit *commit, git_odb_source *src); +int git_commit__parse(git_commit *commit, git_odb_object *obj); #endif diff --git a/vendor/libgit2/src/common.h b/vendor/libgit2/src/common.h index 1ca00471b..5ad878e26 100644 --- a/vendor/libgit2/src/common.h +++ b/vendor/libgit2/src/common.h @@ -11,9 +11,6 @@ #include "git2/thread-utils.h" #include "cc-compat.h" -#ifdef GIT_HAS_PTHREAD -# include -#endif #ifdef GIT_HAVE_INTTYPES_H # include #endif @@ -34,16 +31,21 @@ # include # include "msvc-compat.h" # include "mingw-compat.h" +# ifdef GIT_THREADS +# include "win32/pthread.h" +#endif # define snprintf _snprintf typedef SSIZE_T ssize_t; #else - # include # include +# ifdef GIT_THREADS +# include +# endif #endif #include "git2/common.h" diff --git a/vendor/libgit2/src/delta-apply.h b/vendor/libgit2/src/delta-apply.h index 642442de0..36c5cc60d 100644 --- a/vendor/libgit2/src/delta-apply.h +++ b/vendor/libgit2/src/delta-apply.h @@ -1,6 +1,8 @@ #ifndef INCLUDE_delta_apply_h__ #define INCLUDE_delta_apply_h__ +#include "odb.h" + /** * Apply a git binary delta to recover the original content. * diff --git a/vendor/libgit2/src/errors.c b/vendor/libgit2/src/errors.c index 880163f78..f6b964837 100644 --- a/vendor/libgit2/src/errors.c +++ b/vendor/libgit2/src/errors.c @@ -27,7 +27,8 @@ static struct { {GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"}, {GIT_EINVALIDPATH, "The path is invalid" }, {GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"}, - {GIT_EINVALIDREFSTATE, "The state of the reference is not valid"} + {GIT_EINVALIDREFSTATE, "The state of the reference is not valid"}, + {GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"} }; const char *git_strerror(int num) diff --git a/vendor/libgit2/src/filebuf.c b/vendor/libgit2/src/filebuf.c index 4fc4f1486..607ad618d 100644 --- a/vendor/libgit2/src/filebuf.c +++ b/vendor/libgit2/src/filebuf.c @@ -77,43 +77,81 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->fd >= 0) gitfo_close(file->fd); - if (gitfo_exists(file->path_lock) == GIT_SUCCESS) + if (file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS) gitfo_unlink(file->path_lock); if (file->digest) git_hash_free_ctx(file->digest); free(file->buffer); + free(file->z_buf); -#ifdef GIT_FILEBUF_THREADS - free(file->buffer_back); -#endif + deflateEnd(&file->zs); free(file->path_original); free(file->path_lock); } -static int flush_buffer(git_filebuf *file) +GIT_INLINE(int) flush_buffer(git_filebuf *file) { - int result = GIT_SUCCESS; + int result = file->write(file, file->buffer, file->buf_pos); + file->buf_pos = 0; + return result; +} - if (file->buf_pos > 0) { - result = gitfo_write(file->fd, file->buffer, file->buf_pos); - if (file->digest) - git_hash_update(file->digest, file->buffer, file->buf_pos); +static int write_normal(git_filebuf *file, const void *source, size_t len) +{ + int result = 0; - file->buf_pos = 0; + if (len > 0) { + result = gitfo_write(file->fd, (void *)source, len); + if (file->digest) + git_hash_update(file->digest, source, len); } return result; } +static int write_deflate(git_filebuf *file, const void *source, size_t len) +{ + int result = Z_OK; + z_stream *zs = &file->zs; + + if (len > 0 || file->flush_mode == Z_FINISH) { + zs->next_in = (void *)source; + zs->avail_in = len; + + do { + int have; + + zs->next_out = file->z_buf; + zs->avail_out = file->buf_size; + + result = deflate(zs, file->flush_mode); + assert(result != Z_STREAM_ERROR); + + have = file->buf_size - zs->avail_out; + + if (gitfo_write(file->fd, file->z_buf, have) < GIT_SUCCESS) + return GIT_EOSERR; + + } while (zs->avail_out == 0); + + assert(zs->avail_in == 0); + + if (file->digest) + git_hash_update(file->digest, source, len); + } + + return GIT_SUCCESS; +} + int git_filebuf_open(git_filebuf *file, const char *path, int flags) { int error; size_t path_len; - if (file == NULL || path == NULL) + if (file == NULL) return GIT_ERROR; memset(file, 0x0, sizeof(git_filebuf)); @@ -122,46 +160,93 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) file->buf_pos = 0; file->fd = -1; - path_len = strlen(path); - - file->path_original = git__strdup(path); - if (file->path_original == NULL) { + /* Allocate the main cache buffer */ + file->buffer = git__malloc(file->buf_size); + if (file->buffer == NULL){ error = GIT_ENOMEM; goto cleanup; } - file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH); - if (file->path_lock == NULL) { - error = GIT_ENOMEM; - goto cleanup; + /* If we are hashing on-write, allocate a new hash context */ + if (flags & GIT_FILEBUF_HASH_CONTENTS) { + if ((file->digest = git_hash_new_ctx()) == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } } - memcpy(file->path_lock, file->path_original, path_len); - memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + /* If we are deflating on-write, */ + if (flags & GIT_FILEBUF_DEFLATE_CONTENTS) { - file->buffer = git__malloc(file->buf_size); - if (file->buffer == NULL){ - error = GIT_ENOMEM; - goto cleanup; - } + /* Initialize the ZLib stream */ + if (deflateInit(&file->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + error = GIT_EZLIB; + goto cleanup; + } -#ifdef GIT_FILEBUF_THREADS - file->buffer_back = git__malloc(file->buf_size); - if (file->buffer_back == NULL){ - error = GIT_ENOMEM; - goto cleanup; + /* Allocate the Zlib cache buffer */ + file->z_buf = git__malloc(file->buf_size); + if (file->z_buf == NULL){ + error = GIT_ENOMEM; + goto cleanup; + } + + /* Never flush */ + file->flush_mode = Z_NO_FLUSH; + file->write = &write_deflate; + } else { + file->write = &write_normal; } -#endif - if (flags & GIT_FILEBUF_HASH_CONTENTS) { - if ((file->digest = git_hash_new_ctx()) == NULL) { + /* If we are writing to a temp file */ + if (flags & GIT_FILEBUF_TEMPORARY) { + char tmp_path[GIT_PATH_MAX]; + + /* Open the file as temporary for locking */ + file->fd = gitfo_creat_tmp(tmp_path, "_filebuf_"); + if (file->fd < 0) { + error = GIT_EOSERR; + goto cleanup; + } + + /* No original path */ + file->path_original = NULL; + file->path_lock = git__strdup(tmp_path); + + if (file->path_lock == NULL) { error = GIT_ENOMEM; goto cleanup; } - } + } else { + /* If the file is not temporary, make sure we have a path */ + if (path == NULL) { + error = GIT_ERROR; + goto cleanup; + } - if ((error = lock_file(file, flags)) < GIT_SUCCESS) - goto cleanup; + path_len = strlen(path); + + /* Save the original path of the file */ + file->path_original = git__strdup(path); + if (file->path_original == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + /* create the locking path by appending ".lock" to the original */ + file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH); + if (file->path_lock == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + memcpy(file->path_lock, file->path_original, path_len); + memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH); + + /* open the file for locking */ + if ((error = lock_file(file, flags)) < GIT_SUCCESS) + goto cleanup; + } return GIT_SUCCESS; @@ -187,10 +272,25 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file) return GIT_SUCCESS; } +int git_filebuf_commit_at(git_filebuf *file, const char *path) +{ + free(file->path_original); + file->path_original = git__strdup(path); + if (file->path_original == NULL) + return GIT_ENOMEM; + + return git_filebuf_commit(file); +} + int git_filebuf_commit(git_filebuf *file) { int error; + /* tmp file cannot be commited */ + if (file->path_original == NULL) + return GIT_EOSERR; + + file->flush_mode = Z_FINISH; if ((error = flush_buffer(file)) < GIT_SUCCESS) goto cleanup; @@ -204,16 +304,16 @@ int git_filebuf_commit(git_filebuf *file) return error; } -GIT_INLINE(void) add_to_cache(git_filebuf *file, void *buf, size_t len) +GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len) { memcpy(file->buffer + file->buf_pos, buf, len); file->buf_pos += len; } -int git_filebuf_write(git_filebuf *file, void *buff, size_t len) +int git_filebuf_write(git_filebuf *file, const void *buff, size_t len) { int error; - unsigned char *buf = buff; + const unsigned char *buf = buff; for (;;) { size_t space_left = file->buf_size - file->buf_pos; @@ -237,9 +337,9 @@ int git_filebuf_write(git_filebuf *file, void *buff, size_t len) /* write too-large chunks immediately */ if (len > file->buf_size) { - error = gitfo_write(file->fd, buf, len); - if (file->digest) - git_hash_update(file->digest, buf, len); + error = file->write(file, buf, len); + if (error < GIT_SUCCESS) + return error; } } } diff --git a/vendor/libgit2/src/filebuf.h b/vendor/libgit2/src/filebuf.h index 9db615fbd..37cb36784 100644 --- a/vendor/libgit2/src/filebuf.h +++ b/vendor/libgit2/src/filebuf.h @@ -3,14 +3,17 @@ #include "fileops.h" #include "hash.h" +#include "git2/zlib.h" #ifdef GIT_THREADS # define GIT_FILEBUF_THREADS #endif -#define GIT_FILEBUF_HASH_CONTENTS 0x1 -#define GIT_FILEBUF_APPEND 0x2 -#define GIT_FILEBUF_FORCE 0x4 +#define GIT_FILEBUF_HASH_CONTENTS (1 << 0) +#define GIT_FILEBUF_APPEND (1 << 2) +#define GIT_FILEBUF_FORCE (1 << 3) +#define GIT_FILEBUF_TEMPORARY (1 << 4) +#define GIT_FILEBUF_DEFLATE_CONTENTS (1 << 5) #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 @@ -19,12 +22,16 @@ struct git_filebuf { char *path_original; char *path_lock; + int (*write)(struct git_filebuf *file, + const void *source, size_t len); + git_hash_ctx *digest; unsigned char *buffer; -#ifdef GIT_FILEBUF_THREADS - unsigned char *buffer_back; -#endif + unsigned char *z_buf; + + z_stream zs; + int flush_mode; size_t buf_size, buf_pos; git_file fd; @@ -32,12 +39,13 @@ struct git_filebuf { typedef struct git_filebuf git_filebuf; -int git_filebuf_write(git_filebuf *lock, void *buff, size_t len); +int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len); int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); int git_filebuf_printf(git_filebuf *file, const char *format, ...); int git_filebuf_open(git_filebuf *lock, const char *path, int flags); int git_filebuf_commit(git_filebuf *lock); +int git_filebuf_commit_at(git_filebuf *lock, const char *path); void git_filebuf_cleanup(git_filebuf *lock); int git_filebuf_hash(git_oid *oid, git_filebuf *file); diff --git a/vendor/libgit2/src/fileops.c b/vendor/libgit2/src/fileops.c index 76e689e8a..237a16a9f 100644 --- a/vendor/libgit2/src/fileops.c +++ b/vendor/libgit2/src/fileops.c @@ -2,13 +2,13 @@ #include "fileops.h" #include -static int force_path(const char *to) +int gitfo_mkdir_2file(const char *file_path) { const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; - error = git__dirname_r(target_folder_path, sizeof(target_folder_path), to); + error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) return error; @@ -25,6 +25,87 @@ static int force_path(const char *to) return GIT_SUCCESS; } +static int creat_tempfile(char *path_out, const char *tmp_dir, const char *filename) +{ + int fd; + + git__joinpath(path_out, tmp_dir, filename); + strcat(path_out, "_git2_XXXXXX"); + +#ifdef GIT_WIN32 + /* FIXME: there may be race conditions when multi-threading + * with the library */ + if (_mktemp_s(path_out, GIT_PATH_MAX) != 0) + return GIT_EOSERR; + + fd = gitfo_creat(path_out, 0744); +#else + fd = mkstemp(path_out); +#endif + + return fd >= 0 ? fd : GIT_EOSERR; +} + +static const char *find_tmpdir(void) +{ + static int tmpdir_not_found = 0; + static char temp_dir[GIT_PATH_MAX]; + static const char *env_vars[] = { + "TEMP", "TMP", "TMPDIR" + }; + + unsigned int i, j; + char test_file[GIT_PATH_MAX]; + + if (tmpdir_not_found) + return NULL; + + if (temp_dir[0] != '\0') + return temp_dir; + + for (i = 0; i < ARRAY_SIZE(env_vars); ++i) { + char *env_path; + + env_path = getenv(env_vars[i]); + if (env_path == NULL) + continue; + + strcpy(temp_dir, env_path); + + /* Fix backslashes because Windows environment vars + * are probably fucked up */ + for (j = 0; j < strlen(temp_dir); ++j) + if (temp_dir[j] == '\\') + temp_dir[j] = '/'; + + if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) { + gitfo_unlink(test_file); + return temp_dir; + } + } + + /* last resort: current folder. */ + strcpy(temp_dir, "./"); + if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) { + gitfo_unlink(test_file); + return temp_dir; + } + + tmpdir_not_found = 1; + return NULL; +} + +int gitfo_creat_tmp(char *path_out, const char *filename) +{ + const char *tmp_dir; + + tmp_dir = find_tmpdir(); + if (tmp_dir == NULL) + return GIT_EOSERR; + + return creat_tempfile(path_out, tmp_dir, filename); +} + int gitfo_open(const char *path, int flags) { int fd = open(path, flags | O_BINARY); @@ -39,7 +120,7 @@ int gitfo_creat(const char *path, int mode) int gitfo_creat_force(const char *path, int mode) { - if (force_path(path) < GIT_SUCCESS) + if (gitfo_mkdir_2file(path) < GIT_SUCCESS) return GIT_EOSERR; return gitfo_creat(path, mode); @@ -117,6 +198,7 @@ int gitfo_isdir(const char *path) int gitfo_exists(const char *path) { + assert(path); return access(path, F_OK); } @@ -198,7 +280,7 @@ int gitfo_mv(const char *from, const char *to) int gitfo_mv_force(const char *from, const char *to) { - if (force_path(to) < GIT_SUCCESS) + if (gitfo_mkdir_2file(to) < GIT_SUCCESS) return GIT_EOSERR; return gitfo_mv(from, to); diff --git a/vendor/libgit2/src/fileops.h b/vendor/libgit2/src/fileops.h index 5aa302b54..bc636fc38 100644 --- a/vendor/libgit2/src/fileops.h +++ b/vendor/libgit2/src/fileops.h @@ -58,8 +58,10 @@ extern int gitfo_exists(const char *path); extern int gitfo_open(const char *path, int flags); extern int gitfo_creat(const char *path, int mode); extern int gitfo_creat_force(const char *path, int mode); +extern int gitfo_creat_tmp(char *path_out, const char *filename); extern int gitfo_isdir(const char *path); extern int gitfo_mkdir_recurs(const char *path, int mode); +extern int gitfo_mkdir_2file(const char *path); #define gitfo_close(fd) close(fd) extern int gitfo_read(git_file fd, void *buf, size_t cnt); diff --git a/vendor/libgit2/src/hashtable.c b/vendor/libgit2/src/hashtable.c index c36d8a8e6..ee6d3a461 100644 --- a/vendor/libgit2/src/hashtable.c +++ b/vendor/libgit2/src/hashtable.c @@ -184,6 +184,8 @@ int git_hashtable_insert2(git_hashtable *self, const void *key, void *value, voi int hash_id; git_hashtable_node *node; + assert(self && self->nodes); + *old_value = NULL; for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) { @@ -218,6 +220,8 @@ void *git_hashtable_lookup(git_hashtable *self, const void *key) int hash_id; git_hashtable_node *node; + assert(self && self->nodes); + for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) { node = node_with_hash(self, key, hash_id); if (node->key && self->key_equal(key, node->key) == 0) @@ -232,6 +236,8 @@ int git_hashtable_remove(git_hashtable *self, const void *key) int hash_id; git_hashtable_node *node; + assert(self && self->nodes); + for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) { node = node_with_hash(self, key, hash_id); if (node->key && self->key_equal(key, node->key) == 0) { diff --git a/vendor/libgit2/src/index.c b/vendor/libgit2/src/index.c index 95e56b7d5..6a355e11b 100644 --- a/vendor/libgit2/src/index.c +++ b/vendor/libgit2/src/index.c @@ -324,7 +324,7 @@ int git_index_add(git_index *index, const char *rel_path, int stage) entry.file_size = st.st_size; /* write the blob to disk and get the oid */ - if ((error = git_blob_writefile(&entry.oid, index->repository, full_path)) < GIT_SUCCESS) + if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS) return error; entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT); diff --git a/vendor/libgit2/src/object.c b/vendor/libgit2/src/object.c index fce99153b..0572663eb 100644 --- a/vendor/libgit2/src/object.c +++ b/vendor/libgit2/src/object.c @@ -66,153 +66,6 @@ static struct { { "REF_DELTA", 0, 0 } }; -/* - * Object source methods - * - * Abstract buffer methods that allow the writeback system - * to prepare the contents of any git file in-memory before - * writing them to disk. - */ -static int source_resize(git_odb_source *src) -{ - size_t write_offset, new_size; - void *new_data; - - write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data); - - new_size = src->raw.len * 2; - if ((new_data = git__malloc(new_size)) == NULL) - return GIT_ENOMEM; - - memcpy(new_data, src->raw.data, src->written_bytes); - free(src->raw.data); - - src->raw.data = new_data; - src->raw.len = new_size; - src->write_ptr = (char *)new_data + write_offset; - - return GIT_SUCCESS; -} - -int git__source_printf(git_odb_source *source, const char *format, ...) -{ - va_list arglist; - int len; - - assert(source->open && source->write_ptr); - - va_start(arglist, format); - - len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist); - - while (source->written_bytes + len >= source->raw.len) { - if (source_resize(source) < GIT_SUCCESS) - return GIT_ENOMEM; - - len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist); - } - - source->write_ptr = (char *)source->write_ptr + len; - source->written_bytes += len; - - return GIT_SUCCESS; -} - -int git__source_write(git_odb_source *source, const void *bytes, size_t len) -{ - assert(source); - - assert(source->open && source->write_ptr); - - while (source->written_bytes + len >= source->raw.len) { - if (source_resize(source) < GIT_SUCCESS) - return GIT_ENOMEM; - } - - memcpy(source->write_ptr, bytes, len); - source->write_ptr = (char *)source->write_ptr + len; - source->written_bytes += len; - - return GIT_SUCCESS; -} - -static void prepare_write(git_object *object) -{ - if (object->source.write_ptr != NULL || object->source.open) - git_object__source_close(object); - - /* TODO: proper size calculation */ - object->source.raw.data = git__malloc(OBJECT_BASE_SIZE); - object->source.raw.len = OBJECT_BASE_SIZE; - - object->source.write_ptr = object->source.raw.data; - object->source.written_bytes = 0; - - object->source.open = 1; -} - -static int write_back(git_object *object) -{ - int error; - git_oid new_id; - - assert(object); - - assert(object->source.open); - assert(object->modified); - - object->source.raw.len = object->source.written_bytes; - - if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < GIT_SUCCESS) - return error; - - if (object->in_memory) { - int idx = git_vector_search(&object->repo->memory_objects, object); - git_vector_remove(&object->repo->memory_objects, idx); - } else { - git_hashtable_remove(object->repo->objects, &object->id); - } - - git_oid_cpy(&object->id, &new_id); - git_hashtable_insert(object->repo->objects, &object->id, object); - - object->source.write_ptr = NULL; - object->source.written_bytes = 0; - - object->modified = 0; - object->in_memory = 0; - - git_object__source_close(object); - return GIT_SUCCESS; -} - -int git_object__source_open(git_object *object) -{ - int error; - - assert(object && !object->in_memory); - - if (object->source.open) - git_object__source_close(object); - - error = git_odb_read(&object->source.raw, object->repo->db, &object->id); - if (error < GIT_SUCCESS) - return error; - - object->source.open = 1; - return GIT_SUCCESS; -} - -void git_object__source_close(git_object *object) -{ - assert(object); - - if (object->source.open) { - git_rawobj_close(&object->source.raw); - object->source.open = 0; - } -} - static int create_object(git_object **object_out, git_otype type) { git_object *object = NULL; @@ -225,43 +78,19 @@ static int create_object(git_object **object_out, git_otype type) case GIT_OBJ_COMMIT: case GIT_OBJ_TAG: case GIT_OBJ_BLOB: + case GIT_OBJ_TREE: object = git__malloc(git_object__size(type)); if (object == NULL) return GIT_ENOMEM; memset(object, 0x0, git_object__size(type)); break; - - case GIT_OBJ_TREE: - object = (git_object *)git_tree__new(); - if (object == NULL) - return GIT_ENOMEM; - break; default: return GIT_EINVALIDTYPE; } - *object_out = object; - return GIT_SUCCESS; -} - -int git_object_new(git_object **object_out, git_repository *repo, git_otype type) -{ - git_object *object = NULL; - int error; - - assert(object_out && repo); - - if ((error = create_object(&object, type)) < GIT_SUCCESS) - return error; - - object->repo = repo; - object->in_memory = 1; - object->modified = 1; - - object->source.raw.type = type; + object->type = type; - object->lru = ++repo->lru_counter; *object_out = object; return GIT_SUCCESS; } @@ -269,123 +98,77 @@ int git_object_new(git_object **object_out, git_repository *repo, git_otype type int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) { git_object *object = NULL; - git_rawobj obj_file; + git_odb_object *odb_obj; int error = GIT_SUCCESS; assert(repo && object_out && id); - object = git_hashtable_lookup(repo->objects, id); + object = git_cache_get(&repo->objects, id); if (object != NULL) { + if (type != GIT_OBJ_ANY && type != object->type) + return GIT_EINVALIDTYPE; + *object_out = object; - object->lru = ++repo->lru_counter; - object->can_free = 0; return GIT_SUCCESS; } - error = git_odb_read(&obj_file, repo->db, id); + error = git_odb_read(&odb_obj, repo->db, id); if (error < GIT_SUCCESS) return error; - if (type != GIT_OBJ_ANY && type != obj_file.type) { - git_rawobj_close(&obj_file); + if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) { + git_odb_object_close(odb_obj); return GIT_EINVALIDTYPE; } - type = obj_file.type; + type = odb_obj->raw.type; if ((error = create_object(&object, type)) < GIT_SUCCESS) return error; /* Initialize parent object */ - git_oid_cpy(&object->id, id); + git_oid_cpy(&object->cached.oid, id); object->repo = repo; - memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj)); - object->source.open = 1; switch (type) { case GIT_OBJ_COMMIT: - error = git_commit__parse((git_commit *)object); + error = git_commit__parse((git_commit *)object, odb_obj); break; case GIT_OBJ_TREE: - error = git_tree__parse((git_tree *)object); + error = git_tree__parse((git_tree *)object, odb_obj); break; case GIT_OBJ_TAG: - error = git_tag__parse((git_tag *)object); + error = git_tag__parse((git_tag *)object, odb_obj); break; case GIT_OBJ_BLOB: - error = git_blob__parse((git_blob *)object); + error = git_blob__parse((git_blob *)object, odb_obj); break; default: break; } + git_odb_object_close(odb_obj); + if (error < GIT_SUCCESS) { git_object__free(object); return error; } - git_object__source_close(object); - git_hashtable_insert(repo->objects, &object->id, object); - - object->lru = ++repo->lru_counter; - *object_out = object; + *object_out = git_cache_try_store(&repo->objects, object); return GIT_SUCCESS; } -int git_object_write(git_object *object) +void git_object__free(void *_obj) { - int error; - git_odb_source *source; - - assert(object); - - if (object->modified == 0) - return GIT_SUCCESS; - - prepare_write(object); - source = &object->source; - - switch (source->raw.type) { - case GIT_OBJ_COMMIT: - error = git_commit__writeback((git_commit *)object, source); - break; - - case GIT_OBJ_TREE: - error = git_tree__writeback((git_tree *)object, source); - break; - - case GIT_OBJ_TAG: - error = git_tag__writeback((git_tag *)object, source); - break; - - case GIT_OBJ_BLOB: - error = git_blob__writeback((git_blob *)object, source); - break; + git_object *object = (git_object *)_obj; - default: - error = GIT_ERROR; - break; - } - - if (error < GIT_SUCCESS) { - git_object__source_close(object); - return error; - } - - return write_back(object); -} - -void git_object__free(git_object *object) -{ assert(object); - git_object__source_close(object); - - switch (object->source.raw.type) { + switch (object->type) { case GIT_OBJ_COMMIT: git_commit__free((git_commit *)object); break; @@ -413,29 +196,19 @@ void git_object_close(git_object *object) if (object == NULL) return; - if (object->in_memory) { - int idx = git_vector_search(&object->repo->memory_objects, object); - git_vector_remove(&object->repo->memory_objects, idx); - git_object__free(object); - } else { - object->can_free = 1; - } + git_cached_obj_decref((git_cached_obj *)object, git_object__free); } const git_oid *git_object_id(const git_object *obj) { assert(obj); - - if (obj->in_memory) - return NULL; - - return &obj->id; + return &obj->cached.oid; } git_otype git_object_type(const git_object *obj) { assert(obj); - return obj->source.raw.type; + return obj->type; } git_repository *git_object_owner(const git_object *obj) diff --git a/vendor/libgit2/src/odb.c b/vendor/libgit2/src/odb.c index 2013ac24c..9aeaa8a23 100644 --- a/vendor/libgit2/src/odb.c +++ b/vendor/libgit2/src/odb.c @@ -87,56 +87,49 @@ int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *ob return GIT_SUCCESS; } -void git_rawobj_close(git_rawobj *obj) -{ - free(obj->data); - obj->data = NULL; -} -int git_rawobj_hash(git_oid *id, git_rawobj *obj) +static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source) { - char hdr[64]; - int hdrlen; + git_odb_object *object = git__malloc(sizeof(git_odb_object)); + memset(object, 0x0, sizeof(git_odb_object)); - assert(id && obj); + git_oid_cpy(&object->cached.oid, oid); + memcpy(&object->raw, source, sizeof(git_rawobj)); - return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj); + return object; } -int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) +static void free_odb_object(void *o) { - z_stream zs; - int status = Z_OK; - - memset(&zs, 0x0, sizeof(zs)); - - zs.next_out = out; - zs.avail_out = outlen; - - zs.next_in = in; - zs.avail_in = inlen; + git_odb_object *object = (git_odb_object *)o; - if (inflateInit(&zs) < Z_OK) - return GIT_ERROR; + if (object != NULL) { + free(object->raw.data); + free(object); + } +} - while (status == Z_OK) - status = inflate(&zs, Z_FINISH); +void git_odb_object_close(git_odb_object *object) +{ + git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); +} - inflateEnd(&zs); +int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) +{ + char hdr[64]; + int hdrlen; + git_rawobj raw; - if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */) - return GIT_ERROR; + assert(id); - if (zs.total_out != outlen) - return GIT_ERROR; + raw.data = (void *)data; + raw.len = len; + raw.type = type; - return GIT_SUCCESS; + return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, &raw); } - - - /*********************************************************** * * OBJECT DATABASE PUBLIC API @@ -162,6 +155,8 @@ int git_odb_new(git_odb **out) if (!db) return GIT_ENOMEM; + git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { free(db); return GIT_ENOMEM; @@ -306,16 +301,23 @@ void git_odb_close(git_odb *db) } git_vector_free(&db->backends); + git_cache_free(&db->cache); free(db); } int git_odb_exists(git_odb *db, const git_oid *id) { + git_odb_object *object; unsigned int i; int found = 0; assert(db && id); + if ((object = git_cache_get(&db->cache, id)) != NULL) { + git_odb_object_close(object); + return 1; + } + for (i = 0; i < db->backends.length && !found; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; @@ -327,19 +329,27 @@ int git_odb_exists(git_odb *db, const git_oid *id) return found; } -int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id) +int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) { unsigned int i; int error = GIT_ENOTFOUND; + git_odb_object *object; - assert(out && db && id); + assert(db && id); + + if ((object = git_cache_get(&db->cache, id)) != NULL) { + *len_p = object->raw.len; + *type_p = object->raw.type; + git_odb_object_close(object); + return GIT_SUCCESS; + } for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->read_header != NULL) - error = b->read_header(out, b, id); + error = b->read_header(len_p, type_p, b, id); } /* @@ -347,37 +357,50 @@ int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id) * try reading the whole object and freeing the contents */ if (error < 0) { - error = git_odb_read(out, db, id); - git_rawobj_close(out); + if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS) + return error; + + *len_p = object->raw.len; + *type_p = object->raw.len; + git_odb_object_close(object); } - return error; + return GIT_SUCCESS; } -int git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id) +int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { unsigned int i; int error = GIT_ENOTFOUND; + git_rawobj raw; assert(out && db && id); + *out = git_cache_get(&db->cache, id); + if (*out != NULL) + return GIT_SUCCESS; + for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->read != NULL) - error = b->read(out, b, id); + error = b->read(&raw.data, &raw.len, &raw.type, b, id); + } + + if (error == GIT_SUCCESS) { + *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); } return error; } -int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj) +int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type) { unsigned int i; int error = GIT_ERROR; - assert(obj && db && id); + assert(stream && db); for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); @@ -387,8 +410,26 @@ int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj) if (internal->is_alternate) continue; - if (b->write != NULL) - error = b->write(id, b, obj); + if (b->writestream != NULL) + error = b->writestream(stream, b, size, type); + } + + return error; +} + +int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) +{ + unsigned int i; + int error = GIT_ERROR; + + assert(stream && db); + + for (i = 0; i < db->backends.length && error < 0; ++i) { + backend_internal *internal = git_vector_get(&db->backends, i); + git_odb_backend *b = internal->backend; + + if (b->readstream != NULL) + error = b->readstream(stream, b, oid); } return error; diff --git a/vendor/libgit2/src/odb.h b/vendor/libgit2/src/odb.h index c3d0a17ab..f3685834e 100644 --- a/vendor/libgit2/src/odb.h +++ b/vendor/libgit2/src/odb.h @@ -3,15 +3,31 @@ #include "git2/odb.h" #include "git2/oid.h" +#include "git2/types.h" #include "vector.h" +#include "cache.h" +/* DO NOT EXPORT */ +typedef struct { + void *data; /**< Raw, decompressed object data. */ + size_t len; /**< Total number of bytes in data. */ + git_otype type; /**< Type of this object. */ +} git_rawobj; + +/* EXPORT */ +struct git_odb_object { + git_cached_obj cached; + git_rawobj raw; +}; + +/* EXPORT */ struct git_odb { void *_internal; git_vector backends; + git_cache cache; }; int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj); -int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen); #endif diff --git a/vendor/libgit2/src/odb_loose.c b/vendor/libgit2/src/odb_loose.c index 4e2d9a639..4ab1128f3 100644 --- a/vendor/libgit2/src/odb_loose.c +++ b/vendor/libgit2/src/odb_loose.c @@ -30,14 +30,22 @@ #include "hash.h" #include "odb.h" #include "delta-apply.h" +#include "filebuf.h" #include "git2/odb_backend.h" +#include "git2/types.h" typedef struct { /* object header data */ git_otype type; /* object type */ size_t size; /* object size */ } obj_hdr; +typedef struct { + git_odb_stream stream; + git_filebuf fbuf; + int finished; +} loose_writestream; + typedef struct loose_backend { git_odb_backend parent; @@ -53,38 +61,6 @@ typedef struct loose_backend { * ***********************************************************/ -static int make_temp_file(git_file *fd, char *tmp, size_t n, char *file) -{ - char *template = "/tmp_obj_XXXXXX"; - size_t tmplen = strlen(template); - int dirlen; - - if ((dirlen = git__dirname_r(tmp, n, file)) < 0) - return GIT_ERROR; - - if ((dirlen + tmplen) >= n) - return GIT_ERROR; - - strcpy(tmp + dirlen, (dirlen) ? template : template + 1); - - *fd = gitfo_mkstemp(tmp); - if (*fd < 0 && dirlen) { - /* create directory if it doesn't exist */ - tmp[dirlen] = '\0'; - if ((gitfo_exists(tmp) < 0) && gitfo_mkdir(tmp, 0755)) - return GIT_ERROR; - /* try again */ - strcpy(tmp + dirlen, template); - *fd = gitfo_mkstemp(tmp); - } - if (*fd < 0) - return GIT_ERROR; - - return GIT_SUCCESS; -} - - - static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id) { size_t len = strlen(dir); @@ -236,72 +212,44 @@ static int finish_inflate(z_stream *s) return GIT_SUCCESS; } -static int deflate_buf(z_stream *s, void *in, size_t len, int flush) +static int is_zlib_compressed_data(unsigned char *data) { - int status = Z_OK; + unsigned int w; - set_stream_input(s, in, len); - while (status == Z_OK) { - status = deflate(s, flush); - if (s->avail_in == 0) - break; - } - return status; + w = ((unsigned int)(data[0]) << 8) + data[1]; + return data[0] == 0x78 && !(w % 31); } -static int deflate_obj(gitfo_buf *buf, char *hdr, int hdrlen, git_rawobj *obj, int level) +static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) { z_stream zs; - int status; - size_t size; - - assert(buf && !buf->data && hdr && obj); - assert(level == Z_DEFAULT_COMPRESSION || (level >= 0 && level <= 9)); + int status = Z_OK; - buf->data = NULL; - buf->len = 0; - init_stream(&zs, NULL, 0); + memset(&zs, 0x0, sizeof(zs)); - if (deflateInit(&zs, level) < Z_OK) - return GIT_ERROR; + zs.next_out = out; + zs.avail_out = outlen; - size = deflateBound(&zs, hdrlen + obj->len); + zs.next_in = in; + zs.avail_in = inlen; - if ((buf->data = git__malloc(size)) == NULL) { - deflateEnd(&zs); + if (inflateInit(&zs) < Z_OK) return GIT_ERROR; - } - set_stream_output(&zs, buf->data, size); - - /* compress the header */ - status = deflate_buf(&zs, hdr, hdrlen, Z_NO_FLUSH); + while (status == Z_OK) + status = inflate(&zs, Z_FINISH); - /* if header compressed OK, compress the object */ - if (status == Z_OK) - status = deflate_buf(&zs, obj->data, obj->len, Z_FINISH); + inflateEnd(&zs); - if (status != Z_STREAM_END) { - deflateEnd(&zs); - free(buf->data); - buf->data = NULL; + if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */) return GIT_ERROR; - } - buf->len = zs.total_out; - deflateEnd(&zs); + if (zs.total_out != outlen) + return GIT_ERROR; return GIT_SUCCESS; } -static int is_zlib_compressed_data(unsigned char *data) -{ - unsigned int w; - - w = ((unsigned int)(data[0]) << 8) + data[1]; - return data[0] == 0x78 && !(w % 31); -} - static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) { unsigned char *buf, *head = hb; @@ -371,7 +319,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj) in = ((unsigned char *)obj->data) + used; len = obj->len - used; - if (git_odb__inflate_buffer(in, len, buf, hdr.size)) { + if (inflate_buffer(in, len, buf, hdr.size)) { free(buf); return GIT_ERROR; } @@ -505,37 +453,6 @@ static int read_header_loose(git_rawobj *out, const char *loc) return error; } -static int write_obj(gitfo_buf *buf, git_oid *id, loose_backend *backend) -{ - char file[GIT_PATH_MAX]; - char temp[GIT_PATH_MAX]; - git_file fd; - - if (object_file_name(file, sizeof(file), backend->objects_dir, id)) - return GIT_EOSERR; - - if (make_temp_file(&fd, temp, sizeof(temp), file) < 0) - return GIT_EOSERR; - - if (gitfo_write(fd, buf->data, buf->len) < 0) { - gitfo_close(fd); - gitfo_unlink(temp); - return GIT_EOSERR; - } - - if (backend->fsync_object_files) - gitfo_fsync(fd); - gitfo_close(fd); - gitfo_chmod(temp, 0444); - - if (gitfo_mv(temp, file) < 0) { - gitfo_unlink(temp); - return GIT_EOSERR; - } - - return GIT_SUCCESS; -} - static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid) { object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid); @@ -558,29 +475,44 @@ static int locate_object(char *object_location, loose_backend *backend, const gi * ***********************************************************/ -int loose_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; - assert(obj && backend && oid); + assert(backend && oid); if (locate_object(object_path, (loose_backend *)backend, oid) < 0) return GIT_ENOTFOUND; - return read_header_loose(obj, object_path); -} + if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS) + return error; + *len_p = raw.len; + *type_p = raw.type; + return GIT_SUCCESS; +} -int loose_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; - assert(obj && backend && oid); + assert(backend && oid); if (locate_object(object_path, (loose_backend *)backend, oid) < 0) return GIT_ENOTFOUND; - return read_loose(obj, object_path); + if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) + return error; + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + + return GIT_SUCCESS; } int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) @@ -592,32 +524,104 @@ int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS; } +int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) +{ + loose_writestream *stream = (loose_writestream *)_stream; + loose_backend *backend = (loose_backend *)_stream->backend; + + int error; + char final_path[GIT_PATH_MAX]; + + if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS) + return error; + + if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) + return GIT_ENOMEM; -int loose_backend__write(git_oid *id, git_odb_backend *_backend, git_rawobj *obj) + if ((error = gitfo_mkdir_2file(final_path)) < GIT_SUCCESS) + return error; + + stream->finished = 1; + return git_filebuf_commit_at(&stream->fbuf, final_path); +} + +int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len) { + loose_writestream *stream = (loose_writestream *)_stream; + return git_filebuf_write(&stream->fbuf, data, len); +} + +void loose_backend__stream_free(git_odb_stream *_stream) +{ + loose_writestream *stream = (loose_writestream *)_stream; + + if (!stream->finished) + git_filebuf_cleanup(&stream->fbuf); + + free(stream); +} + +static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type) +{ + const char *type_str = git_object_type2string(obj_type); + int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len); + + assert(len > 0); /* otherwise snprintf() is broken */ + assert(((size_t) len) < n); /* otherwise the caller is broken! */ + + if (len < 0 || ((size_t) len) >= n) + return GIT_ERROR; + return len+1; +} + +int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type) +{ + loose_backend *backend; + loose_writestream *stream; + char hdr[64]; int hdrlen; - gitfo_buf buf = GITFO_BUF_INIT; int error; - loose_backend *backend; - assert(id && _backend && obj); + assert(_backend); backend = (loose_backend *)_backend; + *stream_out = NULL; - if ((error = git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0) - return error; + hdrlen = format_object_header(hdr, sizeof(hdr), length, type); + if (hdrlen < GIT_SUCCESS) + return GIT_EOBJCORRUPTED; + + stream = git__calloc(1, sizeof(loose_writestream)); + if (stream == NULL) + return GIT_ENOMEM; - if (git_odb_exists(_backend->odb, id)) - return GIT_SUCCESS; + stream->stream.backend = _backend; + stream->stream.read = NULL; /* read only */ + stream->stream.write = &loose_backend__stream_write; + stream->stream.finalize_write = &loose_backend__stream_fwrite; + stream->stream.free = &loose_backend__stream_free; + stream->stream.mode = GIT_STREAM_WRONLY; - if ((error = deflate_obj(&buf, hdr, hdrlen, obj, backend->object_zlib_level)) < 0) + error = git_filebuf_open(&stream->fbuf, NULL, + GIT_FILEBUF_HASH_CONTENTS | + GIT_FILEBUF_DEFLATE_CONTENTS | + GIT_FILEBUF_TEMPORARY); + + if (error < GIT_SUCCESS) { + free(stream); return error; + } - error = write_obj(&buf, id, backend); + error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen); + if (error < GIT_SUCCESS) { + git_filebuf_cleanup(&stream->fbuf); + free(stream); + return error; + } - gitfo_free_buf(&buf); - return error; + *stream_out = (git_odb_stream *)stream; + return GIT_SUCCESS; } void loose_backend__free(git_odb_backend *_backend) @@ -649,7 +653,7 @@ int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir backend->parent.read = &loose_backend__read; backend->parent.read_header = &loose_backend__read_header; - backend->parent.write = &loose_backend__write; + backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; backend->parent.free = &loose_backend__free; diff --git a/vendor/libgit2/src/odb_pack.c b/vendor/libgit2/src/odb_pack.c index 3067179be..65210f0b0 100644 --- a/vendor/libgit2/src/odb_pack.c +++ b/vendor/libgit2/src/odb_pack.c @@ -1243,7 +1243,7 @@ static int packfile_unpack_delta( error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type); if (error < GIT_SUCCESS) { - git_rawobj_close(&base); + free(base.data); return error; } @@ -1252,8 +1252,8 @@ static int packfile_unpack_delta( base.data, base.len, delta.data, delta.len); - git_rawobj_close(&base); - git_rawobj_close(&delta); + free(base.data); + free(delta.data); /* TODO: we might want to cache this shit. eventually */ //add_delta_base_cache(p, base_offset, base, base_size, *type); @@ -1337,15 +1337,23 @@ int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const g } */ -int pack_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid) +int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { struct pack_entry e; + git_rawobj raw; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS) return error; - return packfile_unpack(obj, (struct pack_backend *)backend, e.p, e.offset); + if ((error = packfile_unpack(&raw, (struct pack_backend *)backend, e.p, e.offset)) < GIT_SUCCESS) + return error; + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + + return GIT_SUCCESS; } int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) @@ -1397,7 +1405,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->parent.read = &pack_backend__read; backend->parent.read_header = NULL; - backend->parent.write = NULL; backend->parent.exists = &pack_backend__exists; backend->parent.free = &pack_backend__free; diff --git a/vendor/libgit2/src/oid.c b/vendor/libgit2/src/oid.c index 81b7d6005..eb167a685 100644 --- a/vendor/libgit2/src/oid.c +++ b/vendor/libgit2/src/oid.c @@ -143,14 +143,18 @@ int git__parse_oid(git_oid *oid, char **buffer_out, return GIT_SUCCESS; } -int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid) +int git__write_oid(git_odb_stream *stream, const char *header, const git_oid *oid) { - char hex_oid[41]; + char hex_oid[42]; - git_oid_fmt(hex_oid, oid); - hex_oid[40] = 0; + git_oid_fmt(hex_oid + 1, oid); - return git__source_printf(src, "%s %s\n", header, hex_oid); + hex_oid[0] = ' '; + hex_oid[41] = '\n'; + + stream->write(stream, header, strlen(header)); + stream->write(stream, hex_oid, 42); + return GIT_SUCCESS; } void git_oid_mkraw(git_oid *out, const unsigned char *raw) diff --git a/vendor/libgit2/src/pqueue.c b/vendor/libgit2/src/pqueue.c index 98152cb85..6307175e3 100644 --- a/vendor/libgit2/src/pqueue.c +++ b/vendor/libgit2/src/pqueue.c @@ -50,6 +50,10 @@ void git_pqueue_free(git_pqueue *q) q->d = NULL; } +void git_pqueue_clear(git_pqueue *q) +{ + q->size = 1; +} size_t git_pqueue_size(git_pqueue *q) { diff --git a/vendor/libgit2/src/pqueue.h b/vendor/libgit2/src/pqueue.h index 6db74661d..7a1394803 100644 --- a/vendor/libgit2/src/pqueue.h +++ b/vendor/libgit2/src/pqueue.h @@ -53,6 +53,11 @@ int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri); */ void git_pqueue_free(git_pqueue *q); +/** + * clear all the elements in the queue + * @param q the queue + */ +void git_pqueue_clear(git_pqueue *q); /** * return the size of the queue. diff --git a/vendor/libgit2/src/refs.c b/vendor/libgit2/src/refs.c index 1ed6b567c..40b80ec9b 100644 --- a/vendor/libgit2/src/refs.c +++ b/vendor/libgit2/src/refs.c @@ -59,16 +59,16 @@ static uint32_t reftable_hash(const void *key, int hash_id) static void reference_free(git_reference *reference); static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type); +static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name); /* loose refs */ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content); static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content); -static int loose_read(gitfo_buf *file_content, const char *name, const char *repo_path); -static int loose_lookup( git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); +static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); static int loose_write(git_reference *ref); +static int loose_update(git_reference *ref); /* packed refs */ -static int packed_readpack(gitfo_buf *packfile, const char *repo_path); static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end); static int packed_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end); static int packed_load(git_repository *repo); @@ -146,12 +146,73 @@ static int reference_create( return error; } +static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name) +{ + struct stat st; + char path[GIT_PATH_MAX]; + + /* Determine the full path of the file */ + git__joinpath(path, repo_path, ref_name); + + if (gitfo_stat(path, &st) < 0) + return GIT_ENOTFOUND; + + if (S_ISDIR(st.st_mode)) + return GIT_EOBJCORRUPTED; + + if (mtime) + *mtime = st.st_mtime; + + if (file_content) + return gitfo_read_file(file_content, path); + + return GIT_SUCCESS; +} + /***************************************** * Internal methods - Loose references *****************************************/ +static int loose_update(git_reference *ref) +{ + int error; + time_t ref_time; + gitfo_buf ref_file = GITFO_BUF_INIT; + + if (ref->type & GIT_REF_PACKED) + return packed_load(ref->owner); + + error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name); + if (error < GIT_SUCCESS) + goto cleanup; + + if (ref_time == ref->mtime) + return GIT_SUCCESS; + + error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name); + if (error < GIT_SUCCESS) + goto cleanup; + + if (ref->type == GIT_REF_SYMBOLIC) + error = loose_parse_symbolic(ref, &ref_file); + else if (ref->type == GIT_REF_OID) + error = loose_parse_oid(ref, &ref_file); + else + error = GIT_EINVALIDREFSTATE; + + gitfo_free_buf(&ref_file); + +cleanup: + if (error != GIT_SUCCESS) { + reference_free(ref); + git_hashtable_remove(ref->owner->references.loose_cache, ref->name); + } + + return error; +} + static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) { const unsigned int header_len = strlen(GIT_SYMREF); @@ -172,6 +233,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) refname_start += header_len; + free(ref_sym->target); ref_sym->target = git__strdup(refname_start); if (ref_sym->target == NULL) return GIT_ENOMEM; @@ -213,27 +275,6 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content) return GIT_SUCCESS; } -static int loose_read(gitfo_buf *file_content, const char *name, const char *repo_path) -{ - int error = GIT_SUCCESS; - char ref_path[GIT_PATH_MAX]; - - /* Determine the full path of the ref */ - git__joinpath(ref_path, repo_path, name); - - /* Does it even exist ? */ - if (gitfo_exists(ref_path) < GIT_SUCCESS) - return GIT_ENOTFOUND; - - /* A ref can not be a directory */ - if (!gitfo_isdir(ref_path)) - return GIT_ENOTFOUND; - - if (file_content != NULL) - error = gitfo_read_file(file_content, ref_path); - - return error; -} static git_rtype loose_guess_rtype(const char *full_path) { @@ -262,10 +303,11 @@ static int loose_lookup( int error = GIT_SUCCESS; gitfo_buf ref_file = GITFO_BUF_INIT; git_reference *ref = NULL; + time_t ref_time; *ref_out = NULL; - error = loose_read(&ref_file, name, repo->path_repository); + error = reference_read(&ref_file, &ref_time, repo->path_repository, name); if (error < GIT_SUCCESS) goto cleanup; @@ -289,6 +331,7 @@ static int loose_lookup( if (error < GIT_SUCCESS) goto cleanup; + ref->mtime = ref_time; *ref_out = ref; return GIT_SUCCESS; @@ -304,6 +347,7 @@ static int loose_write(git_reference *ref) char ref_path[GIT_PATH_MAX]; int error, contents_size; char *ref_contents = NULL; + struct stat st; assert((ref->type & GIT_REF_PACKED) == 0); @@ -349,6 +393,9 @@ static int loose_write(git_reference *ref) error = git_filebuf_commit(&file); + if (gitfo_stat(ref_path, &st) == GIT_SUCCESS) + ref->mtime = st.st_mtime; + free(ref_contents); return error; @@ -366,19 +413,6 @@ static int loose_write(git_reference *ref) /***************************************** * Internal methods - Packed references *****************************************/ -static int packed_readpack(gitfo_buf *packfile, const char *repo_path) -{ - char ref_path[GIT_PATH_MAX]; - - /* Determine the full path of the file */ - git__joinpath(ref_path, repo_path, GIT_PACKEDREFS_FILE); - - /* Does it even exist ? */ - if (gitfo_exists(ref_path) < GIT_SUCCESS) - return GIT_ENOTFOUND; - - return gitfo_read_file(packfile, ref_path); -} static int packed_parse_peel( reference_oid *tag_ref, @@ -483,19 +517,40 @@ static int packed_load(git_repository *repo) git_refcache *ref_cache = &repo->references; /* already loaded */ - if (repo->references.packfile != NULL) - return GIT_SUCCESS; + if (repo->references.packfile != NULL) { + time_t packed_time; - repo->references.packfile = git_hashtable_alloc( - default_table_size, - reftable_hash, - (git_hash_keyeq_ptr)strcmp); + /* check if we can read the time of the index; + * if we can read it and it matches the time of the + * index we had previously loaded, we don't need to do + * anything else. + * + * if we cannot load the time (e.g. the packfile + * has disappeared) or the time is different, we + * have to reload the packfile */ - if (repo->references.packfile == NULL) - return GIT_ENOMEM; + if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) && + packed_time == ref_cache->packfile_time) + return GIT_SUCCESS; + + git_hashtable_clear(repo->references.packfile); + } else { + ref_cache->packfile = git_hashtable_alloc( + default_table_size, + reftable_hash, + (git_hash_keyeq_ptr)strcmp); - /* read the packfile from disk */ - error = packed_readpack(&packfile, repo->path_repository); + if (ref_cache->packfile == NULL) + return GIT_ENOMEM; + } + + /* read the packfile from disk; + * store its modification time to check for future reloads */ + error = reference_read( + &packfile, + &ref_cache->packfile_time, + repo->path_repository, + GIT_PACKEDREFS_FILE); /* there is no packfile on disk; that's ok */ if (error == GIT_ENOTFOUND) @@ -507,22 +562,14 @@ static int packed_load(git_repository *repo) buffer_start = (const char *)packfile.data; buffer_end = (const char *)(buffer_start) + packfile.len; - /* Does the header look like valid? */ - if (git__prefixcmp((const char *)(buffer_start), GIT_PACKEDREFS_HEADER)) { - error = GIT_EPACKEDREFSCORRUPTED; - goto cleanup; - } - - /* Let's skip the header */ - buffer_start += strlen(GIT_PACKEDREFS_HEADER); - - if (*buffer_start == '\r') + while (buffer_start < buffer_end && buffer_start[0] == '#') { + buffer_start = strchr(buffer_start, '\n'); + if (buffer_start == NULL) { + error = GIT_EPACKEDREFSCORRUPTED; + goto cleanup; + } buffer_start++; - - if (*buffer_start != '\n') - return GIT_EPACKEDREFSCORRUPTED; - - buffer_start++; + } while (buffer_start < buffer_end) { reference_oid *ref = NULL; @@ -544,7 +591,12 @@ static int packed_load(git_repository *repo) } } + gitfo_free_buf(&packfile); + return GIT_SUCCESS; + cleanup: + git_hashtable_free(ref_cache->packfile); + ref_cache->packfile = NULL; gitfo_free_buf(&packfile); return error; } @@ -554,6 +606,7 @@ static int packed_load(git_repository *repo) struct dirent_list_data { git_vector ref_list; + git_repository *repo; size_t repo_path_len; unsigned int list_flags; }; @@ -561,16 +614,19 @@ struct dirent_list_data { static int _dirent_loose_listall(void *_data, char *full_path) { struct dirent_list_data *data = (struct dirent_list_data *)_data; - char *file_path; + char *file_path = full_path + data->repo_path_len; if (gitfo_isdir(full_path) == GIT_SUCCESS) return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data); + /* do not add twice a reference that exists already in the packfile */ + if ((data->list_flags & GIT_REF_PACKED) != 0 && + git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL) + return GIT_SUCCESS; + if ((data->list_flags & loose_guess_rtype(full_path)) == 0) return GIT_SUCCESS; /* we are filtering out this reference */ - file_path = full_path + data->repo_path_len; - return git_vector_insert(&data->ref_list, git__strdup(file_path)); } @@ -810,7 +866,9 @@ static int packed_write(git_repository *repo) if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS) return error; - /* Packfiles have a header! */ + /* Packfiles have a header... apparently + * This is in fact not required, but we might as well print it + * just for kicks */ if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) return error; @@ -836,8 +894,14 @@ static int packed_write(git_repository *repo) /* when and only when the packfile has been properly written, * we can go ahead and remove the loose refs */ - if (error == GIT_SUCCESS) + if (error == GIT_SUCCESS) { + struct stat st; + error = packed_remove_loose(repo, &packing_list); + + if (gitfo_stat(pack_file_path, &st) == GIT_SUCCESS) + repo->references.packfile_time = st.st_mtime; + } } else git_filebuf_cleanup(&pack_file); @@ -872,7 +936,7 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch /* First, check has been previously loaded and cached */ *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name); if (*ref_out != NULL) - return GIT_SUCCESS; + return loose_update(*ref_out); /* Then check if there is a loose file for that reference.*/ error = loose_lookup(ref_out, repo, normalized_name, 0); @@ -890,12 +954,10 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch * If we cannot find a loose reference, we look into the packfile * Load the packfile first if it hasn't been loaded */ - if (!repo->references.packfile) { - /* load all the packed references */ - error = packed_load(repo); - if (error < GIT_SUCCESS) - return error; - } + /* load all the packed references */ + error = packed_load(repo); + if (error < GIT_SUCCESS) + return error; /* Look up on the packfile */ *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name); @@ -1002,6 +1064,9 @@ const git_oid *git_reference_oid(git_reference *ref) if ((ref->type & GIT_REF_OID) == 0) return NULL; + if (loose_update(ref) < GIT_SUCCESS) + return NULL; + return &((reference_oid *)ref)->oid; } @@ -1012,6 +1077,9 @@ const char *git_reference_target(git_reference *ref) if ((ref->type & GIT_REF_SYMBOLIC) == 0) return NULL; + if (loose_update(ref) < GIT_SUCCESS) + return NULL; + return ((reference_symbolic *)ref)->target; } @@ -1295,6 +1363,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) assert(resolved_ref && ref); *resolved_ref = NULL; + + if ((error = loose_update(ref)) < GIT_SUCCESS) + return error; repo = ref->owner; @@ -1342,15 +1413,9 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in git_vector_init(&data.ref_list, 8, NULL); data.repo_path_len = strlen(repo->path_repository); data.list_flags = list_flags; + data.repo = repo; - git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR); - error = gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); - - if (error < GIT_SUCCESS) { - git_vector_free(&data.ref_list); - return error; - } - + /* list all the packed references first */ if (list_flags & GIT_REF_PACKED) { const char *ref_name; void *_unused; @@ -1365,6 +1430,16 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in ); } + /* now list the loose references, trying not to + * duplicate the ref names already in the packed-refs file */ + git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR); + error = gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); + + if (error < GIT_SUCCESS) { + git_vector_free(&data.ref_list); + return error; + } + array->strings = (char **)data.ref_list.contents; array->count = data.ref_list.length; return GIT_SUCCESS; diff --git a/vendor/libgit2/src/refs.h b/vendor/libgit2/src/refs.h index a542ac0f2..bebb1b97d 100644 --- a/vendor/libgit2/src/refs.h +++ b/vendor/libgit2/src/refs.h @@ -23,11 +23,13 @@ struct git_reference { git_repository *owner; char *name; unsigned int type; + time_t mtime; }; typedef struct { git_hashtable *packfile; git_hashtable *loose_cache; + time_t packfile_time; } git_refcache; diff --git a/vendor/libgit2/src/repository.c b/vendor/libgit2/src/repository.c index 37aa44781..132969402 100644 --- a/vendor/libgit2/src/repository.c +++ b/vendor/libgit2/src/repository.c @@ -40,29 +40,11 @@ #define GIT_BRANCH_MASTER "master" -static const int OBJECT_TABLE_SIZE = 32; - typedef struct { char *path_repository; unsigned is_bare:1, has_been_reinit:1; } repo_init; -/* - * Hash table methods - * - * Callbacks for the ODB cache, implemented - * as a hash table - */ -static uint32_t object_table_hash(const void *key, int hash_id) -{ - uint32_t r; - git_oid *id; - - id = (git_oid *)key; - memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); - return r; -} - /* * Git repository open methods * @@ -186,25 +168,9 @@ static git_repository *repository_alloc() memset(repo, 0x0, sizeof(git_repository)); - repo->objects = git_hashtable_alloc( - OBJECT_TABLE_SIZE, - object_table_hash, - (git_hash_keyeq_ptr)git_oid_cmp); - - if (repo->objects == NULL) { - free(repo); - return NULL; - } + git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { - git_hashtable_free(repo->objects); - free(repo); - return NULL; - } - - if (git_vector_init(&repo->memory_objects, 16, NULL) < GIT_SUCCESS) { - git_hashtable_free(repo->objects); - git_repository__refcache_free(&repo->references); free(repo); return NULL; } @@ -330,51 +296,19 @@ int git_repository_open(git_repository **repo_out, const char *path) return error; } -int git_repository_gc(git_repository *repo) -{ - int collected = 0; - git_object *object; - const void *_unused; - - GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - if (object->can_free) { - git_object__free(object); - collected++; - } - ); - - return collected; -} - void git_repository_free(git_repository *repo) { - git_object *object; - const void *_unused; - unsigned int i; - if (repo == NULL) return; - /* force free all the objects */ - GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, - git_object__free(object); - ); - - for (i = 0; i < repo->memory_objects.length; ++i) { - object = git_vector_get(&repo->memory_objects, i); - git_object__free(object); - } + git_cache_free(&repo->objects); + git_repository__refcache_free(&repo->references); free(repo->path_workdir); free(repo->path_index); free(repo->path_repository); free(repo->path_odb); - git_hashtable_free(repo->objects); - git_vector_free(&repo->memory_objects); - - git_repository__refcache_free(&repo->references); - if (repo->db != NULL) git_odb_close(repo->db); diff --git a/vendor/libgit2/src/repository.h b/vendor/libgit2/src/repository.h index 48b8dae6b..fef1c7da0 100644 --- a/vendor/libgit2/src/repository.h +++ b/vendor/libgit2/src/repository.h @@ -9,6 +9,7 @@ #include "hashtable.h" #include "index.h" +#include "cache.h" #include "refs.h" #define DOT_GIT ".git" @@ -16,28 +17,17 @@ #define GIT_OBJECTS_DIR "objects/" #define GIT_INDEX_FILE "index" -typedef struct { - git_rawobj raw; - void *write_ptr; - size_t written_bytes; - int open:1; -} git_odb_source; - struct git_object { - git_oid id; + git_cached_obj cached; git_repository *repo; - git_odb_source source; - unsigned int lru; - unsigned char in_memory, modified, can_free, _pad; + git_otype type; }; struct git_repository { git_odb *db; git_index *index; - git_hashtable *objects; - git_vector memory_objects; - + git_cache objects; git_refcache references; char *path_repository; @@ -49,17 +39,11 @@ struct git_repository { unsigned int lru_counter; }; -int git_object__source_open(git_object *object); -void git_object__source_close(git_object *object); - /* fully free the object; internal method, do not * export */ -void git_object__free(git_object *object); - -int git__source_printf(git_odb_source *source, const char *format, ...); -int git__source_write(git_odb_source *source, const void *bytes, size_t len); +void git_object__free(void *object); int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header); -int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid); +int git__write_oid(git_odb_stream *src, const char *header, const git_oid *oid); #endif diff --git a/vendor/libgit2/src/revwalk.c b/vendor/libgit2/src/revwalk.c index edafbe73d..a9d4f8734 100644 --- a/vendor/libgit2/src/revwalk.c +++ b/vendor/libgit2/src/revwalk.c @@ -25,10 +25,12 @@ #include "common.h" #include "commit.h" -#include "revwalk.h" +#include "odb.h" #include "hashtable.h" #include "pqueue.h" +#include "git2/revwalk.h" + typedef struct commit_object { git_oid oid; uint32_t time; @@ -52,7 +54,6 @@ struct git_revwalk { git_repository *repo; git_hashtable *commits; - git_vector pending; commit_list *iterator_topo; commit_list *iterator_rand; @@ -78,13 +79,17 @@ commit_list *commit_list_insert(commit_object *item, commit_list **list_p) return new_list; } -void commit_list_free(commit_list *list) +void commit_list_free(commit_list **list_p) { + commit_list *list = *list_p; + while (list) { commit_list *temp = list; list = temp->next; free(temp); } + + *list_p = NULL; } commit_object *commit_list_pop(commit_list **stack) @@ -155,71 +160,6 @@ static commit_object **alloc_parents(commit_object *commit, size_t n_parents) return git__malloc(n_parents * sizeof(commit_object *)); } -int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) -{ - git_revwalk *walk; - - walk = git__malloc(sizeof(git_revwalk)); - if (walk == NULL) - return GIT_ENOMEM; - - memset(walk, 0x0, sizeof(git_revwalk)); - - walk->commits = git_hashtable_alloc(64, - object_table_hash, - (git_hash_keyeq_ptr)git_oid_cmp); - - if (walk->commits == NULL) { - free(walk); - return GIT_ENOMEM; - } - - git_vector_init(&walk->pending, 8, NULL); - git_vector_init(&walk->memory_alloc, 8, NULL); - alloc_chunk(walk); - - walk->repo = repo; - - *revwalk_out = walk; - return GIT_SUCCESS; -} - -void git_revwalk_free(git_revwalk *walk) -{ - unsigned int i; - - if (walk == NULL) - return; - - git_revwalk_reset(walk); - git_hashtable_free(walk->commits); - git_vector_free(&walk->pending); - - for (i = 0; i < walk->memory_alloc.length; ++i) { - free(git_vector_get(&walk->memory_alloc, i)); - } - - git_vector_free(&walk->memory_alloc); - free(walk); -} - -git_repository *git_revwalk_repository(git_revwalk *walk) -{ - assert(walk); - return walk->repo; -} - -int git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode) -{ - assert(walk); - - if (walk->walking) - return GIT_EBUSY; - - walk->sorting = sort_mode; - git_revwalk_reset(walk); - return GIT_SUCCESS; -} static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid) { @@ -297,22 +237,22 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo static int commit_parse(git_revwalk *walk, commit_object *commit) { - git_rawobj data; + git_odb_object *obj; int error; if (commit->parsed) return GIT_SUCCESS; - if ((error = git_odb_read(&data, walk->repo->db, &commit->oid)) < GIT_SUCCESS) + if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS) return error; - if (data.type != GIT_OBJ_COMMIT) { - git_rawobj_close(&data); + if (obj->raw.type != GIT_OBJ_COMMIT) { + git_odb_object_close(obj); return GIT_EOBJTYPE; } - error = commit_quick_parse(walk, commit, &data); - git_rawobj_close(&data); + error = commit_quick_parse(walk, commit, &obj->raw); + git_odb_object_close(obj); return error; } @@ -366,10 +306,9 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) if (commit == NULL) return GIT_ENOTFOUND; - if (uninteresting) - mark_uninteresting(commit); + commit->uninteresting = uninteresting; - return git_vector_insert(&walk->pending, commit); + return process_commit(walk, commit); } int git_revwalk_push(git_revwalk *walk, const git_oid *oid) @@ -468,31 +407,11 @@ static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk) static int prepare_walk(git_revwalk *walk) { - unsigned int i; int error; - - if (walk->sorting & GIT_SORT_TIME) { - if ((error = git_pqueue_init(&walk->iterator_time, 32, commit_time_cmp)) < GIT_SUCCESS) - return error; - - walk->get_next = &revwalk_next_timesort; - walk->enqueue = &revwalk_enqueue_timesort; - } else { - walk->get_next = &revwalk_next_unsorted; - walk->enqueue = &revwalk_enqueue_unsorted; - } - - for (i = 0; i < walk->pending.length; ++i) { - commit_object *commit = walk->pending.contents[i]; - if ((error = process_commit(walk, commit)) < GIT_SUCCESS) { - return error; - } - } + commit_object *next; if (walk->sorting & GIT_SORT_TOPOLOGICAL) { - commit_object *next; unsigned short i; - int error; while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) { for (i = 0; i < next->out_degree; ++i) { @@ -510,8 +429,6 @@ static int prepare_walk(git_revwalk *walk) } if (walk->sorting & GIT_SORT_REVERSE) { - commit_object *next; - int error; while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) commit_list_insert(next, &walk->iterator_reverse); @@ -527,6 +444,83 @@ static int prepare_walk(git_revwalk *walk) } + + + +int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) +{ + git_revwalk *walk; + + walk = git__malloc(sizeof(git_revwalk)); + if (walk == NULL) + return GIT_ENOMEM; + + memset(walk, 0x0, sizeof(git_revwalk)); + + walk->commits = git_hashtable_alloc(64, + object_table_hash, + (git_hash_keyeq_ptr)git_oid_cmp); + + if (walk->commits == NULL) { + free(walk); + return GIT_ENOMEM; + } + + git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp); + git_vector_init(&walk->memory_alloc, 8, NULL); + alloc_chunk(walk); + + walk->get_next = &revwalk_next_unsorted; + walk->enqueue = &revwalk_enqueue_unsorted; + + walk->repo = repo; + + *revwalk_out = walk; + return GIT_SUCCESS; +} + +void git_revwalk_free(git_revwalk *walk) +{ + unsigned int i; + + if (walk == NULL) + return; + + git_revwalk_reset(walk); + git_hashtable_free(walk->commits); + git_pqueue_free(&walk->iterator_time); + + for (i = 0; i < walk->memory_alloc.length; ++i) + free(git_vector_get(&walk->memory_alloc, i)); + + git_vector_free(&walk->memory_alloc); + free(walk); +} + +git_repository *git_revwalk_repository(git_revwalk *walk) +{ + assert(walk); + return walk->repo; +} + +void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode) +{ + assert(walk); + + if (walk->walking) + git_revwalk_reset(walk); + + walk->sorting = sort_mode; + + if (walk->sorting & GIT_SORT_TIME) { + walk->get_next = &revwalk_next_timesort; + walk->enqueue = &revwalk_enqueue_timesort; + } else { + walk->get_next = &revwalk_next_unsorted; + walk->enqueue = &revwalk_enqueue_unsorted; + } +} + int git_revwalk_next(git_oid *oid, git_revwalk *walk) { int error; @@ -540,8 +534,11 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) } error = walk->get_next(&next, walk); - if (error < GIT_SUCCESS) + if (error < GIT_SUCCESS) { + if (error == GIT_EREVWALKOVER) + git_revwalk_reset(walk); return error; + } git_oid_cpy(oid, &next->oid); return GIT_SUCCESS; @@ -560,10 +557,10 @@ void git_revwalk_reset(git_revwalk *walk) commit->topo_delay = 0; ); - git_pqueue_free(&walk->iterator_time); - commit_list_free(walk->iterator_topo); - commit_list_free(walk->iterator_rand); - commit_list_free(walk->iterator_reverse); + git_pqueue_clear(&walk->iterator_time); + commit_list_free(&walk->iterator_topo); + commit_list_free(&walk->iterator_rand); + commit_list_free(&walk->iterator_reverse); walk->walking = 0; } diff --git a/vendor/libgit2/src/revwalk.h b/vendor/libgit2/src/revwalk.h deleted file mode 100644 index 2970d773c..000000000 --- a/vendor/libgit2/src/revwalk.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef INCLUDE_revwalk_h__ -#define INCLUDE_revwalk_h__ - -#include "git2/common.h" -#include "git2/revwalk.h" - -#include "commit.h" -#include "repository.h" -#include "hashtable.h" - -#endif /* INCLUDE_revwalk_h__ */ diff --git a/vendor/libgit2/src/signature.c b/vendor/libgit2/src/signature.c index 5c9f15973..13816c396 100644 --- a/vendor/libgit2/src/signature.c +++ b/vendor/libgit2/src/signature.c @@ -46,13 +46,7 @@ git_signature *git_signature_new(const char *name, const char *email, time_t tim goto cleanup; p->name = git__strdup(name); - if (p->name == NULL) - goto cleanup; - p->email = git__strdup(email); - if (p->email == NULL) - goto cleanup; - p->when.time = time; p->when.offset = offset; @@ -179,10 +173,12 @@ int git_signature__parse(git_signature *sig, char **buffer_out, return GIT_SUCCESS; } -int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig) +int git_signature__write(char **signature, const char *header, const git_signature *sig) { - char sign; int offset, hours, mins; + char sig_buffer[2048]; + int sig_buffer_len; + char sign; offset = sig->when.offset; sign = (sig->when.offset < 0) ? '-' : '+'; @@ -193,7 +189,16 @@ int git_signature__write(git_odb_source *src, const char *header, const git_sign hours = offset / 60; mins = offset % 60; - return git__source_printf(src, "%s %s <%s> %u %c%02d%02d\n", header, sig->name, sig->email, (unsigned)sig->when.time, sign, hours, mins); + sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer), + "%s %s <%s> %u %c%02d%02d\n", + header, sig->name, sig->email, + (unsigned)sig->when.time, sign, hours, mins); + + if (sig_buffer_len < 0 || (size_t)sig_buffer_len > sizeof(sig_buffer)) + return GIT_ENOMEM; + + *signature = git__strdup(sig_buffer); + return sig_buffer_len; } diff --git a/vendor/libgit2/src/signature.h b/vendor/libgit2/src/signature.h index ee212c2dc..3534cb21f 100644 --- a/vendor/libgit2/src/signature.h +++ b/vendor/libgit2/src/signature.h @@ -7,6 +7,6 @@ #include int git_signature__parse(git_signature *sig, char **buffer_out, const char *buffer_end, const char *header); -int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig); +int git_signature__write(char **signature, const char *header, const git_signature *sig); #endif diff --git a/vendor/libgit2/src/tag.c b/vendor/libgit2/src/tag.c index 1379425a1..7baababbf 100644 --- a/vendor/libgit2/src/tag.c +++ b/vendor/libgit2/src/tag.c @@ -27,7 +27,6 @@ #include "commit.h" #include "tag.h" #include "signature.h" -#include "revwalk.h" #include "git2/object.h" #include "git2/repository.h" #include "git2/signature.h" @@ -57,21 +56,6 @@ const git_oid *git_tag_target_oid(git_tag *t) return &t->target; } -int git_tag_set_target(git_tag *tag, git_object *target) -{ - const git_oid *oid; - - assert(tag && target); - - if ((oid = git_object_id(target)) == NULL) - return GIT_EMISSINGOBJDATA; - - tag->object.modified = 1; - git_oid_cpy(&tag->target, oid); - tag->type = git_object_type(target); - return GIT_SUCCESS; -} - git_otype git_tag_type(git_tag *t) { assert(t); @@ -84,50 +68,17 @@ const char *git_tag_name(git_tag *t) return t->tag_name; } -void git_tag_set_name(git_tag *tag, const char *name) -{ - assert(tag && name); - - tag->object.modified = 1; - - if (tag->tag_name) - free(tag->tag_name); - - tag->tag_name = git__strdup(name); -} - const git_signature *git_tag_tagger(git_tag *t) { return t->tagger; } -void git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig) -{ - assert(tag && tagger_sig); - tag->object.modified = 1; - - git_signature_free(tag->tagger); - tag->tagger = git_signature_dup(tagger_sig); -} - const char *git_tag_message(git_tag *t) { assert(t); return t->message; } -void git_tag_set_message(git_tag *tag, const char *message) -{ - assert(tag && message); - - tag->object.modified = 1; - - if (tag->message) - free(tag->message); - - tag->message = git__strdup(message); -} - static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) { static const char *tag_types[] = { @@ -188,9 +139,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) buffer = search + 1; - if (tag->tagger != NULL) - git_signature_free(tag->tagger); - tag->tagger = git__malloc(sizeof(git_signature)); if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) @@ -198,9 +146,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) text_len = buffer_end - ++buffer; - if (tag->message != NULL) - free(tag->message); - tag->message = git__malloc(text_len + 1); memcpy(tag->message, buffer, text_len); tag->message[text_len] = '\0'; @@ -208,26 +153,90 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) return GIT_SUCCESS; } -int git_tag__writeback(git_tag *tag, git_odb_source *src) +int git_tag_create_o( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message) { - if (tag->tag_name == NULL || tag->tagger == NULL) - return GIT_EMISSINGOBJDATA; + return git_tag_create( + oid, repo, tag_name, + git_object_id(target), + git_object_type(target), + tagger, message); +} - git__write_oid(src, "object", &tag->target); - git__source_printf(src, "type %s\n", git_object_type2string(tag->type)); - git__source_printf(src, "tag %s\n", tag->tag_name); - git_signature__write(src, "tagger", tag->tagger); +int git_tag_create( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_oid *target, + git_otype target_type, + const git_signature *tagger, + const char *message) +{ + size_t final_size = 0; + git_odb_stream *stream; - if (tag->message != NULL) - git__source_printf(src, "\n%s", tag->message); + const char *type_str; + char *tagger_str; - return GIT_SUCCESS; + int type_str_len, tag_name_len, tagger_str_len, message_len; + int error; + + + type_str = git_object_type2string(target_type); + + tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger); + + type_str_len = strlen(type_str); + tag_name_len = strlen(tag_name); + message_len = strlen(message); + + final_size += GIT_OID_LINE_LENGTH("object"); + final_size += STRLEN("type ") + type_str_len + 1; + final_size += STRLEN("tag ") + tag_name_len + 1; + final_size += tagger_str_len; + final_size += 1 + message_len; + + if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_TAG)) < GIT_SUCCESS) + return error; + + git__write_oid(stream, "object", target); + + stream->write(stream, "type ", STRLEN("type ")); + stream->write(stream, type_str, type_str_len); + + stream->write(stream, "\ntag ", STRLEN("\ntag ")); + stream->write(stream, tag_name, tag_name_len); + stream->write(stream, "\n", 1); + + stream->write(stream, tagger_str, tagger_str_len); + free(tagger_str); + + stream->write(stream, "\n", 1); + stream->write(stream, message, message_len); + + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + if (error == GIT_SUCCESS) { + char ref_name[512]; + git_reference *new_ref; + git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name); + error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + } + + return error; } -int git_tag__parse(git_tag *tag) +int git_tag__parse(git_tag *tag, git_odb_object *obj) { - assert(tag && tag->object.source.open); - return parse_tag_buffer(tag, tag->object.source.raw.data, (char *)tag->object.source.raw.data + tag->object.source.raw.len); + assert(tag); + return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len); } diff --git a/vendor/libgit2/src/tag.h b/vendor/libgit2/src/tag.h index a1782d064..eddf8fa3a 100644 --- a/vendor/libgit2/src/tag.h +++ b/vendor/libgit2/src/tag.h @@ -3,6 +3,7 @@ #include "git2/tag.h" #include "repository.h" +#include "odb.h" struct git_tag { git_object object; @@ -16,7 +17,6 @@ struct git_tag { }; void git_tag__free(git_tag *tag); -int git_tag__parse(git_tag *tag); -int git_tag__writeback(git_tag *tag, git_odb_source *src); +int git_tag__parse(git_tag *tag, git_odb_object *obj); #endif diff --git a/vendor/libgit2/src/thread-utils.h b/vendor/libgit2/src/thread-utils.h index 0029e4bc1..e542639c8 100644 --- a/vendor/libgit2/src/thread-utils.h +++ b/vendor/libgit2/src/thread-utils.h @@ -1,107 +1,96 @@ #ifndef INCLUDE_thread_utils_h__ #define INCLUDE_thread_utils_h__ -#if defined(GIT_HAS_PTHREAD) - typedef pthread_t git_thread; -# define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg) -# define git_thread_kill(thread) pthread_cancel(thread) -# define git_thread_exit(status) pthread_exit(status) -# define git_thread_join(id, status) pthread_join(id, status) - - /* Pthreads Mutex */ - typedef pthread_mutex_t git_lck; -# define GITLCK_INIT PTHREAD_MUTEX_INITIALIZER -# define gitlck_init(a) pthread_mutex_init(a, NULL) -# define gitlck_lock(a) pthread_mutex_lock(a) -# define gitlck_unlock(a) pthread_mutex_unlock(a) -# define gitlck_free(a) pthread_mutex_destroy(a) - - /* Pthreads condition vars */ - typedef pthread_cond_t git_cnd; -# define GITCND_INIT PTHREAD_COND_INITIALIZER -# define gitcnd_init(c, a) pthread_cond_init(c, a) -# define gitcnd_free(c) pthread_cond_destroy(c) -# define gitcnd_wait(c, l) pthread_cond_wait(c, l) -# define gitcnd_signal(c) pthread_cond_signal(c) -# define gitcnd_broadcast(c) pthread_cond_broadcast(c) - -# if defined(GIT_HAS_ASM_ATOMIC) -# include - typedef atomic_t git_refcnt; -# define gitrc_init(a, v) atomic_set(a, v) -# define gitrc_inc(a) atomic_inc_return(a) -# define gitrc_dec(a) atomic_dec_and_test(a) -# define gitrc_free(a) (void)0 -# elif defined(GIT_WIN32) - typedef long git_refcnt; -# define gitrc_init(a, v) (*a = v) -# define gitrc_inc(a) (InterlockedIncrement(a)) -# define gitrc_dec(a) (!InterlockedDecrement(a)) -# define gitrc_free(a) (void)0 -# else - typedef struct { git_lck lock; int counter; } git_refcnt; - - /** Initialize to 0. No memory barrier is issued. */ - GIT_INLINE(void) gitrc_init(git_refcnt *p, int value) - { - gitlck_init(&p->lock); - p->counter = value; - } - - /** - * Increment. - * - * Atomically increments @p by 1. A memory barrier is also - * issued before and after the operation. - * - * @param p pointer of type git_refcnt - */ - GIT_INLINE(void) gitrc_inc(git_refcnt *p) - { - gitlck_lock(&p->lock); - p->counter++; - gitlck_unlock(&p->lock); - } - - /** - * Decrement and test. - * - * Atomically decrements @p by 1 and returns true if the - * result is 0, or false for all other cases. A memory - * barrier is also issued before and after the operation. - * - * @param p pointer of type git_refcnt - */ - GIT_INLINE(int) gitrc_dec(git_refcnt *p) - { - int c; - gitlck_lock(&p->lock); - c = --p->counter; - gitlck_unlock(&p->lock); - return !c; - } - - /** Free any resources associated with the counter. */ -# define gitrc_free(p) gitlck_free(&(p)->lock) -# endif - -#elif defined(GIT_THREADS) -# error GIT_THREADS but no git_lck implementation + +/* Common operations even if threading has been disabled */ +typedef struct { + volatile int val; +} git_atomic; + +GIT_INLINE(void) git_atomic_set(git_atomic *a, int val) +{ + a->val = val; +} + +#ifdef GIT_THREADS + +#define git_thread pthread_t +#define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg) +#define git_thread_kill(thread) pthread_cancel(thread) +#define git_thread_exit(status) pthread_exit(status) +#define git_thread_join(id, status) pthread_join(id, status) + +/* Pthreads Mutex */ +#define git_mutex pthread_mutex_t +#define git_mutex_init(a) pthread_mutex_init(a, NULL) +#define git_mutex_lock(a) pthread_mutex_lock(a) +#define git_mutex_unlock(a) pthread_mutex_unlock(a) +#define git_mutex_free(a) pthread_mutex_destroy(a) + +/* Pthreads condition vars -- disabled by now */ +#define git_cond unsigned int //pthread_cond_t +#define git_cond_init(c, a) (void)0 //pthread_cond_init(c, a) +#define git_cond_free(c) (void)0 //pthread_cond_destroy(c) +#define git_cond_wait(c, l) (void)0 //pthread_cond_wait(c, l) +#define git_cond_signal(c) (void)0 //pthread_cond_signal(c) +#define git_cond_broadcast(c) (void)0 //pthread_cond_broadcast(c) + +GIT_INLINE(int) git_atomic_inc(git_atomic *a) +{ +#ifdef __GNUC__ + return __sync_add_and_fetch(&a->val, 1); +#elif defined(_MSC_VER) + return InterlockedIncrement(&a->val); +#else +# error "Unsupported architecture for atomic operations" +#endif +} + +GIT_INLINE(int) git_atomic_dec(git_atomic *a) +{ +#ifdef __GNUC__ + return __sync_sub_and_fetch(&a->val, 1); +#elif defined(_MSC_VER) + return InterlockedDecrement(&a->val); #else - /* no threads support */ - typedef struct { int dummy; } git_lck; -# define GIT_MUTEX_INIT {} -# define gitlck_init(a) (void)0 -# define gitlck_lock(a) (void)0 -# define gitlck_unlock(a) (void)0 -# define gitlck_free(a) (void)0 - - typedef struct { int counter; } git_refcnt; -# define gitrc_init(a,v) ((a)->counter = v) -# define gitrc_inc(a) ((a)->counter++) -# define gitrc_dec(a) (--(a)->counter == 0) -# define gitrc_free(a) (void)0 +# error "Unsupported architecture for atomic operations" +#endif +} + +#else + +#define git_thread unsigned int +#define git_thread_create(thread, attr, start_routine, arg) (void)0 +#define git_thread_kill(thread) (void)0 +#define git_thread_exit(status) (void)0 +#define git_thread_join(id, status) (void)0 + +/* Pthreads Mutex */ +#define git_mutex unsigned int +#define git_mutex_init(a) (void)0 +#define git_mutex_lock(a) (void)0 +#define git_mutex_unlock(a) (void)0 +#define git_mutex_free(a) (void)0 + +/* Pthreads condition vars */ +#define git_cond unsigned int +#define git_cond_init(c, a) (void)0 +#define git_cond_free(c) (void)0 +#define git_cond_wait(c, l) (void)0 +#define git_cond_signal(c) (void)0 +#define git_cond_broadcast(c) (void)0 + +GIT_INLINE(int) git_atomic_inc(git_atomic *a) +{ + return ++a->val; +} + +GIT_INLINE(int) git_atomic_dec(git_atomic *a) +{ + return --a->val; +} + #endif extern int git_online_cpus(void); diff --git a/vendor/libgit2/src/tree.c b/vendor/libgit2/src/tree.c index 702cccbce..31b286e69 100644 --- a/vendor/libgit2/src/tree.c +++ b/vendor/libgit2/src/tree.c @@ -25,7 +25,6 @@ #include "common.h" #include "commit.h" -#include "revwalk.h" #include "tree.h" #include "git2/repository.h" #include "git2/object.h" @@ -42,9 +41,11 @@ int entry_search_cmp(const void *key, const void *array_member) return strcmp(filename, entry->filename); } +#if 0 static int valid_attributes(const int attributes) { return attributes >= 0 && attributes <= MAX_FILEMODE; } +#endif int entry_sort_cmp(const void *a, const void *b) { @@ -57,13 +58,10 @@ int entry_sort_cmp(const void *a, const void *b) entry_b->attr & 040000); } -void git_tree_clear_entries(git_tree *tree) +void git_tree__free(git_tree *tree) { unsigned int i; - if (tree == NULL) - return; - for (i = 0; i < tree->entries.length; ++i) { git_tree_entry *e; e = git_vector_get(&tree->entries, i); @@ -72,32 +70,6 @@ void git_tree_clear_entries(git_tree *tree) free(e); } - git_vector_clear(&tree->entries); - tree->object.modified = 1; -} - - -git_tree *git_tree__new(void) -{ - git_tree *tree; - - tree = git__malloc(sizeof(struct git_tree)); - if (tree == NULL) - return NULL; - - memset(tree, 0x0, sizeof(struct git_tree)); - - if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) { - free(tree); - return NULL; - } - - return tree; -} - -void git_tree__free(git_tree *tree) -{ - git_tree_clear_entries(tree); git_vector_free(&tree->entries); free(tree); } @@ -107,37 +79,6 @@ const git_oid *git_tree_id(git_tree *c) return git_object_id((git_object *)c); } -int git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr) -{ - assert(entry && entry->owner); - - if (!valid_attributes(attr)) { - return GIT_ERROR; - } - - entry->attr = attr; - entry->owner->object.modified = 1; - return GIT_SUCCESS; -} - -void git_tree_entry_set_name(git_tree_entry *entry, const char *name) -{ - assert(entry && entry->owner); - - free(entry->filename); - entry->filename = git__strdup(name); - git_vector_sort(&entry->owner->entries); - entry->owner->object.modified = 1; -} - -void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid) -{ - assert(entry && entry->owner); - - git_oid_cpy(&entry->oid, oid); - entry->owner->object.modified = 1; -} - unsigned int git_tree_entry_attributes(git_tree_entry *entry) { return entry->attr; @@ -155,15 +96,10 @@ const git_oid *git_tree_entry_id(git_tree_entry *entry) return &entry->oid; } -int git_tree_entry_2object(git_object **object_out, git_tree_entry *entry) +int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry) { assert(entry && object_out); - return git_object_lookup(object_out, entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY); -} - -static void sort_entries(git_tree *tree) -{ - git_vector_sort(&tree->entries); + return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY); } git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) @@ -172,8 +108,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) assert(tree && filename); - sort_entries(tree); - idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); if (idx == GIT_ENOTFOUND) return NULL; @@ -184,9 +118,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) { assert(tree); - - sort_entries(tree); - return git_vector_get(&tree->entries, (unsigned int)idx); } @@ -196,107 +127,12 @@ size_t git_tree_entrycount(git_tree *tree) return tree->entries.length; } -int git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid *id, const char *filename, int attributes) -{ - git_tree_entry *entry; - - assert(tree && id && filename); - if (!valid_attributes(attributes)) { - return GIT_ERROR; - } - - if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL) - return GIT_ENOMEM; - - memset(entry, 0x0, sizeof(git_tree_entry)); - - entry->filename = git__strdup(filename); - git_oid_cpy(&entry->oid, id); - entry->attr = attributes; - entry->owner = tree; - - if (git_vector_insert(&tree->entries, entry) < 0) - return GIT_ENOMEM; - - if (entry_out != NULL) - *entry_out = entry; - - tree->object.modified = 1; - return GIT_SUCCESS; -} - -int git_tree_remove_entry_byindex(git_tree *tree, int idx) -{ - git_tree_entry *remove_ptr; - - assert(tree); - - sort_entries(tree); - - remove_ptr = git_vector_get(&tree->entries, (unsigned int)idx); - if (remove_ptr == NULL) - return GIT_ENOTFOUND; - - free(remove_ptr->filename); - free(remove_ptr); - - tree->object.modified = 1; - - return git_vector_remove(&tree->entries, (unsigned int)idx); -} - -int git_tree_remove_entry_byname(git_tree *tree, const char *filename) -{ - int idx; - - assert(tree && filename); - - sort_entries(tree); - - idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); - if (idx == GIT_ENOTFOUND) - return GIT_ENOTFOUND; - - return git_tree_remove_entry_byindex(tree, idx); -} - -int git_tree__writeback(git_tree *tree, git_odb_source *src) -{ - size_t i; - char filemode[MAX_FILEMODE_BYTES + 1 + 1]; - - assert(tree && src); - - if (tree->entries.length == 0) - return GIT_EMISSINGOBJDATA; - - sort_entries(tree); - - for (i = 0; i < tree->entries.length; ++i) { - git_tree_entry *entry; - - entry = git_vector_get(&tree->entries, i); - - snprintf(filemode, sizeof(filemode), "%o ", entry->attr); - - git__source_write(src, filemode, strlen(filemode)); - git__source_write(src, entry->filename, strlen(entry->filename) + 1); - git__source_write(src, entry->oid.id, GIT_OID_RAWSZ); - } - - return GIT_SUCCESS; -} - - static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) { - static const size_t avg_entry_size = 40; - unsigned int expected_size; int error = GIT_SUCCESS; - expected_size = (tree->object.source.raw.len / avg_entry_size) + 1; - - git_tree_clear_entries(tree); + if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) + return GIT_ENOMEM; while (buffer < buffer_end) { git_tree_entry *entry; @@ -310,7 +146,6 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - entry->owner = tree; entry->attr = strtol(buffer, &buffer, 8); if (*buffer++ != ' ') { @@ -337,16 +172,9 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end) return error; } -int git_tree__parse(git_tree *tree) +int git_tree__parse(git_tree *tree, git_odb_object *obj) { - char *buffer, *buffer_end; - - assert(tree && tree->object.source.open); - assert(!tree->object.in_memory); - - buffer = tree->object.source.raw.data; - buffer_end = buffer + tree->object.source.raw.len; - - return tree_parse_buffer(tree, buffer, buffer_end); + assert(tree); + return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len); } diff --git a/vendor/libgit2/src/tree.h b/vendor/libgit2/src/tree.h index 78500c471..b4e910a9f 100644 --- a/vendor/libgit2/src/tree.h +++ b/vendor/libgit2/src/tree.h @@ -3,14 +3,13 @@ #include "git2/tree.h" #include "repository.h" +#include "odb.h" #include "vector.h" struct git_tree_entry { unsigned int attr; char *filename; git_oid oid; - - git_tree *owner; }; struct git_tree { @@ -19,8 +18,6 @@ struct git_tree { }; void git_tree__free(git_tree *tree); -git_tree *git_tree__new(void); -int git_tree__parse(git_tree *tree); -int git_tree__writeback(git_tree *tree, git_odb_source *src); +int git_tree__parse(git_tree *tree, git_odb_object *obj); #endif diff --git a/vendor/libgit2/src/util.c b/vendor/libgit2/src/util.c index c9a8e5fe9..bfc4f7b27 100644 --- a/vendor/libgit2/src/util.c +++ b/vendor/libgit2/src/util.c @@ -3,6 +3,15 @@ #include #include +void git_strarray_free(git_strarray *array) +{ + size_t i; + for (i = 0; i < array->count; ++i) + free(array->strings[i]); + + free(array->strings); +} + int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...) { va_list va; diff --git a/vendor/libgit2/src/util.h b/vendor/libgit2/src/util.h index e0dfd7b20..653b34d02 100644 --- a/vendor/libgit2/src/util.h +++ b/vendor/libgit2/src/util.h @@ -97,6 +97,8 @@ extern char *git__strtok_keep(char *output, char *src, char *delimit); #define STRLEN(str) (sizeof(str) - 1) +#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1) + /* * Realloc the buffer pointed at by variable 'x' so that it can hold * at least 'nr' entries; the number of entries currently allocated diff --git a/vendor/libgit2/src/win32/pthread.c b/vendor/libgit2/src/win32/pthread.c new file mode 100644 index 000000000..f47364a76 --- /dev/null +++ b/vendor/libgit2/src/win32/pthread.c @@ -0,0 +1,86 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Original code by Ramiro Polla (Public Domain) + */ + +#include "pthread.h" + +int pthread_create(pthread_t *GIT_RESTRICT thread, + const pthread_attr_t *GIT_RESTRICT GIT_UNUSED(attr), + void *(*start_routine)(void*), void *GIT_RESTRICT arg) +{ + GIT_UNUSED_ARG(attr); + *thread = (pthread_t) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL); + return *thread ? GIT_SUCCESS : GIT_EOSERR; +} + +int pthread_join(pthread_t thread, void **value_ptr) +{ + int ret; + ret = WaitForSingleObject(thread, INFINITE); + if (ret && value_ptr) + GetExitCodeThread(thread, (void*) value_ptr); + return -(!!ret); +} + +int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex, + const pthread_mutexattr_t *GIT_RESTRICT GIT_UNUSED(mutexattr)) +{ + GIT_UNUSED_ARG(mutexattr); + InitializeCriticalSection(mutex); + return 0; +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + int ret; + ret = CloseHandle(mutex); + return -(!ret); +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + EnterCriticalSection(mutex); + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); + return 0; +} + +int pthread_num_processors_np(void) +{ + DWORD_PTR p, s; + int n = 0; + + if (GetProcessAffinityMask(GetCurrentProcess(), &p, &s)) + for (; p; p >>= 1) + n += p&1; + + return n ? n : 1; +} + diff --git a/vendor/libgit2/src/win32/pthread.h b/vendor/libgit2/src/win32/pthread.h new file mode 100644 index 000000000..10949f1eb --- /dev/null +++ b/vendor/libgit2/src/win32/pthread.h @@ -0,0 +1,60 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Original code by Ramiro Polla (Public Domain) + */ + +#ifndef GIT_PTHREAD_H +#define GIT_PTHREAD_H + +#include "../common.h" + +#if defined (_MSC_VER) +# define GIT_RESTRICT __restrict +#else +# define GIT_RESTRICT __restrict__ +#endif + +typedef int pthread_mutexattr_t; +typedef int pthread_condattr_t; +typedef int pthread_attr_t; +typedef CRITICAL_SECTION pthread_mutex_t; +typedef HANDLE pthread_t; + +#define PTHREAD_MUTEX_INITIALIZER {(void*)-1}; + +int pthread_create(pthread_t *GIT_RESTRICT, + const pthread_attr_t *GIT_RESTRICT, + void *(*start_routine)(void*), void *__restrict); + +int pthread_join(pthread_t, void **); + +int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT); +int pthread_mutex_destroy(pthread_mutex_t *); +int pthread_mutex_lock(pthread_mutex_t *); +int pthread_mutex_unlock(pthread_mutex_t *); + +int pthread_num_processors_np(void); + +#endif diff --git a/vendor/libgit2/tests/t00-core.c b/vendor/libgit2/tests/t00-core.c index 1f6c06a95..08e42ecf4 100644 --- a/vendor/libgit2/tests/t00-core.c +++ b/vendor/libgit2/tests/t00-core.c @@ -27,17 +27,6 @@ #include "vector.h" #include "fileops.h" -BEGIN_TEST(refcnt0, "increment refcount twice, decrement twice") - git_refcnt p; - - gitrc_init(&p, 0); - gitrc_inc(&p); - gitrc_inc(&p); - must_be_true(!gitrc_dec(&p)); - must_be_true(gitrc_dec(&p)); - gitrc_free(&p); -END_TEST - BEGIN_TEST(string0, "compare prefixes") must_be_true(git__prefixcmp("", "") == 0); must_be_true(git__prefixcmp("a", "") == 0); @@ -626,8 +615,6 @@ END_TEST BEGIN_SUITE(core) - ADD_TEST(refcnt0); - ADD_TEST(string0); ADD_TEST(string1); diff --git a/vendor/libgit2/tests/t01-rawobj.c b/vendor/libgit2/tests/t01-rawobj.c index 3dfa3c9fe..5db9a79fc 100644 --- a/vendor/libgit2/tests/t01-rawobj.c +++ b/vendor/libgit2/tests/t01-rawobj.c @@ -23,10 +23,17 @@ * Boston, MA 02110-1301, USA. */ #include "test_lib.h" -#include "t01-data.h" +#include "odb.h" #include "hash.h" +#include "t01-data.h" + +static int hash_object(git_oid *oid, git_rawobj *obj) +{ + return git_odb_hash(oid, obj->data, obj->len, obj->type); +} + BEGIN_TEST(oid0, "validate size of oid objects") git_oid out; must_be_true(20 == GIT_OID_RAWSZ); @@ -497,28 +504,28 @@ BEGIN_TEST(objhash0, "hash junk data") /* invalid types: */ junk_obj.data = some_data; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); junk_obj.type = GIT_OBJ__EXT1; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); junk_obj.type = GIT_OBJ__EXT2; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); junk_obj.type = GIT_OBJ_OFS_DELTA; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); junk_obj.type = GIT_OBJ_REF_DELTA; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); /* data can be NULL only if len is zero: */ junk_obj.type = GIT_OBJ_BLOB; junk_obj.data = NULL; - must_pass(git_rawobj_hash(&id, &junk_obj)); + must_pass(hash_object(&id, &junk_obj)); must_be_true(git_oid_cmp(&id, &id_zero) == 0); junk_obj.len = 1; - must_fail(git_rawobj_hash(&id, &junk_obj)); + must_fail(hash_object(&id, &junk_obj)); END_TEST BEGIN_TEST(objhash1, "hash a commit object") @@ -526,7 +533,7 @@ BEGIN_TEST(objhash1, "hash a commit object") must_pass(git_oid_mkstr(&id1, commit_id)); - must_pass(git_rawobj_hash(&id2, &commit_obj)); + must_pass(hash_object(&id2, &commit_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -536,7 +543,7 @@ BEGIN_TEST(objhash2, "hash a tree object") must_pass(git_oid_mkstr(&id1, tree_id)); - must_pass(git_rawobj_hash(&id2, &tree_obj)); + must_pass(hash_object(&id2, &tree_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -546,7 +553,7 @@ BEGIN_TEST(objhash3, "hash a tag object") must_pass(git_oid_mkstr(&id1, tag_id)); - must_pass(git_rawobj_hash(&id2, &tag_obj)); + must_pass(hash_object(&id2, &tag_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -556,7 +563,7 @@ BEGIN_TEST(objhash4, "hash a zero-length object") must_pass(git_oid_mkstr(&id1, zero_id)); - must_pass(git_rawobj_hash(&id2, &zero_obj)); + must_pass(hash_object(&id2, &zero_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -566,7 +573,7 @@ BEGIN_TEST(objhash5, "hash an one-byte long object") must_pass(git_oid_mkstr(&id1, one_id)); - must_pass(git_rawobj_hash(&id2, &one_obj)); + must_pass(hash_object(&id2, &one_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -576,7 +583,7 @@ BEGIN_TEST(objhash6, "hash a two-byte long object") must_pass(git_oid_mkstr(&id1, two_id)); - must_pass(git_rawobj_hash(&id2, &two_obj)); + must_pass(hash_object(&id2, &two_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST @@ -586,7 +593,7 @@ BEGIN_TEST(objhash7, "hash an object several bytes long") must_pass(git_oid_mkstr(&id1, some_id)); - must_pass(git_rawobj_hash(&id2, &some_obj)); + must_pass(hash_object(&id2, &some_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); END_TEST diff --git a/vendor/libgit2/tests/t02-objread.c b/vendor/libgit2/tests/t02-objread.c index 2a9d130c4..85b03b026 100644 --- a/vendor/libgit2/tests/t02-objread.c +++ b/vendor/libgit2/tests/t02-objread.c @@ -24,6 +24,7 @@ */ #include "test_lib.h" #include "test_helpers.h" +#include "odb.h" #include "t02-data.h" #include "t02-oids.h" @@ -50,16 +51,16 @@ END_TEST BEGIN_TEST(readloose0, "read a loose commit") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &commit)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, commit.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &commit)); + must_pass(cmp_objects((git_rawobj *)&obj->raw, &commit)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &commit)); END_TEST @@ -67,16 +68,16 @@ END_TEST BEGIN_TEST(readloose1, "read a loose tree") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &tree)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, tree.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &tree)); + must_pass(cmp_objects((git_rawobj *)&obj->raw, &tree)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &tree)); END_TEST @@ -84,16 +85,16 @@ END_TEST BEGIN_TEST(readloose2, "read a loose tag") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &tag)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, tag.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &tag)); + must_pass(cmp_objects((git_rawobj *)&obj->raw, &tag)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &tag)); END_TEST @@ -101,16 +102,16 @@ END_TEST BEGIN_TEST(readloose3, "read a loose zero-bytes object") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &zero)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, zero.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &zero)); + must_pass(cmp_objects((git_rawobj *)&obj->raw, &zero)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &zero)); END_TEST @@ -118,16 +119,16 @@ END_TEST BEGIN_TEST(readloose4, "read a one-byte long loose object") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &one)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, one.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &one)); + must_pass(cmp_objects(&obj->raw, &one)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &one)); END_TEST @@ -135,16 +136,16 @@ END_TEST BEGIN_TEST(readloose5, "read a two-bytes long loose object") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &two)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, two.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &two)); + must_pass(cmp_objects(&obj->raw, &two)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &two)); END_TEST @@ -152,16 +153,16 @@ END_TEST BEGIN_TEST(readloose6, "read a loose object which is several bytes long") git_odb *db; git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(write_object_files(odb_dir, &some)); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id, some.id)); must_pass(git_odb_read(&obj, db, &id)); - must_pass(cmp_objects(&obj, &some)); + must_pass(cmp_objects(&obj->raw, &some)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(odb_dir, &some)); END_TEST @@ -174,13 +175,13 @@ BEGIN_TEST(readpack0, "read several packed objects") for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) { git_oid id; - git_rawobj obj; + git_odb_object *obj; must_pass(git_oid_mkstr(&id, packed_objects[i])); must_be_true(git_odb_exists(db, &id) == 1); must_pass(git_odb_read(&obj, db, &id)); - git_rawobj_close(&obj); + git_odb_object_close(obj); } git_odb_close(db); @@ -194,17 +195,19 @@ BEGIN_TEST(readheader0, "read only the header of several packed objects") for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) { git_oid id; - git_rawobj obj, header; + git_odb_object *obj; + size_t len; + git_otype type; must_pass(git_oid_mkstr(&id, packed_objects[i])); must_pass(git_odb_read(&obj, db, &id)); - must_pass(git_odb_read_header(&header, db, &id)); + must_pass(git_odb_read_header(&len, &type, db, &id)); - must_be_true(obj.len == header.len); - must_be_true(obj.type == header.type); + must_be_true(obj->raw.len == len); + must_be_true(obj->raw.type == type); - git_rawobj_close(&obj); + git_odb_object_close(obj); } git_odb_close(db); @@ -218,19 +221,21 @@ BEGIN_TEST(readheader1, "read only the header of several loose objects") for (i = 0; i < ARRAY_SIZE(loose_objects); ++i) { git_oid id; - git_rawobj obj, header; + git_odb_object *obj; + size_t len; + git_otype type; must_pass(git_oid_mkstr(&id, loose_objects[i])); must_be_true(git_odb_exists(db, &id) == 1); must_pass(git_odb_read(&obj, db, &id)); - must_pass(git_odb_read_header(&header, db, &id)); + must_pass(git_odb_read_header(&len, &type, db, &id)); - must_be_true(obj.len == header.len); - must_be_true(obj.type == header.type); + must_be_true(obj->raw.len == len); + must_be_true(obj->raw.type == type); - git_rawobj_close(&obj); + git_odb_object_close(obj); } git_odb_close(db); diff --git a/vendor/libgit2/tests/t03-objwrite.c b/vendor/libgit2/tests/t03-objwrite.c index 10c6c7f1a..773887397 100644 --- a/vendor/libgit2/tests/t03-objwrite.c +++ b/vendor/libgit2/tests/t03-objwrite.c @@ -24,6 +24,7 @@ */ #include "test_lib.h" #include "fileops.h" +#include "odb.h" static char *odb_dir = "test-objects"; #include "t03-data.h" @@ -80,23 +81,39 @@ static int remove_object_files(object_data *d) return 0; } +static int streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw) +{ + git_odb_stream *stream; + int error; + + if ((error = git_odb_open_wstream(&stream, odb, raw->len, raw->type)) < GIT_SUCCESS) + return error; + + stream->write(stream, raw->data, raw->len); + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + return error; +} + BEGIN_TEST(write0, "write loose commit object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, commit.id)); - must_pass(git_odb_write(&id2, db, &commit_obj)); + must_pass(streaming_write(&id2, db, &commit_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&commit)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &commit_obj)); + must_pass(cmp_objects(&obj->raw, &commit_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&commit)); END_TEST @@ -104,20 +121,20 @@ END_TEST BEGIN_TEST(write1, "write loose tree object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, tree.id)); - must_pass(git_odb_write(&id2, db, &tree_obj)); + must_pass(streaming_write(&id2, db, &tree_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&tree)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &tree_obj)); + must_pass(cmp_objects(&obj->raw, &tree_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&tree)); END_TEST @@ -125,20 +142,20 @@ END_TEST BEGIN_TEST(write2, "write loose tag object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, tag.id)); - must_pass(git_odb_write(&id2, db, &tag_obj)); + must_pass(streaming_write(&id2, db, &tag_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&tag)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &tag_obj)); + must_pass(cmp_objects(&obj->raw, &tag_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&tag)); END_TEST @@ -146,20 +163,20 @@ END_TEST BEGIN_TEST(write3, "write zero-length object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, zero.id)); - must_pass(git_odb_write(&id2, db, &zero_obj)); + must_pass(streaming_write(&id2, db, &zero_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&zero)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &zero_obj)); + must_pass(cmp_objects(&obj->raw, &zero_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&zero)); END_TEST @@ -167,20 +184,20 @@ END_TEST BEGIN_TEST(write4, "write one-byte long object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, one.id)); - must_pass(git_odb_write(&id2, db, &one_obj)); + must_pass(streaming_write(&id2, db, &one_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&one)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &one_obj)); + must_pass(cmp_objects(&obj->raw, &one_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&one)); END_TEST @@ -188,20 +205,20 @@ END_TEST BEGIN_TEST(write5, "write two-byte long object") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, two.id)); - must_pass(git_odb_write(&id2, db, &two_obj)); + must_pass(streaming_write(&id2, db, &two_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&two)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &two_obj)); + must_pass(cmp_objects(&obj->raw, &two_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&two)); END_TEST @@ -209,20 +226,20 @@ END_TEST BEGIN_TEST(write6, "write an object which is several bytes long") git_odb *db; git_oid id1, id2; - git_rawobj obj; + git_odb_object *obj; must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); must_pass(git_oid_mkstr(&id1, some.id)); - must_pass(git_odb_write(&id2, db, &some_obj)); + must_pass(streaming_write(&id2, db, &some_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); must_pass(check_object_files(&some)); must_pass(git_odb_read(&obj, db, &id1)); - must_pass(cmp_objects(&obj, &some_obj)); + must_pass(cmp_objects(&obj->raw, &some_obj)); - git_rawobj_close(&obj); + git_odb_object_close(obj); git_odb_close(db); must_pass(remove_object_files(&some)); END_TEST diff --git a/vendor/libgit2/tests/t04-commit.c b/vendor/libgit2/tests/t04-commit.c index 855cf9859..1140d3319 100644 --- a/vendor/libgit2/tests/t04-commit.c +++ b/vendor/libgit2/tests/t04-commit.c @@ -407,39 +407,42 @@ This is a commit created in memory and it will be written back to disk\n" static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; + BEGIN_TEST(write0, "write a new commit object from memory to disk") git_repository *repo; - git_commit *commit, *parent; - git_tree *tree; - git_oid id; + git_commit *commit; + git_oid tree_id, parent_id, commit_id; const git_signature *author, *committer; /* char hex_oid[41]; */ must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - /* Create commit in memory */ - must_pass(git_commit_new(&commit, repo)); - - /* Add new parent */ - git_oid_mkstr(&id, commit_ids[4]); - must_pass(git_commit_lookup(&parent, repo, &id)); - git_commit_add_parent(commit, parent); + git_oid_mkstr(&tree_id, tree_oid); + git_oid_mkstr(&parent_id, commit_ids[4]); - /* Set other attributes */ + /* create signatures */ committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); must_be_true(committer != NULL); author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90); must_be_true(author != NULL); - git_commit_set_committer(commit, committer); - git_commit_set_author(commit, author); - git_commit_set_message(commit, COMMIT_MESSAGE); + must_pass(git_commit_create_v( + &commit_id, /* out id */ + repo, + NULL, /* do not update the HEAD */ + author, + committer, + COMMIT_MESSAGE, + &tree_id, + 1, &parent_id)); git_signature_free((git_signature *)committer); git_signature_free((git_signature *)author); + must_pass(git_commit_lookup(&commit, repo, &commit_id)); + /* Check attributes were set correctly */ author = git_commit_author(commit); must_be_true(author != NULL); @@ -457,47 +460,6 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk") must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0); - /* add new tree */ - git_oid_mkstr(&id, tree_oid); - must_pass(git_tree_lookup(&tree, repo, &id)); - - git_commit_set_tree(commit, tree); - - /* Test it has no OID */ - must_be_true(git_commit_id(commit) == NULL); - - /* Write to disk */ - must_pass(git_object_write((git_object *)commit)); - - must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit)); - - git_repository_free(repo); -END_TEST - -BEGIN_TEST(write1, "load a commit object, modify it and write it back") - git_repository *repo; - git_oid id; - git_commit *commit, *parent; - const char *message; - /* char hex_oid[41]; */ - - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - - git_oid_mkstr(&id, commit_ids[0]); - - must_pass(git_commit_lookup(&commit, repo, &id)); - - message = git_commit_message(commit); - - git_commit_set_message(commit, "This is a new test message. Cool!\n"); - - git_oid_mkstr(&id, commit_ids[4]); - must_pass(git_commit_lookup(&parent, repo, &id)); - - git_commit_add_parent(commit, parent); - - must_pass(git_object_write((git_object *)commit)); - must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit)); git_repository_free(repo); @@ -509,6 +471,7 @@ BEGIN_SUITE(commit) ADD_TEST(parse1); ADD_TEST(parse2); ADD_TEST(details0); + ADD_TEST(write0); - ADD_TEST(write1); + //ADD_TEST(write1); END_SUITE diff --git a/vendor/libgit2/tests/t05-revwalk.c b/vendor/libgit2/tests/t05-revwalk.c index bdec09e83..cfcf01066 100644 --- a/vendor/libgit2/tests/t05-revwalk.c +++ b/vendor/libgit2/tests/t05-revwalk.c @@ -25,8 +25,6 @@ #include "test_lib.h" #include "test_helpers.h" -#include "revwalk.h" - /* $ git log --oneline --graph --decorate * a4a7dce (HEAD, br2) Merge branch 'master' into br2 @@ -84,7 +82,7 @@ static int get_commit_index(git_oid *raw_oid) return -1; } -static int test_walk(git_revwalk *walk, +static int test_walk(git_revwalk *walk, const git_oid *root, int flags, const int possible_results[][6], int results_count) { git_oid oid; @@ -92,8 +90,8 @@ static int test_walk(git_revwalk *walk, int i; int result_array[commit_count]; - git_revwalk_reset(walk); git_revwalk_sorting(walk, flags); + git_revwalk_push(walk, root); for (i = 0; i < commit_count; ++i) result_array[i] = -1; @@ -124,19 +122,14 @@ BEGIN_TEST(walk0, "do a simple walk on a repo with different sorting modes") git_revwalk *walk; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - must_pass(git_revwalk_new(&walk, repo)); git_oid_mkstr(&id, commit_head); - git_revwalk_push(walk, &id); - - must_pass(test_walk(walk, GIT_SORT_TIME, commit_sorting_time, 1)); - - must_pass(test_walk(walk, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2)); - - must_pass(test_walk(walk, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1)); - must_pass(test_walk(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2)); + must_pass(test_walk(walk, &id, GIT_SORT_TIME, commit_sorting_time, 1)); + must_pass(test_walk(walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2)); + must_pass(test_walk(walk, &id, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1)); + must_pass(test_walk(walk, &id, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2)); git_revwalk_free(walk); git_repository_free(repo); diff --git a/vendor/libgit2/tests/t08-tag.c b/vendor/libgit2/tests/t08-tag.c index c6789266c..70eeb28a6 100644 --- a/vendor/libgit2/tests/t08-tag.c +++ b/vendor/libgit2/tests/t08-tag.c @@ -61,27 +61,62 @@ BEGIN_TEST(read0, "read and parse a tag from the repository") git_repository_free(repo); END_TEST -BEGIN_TEST(write0, "write back a tag to the repository") - git_oid id; + +#define TAGGER_NAME "Vicent Marti" +#define TAGGER_EMAIL "vicent@github.com" +#define TAGGER_MESSAGE "This is my tag.\n\nThere are many tags, but this one is mine\n" + +BEGIN_TEST(write0, "write a tag to the repository and read it again") git_repository *repo; git_tag *tag; + git_oid target_id, tag_id; + const git_signature *tagger; + git_reference *ref_tag; + /* char hex_oid[41]; */ must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id, tag1_id); + git_oid_mkstr(&target_id, tagged_commit); + + /* create signatures */ + tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); + must_be_true(tagger != NULL); + + must_pass(git_tag_create( + &tag_id, /* out id */ + repo, + "the-tag", /* do not update the HEAD */ + &target_id, + GIT_OBJ_COMMIT, + tagger, + TAGGER_MESSAGE)); - must_pass(git_tag_lookup(&tag, repo, &id)); + git_signature_free((git_signature *)tagger); - git_tag_set_name(tag, "This is a different tag LOL"); + must_pass(git_tag_lookup(&tag, repo, &tag_id)); + + /* Check attributes were set correctly */ + tagger = git_tag_tagger(tag); + must_be_true(tagger != NULL); + must_be_true(strcmp(tagger->name, TAGGER_NAME) == 0); + must_be_true(strcmp(tagger->email, TAGGER_EMAIL) == 0); + must_be_true(tagger->when.time == 123456789); + must_be_true(tagger->when.offset == 60); + + must_be_true(strcmp(git_tag_message(tag), TAGGER_MESSAGE) == 0); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/the-tag")); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0); + must_pass(git_reference_delete(ref_tag)); - must_pass(git_object_write((git_object *)tag)); must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag)); git_repository_free(repo); + END_TEST BEGIN_SUITE(tag) ADD_TEST(read0); - ADD_TEST(write0); + ADD_TEST(write0); END_SUITE diff --git a/vendor/libgit2/tests/t09-tree.c b/vendor/libgit2/tests/t09-tree.c index 6bc2a84bd..6c1b2e643 100644 --- a/vendor/libgit2/tests/t09-tree.c +++ b/vendor/libgit2/tests/t09-tree.c @@ -66,98 +66,24 @@ BEGIN_TEST(read1, "read a tree from the repository") must_be_true(git_tree_entrycount(tree) == 3); + /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */ + must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_TREE) == 0); + must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_BLOB) == GIT_EINVALIDTYPE); + entry = git_tree_entry_byname(tree, "README"); must_be_true(entry != NULL); must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0); - must_pass(git_tree_entry_2object(&obj, entry)); - - git_repository_free(repo); -END_TEST - -BEGIN_TEST(write0, "add a new entry to a tree and write it back to disk") - const unsigned int entry_count = 128; - - git_repository *repo; - git_tree *tree; - unsigned int i; - git_oid entry_id; - - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - must_pass(git_tree_new(&tree, repo)); - - git_oid_mkstr(&entry_id, tree_oid); - for (i = 0; i < entry_count; ++i) { - char filename[32]; - git_tree_entry *ent = NULL; - - sprintf(filename, "file%d.txt", i); - must_pass(git_tree_add_entry(&ent, tree, &entry_id, filename, 040000)); - must_be_true(ent != NULL); - } - - must_be_true(git_tree_entrycount(tree) == entry_count); - must_pass(git_object_write((git_object *)tree)); - must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tree)); + must_pass(git_tree_entry_2object(&obj, repo, entry)); git_repository_free(repo); END_TEST -BEGIN_TEST(write1, "add several entries in-memory and validate that they exist; write back to disk") - git_oid id; - git_repository *repo; - git_tree *tree; - git_tree_entry *entry; - unsigned int i; - /* char hex_oid[41]; */ - - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - - git_oid_mkstr(&id, tree_oid); - - must_pass(git_tree_lookup(&tree, repo, &id)); - - must_be_true(git_tree_entrycount(tree) == 3); - - /* check there is NP if we don't want the - * created entry back */ - git_tree_add_entry(NULL, tree, &id, "zzz_test_entry.dat", 0); - git_tree_add_entry(NULL, tree, &id, "01_test_entry.txt", 0); - - must_be_true(git_tree_entrycount(tree) == 5); - - entry = git_tree_entry_byindex(tree, 0); - must_be_true(strcmp(git_tree_entry_name(entry), "01_test_entry.txt") == 0); - - entry = git_tree_entry_byindex(tree, 4); - must_be_true(strcmp(git_tree_entry_name(entry), "zzz_test_entry.dat") == 0); - - must_pass(git_tree_remove_entry_byname(tree, "README")); - must_be_true(git_tree_entrycount(tree) == 4); - - for (i = 0; i < git_tree_entrycount(tree); ++i) { - entry = git_tree_entry_byindex(tree, i); - must_be_true(strcmp(git_tree_entry_name(entry), "README") != 0); - } - - must_pass(git_object_write((git_object *)tree)); - -/* - git_oid_fmt(hex_oid, git_tree_id(tree)); - hex_oid[40] = 0; - printf("TREE New SHA1: %s\n", hex_oid); -*/ - - must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tree)); - git_repository_free(repo); -END_TEST - - BEGIN_SUITE(tree) ADD_TEST(read0); ADD_TEST(read1); - ADD_TEST(write0); - ADD_TEST(write1); +// ADD_TEST(write0); /* TODO THREADSAFE */ +// ADD_TEST(write1); END_SUITE diff --git a/vendor/libgit2/tests/t10-refs.c b/vendor/libgit2/tests/t10-refs.c index c70fb69ce..565d636ba 100644 --- a/vendor/libgit2/tests/t10-refs.c +++ b/vendor/libgit2/tests/t10-refs.c @@ -716,7 +716,29 @@ BEGIN_TEST(list0, "try to list all the references in our test repo") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_listall(&ref_list, repo, GIT_REF_LISTALL)); - must_be_true(ref_list.count == 8); /* 8 refs in total if we include the packed ones */ + + /*{ + unsigned short i; + for (i = 0; i < ref_list.count; ++i) + printf("# %s\n", ref_list.strings[i]); + }*/ + + /* We have exactly 7 refs in total if we include the packed ones: + * there is a reference that exists both in the packfile and as + * loose, but we only list it once */ + must_be_true(ref_list.count == 7); + + git_strarray_free(&ref_list); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(list1, "try to list only the symbolic references") + git_repository *repo; + git_strarray ref_list; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_reference_listall(&ref_list, repo, GIT_REF_SYMBOLIC)); + must_be_true(ref_list.count == 0); /* no symrefs in the test repo */ git_strarray_free(&ref_list); git_repository_free(repo); @@ -754,4 +776,5 @@ BEGIN_SUITE(refs) ADD_TEST(delete0); ADD_TEST(list0); + ADD_TEST(list1); END_SUITE diff --git a/vendor/libgit2/tests/t11-sqlite.c b/vendor/libgit2/tests/t11-sqlite.c index 9e9d1b786..fecf7886f 100644 --- a/vendor/libgit2/tests/t11-sqlite.c +++ b/vendor/libgit2/tests/t11-sqlite.c @@ -23,12 +23,15 @@ * Boston, MA 02110-1301, USA. */ #include "test_lib.h" -#include "t03-data.h" + +#ifdef GIT2_SQLITE_BACKEND +#include "t03-data.h" #include "fileops.h" #include "git2/odb_backend.h" -static int cmp_objects(git_rawobj *o1, git_rawobj *o2) + +static int cmp_objects(raw_object *o1, raw_object *o2) { if (o1->type != o2->type) return -1; @@ -41,7 +44,6 @@ static int cmp_objects(git_rawobj *o1, git_rawobj *o2) static git_odb *open_sqlite_odb(void) { -#ifdef GIT2_SQLITE_BACKEND git_odb *odb; git_odb_backend *sqlite; @@ -55,15 +57,12 @@ static git_odb *open_sqlite_odb(void) return NULL; return odb; -#else - return NULL; -#endif } #define TEST_WRITE(PTR) {\ git_odb *db; \ git_oid id1, id2; \ - git_rawobj obj; \ + raw_object obj; \ db = open_sqlite_odb(); \ must_be_true(db != NULL); \ must_pass(git_oid_mkstr(&id1, PTR.id)); \ @@ -105,7 +104,6 @@ END_TEST BEGIN_SUITE(sqlite) -#ifdef GIT2_SQLITE_BACKEND ADD_TEST(sqlite0); ADD_TEST(sqlite1); ADD_TEST(sqlite2); @@ -113,5 +111,13 @@ BEGIN_SUITE(sqlite) ADD_TEST(sqlite4); ADD_TEST(sqlite5); ADD_TEST(sqlite6); -#endif END_SUITE + +#else /* no sqlite builtin */ +BEGIN_SUITE(sqlite) + /* empty */ +END_SUITE +#endif + + + diff --git a/vendor/libgit2/tests/t13-threads.c b/vendor/libgit2/tests/t13-threads.c new file mode 100644 index 000000000..3888b70ce --- /dev/null +++ b/vendor/libgit2/tests/t13-threads.c @@ -0,0 +1,41 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "test_lib.h" +#include "test_helpers.h" +#include "cache.h" + + +typedef struct { + git_cached_obj cached; + unsigned int __dummy; +} ttest_obj; + +BEGIN_TEST(cache0, "run several threads polling the cache at the same time") + +END_TEST + +BEGIN_SUITE(threads) + ADD_TEST(cache0); +END_SUITE diff --git a/vendor/libgit2/tests/test_helpers.h b/vendor/libgit2/tests/test_helpers.h index 97b81ab40..9a24ebccf 100644 --- a/vendor/libgit2/tests/test_helpers.h +++ b/vendor/libgit2/tests/test_helpers.h @@ -29,6 +29,8 @@ #include "test_lib.h" #include +#include "odb.h" + #define TEST_REPOSITORY_NAME "testrepo.git" #define REPOSITORY_FOLDER TEST_RESOURCES "/" TEST_REPOSITORY_NAME "/" #define ODB_FOLDER (REPOSITORY_FOLDER "objects/") diff --git a/vendor/libgit2/tests/test_main.c b/vendor/libgit2/tests/test_main.c index 9308b8d45..f2a623a48 100644 --- a/vendor/libgit2/tests/test_main.c +++ b/vendor/libgit2/tests/test_main.c @@ -42,6 +42,7 @@ DECLARE_SUITE(tree); DECLARE_SUITE(refs); DECLARE_SUITE(sqlite); DECLARE_SUITE(repository); +DECLARE_SUITE(threads); static libgit2_suite suite_methods[]= { SUITE_NAME(core), @@ -57,6 +58,7 @@ static libgit2_suite suite_methods[]= { SUITE_NAME(refs), SUITE_NAME(sqlite), SUITE_NAME(repository), + SUITE_NAME(threads), }; #define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods)) diff --git a/vendor/libgit2/wscript b/vendor/libgit2/wscript index f9daca375..b990e148a 100644 --- a/vendor/libgit2/wscript +++ b/vendor/libgit2/wscript @@ -16,7 +16,7 @@ CFLAGS_WIN32_L = ['/RELEASE'] # used for /both/ debug and release builds. # sets the module's checksum in the header. CFLAGS_WIN32_L_DBG = ['/DEBUG'] -ALL_LIBS = ['z', 'crypto', 'pthread', 'sqlite3'] +ALL_LIBS = ['crypto', 'pthread', 'sqlite3'] def options(opt): opt.load('compiler_c') @@ -29,8 +29,10 @@ PPC optimized version (ppc) or the SHA1 functions from OpenSSL (openssl)") help='Force a specific MSVC++ version (7.1, 8.0, 9.0, 10.0), if more than one is installed') opt.add_option('--arch', action='store', default='x86', help='Select target architecture (ia64, x64, x86, x86_amd64, x86_ia64)') - opt.add_option('--without-sqlite', action='store_false', default=True, + opt.add_option('--with-sqlite', action='store_true', default=False, dest='use_sqlite', help='Disable sqlite support') + opt.add_option('--threadsafe', action='store_true', default=False, + help='Make libgit2 thread-safe (requires pthreads)') def configure(conf): @@ -44,7 +46,6 @@ def configure(conf): conf.load('compiler_c') dbg = conf.options.debug - zlib_name = 'z' conf.env.CFLAGS = CFLAGS_UNIX + (CFLAGS_UNIX_DBG if dbg else []) @@ -56,17 +57,15 @@ def configure(conf): (CFLAGS_WIN32_DBG if dbg else CFLAGS_WIN32_RELEASE) conf.env.LINKFLAGS += CFLAGS_WIN32_L + \ (CFLAGS_WIN32_L_DBG if dbg else []) - conf.env.DEFINES += ['WIN32', '_DEBUG', '_LIB', 'ZLIB_WINAPI'] - zlib_name = 'zlibwapi' - - elif conf.env.CC_NAME == 'gcc': - conf.check_cc(lib='pthread', uselib_store='pthread') + conf.env.DEFINES += ['WIN32', '_DEBUG', '_LIB'] else: conf.env.PLATFORM = 'unix' - # check for Z lib - conf.check_cc(lib=zlib_name, uselib_store='z', install_path=None) + if conf.options.threadsafe: + if conf.env.PLATFORM == 'unix': + conf.check_cc(lib='pthread', uselib_store='pthread') + conf.env.DEFINES += ['GIT_THREADS'] # check for sqlite3 if conf.options.use_sqlite and conf.check_cc( @@ -139,6 +138,7 @@ def build_library(bld, build_type): # src/win32/*.c sources = sources + directory.ant_glob('src/%s/*.c' % bld.env.PLATFORM) sources = sources + directory.ant_glob('src/backends/*.c') + sources = sources + directory.ant_glob('deps/zlib/*.c') # SHA1 methods source if bld.env.sha1 == "ppc": @@ -153,7 +153,7 @@ def build_library(bld, build_type): BUILD[build_type]( source=sources, target='git2', - includes=['src', 'include'], + includes=['src', 'include', 'deps/zlib'], install_path='${LIBDIR}', use=ALL_LIBS, vnum=version, From 4d0276bafb0ceb7a1031da95c90fae6d0c17499e Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Tue, 22 Mar 2011 00:35:23 -0400 Subject: [PATCH 06/37] Updated error to be GitError and moved its header file --- include/blob.h | 22 +- include/error.h | 80 +++++++ src/base.cc | 6 +- src/blob.cc | 25 +-- src/commit.cc | 535 +++++++++++++++++++++++----------------------- src/commit.h | 157 +++++++------- src/error.cc | 63 +++--- src/error.h | 31 --- src/tree_entry.cc | 2 +- 9 files changed, 486 insertions(+), 435 deletions(-) create mode 100755 include/error.h delete mode 100755 src/error.h diff --git a/include/blob.h b/include/blob.h index 79432ab7e..f90f453f7 100755 --- a/include/blob.h +++ b/include/blob.h @@ -19,18 +19,18 @@ using namespace node; namespace { /** * Class: GitBlob - * Wrapper for libgit2 git_blob. + * Wrapper for libgit2 git_blob. */ class GitBlob : public ObjectWrap { public: /** * Variable: constructor_template - * Used to create Node.js constructor. + * Used to create Node.js constructor. */ static v8::Persistent constructor_template; /** * Function: Initialize - * Used to intialize the EventEmitter from Node.js + * Used to intialize the EventEmitter from Node.js * * Parameters: * target - v8::Object the Node.js global module object @@ -50,7 +50,7 @@ namespace { void SetValue(git_blob* blob); /** * Function: Lookup - * Lookup a blob object from a repository. + * Lookup a blob object from a repository. * * Parameters: * repo the repo to use when locating the blob. @@ -62,15 +62,15 @@ namespace { int Lookup(git_repository* repo, const git_oid *id); /** * Function: RawContent - * Get a read-only buffer with the raw content of a blob. + * Get a read-only buffer with the raw content of a blob. * * Returns: * raw content buffer; NULL if the blob has no contents */ - const char* RawContent(); + const void* RawContent(); /** * Function: RawSize - * Lookup a blob object from a repository. + * Lookup a blob object from a repository. * * Returns: * size in bytes @@ -150,15 +150,15 @@ namespace { private: /** * Variable: blob - * Internal reference to git_blob object + * Internal reference to git_blob object */ git_blob* blob; /** * Struct: lookup_request - * Contains references to the current blob, repo, and oid for a - * commit lookup, also contains references to an error code post - * lookup, and a callback function to execute. + * Contains references to the current blob, repo, and oid for a + * commit lookup, also contains references to an error code post + * lookup, and a callback function to execute. */ struct lookup_request { GitBlob* blob; diff --git a/include/error.h b/include/error.h new file mode 100755 index 000000000..e1143acac --- /dev/null +++ b/include/error.h @@ -0,0 +1,80 @@ +/* + * Copyright 2011, Tim Branyen @tbranyen + * Dual licensed under the MIT and GPL licenses. + */ + +#ifndef ERROR_H +#define ERROR_H + +#include +#include + +#include "../vendor/libgit2/include/git2.h" + +using namespace node; + +namespace { + /** + * Class: GitError + * Wrapper for libgit2 git_error. + */ + class GitError : public ObjectWrap { + public: + /** + * Variable: constructor_template + * Used to create Node.js constructor. + */ + static v8::Persistent constructor_template; + /** + * Function: Initialize + * Used to intialize the EventEmitter from Node.js + * + * Parameters: + * target - v8::Object the Node.js global module object + */ + static void Initialize(v8::Handle target); + /** + * Function: StrError + * Get a read-only buffer with the raw content of a blob. + * + * Parameters: + * err - A signed int error code + * + * Returns: + * a string explaining the error code. + */ + const char* StrError(int err); + + protected: + /** + * Constructor: GitBlob + */ + GitError() {}; + /** + * Deconstructor: GitBlob + */ + ~GitError() {}; + /** + * Function: New + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle New(const v8::Arguments& args); + /** + * Function: StrError + * + * Parameters: + * args v8::Arguments function call + * + * Returns: + * v8::Object args.This() + */ + static v8::Handle StrError(const v8::Arguments& args); + }; +} + +#endif diff --git a/src/base.cc b/src/base.cc index 5f0b0a613..6215185bd 100755 --- a/src/base.cc +++ b/src/base.cc @@ -10,7 +10,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "reference.h" #include "sig.h" -#include "error.h" +#include "../include/error.h" #include "../include/blob.h" #include "repo.h" #include "oid.h" @@ -25,12 +25,12 @@ extern "C" void init(Handle target) { Reference::Initialize(target); Sig::Initialize(target); - Error::Initialize(target); + GitError::Initialize(target); GitBlob::Initialize(target); Oid::Initialize(target); GitObject::Initialize(target); Repo::Initialize(target); - Commit::Initialize(target); + GitCommit::Initialize(target); RevWalk::Initialize(target); GitTree::Initialize(target); GitTreeEntry::Initialize(target); diff --git a/src/blob.cc b/src/blob.cc index d2e168d5c..a4821e3c5 100755 --- a/src/blob.cc +++ b/src/blob.cc @@ -40,11 +40,11 @@ namespace { this->blob = blob; } - int GitBlob::Lookup(git_repository *repo, const git_oid *id) { + int GitBlob::Lookup(git_repository* repo, const git_oid* id) { return git_blob_lookup(&this->blob, repo, id); } - const char* GitBlob::RawContent() { + const void* GitBlob::RawContent() { return git_blob_rawcontent(this->blob); } @@ -55,7 +55,7 @@ namespace { Handle GitBlob::New(const Arguments& args) { HandleScope scope; - GitBlob *blob = new GitBlob(); + GitBlob* blob = new GitBlob(); blob->Wrap(args.This()); return args.This(); @@ -64,13 +64,13 @@ namespace { Handle GitBlob::RawContent(const Arguments& args) { HandleScope scope; - GitBlob *blob = ObjectWrap::Unwrap(args.This()); + GitBlob* blob = ObjectWrap::Unwrap(args.This()); - return String::New(blob->RawContent()); + return String::New((const char*)blob->RawContent()); } Handle GitBlob::Lookup(const Arguments& args) { - GitBlob *blob = ObjectWrap::Unwrap(args.This()); + GitBlob* blob = ObjectWrap::Unwrap(args.This()); Local callback; HandleScope scope; @@ -89,7 +89,7 @@ namespace { callback = Local::Cast(args[3]); - lookup_request *ar = new lookup_request(); + lookup_request* ar = new lookup_request(); ar->blob = blob; ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); ar->oid = ObjectWrap::Unwrap(args[1]->ToObject()); @@ -103,18 +103,18 @@ namespace { return Undefined(); } - int GitBlob::EIO_Lookup(eio_req *req) { - lookup_request *ar = static_cast(req->data); + int GitBlob::EIO_Lookup(eio_req* req) { + lookup_request* ar = static_cast(req->data); ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue()); return 0; } - int GitBlob::EIO_AfterLookup(eio_req *req) { + int GitBlob::EIO_AfterLookup(eio_req* req) { HandleScope scope; - lookup_request *ar = static_cast(req->data); + lookup_request* ar = static_cast(req->data); ev_unref(EV_DEFAULT_UC); ar->blob->Unref(); @@ -138,9 +138,10 @@ namespace { Handle GitBlob::RawSize(const Arguments& args) { HandleScope scope; - GitBlob *blob = new GitBlob(); + GitBlob* blob = new GitBlob(); return Integer::New(blob->RawSize()); } + Persistent GitBlob::constructor_template; } diff --git a/src/commit.cc b/src/commit.cc index 234b05eab..a123b3ca9 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -19,360 +19,349 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace v8; using namespace node; -void Commit::Initialize(Handle target) { - HandleScope scope; - - Local t = FunctionTemplate::New(New); - - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Commit")); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "messageShort", MessageShort); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "message", Message); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "time", Time); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "timeOffset", TimeOffset); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "author", Author); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "tree", Tree); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "parentCount", ParentCount); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "parent", Parent); - - target->Set(String::NewSymbol("Commit"), constructor_template->GetFunction()); -} - -git_commit* Commit::GetValue() { - return this->commit; -} +namespace { + void GitCommit::Initialize(Handle target) { + HandleScope scope; -void Commit::SetValue(git_commit* commit) { - this->commit = commit; -} + Local t = FunctionTemplate::New(New); + + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("Commit")); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "messageShort", MessageShort); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "message", Message); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "time", Time); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "timeOffset", TimeOffset); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "author", Author); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "tree", Tree); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "parentCount", ParentCount); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "parent", Parent); + + target->Set(String::NewSymbol("Commit"), constructor_template->GetFunction()); + } -int Commit::Lookup(git_oid* oid) { - git_commit* commit; + git_commit* GitCommit::GetValue() { + return this->commit; + } - //this->oid = oid; + void GitCommit::SetValue(git_commit* commit) { + this->commit = commit; + } - //int err = git_commit_lookup(&commit, this->repo, oid); + int GitCommit::Lookup(git_oid* oid) { + git_commit* commit; - //this->commit = commit; + //this->oid = oid; - //return err; - return 0; -} + //int err = git_commit_lookup(&commit, this->repo, oid); -int Commit::New(git_repository* repo) { - this->repo = repo; + //this->commit = commit; - return git_commit_new(&this->commit, this->repo); -} + //return err; + return 0; + } -const git_oid* Commit::Id() { - return git_commit_id(this->commit); -} + const git_oid* GitCommit::Id() { + return git_commit_id(this->commit); + } -const char* Commit::MessageShort() { - return git_commit_message_short(this->commit); -} + const char* GitCommit::MessageShort() { + return git_commit_message_short(this->commit); + } -const char* Commit::Message() { - return git_commit_message(this->commit); -} + const char* GitCommit::Message() { + return git_commit_message(this->commit); + } -time_t Commit::Time() { - return git_commit_time(this->commit); -} + time_t GitCommit::Time() { + return git_commit_time(this->commit); + } -int Commit::TimeOffset() { - return git_commit_time_offset(this->commit); -} + int GitCommit::TimeOffset() { + return git_commit_time_offset(this->commit); + } -const git_signature* Commit::Committer() { - return git_commit_author(this->commit); -} + const git_signature* GitCommit::Committer() { + return git_commit_author(this->commit); + } -const git_signature* Commit::Author() { - return git_commit_author(this->commit); -} + const git_signature* GitCommit::Author() { + return git_commit_author(this->commit); + } -int Commit::Tree(git_tree** tree) { - return git_commit_tree(tree, this->commit); -} + int GitCommit::Tree(git_tree** tree) { + return git_commit_tree(tree, this->commit); + } -unsigned int Commit::ParentCount() { - return git_commit_parentcount(this->commit); -} + unsigned int GitCommit::ParentCount() { + return git_commit_parentcount(this->commit); + } -int Commit::Parent(git_commit** commit, int pos) { - return git_commit_parent(commit, this->commit, pos); -} + int GitCommit::Parent(git_commit** commit, int pos) { + return git_commit_parent(commit, this->commit, pos); + } -Handle Commit::New(const Arguments& args) { - HandleScope scope; + Handle GitCommit::New(const Arguments& args) { + HandleScope scope; - Commit *commit = new Commit(); + GitCommit *commit = new GitCommit(); + commit->Wrap(args.This()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); + return args.This(); } - Repo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); + Handle GitCommit::Lookup(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); + Local callback; - commit->New(repo->GetValue()); - commit->Wrap(args.This()); + HandleScope scope; - return args.This(); -} + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + } -Handle Commit::Lookup(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); - Local callback; + if(args.Length() == 1 || !args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } - HandleScope scope; + callback = Local::Cast(args[1]); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); - } + lookup_request *ar = new lookup_request(); + ar->commit = commit; + ar->oid = ObjectWrap::Unwrap(args[0]->ToObject()); + ar->callback = Persistent::New(callback); - if(args.Length() == 1 || !args[1]->IsFunction()) { - return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); - } + commit->Ref(); - callback = Local::Cast(args[1]); + eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar); + ev_ref(EV_DEFAULT_UC); - lookup_request *ar = new lookup_request(); - ar->commit = commit; - ar->oid = ObjectWrap::Unwrap(args[0]->ToObject()); - ar->callback = Persistent::New(callback); - - commit->Ref(); + return Undefined(); + } - eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar); - ev_ref(EV_DEFAULT_UC); + int GitCommit::EIO_Lookup(eio_req *req) { + lookup_request *ar = static_cast(req->data); - return Undefined(); -} + ar->err = ar->commit->Lookup(ar->oid->GetValue()); -int Commit::EIO_Lookup(eio_req *req) { - lookup_request *ar = static_cast(req->data); + return 0; + } - ar->err = ar->commit->Lookup(ar->oid->GetValue()); + int GitCommit::EIO_AfterLookup(eio_req *req) { + HandleScope scope; - return 0; -} + lookup_request *ar = static_cast(req->data); + ev_unref(EV_DEFAULT_UC); + ar->commit->Unref(); -int Commit::EIO_AfterLookup(eio_req *req) { - HandleScope scope; + Local argv[0]; + argv[0] = Integer::New(ar->err); - lookup_request *ar = static_cast(req->data); - ev_unref(EV_DEFAULT_UC); - ar->commit->Unref(); + TryCatch try_catch; - Local argv[0]; - argv[0] = Integer::New(ar->err); + ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); - TryCatch try_catch; + if(try_catch.HasCaught()) + FatalException(try_catch); + + ar->callback.Dispose(); - ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); + delete ar; - if(try_catch.HasCaught()) - FatalException(try_catch); - - ar->callback.Dispose(); + return 0; + } - delete ar; + Handle GitCommit::Id(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - return 0; -} + HandleScope scope; -Handle Commit::Id(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + } - HandleScope scope; + Oid *oid = ObjectWrap::Unwrap(args[0]->ToObject()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + oid->SetValue(const_cast(commit->Id())); + + return Undefined(); } - Oid *oid = ObjectWrap::Unwrap(args[0]->ToObject()); + Handle GitCommit::MessageShort(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - oid->SetValue(const_cast(commit->Id())); - - return Undefined(); -} + HandleScope scope; + + return String::New(commit->MessageShort()); + } -Handle Commit::MessageShort(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + Handle GitCommit::Message(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - HandleScope scope; - - return String::New(commit->MessageShort()); -} + HandleScope scope; + + return String::New(commit->Message()); + } -Handle Commit::Message(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + Handle GitCommit::Time(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - HandleScope scope; - - return String::New(commit->Message()); -} + HandleScope scope; + + return Integer::New(commit->Time()); + } -Handle Commit::Time(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + Handle GitCommit::TimeOffset(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - HandleScope scope; - - return Integer::New(commit->Time()); -} + HandleScope scope; + + return Integer::New(commit->TimeOffset()); + } -Handle Commit::TimeOffset(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + Handle GitCommit::Committer(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - HandleScope scope; - - return Integer::New(commit->TimeOffset()); -} + HandleScope scope; -Handle Commit::Committer(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Signature is required and must be an Object."))); + } - HandleScope scope; + Sig *sig = ObjectWrap::Unwrap(args[0]->ToObject()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Signature is required and must be an Object."))); + sig->SetValue(const_cast(commit->Committer())); + + return Undefined(); } - Sig *sig = ObjectWrap::Unwrap(args[0]->ToObject()); + Handle GitCommit::Author(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - sig->SetValue(const_cast(commit->Committer())); - - return Undefined(); -} + HandleScope scope; -Handle Commit::Author(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Signature is required and must be an Object."))); + } - HandleScope scope; + Sig *sig = ObjectWrap::Unwrap(args[0]->ToObject()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Signature is required and must be an Object."))); + sig->SetValue(const_cast(commit->Author())); + + return Undefined(); } - Sig *sig = ObjectWrap::Unwrap(args[0]->ToObject()); + Handle GitCommit::Tree(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - sig->SetValue(const_cast(commit->Author())); - - return Undefined(); -} + HandleScope scope; + + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Tree is required and must be an Object."))); + } -Handle Commit::Tree(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + git_tree* in; + GitTree* tree = ObjectWrap::Unwrap(args[0]->ToObject()); - HandleScope scope; + int err = commit->Tree(&in); + tree->SetValue(in); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Tree is required and must be an Object."))); + return Integer::New(err); + } + //Handle GitCommit::Tree(const Arguments& args) { + // GitCommit *commit = ObjectWrap::Unwrap(args.This()); + // Local callback; + // + // HandleScope scope; + // + // if(args.Length() == 0 || !args[0]->IsObject()) { + // return ThrowException(Exception::Error(String::New("Tree is required and must be an Object."))); + // } + // + // callback = Local::Cast(args[1]); + // + // tree_request *ar = new tree_request(); + // ar->commit = commit; + // ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); + // ar->callback = Persistent::New(callback); + // + // commit->Ref(); + // + // eio_custom(EIO_Tree, EIO_PRI_DEFAULT, EIO_AfterTree, ar); + // ev_ref(EV_DEFAULT_UC); + // + // return Undefined(); + //} + // + //int GitCommit::EIO_Tree(eio_req *req) { + // tree_request *ar = static_cast(req->data); + // + // git_tree *tree = ar->commit->Tree(); + // + // ar->tree->SetValue(tree); + // + // return 0; + //} + // + //int GitCommit::EIO_AfterTree(eio_req *req) { + // HandleScope scope; + // + // tree_request *ar = static_cast(req->data); + // ev_unref(EV_DEFAULT_UC); + // ar->commit->Unref(); + // + // Local argv[1]; + // + // TryCatch try_catch; + // + // ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); + // + // if(try_catch.HasCaught()) + // FatalException(try_catch); + // + // ar->err.Dispose(); + // ar->callback.Dispose(); + // + // delete ar; + // + // return 0; + //} + + Handle GitCommit::ParentCount(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); + + HandleScope scope; + + unsigned int count = commit->ParentCount(); + + return Integer::New(count); } - git_tree* in; - GitTree* tree = ObjectWrap::Unwrap(args[0]->ToObject()); + Handle GitCommit::Parent(const Arguments& args) { + GitCommit *commit = ObjectWrap::Unwrap(args.This()); - int err = commit->Tree(&in); - tree->SetValue(in); + HandleScope scope; - return Integer::New(err); -} -//Handle Commit::Tree(const Arguments& args) { -// Commit *commit = ObjectWrap::Unwrap(args.This()); -// Local callback; -// -// HandleScope scope; -// -// if(args.Length() == 0 || !args[0]->IsObject()) { -// return ThrowException(Exception::Error(String::New("Tree is required and must be an Object."))); -// } -// -// callback = Local::Cast(args[1]); -// -// tree_request *ar = new tree_request(); -// ar->commit = commit; -// ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); -// ar->callback = Persistent::New(callback); -// -// commit->Ref(); -// -// eio_custom(EIO_Tree, EIO_PRI_DEFAULT, EIO_AfterTree, ar); -// ev_ref(EV_DEFAULT_UC); -// -// return Undefined(); -//} -// -//int Commit::EIO_Tree(eio_req *req) { -// tree_request *ar = static_cast(req->data); -// -// git_tree *tree = ar->commit->Tree(); -// -// ar->tree->SetValue(tree); -// -// return 0; -//} -// -//int Commit::EIO_AfterTree(eio_req *req) { -// HandleScope scope; -// -// tree_request *ar = static_cast(req->data); -// ev_unref(EV_DEFAULT_UC); -// ar->commit->Unref(); -// -// Local argv[1]; -// -// TryCatch try_catch; -// -// ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); -// -// if(try_catch.HasCaught()) -// FatalException(try_catch); -// -// ar->err.Dispose(); -// ar->callback.Dispose(); -// -// delete ar; -// -// return 0; -//} - -Handle Commit::ParentCount(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); - - HandleScope scope; - - unsigned int count = commit->ParentCount(); - - return Integer::New(count); -} + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Commit is required and must be an Object."))); + } -Handle Commit::Parent(const Arguments& args) { - Commit *commit = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 1 || !args[1]->IsNumber()) { + return ThrowException(Exception::Error(String::New("Position is required and must be a Number."))); + } - HandleScope scope; + GitCommit* out = ObjectWrap::Unwrap(args[0]->ToObject()); + git_commit* in; + int index = args[1]->ToInteger()->Value(); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Commit is required and must be an Object."))); - } + int err = commit->Parent(&in, index); + out->SetValue(in); - if(args.Length() == 1 || !args[1]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Position is required and must be a Number."))); + return Integer::New(err); } - Commit* out = ObjectWrap::Unwrap(args[0]->ToObject()); - git_commit* in; - int index = args[1]->ToInteger()->Value(); - - int err = commit->Parent(&in, index); - out->SetValue(in); - - return Integer::New(err); + Persistent GitCommit::constructor_template; } -Persistent Commit::constructor_template; diff --git a/src/commit.h b/src/commit.h index 45336a81b..2b0b2d69a 100755 --- a/src/commit.h +++ b/src/commit.h @@ -1,6 +1,7 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2011, Tim Branyen @tbranyen + * Dual licensed under the MIT and GPL licenses. + */ #ifndef COMMIT_H #define COMMIT_H @@ -19,80 +20,80 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace node; using namespace v8; -/** - * Class wrapper for libgit2 git_commit - */ -class Commit : public EventEmitter { - public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ - static Persistent constructor_template; - - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ - static void Initialize (Handle target); - - git_commit* GetValue(); - void SetValue(git_commit* commit); - int Lookup(git_oid* oid); - int New(git_repository* repo); - const git_oid* Id(); - const char* MessageShort(); - const char* Message(); - time_t Time(); - int TimeOffset(); - const git_signature* Committer(); - const git_signature* Author(); - int Tree(git_tree** tree); - unsigned int ParentCount(); - int Parent(git_commit** commit, int pos); - - protected: - Commit() {} - ~Commit() {} - - static Handle New(const Arguments& args); - - static Handle Lookup(const Arguments& args); - static int EIO_Lookup(eio_req *req); - static int EIO_AfterLookup(eio_req *req); - - static Handle Id(const Arguments& args); - static Handle MessageShort(const Arguments& args); - static Handle Message(const Arguments& args); - static Handle Time(const Arguments& args); - static Handle TimeOffset(const Arguments& args); - static Handle Committer(const Arguments& args); - static Handle Author(const Arguments& args); - - static Handle Tree(const Arguments& args); - static int EIO_Tree(eio_req* req); - static int EIO_AfterTree(eio_req* req); - - static Handle ParentCount(const Arguments& args); - static Handle Parent(const Arguments& args); - - private: - git_commit* commit; - git_repository* repo; - git_oid* oid; - - struct lookup_request { - Commit* commit; - Oid* oid; - int err; - Persistent callback; - }; - - //struct tree_request { - // Commit* commit; - // Tree* tree; - // Persistent callback; - //}; -}; - +namespace { + /** + * Class wrapper for libgit2 git_commit + */ + class GitCommit : public EventEmitter { + public: + /** + * v8::FunctionTemplate used to create Node.js constructor + */ + static Persistent constructor_template; + + /** + * Used to intialize the EventEmitter from Node.js + * + * @param target v8::Object the Node.js module object + */ + static void Initialize (Handle target); + + git_commit* GetValue(); + void SetValue(git_commit* commit); + int Lookup(git_oid* oid); + const git_oid* Id(); + const char* MessageShort(); + const char* Message(); + time_t Time(); + int TimeOffset(); + const git_signature* Committer(); + const git_signature* Author(); + int Tree(git_tree** tree); + unsigned int ParentCount(); + int Parent(git_commit** commit, int pos); + + protected: + GitCommit() {} + ~GitCommit() {} + + static Handle New(const Arguments& args); + + static Handle Lookup(const Arguments& args); + static int EIO_Lookup(eio_req *req); + static int EIO_AfterLookup(eio_req *req); + + static Handle Id(const Arguments& args); + static Handle MessageShort(const Arguments& args); + static Handle Message(const Arguments& args); + static Handle Time(const Arguments& args); + static Handle TimeOffset(const Arguments& args); + static Handle Committer(const Arguments& args); + static Handle Author(const Arguments& args); + + static Handle Tree(const Arguments& args); + static int EIO_Tree(eio_req* req); + static int EIO_AfterTree(eio_req* req); + + static Handle ParentCount(const Arguments& args); + static Handle Parent(const Arguments& args); + + private: + git_commit* commit; + git_repository* repo; + git_oid* oid; + + struct lookup_request { + GitCommit* commit; + Oid* oid; + int err; + Persistent callback; + }; + + //struct tree_request { + // GitCommit* commit; + // Tree* tree; + // Persistent callback; + //}; + }; +} #endif diff --git a/src/error.cc b/src/error.cc index 18f50cb47..d46974f5d 100755 --- a/src/error.cc +++ b/src/error.cc @@ -1,6 +1,7 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2011, Tim Branyen @tbranyen + * Dual licensed under the MIT and GPL licenses. + */ #include #include @@ -8,42 +9,52 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "../vendor/libgit2/include/git2.h" -#include "error.h" +#include "../include/error.h" using namespace v8; using namespace node; -void Error::Initialize (Handle target) { - HandleScope scope; +namespace { + void GitError::Initialize (Handle target) { + HandleScope scope; - Local t = FunctionTemplate::New(New); - - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Error")); + Local t = FunctionTemplate::New(New); + + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("Error")); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "strError", StrError); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "strError", StrError); - target->Set(String::NewSymbol("Error"), constructor_template->GetFunction()); -} + target->Set(String::NewSymbol("Error"), constructor_template->GetFunction()); + } -Handle Error::New(const Arguments& args) { - HandleScope scope; + const char* GitError::StrError(int err) { + return git_strerror(err); + } - Error *error = new Error(); - error->Wrap(args.This()); + Handle GitError::New(const Arguments& args) { + HandleScope scope; - return args.This(); -} + GitError *error = new GitError(); + error->Wrap(args.This()); + + return args.This(); + } + + Handle GitError::StrError(const Arguments& args) { + GitError* error = ObjectWrap::Unwrap(args.This()); + + HandleScope scope; + + if(args.Length() == 0 || !args[0]->IsNumber()) { + return ThrowException(Exception::Error(String::New("Error is required and must be a Number."))); + } -Handle Error::StrError(const Arguments& args) { - HandleScope scope; + Local err = Local::Cast(args[0]); - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Error is required and must be a Number."))); + return String::New(error->StrError(err->Value())); } - Local err = Local::Cast(args[0]); - return String::New(git_strerror(err->Value())); + Persistent GitError::constructor_template; } -Persistent Error::constructor_template; diff --git a/src/error.h b/src/error.h deleted file mode 100755 index e6c7d7492..000000000 --- a/src/error.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#ifndef ERROR_H -#define ERROR_H - -#include -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -using namespace v8; -using namespace node; - -class Error : public EventEmitter { - public: - static Persistent constructor_template; - static void Initialize(Handle target); - - protected: - Error() {}; - ~Error() {}; - - static Handle New(const Arguments& args); - - static Handle StrError(const Arguments& args); -}; - -#endif diff --git a/src/tree_entry.cc b/src/tree_entry.cc index c1d545080..f3c4f46d7 100644 --- a/src/tree_entry.cc +++ b/src/tree_entry.cc @@ -9,7 +9,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "../vendor/libgit2/include/git2.h" #include "repo.h" -#include "blob.h" +#include "../include/blob.h" #include "tree.h" #include "object.h" #include "oid.h" From 7236321cd5d11acda8e87db0fa61d2f8476b2bac Mon Sep 17 00:00:00 2001 From: Tim Branyen Date: Tue, 22 Mar 2011 00:36:40 -0400 Subject: [PATCH 07/37] Removed doxygen --- Api.doxygen | 1719 --------------------------------------------------- 1 file changed, 1719 deletions(-) delete mode 100644 Api.doxygen diff --git a/Api.doxygen b/Api.doxygen deleted file mode 100644 index 9e609d57c..000000000 --- a/Api.doxygen +++ /dev/null @@ -1,1719 +0,0 @@ -# Doxyfile 1.7.3 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = NodeGit - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = 0.0.1 - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "NodeJS libgit2 asynchronous native bindings" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs/native - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = YES - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = src - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.for \ - *.vhd \ - *.vhdl - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = * - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = YES - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 20 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 105 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = docs/native - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [0,1..20]) -# that doxygen will group on one line in the generated HTML documentation. -# Note that a value of 0 will completely suppress the enum values from -# appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. - -MATHJAX_RELPATH = http://www.mathjax.org/mathjax - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will write a font called Helvetica to the output -# directory and reference it in all dot files that doxygen generates. -# When you want a differently looking font you can specify the font name -# using DOT_FONTNAME. You need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, svg, gif or svg. -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES From 2fcf21c1699ec00562b888ca5b127aa44f7e285c Mon Sep 17 00:00:00 2001 From: tbranyen Date: Thu, 31 Mar 2011 21:24:36 -0400 Subject: [PATCH 08/37] various updates to source, added *hopefuls for 0.0.3* now bundling naturaldocs --- Data/ClassHierarchy.nd | Bin 0 -> 175 bytes Data/ConfigFileInfo.nd | Bin 0 -> 26 bytes Data/FileInfo.nd | 4 + Data/ImageFileInfo.nd | Bin 0 -> 8 bytes Data/ImageReferenceTable.nd | Bin 0 -> 8 bytes Data/IndexInfo.nd | Bin 0 -> 154 bytes Data/PreviousMenuState.nd | Bin 0 -> 198 bytes Data/PreviousSettings.nd | Bin 0 -> 82 bytes Data/SymbolTable.nd | Bin 0 -> 3366 bytes Languages.txt | 113 + Makefile | 15 +- Menu.txt | 59 + README.md | 25 +- Theme.css | 796 ++++ Topics.txt | 81 + docs/files/blob-h.html | 84 + docs/files/error-h.html | 54 + docs/index.html | 1 + docs/index/Classes.html | 37 + docs/index/Functions.html | 61 + docs/index/General.html | 73 + docs/index/Variables.html | 37 + docs/javascript/main.js | 841 ++++ docs/javascript/prettify.js | 1526 +++++++ docs/javascript/searchdata.js | 122 + docs/search/ClassesG.html | 20 + docs/search/ClassesL.html | 20 + docs/search/FunctionsC.html | 20 + docs/search/FunctionsE.html | 20 + docs/search/FunctionsG.html | 20 + docs/search/FunctionsI.html | 20 + docs/search/FunctionsL.html | 20 + docs/search/FunctionsN.html | 20 + docs/search/FunctionsR.html | 20 + docs/search/FunctionsS.html | 20 + docs/search/GeneralB.html | 20 + docs/search/GeneralC.html | 20 + docs/search/GeneralE.html | 20 + docs/search/GeneralF.html | 20 + docs/search/GeneralG.html | 20 + docs/search/GeneralI.html | 20 + docs/search/GeneralL.html | 20 + docs/search/GeneralN.html | 20 + docs/search/GeneralR.html | 20 + docs/search/GeneralS.html | 20 + docs/search/GeneralV.html | 20 + docs/search/NoResults.html | 15 + docs/search/VariablesB.html | 20 + docs/search/VariablesC.html | 20 + docs/styles/main.css | 796 ++++ include/blob.h | 352 +- include/error.h | 124 +- package.json | 3 + src/base.cc | 4 + src/blob.cc | 209 +- src/commit.cc | 533 ++- src/commit.h | 150 +- src/error.cc | 60 +- src/tree.cc | 18 - src/tree.h | 15 - src/tree_entry.cc | 5 +- vendor/libgit2/README.md | 2 + vendor/libgit2/include/git2.h | 2 +- vendor/libgit2/include/git2/blob.h | 18 + vendor/libgit2/include/git2/commit.h | 19 +- vendor/libgit2/include/git2/common.h | 6 +- vendor/libgit2/include/git2/index.h | 4 +- vendor/libgit2/include/git2/odb.h | 64 +- vendor/libgit2/include/git2/odb_backend.h | 8 +- vendor/libgit2/include/git2/refs.h | 23 + vendor/libgit2/include/git2/repository.h | 16 +- vendor/libgit2/include/git2/signature.h | 4 +- vendor/libgit2/include/git2/tag.h | 18 + vendor/libgit2/include/git2/tree.h | 18 + vendor/libgit2/include/git2/types.h | 10 +- vendor/libgit2/src/backends/sqlite.c | 38 +- vendor/libgit2/src/commit.c | 2 +- vendor/libgit2/src/filebuf.c | 11 +- vendor/libgit2/src/fileops.c | 167 +- vendor/libgit2/src/fileops.h | 11 +- vendor/libgit2/src/index.c | 18 +- vendor/libgit2/src/odb.c | 55 + vendor/libgit2/src/odb_loose.c | 6 +- vendor/libgit2/src/odb_pack.c | 4 +- vendor/libgit2/src/refs.c | 67 +- vendor/libgit2/src/repository.c | 10 +- vendor/libgit2/src/revwalk.c | 11 + vendor/libgit2/src/signature.c | 2 +- vendor/libgit2/src/thread-utils.h | 4 + vendor/libgit2/src/util.c | 3 + .../tests/resources/empty_bare.git/HEAD | 1 + .../tests/resources/empty_bare.git/config | 7 + .../resources/empty_bare.git/description | 1 + .../resources/empty_bare.git/info/exclude | 6 + .../objects/info/dummy-marker.txt | 0 .../objects/pack/dummy-marker.txt | 0 .../refs/heads/dummy-marker.txt | 0 .../empty_bare.git/refs/tags/dummy-marker.txt | 0 .../empty_standard_repo/.gitted/HEAD | 1 + .../empty_standard_repo/.gitted/config | 8 + .../empty_standard_repo/.gitted/description | 1 + .../empty_standard_repo/.gitted/info/exclude | 6 + .../.gitted/objects/info/dummy-marker.txt | 0 .../.gitted/objects/pack/dummy-marker.txt | 0 .../.gitted/refs/heads/dummy-marker.txt | 0 .../.gitted/refs/tags/dummy-marker.txt | 0 vendor/libgit2/tests/t00-core.c | 359 +- vendor/libgit2/tests/t04-commit.c | 2 +- vendor/libgit2/tests/t06-index.c | 2 +- vendor/libgit2/tests/t11-sqlite.c | 21 +- vendor/libgit2/tests/t12-repo.c | 101 +- vendor/libgit2/tests/test_helpers.c | 37 +- vendor/libgit2/tests/test_helpers.h | 1 + vendor/naturaldocs/Config/Languages.txt | 286 ++ vendor/naturaldocs/Config/Topics.txt | 382 ++ .../Help/customizinglanguages.html | 52 + .../naturaldocs/Help/customizingtopics.html | 74 + vendor/naturaldocs/Help/documenting.html | 58 + .../Help/documenting/reference.html | 147 + .../Help/documenting/walkthrough.html | 181 + vendor/naturaldocs/Help/example/Default.css | 528 +++ .../naturaldocs/Help/example/NaturalDocs.js | 204 + vendor/naturaldocs/Help/example/Roman.css | 507 +++ vendor/naturaldocs/Help/example/Small.css | 507 +++ .../naturaldocs/Help/example/showstyle.html | 43 + vendor/naturaldocs/Help/examples.css | 90 + .../Help/images/header/background.png | Bin 0 -> 229 bytes .../Help/images/header/leftside.png | Bin 0 -> 1215 bytes .../naturaldocs/Help/images/header/logo.png | Bin 0 -> 12146 bytes .../Help/images/header/overbody.png | Bin 0 -> 283 bytes .../Help/images/header/overbodybg.png | Bin 0 -> 141 bytes .../Help/images/header/overleftmargin.png | Bin 0 -> 188 bytes .../Help/images/header/overmenu.png | Bin 0 -> 244 bytes .../Help/images/header/overmenubg.png | Bin 0 -> 141 bytes .../Help/images/header/rightside.png | Bin 0 -> 1186 bytes vendor/naturaldocs/Help/images/menu/about.png | Bin 0 -> 397 bytes .../Help/images/menu/background.png | Bin 0 -> 187 bytes .../Help/images/menu/bottomleft.png | Bin 0 -> 235 bytes .../Help/images/menu/bottomright.png | Bin 0 -> 234 bytes .../Help/images/menu/community.png | Bin 0 -> 507 bytes .../Help/images/menu/customizing.png | Bin 0 -> 575 bytes vendor/naturaldocs/Help/images/menu/using.png | Bin 0 -> 390 bytes vendor/naturaldocs/Help/index.html | 9 + .../Help/javascript/BrowserStyles.js | 77 + .../Help/javascript/PNGHandling.js | 72 + vendor/naturaldocs/Help/keywords.html | 38 + vendor/naturaldocs/Help/languages.html | 32 + vendor/naturaldocs/Help/menu.html | 79 + vendor/naturaldocs/Help/output.html | 84 + vendor/naturaldocs/Help/running.html | 40 + vendor/naturaldocs/Help/styles.css | 292 ++ vendor/naturaldocs/Help/styles.html | 52 + vendor/naturaldocs/Help/troubleshooting.html | 18 + vendor/naturaldocs/Info/CSSGuide.txt | 947 +++++ vendor/naturaldocs/Info/File Parsing.txt | 83 + vendor/naturaldocs/Info/HTMLTestCases.pm | 270 ++ vendor/naturaldocs/Info/Languages.txt | 107 + vendor/naturaldocs/Info/NDMarkup.txt | 92 + vendor/naturaldocs/Info/Symbol Management.txt | 59 + vendor/naturaldocs/Info/images/Logo.png | Bin 0 -> 12405 bytes .../naturaldocs/JavaScript/GooglePrettify.js | 1526 +++++++ vendor/naturaldocs/JavaScript/NaturalDocs.js | 841 ++++ vendor/naturaldocs/License.txt | 275 ++ .../Modules/NaturalDocs/BinaryFile.pm | 295 ++ .../Modules/NaturalDocs/Builder.pm | 281 ++ .../Modules/NaturalDocs/Builder/Base.pm | 349 ++ .../Modules/NaturalDocs/Builder/FramedHTML.pm | 354 ++ .../Modules/NaturalDocs/Builder/HTML.pm | 414 ++ .../Modules/NaturalDocs/Builder/HTMLBase.pm | 3745 +++++++++++++++++ .../Modules/NaturalDocs/ClassHierarchy.pm | 861 ++++ .../NaturalDocs/ClassHierarchy/Class.pm | 413 ++ .../NaturalDocs/ClassHierarchy/File.pm | 158 + .../Modules/NaturalDocs/ConfigFile.pm | 508 +++ .../Modules/NaturalDocs/Constants.pm | 166 + .../Modules/NaturalDocs/DefineMembers.pm | 101 + .../naturaldocs/Modules/NaturalDocs/Error.pm | 306 ++ .../naturaldocs/Modules/NaturalDocs/File.pm | 541 +++ .../NaturalDocs/ImageReferenceTable.pm | 384 ++ .../ImageReferenceTable/Reference.pm | 45 + .../NaturalDocs/ImageReferenceTable/String.pm | 111 + .../Modules/NaturalDocs/Languages.pm | 1476 +++++++ .../NaturalDocs/Languages/ActionScript.pm | 1487 +++++++ .../Modules/NaturalDocs/Languages/Ada.pm | 39 + .../Modules/NaturalDocs/Languages/Advanced.pm | 817 ++++ .../NaturalDocs/Languages/Advanced/Scope.pm | 96 + .../Languages/Advanced/ScopeChange.pm | 71 + .../Modules/NaturalDocs/Languages/Base.pm | 833 ++++ .../Modules/NaturalDocs/Languages/CSharp.pm | 1552 +++++++ .../Modules/NaturalDocs/Languages/PLSQL.pm | 320 ++ .../Modules/NaturalDocs/Languages/Pascal.pm | 144 + .../Modules/NaturalDocs/Languages/Perl.pm | 1371 ++++++ .../NaturalDocs/Languages/Prototype.pm | 93 + .../Languages/Prototype/Parameter.pm | 88 + .../Modules/NaturalDocs/Languages/Simple.pm | 487 +++ .../Modules/NaturalDocs/Languages/Tcl.pm | 220 + .../Modules/NaturalDocs/LineReader.pm | 166 + .../naturaldocs/Modules/NaturalDocs/Menu.pm | 3405 +++++++++++++++ .../Modules/NaturalDocs/Menu/Entry.pm | 202 + .../Modules/NaturalDocs/NDMarkup.pm | 77 + .../naturaldocs/Modules/NaturalDocs/Parser.pm | 1335 ++++++ .../Modules/NaturalDocs/Parser/JavaDoc.pm | 465 ++ .../Modules/NaturalDocs/Parser/Native.pm | 1073 +++++ .../Modules/NaturalDocs/Parser/ParsedTopic.pm | 254 ++ .../Modules/NaturalDocs/Project.pm | 1404 ++++++ .../Modules/NaturalDocs/Project/ImageFile.pm | 161 + .../Modules/NaturalDocs/Project/SourceFile.pm | 114 + .../Modules/NaturalDocs/ReferenceString.pm | 335 ++ .../Modules/NaturalDocs/Settings.pm | 1480 +++++++ .../NaturalDocs/Settings/BuildTarget.pm | 67 + .../Modules/NaturalDocs/SourceDB.pm | 679 +++ .../Modules/NaturalDocs/SourceDB/Extension.pm | 85 + .../Modules/NaturalDocs/SourceDB/File.pm | 130 + .../Modules/NaturalDocs/SourceDB/Item.pm | 202 + .../NaturalDocs/SourceDB/ItemDefinition.pm | 46 + .../SourceDB/WatchedFileDefinitions.pm | 160 + .../Modules/NaturalDocs/StatusMessage.pm | 103 + .../Modules/NaturalDocs/SymbolString.pm | 213 + .../Modules/NaturalDocs/SymbolTable.pm | 1985 +++++++++ .../Modules/NaturalDocs/SymbolTable/File.pm | 187 + .../NaturalDocs/SymbolTable/IndexElement.pm | 523 +++ .../NaturalDocs/SymbolTable/Reference.pm | 274 ++ .../SymbolTable/ReferenceTarget.pm | 98 + .../Modules/NaturalDocs/SymbolTable/Symbol.pm | 429 ++ .../SymbolTable/SymbolDefinition.pm | 97 + .../naturaldocs/Modules/NaturalDocs/Topics.pm | 1320 ++++++ .../Modules/NaturalDocs/Topics/Type.pm | 152 + .../Modules/NaturalDocs/Version.pm | 361 ++ vendor/naturaldocs/NaturalDocs | 367 ++ vendor/naturaldocs/NaturalDocs.bat | 17 + vendor/naturaldocs/Styles/Default.css | 828 ++++ vendor/naturaldocs/Styles/Roman.css | 826 ++++ vendor/naturaldocs/Styles/Small.css | 824 ++++ 232 files changed, 53383 insertions(+), 1087 deletions(-) create mode 100644 Data/ClassHierarchy.nd create mode 100644 Data/ConfigFileInfo.nd create mode 100644 Data/FileInfo.nd create mode 100644 Data/ImageFileInfo.nd create mode 100644 Data/ImageReferenceTable.nd create mode 100644 Data/IndexInfo.nd create mode 100644 Data/PreviousMenuState.nd create mode 100644 Data/PreviousSettings.nd create mode 100644 Data/SymbolTable.nd create mode 100644 Languages.txt create mode 100644 Menu.txt create mode 100644 Theme.css create mode 100644 Topics.txt create mode 100644 docs/files/blob-h.html create mode 100644 docs/files/error-h.html create mode 100644 docs/index.html create mode 100644 docs/index/Classes.html create mode 100644 docs/index/Functions.html create mode 100644 docs/index/General.html create mode 100644 docs/index/Variables.html create mode 100644 docs/javascript/main.js create mode 100644 docs/javascript/prettify.js create mode 100644 docs/javascript/searchdata.js create mode 100644 docs/search/ClassesG.html create mode 100644 docs/search/ClassesL.html create mode 100644 docs/search/FunctionsC.html create mode 100644 docs/search/FunctionsE.html create mode 100644 docs/search/FunctionsG.html create mode 100644 docs/search/FunctionsI.html create mode 100644 docs/search/FunctionsL.html create mode 100644 docs/search/FunctionsN.html create mode 100644 docs/search/FunctionsR.html create mode 100644 docs/search/FunctionsS.html create mode 100644 docs/search/GeneralB.html create mode 100644 docs/search/GeneralC.html create mode 100644 docs/search/GeneralE.html create mode 100644 docs/search/GeneralF.html create mode 100644 docs/search/GeneralG.html create mode 100644 docs/search/GeneralI.html create mode 100644 docs/search/GeneralL.html create mode 100644 docs/search/GeneralN.html create mode 100644 docs/search/GeneralR.html create mode 100644 docs/search/GeneralS.html create mode 100644 docs/search/GeneralV.html create mode 100644 docs/search/NoResults.html create mode 100644 docs/search/VariablesB.html create mode 100644 docs/search/VariablesC.html create mode 100644 docs/styles/main.css create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/HEAD create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/config create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/description create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/info/exclude create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/objects/info/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/HEAD create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/config create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/description create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/info/exclude create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt create mode 100644 vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt create mode 100644 vendor/naturaldocs/Config/Languages.txt create mode 100644 vendor/naturaldocs/Config/Topics.txt create mode 100644 vendor/naturaldocs/Help/customizinglanguages.html create mode 100644 vendor/naturaldocs/Help/customizingtopics.html create mode 100644 vendor/naturaldocs/Help/documenting.html create mode 100644 vendor/naturaldocs/Help/documenting/reference.html create mode 100644 vendor/naturaldocs/Help/documenting/walkthrough.html create mode 100644 vendor/naturaldocs/Help/example/Default.css create mode 100644 vendor/naturaldocs/Help/example/NaturalDocs.js create mode 100644 vendor/naturaldocs/Help/example/Roman.css create mode 100644 vendor/naturaldocs/Help/example/Small.css create mode 100644 vendor/naturaldocs/Help/example/showstyle.html create mode 100644 vendor/naturaldocs/Help/examples.css create mode 100644 vendor/naturaldocs/Help/images/header/background.png create mode 100644 vendor/naturaldocs/Help/images/header/leftside.png create mode 100644 vendor/naturaldocs/Help/images/header/logo.png create mode 100644 vendor/naturaldocs/Help/images/header/overbody.png create mode 100644 vendor/naturaldocs/Help/images/header/overbodybg.png create mode 100644 vendor/naturaldocs/Help/images/header/overleftmargin.png create mode 100644 vendor/naturaldocs/Help/images/header/overmenu.png create mode 100644 vendor/naturaldocs/Help/images/header/overmenubg.png create mode 100644 vendor/naturaldocs/Help/images/header/rightside.png create mode 100644 vendor/naturaldocs/Help/images/menu/about.png create mode 100644 vendor/naturaldocs/Help/images/menu/background.png create mode 100644 vendor/naturaldocs/Help/images/menu/bottomleft.png create mode 100644 vendor/naturaldocs/Help/images/menu/bottomright.png create mode 100644 vendor/naturaldocs/Help/images/menu/community.png create mode 100644 vendor/naturaldocs/Help/images/menu/customizing.png create mode 100644 vendor/naturaldocs/Help/images/menu/using.png create mode 100644 vendor/naturaldocs/Help/index.html create mode 100644 vendor/naturaldocs/Help/javascript/BrowserStyles.js create mode 100644 vendor/naturaldocs/Help/javascript/PNGHandling.js create mode 100644 vendor/naturaldocs/Help/keywords.html create mode 100644 vendor/naturaldocs/Help/languages.html create mode 100644 vendor/naturaldocs/Help/menu.html create mode 100644 vendor/naturaldocs/Help/output.html create mode 100644 vendor/naturaldocs/Help/running.html create mode 100644 vendor/naturaldocs/Help/styles.css create mode 100644 vendor/naturaldocs/Help/styles.html create mode 100644 vendor/naturaldocs/Help/troubleshooting.html create mode 100644 vendor/naturaldocs/Info/CSSGuide.txt create mode 100644 vendor/naturaldocs/Info/File Parsing.txt create mode 100644 vendor/naturaldocs/Info/HTMLTestCases.pm create mode 100644 vendor/naturaldocs/Info/Languages.txt create mode 100644 vendor/naturaldocs/Info/NDMarkup.txt create mode 100644 vendor/naturaldocs/Info/Symbol Management.txt create mode 100644 vendor/naturaldocs/Info/images/Logo.png create mode 100644 vendor/naturaldocs/JavaScript/GooglePrettify.js create mode 100644 vendor/naturaldocs/JavaScript/NaturalDocs.js create mode 100644 vendor/naturaldocs/License.txt create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/BinaryFile.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Builder.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Builder/Base.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Builder/FramedHTML.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Builder/HTML.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Builder/HTMLBase.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/Class.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/File.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ConfigFile.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Constants.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/DefineMembers.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Error.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/File.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/String.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/ActionScript.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Ada.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Base.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/CSharp.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/PLSQL.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Pascal.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Perl.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Simple.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Languages/Tcl.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/LineReader.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Menu.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Menu/Entry.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/NDMarkup.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Parser.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Parser/JavaDoc.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Parser/Native.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Parser/ParsedTopic.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Project.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Project/ImageFile.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Project/SourceFile.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/ReferenceString.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Settings.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Settings/BuildTarget.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Extension.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB/File.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Item.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/StatusMessage.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolString.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/File.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Reference.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Symbol.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Topics.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Topics/Type.pm create mode 100644 vendor/naturaldocs/Modules/NaturalDocs/Version.pm create mode 100755 vendor/naturaldocs/NaturalDocs create mode 100644 vendor/naturaldocs/NaturalDocs.bat create mode 100644 vendor/naturaldocs/Styles/Default.css create mode 100644 vendor/naturaldocs/Styles/Roman.css create mode 100644 vendor/naturaldocs/Styles/Small.css diff --git a/Data/ClassHierarchy.nd b/Data/ClassHierarchy.nd new file mode 100644 index 0000000000000000000000000000000000000000..162a7f59ba7dafc3838b3409fcf6d0ae91b7181f GIT binary patch literal 175 zcmZQ$G-hC6U}WIS$5lEi6qfE&+?F=x5~Trs|hu=IWDFoWZ004&yH6;K5 literal 0 HcmV?d00001 diff --git a/Data/ConfigFileInfo.nd b/Data/ConfigFileInfo.nd new file mode 100644 index 0000000000000000000000000000000000000000..005a6aa2552ad5274c2e779fa4cbc3d58b905311 GIT binary patch literal 26 ScmZQ$G-hC6@SQ57jE(_AMFrph literal 0 HcmV?d00001 diff --git a/Data/FileInfo.nd b/Data/FileInfo.nd new file mode 100644 index 000000000..2fe726f5b --- /dev/null +++ b/Data/FileInfo.nd @@ -0,0 +1,4 @@ +1.51 +C/C++ +/home/tim/git/nodegit/include/blob.h 1301617421 1 GitBlob +/home/tim/git/nodegit/include/error.h 1301617768 1 GitError diff --git a/Data/ImageFileInfo.nd b/Data/ImageFileInfo.nd new file mode 100644 index 0000000000000000000000000000000000000000..b6cb43bc50d6a1723dadb0392ba74e8f4c83d110 GIT binary patch literal 8 McmZQ$G-dz+00D6TI{*Lx literal 0 HcmV?d00001 diff --git a/Data/ImageReferenceTable.nd b/Data/ImageReferenceTable.nd new file mode 100644 index 0000000000000000000000000000000000000000..b6cb43bc50d6a1723dadb0392ba74e8f4c83d110 GIT binary patch literal 8 McmZQ$G-dz+00D6TI{*Lx literal 0 HcmV?d00001 diff --git a/Data/IndexInfo.nd b/Data/IndexInfo.nd new file mode 100644 index 0000000000000000000000000000000000000000..5c9e0d47bd933d2811bb5c569d1a54eacdc36058 GIT binary patch literal 154 zcmZQ$G-dz-cK6i0)S|>3Ad`_1h=3T#0udlSm}X$$a4XGAF3HT#1L*)sqNxH216743 c7G)+T<)i}HAidZDt8-3baWPB|tPD*p04LN7jsO4v literal 0 HcmV?d00001 diff --git a/Data/PreviousMenuState.nd b/Data/PreviousMenuState.nd new file mode 100644 index 0000000000000000000000000000000000000000..a83adbe61fa9836006f83e7edae5618d429111f7 GIT binary patch literal 198 zcmaKjK@Ng25JkUHqZ&4^CI- zJDjxj6Y0d09yw|81C?dz-8_))veVccs2sE*_<-~Of{N7G9jd1c^iepr3x#j8EF-vy^ohMhsP~HbeVM?fb6Hf&iueIlAwA*% literal 0 HcmV?d00001 diff --git a/Data/PreviousSettings.nd b/Data/PreviousSettings.nd new file mode 100644 index 0000000000000000000000000000000000000000..89bac756af185e3036ff5d65ea797af93516539d GIT binary patch literal 82 zcmZQ$G-hC6U;$!AAe7b5$j?pHFUic+PtPpT&&y9q1<{##$vLGdsSJ#Uj0{rPm89e+ M7c;PUg!uXZ03zrXi2wiq literal 0 HcmV?d00001 diff --git a/Data/SymbolTable.nd b/Data/SymbolTable.nd new file mode 100644 index 0000000000000000000000000000000000000000..5aebce9574d6ba2fa1b56771498ed3caa5db87dc GIT binary patch literal 3366 zcmcguZBG+H5ZZ-SNVeB4UD)2%-8~S0kiXNJ z-Fqcqt~G%VSJLawJ~PiV^UP#rnGkY=Ol=CgE>(xz-PqneT|_Hgn^8@yxD-p51E83dR+4`;M6Lb{EDj1F%N8o%u1IZqKUY% zrEi`HBr}FQG*(A}r5B?7X{pdM5t{6FgV);r&o8QWkcp5?$I;nlc)w z!9XN2;iWVRzPmIa3n|wcP6a>*9V(MM1~DNPld{8t6PoaXF!(PXM3yh*2%j9A=Vc_= zpRQqfF(V_a(&tDoW(U{-7rw(B8=_k$ulxZ3h-}4d(4JNU_$rG42&XrN-M|#d%t1`H z0hSj2twvWAkmJOWR-j%d?krMuI}}zhDSq?vp)zH^jg@_h>13g5r|(x*wtz~>KjOc; zPGYOnGJV8toix82-o^4m!TjY;4;!bTh7BMb^y(|H16(d0n|$s!clg;jP(LJ4i_i|3 zjQA>rpfyhp;$726(EzZRmlP12I&mMg@G6rL3M;3d2QCiwg3^bU4~CLiEN;oEk@oSY z9N@298(}O!a`kM-ljU0+ebo!=;+^wJnFGxM1C6Vh|8YT9)B9F?ft6NOCWSd)zM#)^ zgC>D$*b$(>I(fIrtrIlszE+_;r=6$=(}pJLLN^Ccb1DlO`wE z#(S}qMumB8os=3|6|HFOSvP;d)8esrwVll*qQ#k2t~!^GtU{oXhhHUg_R=uC!98;y z6o&9R4(b&;j5-or-4C}PnhnVuK8HBGz=x#hz@UGV8f{+7Q+w=e-_P7~rO5j14z^T$ zyf%t`q{J88qulj*Cek=nK+*CYTbj}=k4Ju9pmQ`ZpEwev^#7w`q9v6XVdkd8;LG88LvpsQw6yxd#Qw;UL_&#<2j#6c_^#ntoAdph#|-Psyo|q zKGBBVqb*>Q96j(BinAESZ91#LJl(c(UmDZgTj!|twp2Ss(&GgDTWeZUsUT;D literal 0 HcmV?d00001 diff --git a/Languages.txt b/Languages.txt new file mode 100644 index 000000000..85d5fde47 --- /dev/null +++ b/Languages.txt @@ -0,0 +1,113 @@ +Format: 1.51 + +# This is the Natural Docs languages file for this project. If you change +# anything here, it will apply to THIS PROJECT ONLY. If you'd like to change +# something for all your projects, edit the Languages.txt in Natural Docs' +# Config directory instead. + + +# You can prevent certain file extensions from being scanned like this: +# Ignore Extensions: [extension] [extension] ... + + +#------------------------------------------------------------------------------- +# SYNTAX: +# +# Unlike other Natural Docs configuration files, in this file all comments +# MUST be alone on a line. Some languages deal with the # character, so you +# cannot put comments on the same line as content. +# +# Also, all lists are separated with spaces, not commas, again because some +# languages may need to use them. +# +# Language: [name] +# Alter Language: [name] +# Defines a new language or alters an existing one. Its name can use any +# characters. If any of the properties below have an add/replace form, you +# must use that when using Alter Language. +# +# The language Shebang Script is special. It's entry is only used for +# extensions, and files with those extensions have their shebang (#!) lines +# read to determine the real language of the file. Extensionless files are +# always treated this way. +# +# The language Text File is also special. It's treated as one big comment +# so you can put Natural Docs content in them without special symbols. Also, +# if you don't specify a package separator, ignored prefixes, or enum value +# behavior, it will copy those settings from the language that is used most +# in the source tree. +# +# Extensions: [extension] [extension] ... +# [Add/Replace] Extensions: [extension] [extension] ... +# Defines the file extensions of the language's source files. You can +# redefine extensions found in the main languages file. You can use * to +# mean any undefined extension. +# +# Shebang Strings: [string] [string] ... +# [Add/Replace] Shebang Strings: [string] [string] ... +# Defines a list of strings that can appear in the shebang (#!) line to +# designate that it's part of the language. You can redefine strings found +# in the main languages file. +# +# Ignore Prefixes in Index: [prefix] [prefix] ... +# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ... +# +# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ... +# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ... +# Specifies prefixes that should be ignored when sorting symbols in an +# index. Can be specified in general or for a specific topic type. +# +#------------------------------------------------------------------------------ +# For basic language support only: +# +# Line Comments: [symbol] [symbol] ... +# Defines a space-separated list of symbols that are used for line comments, +# if any. +# +# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ... +# Defines a space-separated list of symbol pairs that are used for block +# comments, if any. +# +# Package Separator: [symbol] +# Defines the default package separator symbol. The default is a dot. +# +# [Topic Type] Prototype Enders: [symbol] [symbol] ... +# When defined, Natural Docs will attempt to get a prototype from the code +# immediately following the topic type. It stops when it reaches one of +# these symbols. Use \n for line breaks. +# +# Line Extender: [symbol] +# Defines the symbol that allows a prototype to span multiple lines if +# normally a line break would end it. +# +# Enum Values: [global|under type|under parent] +# Defines how enum values are referenced. The default is global. +# global - Values are always global, referenced as 'value'. +# under type - Values are under the enum type, referenced as +# 'package.enum.value'. +# under parent - Values are under the enum's parent, referenced as +# 'package.value'. +# +# Perl Package: [perl package] +# Specifies the Perl package used to fine-tune the language behavior in ways +# too complex to do in this file. +# +#------------------------------------------------------------------------------ +# For full language support only: +# +# Full Language Support: [perl package] +# Specifies the Perl package that has the parsing routines necessary for full +# language support. +# +#------------------------------------------------------------------------------- + +# The following languages are defined in the main file, if you'd like to alter +# them: +# +# Text File, Shebang Script, C/C++, C#, Java, JavaScript, Perl, Python, +# PHP, SQL, Visual Basic, Pascal, Assembly, Ada, Tcl, Ruby, Makefile, +# ActionScript, ColdFusion, R, Fortran + +# If you add a language that you think would be useful to other developers +# and should be included in Natural Docs by default, please e-mail it to +# languages [at] naturaldocs [dot] org. diff --git a/Makefile b/Makefile index 0ac04433d..c421c3cab 100644 --- a/Makefile +++ b/Makefile @@ -5,14 +5,16 @@ NODE_LIB_PATH = ~/.node_libraries BASE = . INSTALL_PATH = $(NODE_LIB_PATH)/nodegit -all: build_bindings +NATURAL_DOCS_PATH = $(BASE)/vendor/naturaldocs/ -update: clean config build_bindings uninstall install +all: build + +update: clean config build uninstall install config: @@$(BASE)/configure -build_bindings: +build: @@$(NODE_BLD) build install: @@ -34,8 +36,13 @@ clean: @@rm -rf $(BASE)/build @@rm -rf $(BASE)/vendor/libgit2/build -unittest: +test: @@$(NODE_JS) $(BASE)/test/index.js test lint: @@$(NODE_JS) $(BASE)/util/hint-check.js + +doc: + @@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/docs -p $(BASE) -s $(BASE)/Theme + +.PHONY: test build diff --git a/Menu.txt b/Menu.txt new file mode 100644 index 000000000..3b7b30960 --- /dev/null +++ b/Menu.txt @@ -0,0 +1,59 @@ +Format: 1.51 + + +# You can add a title and sub-title to your menu like this: +# Title: [project name] +# SubTitle: [subtitle] + +# You can add a footer to your documentation like this: +# Footer: [text] +# If you want to add a copyright notice, this would be the place to do it. + +# You can add a timestamp to your documentation like one of these: +# Timestamp: Generated on month day, year +# Timestamp: Updated mm/dd/yyyy +# Timestamp: Last updated mon day +# +# m - One or two digit month. January is "1" +# mm - Always two digit month. January is "01" +# mon - Short month word. January is "Jan" +# month - Long month word. January is "January" +# d - One or two digit day. 1 is "1" +# dd - Always two digit day. 1 is "01" +# day - Day with letter extension. 1 is "1st" +# yy - Two digit year. 2006 is "06" +# yyyy - Four digit year. 2006 is "2006" +# year - Four digit year. 2006 is "2006" + + +# -------------------------------------------------------------------------- +# +# Cut and paste the lines below to change the order in which your files +# appear on the menu. Don't worry about adding or removing files, Natural +# Docs will take care of that. +# +# You can further organize the menu by grouping the entries. Add a +# "Group: [name] {" line to start a group, and add a "}" to end it. +# +# You can add text and web links to the menu by adding "Text: [text]" and +# "Link: [name] ([URL])" lines, respectively. +# +# The formatting and comments are auto-generated, so don't worry about +# neatness when editing the file. Natural Docs will clean it up the next +# time it is run. When working with groups, just deal with the braces and +# forget about the indentation and comments. +# +# -------------------------------------------------------------------------- + + +File: GitBlob (blob.h) +File: GitError (error.h) + +Group: Index { + + Index: Everything + Class Index: Classes + Function Index: Functions + Variable Index: Variables + } # Group: Index + diff --git a/README.md b/README.md index d6489438d..1d67cf173 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,12 @@ This will install and configure everything you need to use `nodegit`. $ sudo npm install nodegit + $ sudo npm update nodegit + ### Mac OS X/Linux/Unix ### #### Install `nodegit` by cloning source from __GitHub__ and running the `configure`, `make`, and `make install` commands: #### -\*Note: `nodegit` assumes your library path exists at `~/.node_libraries` you can change this by specifying a new path\* +\*Note: `nodegit` assumes your library path exists at `~/.node_libraries` you can change this by specifying a new lib path\* $ git clone git://github.com/tbranyen/nodegit.git $ cd nodegit @@ -62,8 +64,9 @@ API Example Usage if( err ) { throw err; } // Iterate over the revision history - branch.history.each( function( i, commit ) { - + var history = branch.history(); + + history.on( 'commit', function( err, commit ) { // Print out `git log` emulation console.log( 'commit ' + commit.sha ); console.log( commit.author.name + '<' + commit.author.email + '>' ); @@ -72,6 +75,10 @@ API Example Usage console.log( commit.message ); console.log( '\n' ); }); + + history.on( 'end', function( err ) { + + }); }); }); @@ -173,16 +180,22 @@ __ To run unit tests ensure the submodules `nodeunit` and `rimraf` are located i If they are not, `cd` into the `nodegit` dir and run the following `git` commands to automatically fetch them: $ cd nodegit - $ git submodule init vendor/ - $ git submodule update vendor/ + $ git submodule update --init -Then simply run `make unittest` in the project root. +Then simply run `make test` in the project root. Release information ------------------- __ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http://bit.ly/tb_methods) __ +### v0.0.3: ### + * Fully documented native source code + * Reworked convenience API to make development significantly easier + * More unit tests + * Blob write support + * Updated libgit2 to version 0.11.0 + ### v0.0.2: ### * More methods implemented * More unit tests diff --git a/Theme.css b/Theme.css new file mode 100644 index 000000000..111dd6573 --- /dev/null +++ b/Theme.css @@ -0,0 +1,796 @@ +@import('http://fonts.googleapis.com/css?family=EB+Garamond'); + +body { + background-color: #FFFFFF; + font-family: Georgia, sans-serif; + font-size: 14px; + margin: 40px; +} + +a:link, +a:visited { color: #900000; text-decoration: none } +a:hover { color: #900000; text-decoration: underline } +a:active { color: #FF0000; text-decoration: underline } + +td { + vertical-align: top } + +img { border: 0; } + + +/* + Comment out this line to use web-style paragraphs (blank line between + paragraphs, no indent) instead of print-style paragraphs (no blank line, + indented.) +*/ +p { + text-indent: 5ex; margin: 0 } + + +/* Opera doesn't break with just wbr, but will if you add this. */ +.Opera wbr:after { + content: "\00200B"; + } + + +/* Blockquotes are used as containers for things that may need to scroll. */ +blockquote { + padding: 0; + margin: 0; + overflow: auto; + } + + +.Firefox1 blockquote { + padding-bottom: .5em; + } + +/* Turn off scrolling when printing. */ +@media print { + blockquote { + overflow: visible; + } + .IE blockquote { + width: auto; + } + } + + + +#Menu { + padding: 10px 0 0 0; + } +.ContentPage #Menu, +.IndexPage #Menu { + position: absolute; + top: 0; + left: 0; + width: 31ex; + overflow: hidden; + } +.ContentPage .Firefox #Menu, +.IndexPage .Firefox #Menu { + width: 27ex; + } + + + .MTitle { + font-size: 16pt; font-weight: bold; font-variant: small-caps; + text-align: center; + padding: 5px 10px 15px 10px; + border-bottom: 1px dotted #000000; + margin-bottom: 15px } + + .MSubTitle { + font-size: 9pt; font-weight: normal; font-variant: normal; + margin-top: 1ex; margin-bottom: 5px } + + + .MEntry a:link, + .MEntry a:hover, + .MEntry a:visited { color: #606060; margin-right: 0 } + .MEntry a:active { color: #A00000; margin-right: 0 } + + + .MGroup { + font-variant: small-caps; font-weight: bold; + margin: 1em 0 1em 10px; + } + + .MGroupContent { + font-variant: normal; font-weight: normal } + + .MGroup a:link, + .MGroup a:hover, + .MGroup a:visited { color: #545454; margin-right: 10px } + .MGroup a:active { color: #A00000; margin-right: 10px } + + + .MFile, + .MText, + .MLink, + .MIndex { + padding: 1px 17px 2px 10px; + margin: .25em 0 .25em 0; + } + + .MText { + font-size: 8pt; font-style: italic } + + .MLink { + font-style: italic } + + #MSelected { + color: #000000; background-color: #FFFFFF; + /* Replace padding with border. */ + padding: 0 10px 0 10px; + border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000; + margin-right: 5px; + } + + /* Close off the left side when its in a group. */ + .MGroup #MSelected { + padding-left: 9px; border-left-width: 1px } + + /* A treat for Mozilla users. Blatantly non-standard. Will be replaced with CSS 3 attributes when finalized/supported. */ + .Firefox #MSelected { + -moz-border-radius-topright: 10px; + -moz-border-radius-bottomright: 10px } + .Firefox .MGroup #MSelected { + -moz-border-radius-topleft: 10px; + -moz-border-radius-bottomleft: 10px } + + + #MSearchPanel { + padding: 0px 6px; + margin: .25em 0; + } + + + #MSearchField { + font: italic 9pt Verdana, sans-serif; + color: #606060; + background-color: #E8E8E8; + border: none; + padding: 2px 4px; + width: 100%; + } + /* Only Opera gets it right. */ + .Firefox #MSearchField, + .IE #MSearchField, + .Safari #MSearchField { + width: 94%; + } + .Opera9 #MSearchField, + .Konqueror #MSearchField { + width: 97%; + } + .FramedMenuPage .Firefox #MSearchField, + .FramedMenuPage .Safari #MSearchField, + .FramedMenuPage .Konqueror #MSearchField { + width: 98%; + } + + /* Firefox doesn't do this right in frames without #MSearchPanel added on. + It's presence doesn't hurt anything other browsers. */ + #MSearchPanel.MSearchPanelInactive:hover #MSearchField { + background-color: #FFFFFF; + border: 1px solid #C0C0C0; + padding: 1px 3px; + } + .MSearchPanelActive #MSearchField { + background-color: #FFFFFF; + border: 1px solid #C0C0C0; + font-style: normal; + padding: 1px 3px; + } + + #MSearchType { + visibility: hidden; + font: 8pt Verdana, sans-serif; + width: 98%; + padding: 0; + border: 1px solid #C0C0C0; + } + .MSearchPanelActive #MSearchType, + /* As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */ + #MSearchPanel.MSearchPanelInactive:hover #MSearchType, + #MSearchType:focus { + visibility: visible; + color: #606060; + } + #MSearchType option#MSearchEverything { + font-weight: bold; + } + + .Opera8 .MSearchPanelInactive:hover, + .Opera8 .MSearchPanelActive { + margin-left: -1px; + } + + + iframe#MSearchResults { + width: 60ex; + height: 15em; + } + #MSearchResultsWindow { + display: none; + position: absolute; + left: 0; top: 0; + border: 1px solid #000000; + background-color: #E8E8E8; + } + #MSearchResultsWindowClose { + font-weight: bold; + font-size: 8pt; + display: block; + padding: 2px 5px; + } + #MSearchResultsWindowClose:link, + #MSearchResultsWindowClose:visited { + color: #000000; + text-decoration: none; + } + #MSearchResultsWindowClose:active, + #MSearchResultsWindowClose:hover { + color: #800000; + text-decoration: none; + background-color: #F4F4F4; + } + + + + +#Content { + padding-bottom: 15px; + } + +.ContentPage #Content { + border-width: 0 0 1px 1px; + border-style: solid; + border-color: #000000; + background-color: #FFFFFF; + font-size: 9pt; /* To make 31ex match the menu's 31ex. */ + margin-left: 31ex; + } +.ContentPage .Firefox #Content { + margin-left: 27ex; + } + + + + .CTopic { + font-size: 10pt; + margin-bottom: 3em; + } + + + .CTitle { + font-size: 12pt; font-weight: bold; + border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0; + margin: 0 15px .5em 15px } + + .CGroup .CTitle { + font-size: 16pt; font-variant: small-caps; + padding-left: 15px; padding-right: 15px; + border-width: 0 0 2px 0; border-color: #000000; + margin-left: 0; margin-right: 0 } + + .CClass .CTitle, + .CInterface .CTitle, + .CDatabase .CTitle, + .CDatabaseTable .CTitle, + .CSection .CTitle { + font-size: 18pt; + color: #FFFFFF; background-color: #A0A0A0; + padding: 10px 15px 10px 15px; + border-width: 2px 0; border-color: #000000; + margin-left: 0; margin-right: 0 } + + #MainTopic .CTitle { + font-size: 20pt; + color: #FFFFFF; background-color: #7070C0; + padding: 10px 15px 10px 15px; + border-width: 0 0 3px 0; border-color: #000000; + margin-left: 0; margin-right: 0 } + + .CBody { + margin-left: 15px; margin-right: 15px } + + + .CToolTip { + position: absolute; visibility: hidden; + left: 0; top: 0; + background-color: #FFFFE0; + padding: 5px; + border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000; + font-size: 8pt; + } + + .Opera .CToolTip { + max-width: 98%; + } + + /* Scrollbars would be useless. */ + .CToolTip blockquote { + overflow: hidden; + } + .IE6 .CToolTip blockquote { + overflow: visible; + } + + .CHeading { + font-weight: bold; font-size: 10pt; + margin: 1.5em 0 .5em 0; + } + + .CBody pre { + font: 10pt "Courier New", Courier, monospace; + background-color: #FCFCFC; + margin: 1em 35px; + padding: 10px 15px 10px 10px; + border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4; + border-width: 1px 1px 1px 6px; + border-style: dashed dashed dashed solid; + } + + .CBody ul { + /* I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever. + Reapply it here as padding. */ + padding-left: 15px; padding-right: 15px; + margin: .5em 5ex .5em 5ex; + } + + .CDescriptionList { + margin: .5em 5ex 0 5ex } + + .CDLEntry { + font: 10pt "Courier New", Courier, monospace; color: #808080; + padding-bottom: .25em; + white-space: nowrap } + + .CDLDescription { + font-size: 10pt; /* For browsers that don't inherit correctly, like Opera 5. */ + padding-bottom: .5em; padding-left: 5ex } + + + .CTopic img { + text-align: center; + display: block; + margin: 1em auto; + } + .CImageCaption { + font-variant: small-caps; + font-size: 8pt; + color: #808080; + text-align: center; + position: relative; + top: 1em; + } + + .CImageLink { + color: #808080; + font-style: italic; + } + a.CImageLink:link, + a.CImageLink:visited, + a.CImageLink:hover { color: #808080 } + + + + + +.Prototype { + font: 10pt "Courier New", Courier, monospace; + padding: 5px 3ex; + border-width: 1px; border-style: solid; + margin: 0 5ex 1.5em 5ex; + } + + .Prototype td { + font-size: 10pt; + } + + .PDefaultValue, + .PDefaultValuePrefix, + .PTypePrefix { + color: #8F8F8F; + } + .PTypePrefix { + text-align: right; + } + .PAfterParameters { + vertical-align: bottom; + } + + .IE .Prototype table { + padding: 0; + } + + .CFunction .Prototype { + background-color: #F4F4F4; border-color: #D0D0D0 } + .CProperty .Prototype { + background-color: #F4F4FF; border-color: #C0C0E8 } + .CVariable .Prototype { + background-color: #FFFFF0; border-color: #E0E0A0 } + + .CClass .Prototype { + border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0; + background-color: #F4F4F4; + } + .CInterface .Prototype { + border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0; + background-color: #F4F4FF; + } + + .CDatabaseIndex .Prototype, + .CConstant .Prototype { + background-color: #D0D0D0; border-color: #000000 } + .CType .Prototype, + .CEnumeration .Prototype { + background-color: #FAF0F0; border-color: #E0B0B0; + } + .CDatabaseTrigger .Prototype, + .CEvent .Prototype, + .CDelegate .Prototype { + background-color: #F0FCF0; border-color: #B8E4B8 } + + .CToolTip .Prototype { + margin: 0 0 .5em 0; + white-space: nowrap; + } + + + + + +.Summary { + margin: 1.5em 5ex 0 5ex } + + .STitle { + font-size: 12pt; font-weight: bold; + margin-bottom: .5em } + + + .SBorder { + background-color: #FFFFF0; + padding: 15px; + border: 1px solid #C0C060 } + + /* In a frame IE 6 will make them too long unless you set the width to 100%. Without frames it will be correct without a width + or slightly too long (but not enough to scroll) with a width. This arbitrary weirdness simply astounds me. IE 7 has the same + problem with frames, haven't tested it without. */ + .FramedContentPage .IE .SBorder { + width: 100% } + + /* A treat for Mozilla users. Blatantly non-standard. Will be replaced with CSS 3 attributes when finalized/supported. */ + .Firefox .SBorder { + -moz-border-radius: 20px } + + + .STable { + font-size: 9pt; width: 100% } + + .SEntry { + width: 30% } + .SDescription { + width: 70% } + + + .SMarked { + background-color: #F8F8D8 } + + .SDescription { padding-left: 2ex } + .SIndent1 .SEntry { padding-left: 1.5ex } .SIndent1 .SDescription { padding-left: 3.5ex } + .SIndent2 .SEntry { padding-left: 3.0ex } .SIndent2 .SDescription { padding-left: 5.0ex } + .SIndent3 .SEntry { padding-left: 4.5ex } .SIndent3 .SDescription { padding-left: 6.5ex } + .SIndent4 .SEntry { padding-left: 6.0ex } .SIndent4 .SDescription { padding-left: 8.0ex } + .SIndent5 .SEntry { padding-left: 7.5ex } .SIndent5 .SDescription { padding-left: 9.5ex } + + .SDescription a { color: #800000} + .SDescription a:active { color: #A00000 } + + .SGroup td { + padding-top: .5em; padding-bottom: .25em } + + .SGroup .SEntry { + font-weight: bold; font-variant: small-caps } + + .SGroup .SEntry a { color: #800000 } + .SGroup .SEntry a:active { color: #F00000 } + + + .SMain td, + .SClass td, + .SDatabase td, + .SDatabaseTable td, + .SSection td { + font-size: 10pt; + padding-bottom: .25em } + + .SClass td, + .SDatabase td, + .SDatabaseTable td, + .SSection td { + padding-top: 1em } + + .SMain .SEntry, + .SClass .SEntry, + .SDatabase .SEntry, + .SDatabaseTable .SEntry, + .SSection .SEntry { + font-weight: bold; + } + + .SMain .SEntry a, + .SClass .SEntry a, + .SDatabase .SEntry a, + .SDatabaseTable .SEntry a, + .SSection .SEntry a { color: #000000 } + + .SMain .SEntry a:active, + .SClass .SEntry a:active, + .SDatabase .SEntry a:active, + .SDatabaseTable .SEntry a:active, + .SSection .SEntry a:active { color: #A00000 } + + + + + +.ClassHierarchy { + margin: 0 15px 1em 15px } + + .CHEntry { + border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0; + margin-bottom: 3px; + padding: 2px 2ex; + font-size: 10pt; + background-color: #F4F4F4; color: #606060; + } + + .Firefox .CHEntry { + -moz-border-radius: 4px; + } + + .CHCurrent .CHEntry { + font-weight: bold; + border-color: #000000; + color: #000000; + } + + .CHChildNote .CHEntry { + font-style: italic; + font-size: 8pt; + } + + .CHIndent { + margin-left: 3ex; + } + + .CHEntry a:link, + .CHEntry a:visited, + .CHEntry a:hover { + color: #606060; + } + .CHEntry a:active { + color: #800000; + } + + + + + +#Index { + background-color: #FFFFFF; + } + +/* As opposed to .PopupSearchResultsPage #Index */ +.IndexPage #Index, +.FramedIndexPage #Index, +.FramedSearchResultsPage #Index { + padding: 15px; + } + +.IndexPage #Index { + border-width: 0 0 1px 1px; + border-style: solid; + border-color: #000000; + font-size: 9pt; /* To make 27ex match the menu's 27ex. */ + margin-left: 27ex; + } + + + .IPageTitle { + font-size: 20pt; font-weight: bold; + color: #FFFFFF; background-color: #7070C0; + padding: 10px 15px 10px 15px; + border-width: 0 0 3px 0; border-color: #000000; border-style: solid; + margin: -15px -15px 0 -15px } + + .FramedSearchResultsPage .IPageTitle { + margin-bottom: 15px; + } + + .INavigationBar { + font-size: 10pt; + text-align: center; + background-color: #FFFFF0; + padding: 5px; + border-bottom: solid 1px black; + margin: 0 -15px 15px -15px; + } + + .INavigationBar a { + font-weight: bold } + + .IHeading { + font-size: 16pt; font-weight: bold; + padding: 2.5em 0 .5em 0; + text-align: center; + width: 3.5ex; + } + #IFirstHeading { + padding-top: 0; + } + + .IEntry { + font-size: 10pt; + padding-left: 1ex; + } + .PopupSearchResultsPage .IEntry { + font-size: 8pt; + padding: 1px 5px; + } + .PopupSearchResultsPage .Opera9 .IEntry, + .FramedSearchResultsPage .Opera9 .IEntry { + text-align: left; + } + .FramedSearchResultsPage .IEntry { + padding: 0; + } + + .ISubIndex { + padding-left: 3ex; padding-bottom: .5em } + .PopupSearchResultsPage .ISubIndex { + display: none; + } + + /* While it may cause some entries to look like links when they aren't, I found it's much easier to read the + index if everything's the same color. */ + .ISymbol { + font-weight: bold; color: #900000 } + + .IndexPage .ISymbolPrefix, + .FramedIndexPage .ISymbolPrefix { + font-size: 10pt; + text-align: right; + color: #C47C7C; + background-color: #F8F8F8; + border-right: 3px solid #E0E0E0; + border-left: 1px solid #E0E0E0; + padding: 0 1px 0 2px; + } + .PopupSearchResultsPage .ISymbolPrefix, + .FramedSearchResultsPage .ISymbolPrefix { + color: #900000; + } + .PopupSearchResultsPage .ISymbolPrefix { + font-size: 8pt; + } + + .IndexPage #IFirstSymbolPrefix, + .FramedIndexPage #IFirstSymbolPrefix { + border-top: 1px solid #E0E0E0; + } + .IndexPage #ILastSymbolPrefix, + .FramedIndexPage #ILastSymbolPrefix { + border-bottom: 1px solid #E0E0E0; + } + .IndexPage #IOnlySymbolPrefix, + .FramedIndexPage #IOnlySymbolPrefix { + border-top: 1px solid #E0E0E0; + border-bottom: 1px solid #E0E0E0; + } + + a.IParent, + a.IFile { + display: block; + } + + .PopupSearchResultsPage .SRStatus { + padding: 2px 5px; + font-size: 8pt; + font-style: italic; + } + .FramedSearchResultsPage .SRStatus { + font-size: 10pt; + font-style: italic; + } + + .SRResult { + display: none; + } + + + +#Footer { + font-size: 8pt; + color: #989898; + text-align: right; + } + +#Footer p { + text-indent: 0; + margin-bottom: .5em; + } + +.ContentPage #Footer, +.IndexPage #Footer { + text-align: right; + margin: 2px; + } + +.FramedMenuPage #Footer { + text-align: center; + margin: 5em 10px 10px 10px; + padding-top: 1em; + border-top: 1px solid #C8C8C8; + } + + #Footer a:link, + #Footer a:hover, + #Footer a:visited { color: #989898 } + #Footer a:active { color: #A00000 } + + + +.prettyprint .kwd { color: #800000; } /* keywords */ + + .prettyprint.PDefaultValue .kwd, + .prettyprint.PDefaultValuePrefix .kwd, + .prettyprint.PTypePrefix .kwd { + color: #C88F8F; + } + +.prettyprint .com { color: #008000; } /* comments */ + + .prettyprint.PDefaultValue .com, + .prettyprint.PDefaultValuePrefix .com, + .prettyprint.PTypePrefix .com { + color: #8FC88F; + } + +.prettyprint .str { color: #0000B0; } /* strings */ +.prettyprint .lit { color: #0000B0; } /* literals */ + + .prettyprint.PDefaultValue .str, + .prettyprint.PDefaultValuePrefix .str, + .prettyprint.PTypePrefix .str, + .prettyprint.PDefaultValue .lit, + .prettyprint.PDefaultValuePrefix .lit, + .prettyprint.PTypePrefix .lit { + color: #8F8FC0; + } + +.prettyprint .typ { color: #000000; } /* types */ +.prettyprint .pun { color: #000000; } /* punctuation */ +.prettyprint .pln { color: #000000; } /* punctuation */ + + .prettyprint.PDefaultValue .typ, + .prettyprint.PDefaultValuePrefix .typ, + .prettyprint.PTypePrefix .typ, + .prettyprint.PDefaultValue .pun, + .prettyprint.PDefaultValuePrefix .pun, + .prettyprint.PTypePrefix .pun, + .prettyprint.PDefaultValue .pln, + .prettyprint.PDefaultValuePrefix .pln, + .prettyprint.PTypePrefix .pln { + color: #8F8F8F; + } + +.prettyprint .tag { color: #008; } +.prettyprint .atn { color: #606; } +.prettyprint .atv { color: #080; } +.prettyprint .dec { color: #606; } + diff --git a/Topics.txt b/Topics.txt new file mode 100644 index 000000000..21530908d --- /dev/null +++ b/Topics.txt @@ -0,0 +1,81 @@ +Format: 1.51 + +# This is the Natural Docs topics file for this project. If you change anything +# here, it will apply to THIS PROJECT ONLY. If you'd like to change something +# for all your projects, edit the Topics.txt in Natural Docs' Config directory +# instead. + + +# If you'd like to prevent keywords from being recognized by Natural Docs, you +# can do it like this: +# Ignore Keywords: [keyword], [keyword], ... +# +# Or you can use the list syntax like how they are defined: +# Ignore Keywords: +# [keyword] +# [keyword], [plural keyword] +# ... + + +#------------------------------------------------------------------------------- +# SYNTAX: +# +# Topic Type: [name] +# Alter Topic Type: [name] +# Creates a new topic type or alters one from the main file. Each type gets +# its own index and behavior settings. Its name can have letters, numbers, +# spaces, and these charaters: - / . ' +# +# Plural: [name] +# Sets the plural name of the topic type, if different. +# +# Keywords: +# [keyword] +# [keyword], [plural keyword] +# ... +# Defines or adds to the list of keywords for the topic type. They may only +# contain letters, numbers, and spaces and are not case sensitive. Plural +# keywords are used for list topics. You can redefine keywords found in the +# main topics file. +# +# Index: [yes|no] +# Whether the topics get their own index. Defaults to yes. Everything is +# included in the general index regardless of this setting. +# +# Scope: [normal|start|end|always global] +# How the topics affects scope. Defaults to normal. +# normal - Topics stay within the current scope. +# start - Topics start a new scope for all the topics beneath it, +# like class topics. +# end - Topics reset the scope back to global for all the topics +# beneath it. +# always global - Topics are defined as global, but do not change the scope +# for any other topics. +# +# Class Hierarchy: [yes|no] +# Whether the topics are part of the class hierarchy. Defaults to no. +# +# Page Title If First: [yes|no] +# Whether the topic's title becomes the page title if it's the first one in +# a file. Defaults to no. +# +# Break Lists: [yes|no] +# Whether list topics should be broken into individual topics in the output. +# Defaults to no. +# +# Can Group With: [type], [type], ... +# Defines a list of topic types that this one can possibly be grouped with. +# Defaults to none. +#------------------------------------------------------------------------------- + +# The following topics are defined in the main file, if you'd like to alter +# their behavior or add keywords: +# +# Generic, Class, Interface, Section, File, Group, Function, Variable, +# Property, Type, Constant, Enumeration, Event, Delegate, Macro, +# Database, Database Table, Database View, Database Index, Database +# Cursor, Database Trigger, Cookie, Build Target + +# If you add something that you think would be useful to other developers +# and should be included in Natural Docs by default, please e-mail it to +# topics [at] naturaldocs [dot] org. diff --git a/docs/files/blob-h.html b/docs/files/blob-h.html new file mode 100644 index 000000000..b4330c3f6 --- /dev/null +++ b/docs/files/blob-h.html @@ -0,0 +1,84 @@ + + +GitBlob + + + + + + + + + +

GitBlob

class GitBlob : public ObjectWrap

Wrapper for libgit2 git_blob.

Summary
GitBlobWrapper for libgit2 git_blob.
Variables
constructor_templateUsed to create Node.js constructor.
Functions
InitializeUsed to intialize the EventEmitter from Node.js
LookupLookup a blob object from a repository.
RawContentGet a read-only buffer with the raw content of a blob.
RawSizeLookup a blob object from a repository.
CloseFree a blob object.
CreateFromFileRead a file into the ODB.
CreateFromBufferRead a buffer into the ODB.
GitBlob
Newargs v8::Arguments function call
Lookupargs v8::Arguments function call
EIO_Lookup
EIO_AfterLookup
RawContentargs v8::Arguments function call
RawSizeargs v8::Arguments function call
Closeargs v8::Arguments function call
CreateFromFileargs v8::Arguments function call
CreateFromBufferargs v8::Arguments function call
Variables
blobInternal reference to git_blob object
lookup_requestContains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.
+ +

Variables

+ +

constructor_template

static v8::Persistent<v8::FunctionTemplate> constructor_template

Used to create Node.js constructor.

+ +

Functions

+ +

Initialize

static void Initialize(v8::Handle<v8::Object> target)

Used to intialize the EventEmitter from Node.js

Parameters

targetv8::Object the Node.js global module object
+ +

Lookup

int Lookup(git_repository *repo,
const git_oid *id)

Lookup a blob object from a repository.

Parameters

repo the repo to use when locating the blob. id identity of the blob to locate.

Returns

0 on success; error code otherwise

+ +

RawContent

const void* RawContent()

Get a read-only buffer with the raw content of a blob.

Returns

raw content buffer; NULL if the blob has no contents

+ +

RawSize

int RawSize()

Lookup a blob object from a repository.

Returns

size in bytes

+ +

Close

void Close()

Free a blob object.

+ +

CreateFromFile

int CreateFromFile(git_oid *oid,
git_repository *repo,
const char *path)

Read a file into the ODB.

Returns

0 on success, error code otherwise

+ +

CreateFromBuffer

Read a buffer into the ODB.

Returns

0 on success, error code otherwise

+ +

GitBlob

GitBlob()
+ +

New

static v8::Handle<v8::Value> New(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

Lookup

static v8::Handle<v8::Value> Lookup(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

EIO_Lookup

static int EIO_Lookup(eio_req *req)

Parameters

reqan eio_req pointer

Returns

completion code integer

+ +

EIO_AfterLookup

static int EIO_AfterLookup(eio_req *req)

Parameters

reqan eio_req pointer

Returns

completion code integer

+ +

RawContent

static v8::Handle<v8::Value> RawContent(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

RawSize

static v8::Handle<v8::Value> RawSize(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

Close

static v8::Handle<v8::Value> Close(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

CreateFromFile

static v8::Handle<v8::Value> CreateFromFile(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

CreateFromBuffer

static v8::Handle<v8::Value> CreateFromBuffer(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

Variables

+ +

blob

git_blob* blob

Internal reference to git_blob object

+ +

lookup_request

struct lookup_request

Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.

+ +
+ + + + + + + + + + +
class GitBlob : public ObjectWrap
Wrapper for libgit2 git_blob.
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
int Lookup(git_repository *repo,
const git_oid *id)
Lookup a blob object from a repository.
const void* RawContent()
Get a read-only buffer with the raw content of a blob.
int RawSize()
Lookup a blob object from a repository.
void Close()
Free a blob object.
int CreateFromFile(git_oid *oid,
git_repository *repo,
const char *path)
Read a file into the ODB.
GitBlob()
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
static int EIO_Lookup(eio_req *req)
static int EIO_AfterLookup(eio_req *req)
static v8::Handle<v8::Value> CreateFromBuffer(const v8::Arguments &args)
args v8::Arguments function call
git_blob* blob
Internal reference to git_blob object
struct lookup_request
Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.
+ + + + + + + + \ No newline at end of file diff --git a/docs/files/error-h.html b/docs/files/error-h.html new file mode 100644 index 000000000..bbbb2667d --- /dev/null +++ b/docs/files/error-h.html @@ -0,0 +1,54 @@ + + +GitError + + + + + + + + + +

GitError

class GitError : public ObjectWrap

Wrapper for libgit2 git_error.

Summary
GitErrorWrapper for libgit2 git_error.
Variables
constructor_templateUsed to create Node.js constructor.
Functions
InitializeUsed to intialize the EventEmitter from Node.js
StrErrorGet a read-only buffer with the raw content of a blob.
GitError
Newargs v8::Arguments function call
StrErrorargs v8::Arguments function call
+ +

Variables

+ +

constructor_template

static v8::Persistent<v8::FunctionTemplate> constructor_template

Used to create Node.js constructor.

+ +

Functions

+ +

Initialize

static void Initialize(v8::Handle<v8::Object> target)

Used to intialize the EventEmitter from Node.js

Parameters

targetv8::Object the Node.js global module object
+ +

StrError

const char* StrError(int err)

Get a read-only buffer with the raw content of a blob.

Parameters

errA signed int error code

Returns

a string explaining the error code.

+ +

GitError

GitError()
+ +

New

static v8::Handle<v8::Value> New(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +

StrError

static v8::Handle<v8::Value> StrError(const v8::Arguments &args)

Parameters

args v8::Arguments function call

Returns

v8::Object args.This()

+ +
+ + + + + + + + + + +
class GitError : public ObjectWrap
Wrapper for libgit2 git_error.
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
const char* StrError(int err)
Get a read-only buffer with the raw content of a blob.
GitError()
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
+ + + + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..e39a046d3 --- /dev/null +++ b/docs/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/index/Classes.html b/docs/index/Classes.html new file mode 100644 index 000000000..73f8ef707 --- /dev/null +++ b/docs/index/Classes.html @@ -0,0 +1,37 @@ + + +Class Index + + + + + + + + + +
Class Index
$#! · 0-9 · A · B · C · D · E · F · G · H · I · J · K · L · M · N · O · P · Q · R · S · T · U · V · W · X · Y · Z
G
 GitBlob
 GitError
L
 lookup_request
+ +
class GitBlob : public ObjectWrap
Wrapper for libgit2 git_blob.
class GitError : public ObjectWrap
Wrapper for libgit2 git_error.
+ + + +
struct lookup_request
Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/index/Functions.html b/docs/index/Functions.html new file mode 100644 index 000000000..e62b55581 --- /dev/null +++ b/docs/index/Functions.html @@ -0,0 +1,61 @@ + + +Function Index + + + + + + + + + +
Function Index
$#! · 0-9 · A · B · C · D · E · F · G · H · I · J · K · L · M · N · O · P · Q · R · S · T · U · V · W · X · Y · Z
C
 Close, GitBlob
 CreateFromBuffer, GitBlob
 CreateFromFile, GitBlob
E
 EIO_AfterLookup, GitBlob
 EIO_Lookup, GitBlob
G
 GitBlob, GitBlob
 GitError, GitError
I
 Initialize
L
 Lookup, GitBlob
N
 New
R
 RawContent, GitBlob
 RawSize, GitBlob
S
 StrError, GitError
+ +
void Close()
Free a blob object.
Read a buffer into the ODB.
int CreateFromFile(git_oid *oid,
git_repository *repo,
const char *path)
Read a file into the ODB.
+ + + +
static int EIO_AfterLookup(eio_req *req)
static int EIO_Lookup(eio_req *req)
+ + + +
GitBlob()
GitError()
+ + + +
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
+ + + +
int Lookup(git_repository *repo,
const git_oid *id)
Lookup a blob object from a repository.
+ + + +
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
+ + + +
const void* RawContent()
Get a read-only buffer with the raw content of a blob.
int RawSize()
Lookup a blob object from a repository.
+ + + +
const char* StrError(int err)
Get a read-only buffer with the raw content of a blob.
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/index/General.html b/docs/index/General.html new file mode 100644 index 000000000..c803fd514 --- /dev/null +++ b/docs/index/General.html @@ -0,0 +1,73 @@ + + +Index + + + + + + + + + +
Index
$#! · 0-9 · A · B · C · D · E · F · G · H · I · J · K · L · M · N · O · P · Q · R · S · T · U · V · W · X · Y · Z
B
 blob, GitBlob
C
 Close, GitBlob
 constructor_template
 CreateFromBuffer, GitBlob
 CreateFromFile, GitBlob
E
 EIO_AfterLookup, GitBlob
 EIO_Lookup, GitBlob
F
 Functions
G
 GitBlob
 GitError
I
 Initialize
L
 Lookup, GitBlob
 lookup_request
N
 New
R
 RawContent, GitBlob
 RawSize, GitBlob
S
 StrError, GitError
V
 Variables
+ +
git_blob* blob
Internal reference to git_blob object
+ + + +
void Close()
Free a blob object.
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
Read a buffer into the ODB.
int CreateFromFile(git_oid *oid,
git_repository *repo,
const char *path)
Read a file into the ODB.
+ + + +
static int EIO_AfterLookup(eio_req *req)
static int EIO_Lookup(eio_req *req)
+ + + + + + + +
class GitBlob : public ObjectWrap
Wrapper for libgit2 git_blob.
GitBlob()
class GitError : public ObjectWrap
Wrapper for libgit2 git_error.
GitError()
+ + + +
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
static void Initialize(v8::Handle<v8::Object> target)
Used to intialize the EventEmitter from Node.js
+ + + +
int Lookup(git_repository *repo,
const git_oid *id)
Lookup a blob object from a repository.
struct lookup_request
Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.
+ + + +
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
static v8::Handle<v8::Value> New(const v8::Arguments &args)
args v8::Arguments function call
+ + + +
const void* RawContent()
Get a read-only buffer with the raw content of a blob.
int RawSize()
Lookup a blob object from a repository.
+ + + +
const char* StrError(int err)
Get a read-only buffer with the raw content of a blob.
+ + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/index/Variables.html b/docs/index/Variables.html new file mode 100644 index 000000000..6a2d37b45 --- /dev/null +++ b/docs/index/Variables.html @@ -0,0 +1,37 @@ + + +Variable Index + + + + + + + + + +
Variable Index
$#! · 0-9 · A · B · C · D · E · F · G · H · I · J · K · L · M · N · O · P · Q · R · S · T · U · V · W · X · Y · Z
B
 blob, GitBlob
C
 constructor_template
+ +
git_blob* blob
Internal reference to git_blob object
+ + + +
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
static v8::Persistent<v8::FunctionTemplate> constructor_template
Used to create Node.js constructor.
+ +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/javascript/main.js b/docs/javascript/main.js new file mode 100644 index 000000000..3f42acde6 --- /dev/null +++ b/docs/javascript/main.js @@ -0,0 +1,841 @@ +// This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure +// Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL) +// Refer to License.txt for the complete details + +// This file may be distributed with documentation files generated by Natural Docs. +// Such documentation is not covered by Natural Docs' copyright and licensing, +// and may have its own copyright and distribution terms as decided by its author. + + +// +// Browser Styles +// ____________________________________________________________________________ + +var agt=navigator.userAgent.toLowerCase(); +var browserType; +var browserVer; + +if (agt.indexOf("opera") != -1) + { + browserType = "Opera"; + + if (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1) + { browserVer = "Opera7"; } + else if (agt.indexOf("opera 8") != -1 || agt.indexOf("opera/8") != -1) + { browserVer = "Opera8"; } + else if (agt.indexOf("opera 9") != -1 || agt.indexOf("opera/9") != -1) + { browserVer = "Opera9"; } + } + +else if (agt.indexOf("applewebkit") != -1) + { + browserType = "Safari"; + + if (agt.indexOf("version/3") != -1) + { browserVer = "Safari3"; } + else if (agt.indexOf("safari/4") != -1) + { browserVer = "Safari2"; } + } + +else if (agt.indexOf("khtml") != -1) + { + browserType = "Konqueror"; + } + +else if (agt.indexOf("msie") != -1) + { + browserType = "IE"; + + if (agt.indexOf("msie 6") != -1) + { browserVer = "IE6"; } + else if (agt.indexOf("msie 7") != -1) + { browserVer = "IE7"; } + } + +else if (agt.indexOf("gecko") != -1) + { + browserType = "Firefox"; + + if (agt.indexOf("rv:1.7") != -1) + { browserVer = "Firefox1"; } + else if (agt.indexOf("rv:1.8)") != -1 || agt.indexOf("rv:1.8.0") != -1) + { browserVer = "Firefox15"; } + else if (agt.indexOf("rv:1.8.1") != -1) + { browserVer = "Firefox2"; } + } + + +// +// Support Functions +// ____________________________________________________________________________ + + +function GetXPosition(item) + { + var position = 0; + + if (item.offsetWidth != null) + { + while (item != document.body && item != null) + { + position += item.offsetLeft; + item = item.offsetParent; + }; + }; + + return position; + }; + + +function GetYPosition(item) + { + var position = 0; + + if (item.offsetWidth != null) + { + while (item != document.body && item != null) + { + position += item.offsetTop; + item = item.offsetParent; + }; + }; + + return position; + }; + + +function MoveToPosition(item, x, y) + { + // Opera 5 chokes on the px extension, so it can use the Microsoft one instead. + + if (item.style.left != null) + { + item.style.left = x + "px"; + item.style.top = y + "px"; + } + else if (item.style.pixelLeft != null) + { + item.style.pixelLeft = x; + item.style.pixelTop = y; + }; + }; + + +// +// Menu +// ____________________________________________________________________________ + + +function ToggleMenu(id) + { + if (!window.document.getElementById) + { return; }; + + var display = window.document.getElementById(id).style.display; + + if (display == "none") + { display = "block"; } + else + { display = "none"; } + + window.document.getElementById(id).style.display = display; + } + +function HideAllBut(ids, max) + { + if (document.getElementById) + { + ids.sort( function(a,b) { return a - b; } ); + var number = 1; + + while (number < max) + { + if (ids.length > 0 && number == ids[0]) + { ids.shift(); } + else + { + document.getElementById("MGroupContent" + number).style.display = "none"; + }; + + number++; + }; + }; + } + + +// +// Tooltips +// ____________________________________________________________________________ + + +var tooltipTimer = 0; + +function ShowTip(event, tooltipID, linkID) + { + if (tooltipTimer) + { clearTimeout(tooltipTimer); }; + + var docX = event.clientX + window.pageXOffset; + var docY = event.clientY + window.pageYOffset; + + var showCommand = "ReallyShowTip('" + tooltipID + "', '" + linkID + "', " + docX + ", " + docY + ")"; + + tooltipTimer = setTimeout(showCommand, 1000); + } + +function ReallyShowTip(tooltipID, linkID, docX, docY) + { + tooltipTimer = 0; + + var tooltip; + var link; + + if (document.getElementById) + { + tooltip = document.getElementById(tooltipID); + link = document.getElementById(linkID); + } +/* else if (document.all) + { + tooltip = eval("document.all['" + tooltipID + "']"); + link = eval("document.all['" + linkID + "']"); + } +*/ + if (tooltip) + { + var left = GetXPosition(link); + var top = GetYPosition(link); + top += link.offsetHeight; + + + // The fallback method is to use the mouse X and Y relative to the document. We use a separate if and test if its a number + // in case some browser snuck through the above if statement but didn't support everything. + + if (!isFinite(top) || top == 0) + { + left = docX; + top = docY; + } + + // Some spacing to get it out from under the cursor. + + top += 10; + + // Make sure the tooltip doesnt get smushed by being too close to the edge, or in some browsers, go off the edge of the + // page. We do it here because Konqueror does get offsetWidth right even if it doesnt get the positioning right. + + if (tooltip.offsetWidth != null) + { + var width = tooltip.offsetWidth; + var docWidth = document.body.clientWidth; + + if (left + width > docWidth) + { left = docWidth - width - 1; } + + // If there's a horizontal scroll bar we could go past zero because it's using the page width, not the window width. + if (left < 0) + { left = 0; }; + } + + MoveToPosition(tooltip, left, top); + tooltip.style.visibility = "visible"; + } + } + +function HideTip(tooltipID) + { + if (tooltipTimer) + { + clearTimeout(tooltipTimer); + tooltipTimer = 0; + } + + var tooltip; + + if (document.getElementById) + { tooltip = document.getElementById(tooltipID); } + else if (document.all) + { tooltip = eval("document.all['" + tooltipID + "']"); } + + if (tooltip) + { tooltip.style.visibility = "hidden"; } + } + + +// +// Blockquote fix for IE +// ____________________________________________________________________________ + + +function NDOnLoad() + { + if (browserVer == "IE6") + { + var scrollboxes = document.getElementsByTagName('blockquote'); + + if (scrollboxes.item(0)) + { + NDDoResize(); + window.onresize=NDOnResize; + }; + }; + }; + + +var resizeTimer = 0; + +function NDOnResize() + { + if (resizeTimer != 0) + { clearTimeout(resizeTimer); }; + + resizeTimer = setTimeout(NDDoResize, 250); + }; + + +function NDDoResize() + { + var scrollboxes = document.getElementsByTagName('blockquote'); + + var i; + var item; + + i = 0; + while (item = scrollboxes.item(i)) + { + item.style.width = 100; + i++; + }; + + i = 0; + while (item = scrollboxes.item(i)) + { + item.style.width = item.parentNode.offsetWidth; + i++; + }; + + clearTimeout(resizeTimer); + resizeTimer = 0; + } + + + +/* ________________________________________________________________________________________________________ + + Class: SearchPanel + ________________________________________________________________________________________________________ + + A class handling everything associated with the search panel. + + Parameters: + + name - The name of the global variable that will be storing this instance. Is needed to be able to set timeouts. + mode - The mode the search is going to work in. Pass CommandLineOption()>, so the + value will be something like "HTML" or "FramedHTML". + + ________________________________________________________________________________________________________ +*/ + + +function SearchPanel(name, mode, resultsPath) + { + if (!name || !mode || !resultsPath) + { alert("Incorrect parameters to SearchPanel."); }; + + + // Group: Variables + // ________________________________________________________________________ + + /* + var: name + The name of the global variable that will be storing this instance of the class. + */ + this.name = name; + + /* + var: mode + The mode the search is going to work in, such as "HTML" or "FramedHTML". + */ + this.mode = mode; + + /* + var: resultsPath + The relative path from the current HTML page to the results page directory. + */ + this.resultsPath = resultsPath; + + /* + var: keyTimeout + The timeout used between a keystroke and when a search is performed. + */ + this.keyTimeout = 0; + + /* + var: keyTimeoutLength + The length of in thousandths of a second. + */ + this.keyTimeoutLength = 500; + + /* + var: lastSearchValue + The last search string executed, or an empty string if none. + */ + this.lastSearchValue = ""; + + /* + var: lastResultsPage + The last results page. The value is only relevant if is set. + */ + this.lastResultsPage = ""; + + /* + var: deactivateTimeout + + The timeout used between when a control is deactivated and when the entire panel is deactivated. Is necessary + because a control may be deactivated in favor of another control in the same panel, in which case it should stay + active. + */ + this.deactivateTimout = 0; + + /* + var: deactivateTimeoutLength + The length of in thousandths of a second. + */ + this.deactivateTimeoutLength = 200; + + + + + // Group: DOM Elements + // ________________________________________________________________________ + + + // Function: DOMSearchField + this.DOMSearchField = function() + { return document.getElementById("MSearchField"); }; + + // Function: DOMSearchType + this.DOMSearchType = function() + { return document.getElementById("MSearchType"); }; + + // Function: DOMPopupSearchResults + this.DOMPopupSearchResults = function() + { return document.getElementById("MSearchResults"); }; + + // Function: DOMPopupSearchResultsWindow + this.DOMPopupSearchResultsWindow = function() + { return document.getElementById("MSearchResultsWindow"); }; + + // Function: DOMSearchPanel + this.DOMSearchPanel = function() + { return document.getElementById("MSearchPanel"); }; + + + + + // Group: Event Handlers + // ________________________________________________________________________ + + + /* + Function: OnSearchFieldFocus + Called when focus is added or removed from the search field. + */ + this.OnSearchFieldFocus = function(isActive) + { + this.Activate(isActive); + }; + + + /* + Function: OnSearchFieldChange + Called when the content of the search field is changed. + */ + this.OnSearchFieldChange = function() + { + if (this.keyTimeout) + { + clearTimeout(this.keyTimeout); + this.keyTimeout = 0; + }; + + var searchValue = this.DOMSearchField().value.replace(/ +/g, ""); + + if (searchValue != this.lastSearchValue) + { + if (searchValue != "") + { + this.keyTimeout = setTimeout(this.name + ".Search()", this.keyTimeoutLength); + } + else + { + if (this.mode == "HTML") + { this.DOMPopupSearchResultsWindow().style.display = "none"; }; + this.lastSearchValue = ""; + }; + }; + }; + + + /* + Function: OnSearchTypeFocus + Called when focus is added or removed from the search type. + */ + this.OnSearchTypeFocus = function(isActive) + { + this.Activate(isActive); + }; + + + /* + Function: OnSearchTypeChange + Called when the search type is changed. + */ + this.OnSearchTypeChange = function() + { + var searchValue = this.DOMSearchField().value.replace(/ +/g, ""); + + if (searchValue != "") + { + this.Search(); + }; + }; + + + + // Group: Action Functions + // ________________________________________________________________________ + + + /* + Function: CloseResultsWindow + Closes the results window. + */ + this.CloseResultsWindow = function() + { + this.DOMPopupSearchResultsWindow().style.display = "none"; + this.Activate(false, true); + }; + + + /* + Function: Search + Performs a search. + */ + this.Search = function() + { + this.keyTimeout = 0; + + var searchValue = this.DOMSearchField().value.replace(/^ +/, ""); + var searchTopic = this.DOMSearchType().value; + + var pageExtension = searchValue.substr(0,1); + + if (pageExtension.match(/^[a-z]/i)) + { pageExtension = pageExtension.toUpperCase(); } + else if (pageExtension.match(/^[0-9]/)) + { pageExtension = 'Numbers'; } + else + { pageExtension = "Symbols"; }; + + var resultsPage; + var resultsPageWithSearch; + var hasResultsPage; + + // indexSectionsWithContent is defined in searchdata.js + if (indexSectionsWithContent[searchTopic][pageExtension] == true) + { + resultsPage = this.resultsPath + '/' + searchTopic + pageExtension + '.html'; + resultsPageWithSearch = resultsPage+'?'+escape(searchValue); + hasResultsPage = true; + } + else + { + resultsPage = this.resultsPath + '/NoResults.html'; + resultsPageWithSearch = resultsPage; + hasResultsPage = false; + }; + + var resultsFrame; + if (this.mode == "HTML") + { resultsFrame = window.frames.MSearchResults; } + else if (this.mode == "FramedHTML") + { resultsFrame = window.top.frames['Content']; }; + + + if (resultsPage != this.lastResultsPage || + + // Bug in IE. If everything becomes hidden in a run, none of them will be able to be reshown in the next for some + // reason. It counts the right number of results, and you can even read the display as "block" after setting it, but it + // just doesn't work in IE 6 or IE 7. So if we're on the right page but the previous search had no results, reload the + // page anyway to get around the bug. + (browserType == "IE" && hasResultsPage && + (!resultsFrame.searchResults || resultsFrame.searchResults.lastMatchCount == 0)) ) + + { + resultsFrame.location.href = resultsPageWithSearch; + } + + // So if the results page is right and there's no IE bug, reperform the search on the existing page. We have to check if there + // are results because NoResults.html doesn't have any JavaScript, and it would be useless to do anything on that page even + // if it did. + else if (hasResultsPage) + { + // We need to check if this exists in case the frame is present but didn't finish loading. + if (resultsFrame.searchResults) + { resultsFrame.searchResults.Search(searchValue); } + + // Otherwise just reload instead of waiting. + else + { resultsFrame.location.href = resultsPageWithSearch; }; + }; + + + var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow(); + + if (this.mode == "HTML" && domPopupSearchResultsWindow.style.display != "block") + { + var domSearchType = this.DOMSearchType(); + + var left = GetXPosition(domSearchType); + var top = GetYPosition(domSearchType) + domSearchType.offsetHeight; + + MoveToPosition(domPopupSearchResultsWindow, left, top); + domPopupSearchResultsWindow.style.display = 'block'; + }; + + + this.lastSearchValue = searchValue; + this.lastResultsPage = resultsPage; + }; + + + + // Group: Activation Functions + // Functions that handle whether the entire panel is active or not. + // ________________________________________________________________________ + + + /* + Function: Activate + + Activates or deactivates the search panel, resetting things to their default values if necessary. You can call this on every + control's OnBlur() and it will handle not deactivating the entire panel when focus is just switching between them transparently. + + Parameters: + + isActive - Whether you're activating or deactivating the panel. + ignoreDeactivateDelay - Set if you're positive the action will deactivate the panel and thus want to skip the delay. + */ + this.Activate = function(isActive, ignoreDeactivateDelay) + { + // We want to ignore isActive being false while the results window is open. + if (isActive || (this.mode == "HTML" && this.DOMPopupSearchResultsWindow().style.display == "block")) + { + if (this.inactivateTimeout) + { + clearTimeout(this.inactivateTimeout); + this.inactivateTimeout = 0; + }; + + this.DOMSearchPanel().className = 'MSearchPanelActive'; + + var searchField = this.DOMSearchField(); + + if (searchField.value == 'Search') + { searchField.value = ""; } + } + else if (!ignoreDeactivateDelay) + { + this.inactivateTimeout = setTimeout(this.name + ".InactivateAfterTimeout()", this.inactivateTimeoutLength); + } + else + { + this.InactivateAfterTimeout(); + }; + }; + + + /* + Function: InactivateAfterTimeout + + Called by , which is set by . Inactivation occurs on a timeout because a control may + receive OnBlur() when focus is really transferring to another control in the search panel. In this case we don't want to + actually deactivate the panel because not only would that cause a visible flicker but it could also reset the search value. + So by doing it on a timeout instead, there's a short period where the second control's OnFocus() can cancel the deactivation. + */ + this.InactivateAfterTimeout = function() + { + this.inactivateTimeout = 0; + + this.DOMSearchPanel().className = 'MSearchPanelInactive'; + this.DOMSearchField().value = "Search"; + + this.lastSearchValue = ""; + this.lastResultsPage = ""; + }; + }; + + + + +/* ________________________________________________________________________________________________________ + + Class: SearchResults + _________________________________________________________________________________________________________ + + The class that handles everything on the search results page. + _________________________________________________________________________________________________________ +*/ + + +function SearchResults(name, mode) + { + /* + var: mode + The mode the search is going to work in, such as "HTML" or "FramedHTML". + */ + this.mode = mode; + + /* + var: lastMatchCount + The number of matches from the last run of . + */ + this.lastMatchCount = 0; + + + /* + Function: Toggle + Toggles the visibility of the passed element ID. + */ + this.Toggle = function(id) + { + if (this.mode == "FramedHTML") + { return; }; + + var parentElement = document.getElementById(id); + + var element = parentElement.firstChild; + + while (element && element != parentElement) + { + if (element.nodeName == 'DIV' && element.className == 'ISubIndex') + { + if (element.style.display == 'block') + { element.style.display = "none"; } + else + { element.style.display = 'block'; } + }; + + if (element.nodeName == 'DIV' && element.hasChildNodes()) + { element = element.firstChild; } + else if (element.nextSibling) + { element = element.nextSibling; } + else + { + do + { + element = element.parentNode; + } + while (element && element != parentElement && !element.nextSibling); + + if (element && element != parentElement) + { element = element.nextSibling; }; + }; + }; + }; + + + /* + Function: Search + + Searches for the passed string. If there is no parameter, it takes it from the URL query. + + Always returns true, since other documents may try to call it and that may or may not be possible. + */ + this.Search = function(search) + { + if (!search) + { + search = window.location.search; + search = search.substring(1); // Remove the leading ? + search = unescape(search); + }; + + search = search.replace(/^ +/, ""); + search = search.replace(/ +$/, ""); + search = search.toLowerCase(); + + if (search.match(/[^a-z0-9]/)) // Just a little speedup so it doesn't have to go through the below unnecessarily. + { + search = search.replace(/\_/g, "_und"); + search = search.replace(/\ +/gi, "_spc"); + search = search.replace(/\~/g, "_til"); + search = search.replace(/\!/g, "_exc"); + search = search.replace(/\@/g, "_att"); + search = search.replace(/\#/g, "_num"); + search = search.replace(/\$/g, "_dol"); + search = search.replace(/\%/g, "_pct"); + search = search.replace(/\^/g, "_car"); + search = search.replace(/\&/g, "_amp"); + search = search.replace(/\*/g, "_ast"); + search = search.replace(/\(/g, "_lpa"); + search = search.replace(/\)/g, "_rpa"); + search = search.replace(/\-/g, "_min"); + search = search.replace(/\+/g, "_plu"); + search = search.replace(/\=/g, "_equ"); + search = search.replace(/\{/g, "_lbc"); + search = search.replace(/\}/g, "_rbc"); + search = search.replace(/\[/g, "_lbk"); + search = search.replace(/\]/g, "_rbk"); + search = search.replace(/\:/g, "_col"); + search = search.replace(/\;/g, "_sco"); + search = search.replace(/\"/g, "_quo"); + search = search.replace(/\'/g, "_apo"); + search = search.replace(/\/g, "_ran"); + search = search.replace(/\,/g, "_com"); + search = search.replace(/\./g, "_per"); + search = search.replace(/\?/g, "_que"); + search = search.replace(/\//g, "_sla"); + search = search.replace(/[^a-z0-9\_]i/gi, "_zzz"); + }; + + var resultRows = document.getElementsByTagName("div"); + var matches = 0; + + var i = 0; + while (i < resultRows.length) + { + var row = resultRows.item(i); + + if (row.className == "SRResult") + { + var rowMatchName = row.id.toLowerCase(); + rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); + + if (search.length <= rowMatchName.length && rowMatchName.substr(0, search.length) == search) + { + row.style.display = "block"; + matches++; + } + else + { row.style.display = "none"; }; + }; + + i++; + }; + + document.getElementById("Searching").style.display="none"; + + if (matches == 0) + { document.getElementById("NoMatches").style.display="block"; } + else + { document.getElementById("NoMatches").style.display="none"; } + + this.lastMatchCount = matches; + + return true; + }; + }; + diff --git a/docs/javascript/prettify.js b/docs/javascript/prettify.js new file mode 100644 index 000000000..fda4bf1ed --- /dev/null +++ b/docs/javascript/prettify.js @@ -0,0 +1,1526 @@ + +// This code comes from the December 2009 release of Google Prettify, which is Copyright © 2006 Google Inc. +// Minor modifications are marked with "ND Change" comments. +// As part of Natural Docs, this code is licensed under version 3 of the GNU Affero General Public License (AGPL.) +// However, it may also be obtained separately under version 2.0 of the Apache License. +// Refer to License.txt for the complete details + + +// Main code +// ____________________________________________________________________________ + +// Copyright (C) 2006 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview + * some functions for browser-side pretty printing of code contained in html. + *

+ * + * For a fairly comprehensive set of languages see the + * README + * file that came with this source. At a minimum, the lexer should work on a + * number of languages including C and friends, Java, Python, Bash, SQL, HTML, + * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk + * and a subset of Perl, but, because of commenting conventions, doesn't work on + * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. + *

+ * Usage:

    + *
  1. include this source file in an html page via + * {@code } + *
  2. define style rules. See the example page for examples. + *
  3. mark the {@code
    } and {@code } tags in your source with
    + *    {@code class=prettyprint.}
    + *    You can also use the (html deprecated) {@code } tag, but the pretty
    + *    printer needs to do more substantial DOM manipulations to support that, so
    + *    some css styles may not be preserved.
    + * </ol>
    + * That's it.  I wanted to keep the API as simple as possible, so there's no
    + * need to specify which language the code is in, but if you wish, you can add
    + * another class to the {@code <pre>} or {@code <code>} element to specify the
    + * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    + * starts with "lang-" followed by a file extension, specifies the file type.
    + * See the "lang-*.js" files in this directory for code that implements
    + * per-language file handlers.
    + * <p>
    + * Change log:<br>
    + * cbeust, 2006/08/22
    + * <blockquote>
    + *   Java annotations (start with "@") are now captured as literals ("lit")
    + * </blockquote>
    + * @requires console
    + * @overrides window
    + */
    +
    +// JSLint declarations
    +/*global console, document, navigator, setTimeout, window */
    +
    +/**
    + * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    + * UI events.
    + * If set to {@code false}, {@code prettyPrint()} is synchronous.
    + */
    +window['PR_SHOULD_USE_CONTINUATION'] = true;
    +
    +/** the number of characters between tab columns */
    +window['PR_TAB_WIDTH'] = 8;
    +
    +/** Walks the DOM returning a properly escaped version of innerHTML.
    +  * @param {Node} node
    +  * @param {Array.<string>} out output buffer that receives chunks of HTML.
    +  */
    +window['PR_normalizedHtml']
    +
    +/** Contains functions for creating and registering new language handlers.
    +  * @type {Object}
    +  */
    +  = window['PR']
    +
    +/** Pretty print a chunk of code.
    +  *
    +  * @param {string} sourceCodeHtml code as html
    +  * @return {string} code as html, but prettier
    +  */
    +  = window['prettyPrintOne']
    +/** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +  * {@code class=prettyprint} and prettify them.
    +  * @param {Function?} opt_whenDone if specified, called when the last entry
    +  *     has been finished.
    +  */
    +  = window['prettyPrint'] = void 0;
    +
    +/** browser detection. @extern @returns false if not IE, otherwise the major version. */
    +window['_pr_isIE6'] = function () {
    +  var ieVersion = navigator && navigator.userAgent &&
    +      navigator.userAgent.match(/\bMSIE ([678])\./);
    +  ieVersion = ieVersion ? +ieVersion[1] : false;
    +  window['_pr_isIE6'] = function () { return ieVersion; };
    +  return ieVersion;
    +};
    +
    +
    +(function () {
    +  // Keyword lists for various languages.
    +  var FLOW_CONTROL_KEYWORDS =
    +      "break continue do else for if return while ";
    +  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
    +      "double enum extern float goto int long register short signed sizeof " +
    +      "static struct switch typedef union unsigned void volatile ";
    +  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
    +      "new operator private protected public this throw true try typeof ";
    +  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
    +      "concept concept_map const_cast constexpr decltype " +
    +      "dynamic_cast explicit export friend inline late_check " +
    +      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
    +      "template typeid typename using virtual wchar_t where ";
    +  var JAVA_KEYWORDS = COMMON_KEYWORDS +
    +      "abstract boolean byte extends final finally implements import " +
    +      "instanceof null native package strictfp super synchronized throws " +
    +      "transient ";
    +  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
    +      "as base by checked decimal delegate descending event " +
    +      "fixed foreach from group implicit in interface internal into is lock " +
    +      "object out override orderby params partial readonly ref sbyte sealed " +
    +      "stackalloc string select uint ulong unchecked unsafe ushort var ";
    +  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
    +      "debugger eval export function get null set undefined var with " +
    +      "Infinity NaN ";
    +  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
    +      "goto if import last local my next no our print package redo require " +
    +      "sub undef unless until use wantarray while BEGIN END ";
    +  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
    +      "elif except exec finally from global import in is lambda " +
    +      "nonlocal not or pass print raise try with yield " +
    +      "False True None ";
    +  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
    +      " defined elsif end ensure false in module next nil not or redo rescue " +
    +      "retry self super then true undef unless until when yield BEGIN END ";
    +  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
    +      "function in local set then until ";
    +  var ALL_KEYWORDS = (
    +      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
    +      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
    +
    +  // token style names.  correspond to css classes
    +  /** token style for a string literal */
    +  var PR_STRING = 'str';
    +  /** token style for a keyword */
    +  var PR_KEYWORD = 'kwd';
    +  /** token style for a comment */
    +  var PR_COMMENT = 'com';
    +  /** token style for a type */
    +  var PR_TYPE = 'typ';
    +  /** token style for a literal value.  e.g. 1, null, true. */
    +  var PR_LITERAL = 'lit';
    +  /** token style for a punctuation string. */
    +  var PR_PUNCTUATION = 'pun';
    +  /** token style for a punctuation string. */
    +  var PR_PLAIN = 'pln';
    +
    +  /** token style for an sgml tag. */
    +  var PR_TAG = 'tag';
    +  /** token style for a markup declaration such as a DOCTYPE. */
    +  var PR_DECLARATION = 'dec';
    +  /** token style for embedded source. */
    +  var PR_SOURCE = 'src';
    +  /** token style for an sgml attribute name. */
    +  var PR_ATTRIB_NAME = 'atn';
    +  /** token style for an sgml attribute value. */
    +  var PR_ATTRIB_VALUE = 'atv';
    +
    +  /**
    +   * A class that indicates a section of markup that is not code, e.g. to allow
    +   * embedding of line numbers within code listings.
    +   */
    +  var PR_NOCODE = 'nocode';
    +
    +  /** A set of tokens that can precede a regular expression literal in
    +    * javascript.
    +    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
    +    * list, but I've removed ones that might be problematic when seen in
    +    * languages that don't support regular expression literals.
    +    *
    +    * <p>Specifically, I've removed any keywords that can't precede a regexp
    +    * literal in a syntactically legal javascript program, and I've removed the
    +    * "in" keyword since it's not a keyword in many languages, and might be used
    +    * as a count of inches.
    +    *
    +    * <p>The link a above does not accurately describe EcmaScript rules since
    +    * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    +    * very well in practice.
    +    *
    +    * @private
    +    */
    +  var REGEXP_PRECEDER_PATTERN = function () {
    +      var preceders = [
    +          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
    +          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
    +          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
    +          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
    +          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
    +          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
    +          "||=", "~" /* handles =~ and !~ */,
    +          "break", "case", "continue", "delete",
    +          "do", "else", "finally", "instanceof",
    +          "return", "throw", "try", "typeof"
    +          ];
    +      var pattern = '(?:^^|[+-]';
    +      for (var i = 0; i < preceders.length; ++i) {
    +        pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
    +      }
    +      pattern += ')\\s*';  // matches at end, and matches empty string
    +      return pattern;
    +      // CAVEAT: this does not properly handle the case where a regular
    +      // expression immediately follows another since a regular expression may
    +      // have flags for case-sensitivity and the like.  Having regexp tokens
    +      // adjacent is not valid in any language I'm aware of, so I'm punting.
    +      // TODO: maybe style special characters inside a regexp as punctuation.
    +    }();
    +
    +  // Define regexps here so that the interpreter doesn't have to create an
    +  // object each time the function containing them is called.
    +  // The language spec requires a new object created even if you don't access
    +  // the $1 members.
    +  var pr_amp = /&/g;
    +  var pr_lt = /</g;
    +  var pr_gt = />/g;
    +  var pr_quot = /\"/g;
    +  /** like textToHtml but escapes double quotes to be attribute safe. */
    +  function attribToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;')
    +        .replace(pr_quot, '&quot;');
    +  }
    +
    +  /** escapest html special characters to html. */
    +  function textToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;');
    +  }
    +
    +
    +  var pr_ltEnt = /&lt;/g;
    +  var pr_gtEnt = /&gt;/g;
    +  var pr_aposEnt = /&apos;/g;
    +  var pr_quotEnt = /&quot;/g;
    +  var pr_ampEnt = /&amp;/g;
    +  var pr_nbspEnt = /&nbsp;/g;
    +  /** unescapes html to plain text. */
    +  function htmlToText(html) {
    +    var pos = html.indexOf('&');
    +    if (pos < 0) { return html; }
    +    // Handle numeric entities specially.  We can't use functional substitution
    +    // since that doesn't work in older versions of Safari.
    +    // These should be rare since most browsers convert them to normal chars.
    +    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
    +      var end = html.indexOf(';', pos);
    +      if (end >= 0) {
    +        var num = html.substring(pos + 3, end);
    +        var radix = 10;
    +        if (num && num.charAt(0) === 'x') {
    +          num = num.substring(1);
    +          radix = 16;
    +        }
    +        var codePoint = parseInt(num, radix);
    +        if (!isNaN(codePoint)) {
    +          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
    +                  html.substring(end + 1));
    +        }
    +      }
    +    }
    +
    +    return html.replace(pr_ltEnt, '<')
    +        .replace(pr_gtEnt, '>')
    +        .replace(pr_aposEnt, "'")
    +        .replace(pr_quotEnt, '"')
    +        .replace(pr_nbspEnt, ' ')
    +        .replace(pr_ampEnt, '&');
    +  }
    +
    +  /** is the given node's innerHTML normally unescaped? */
    +  function isRawContent(node) {
    +    return 'XMP' === node.tagName;
    +  }
    +
    +  var newlineRe = /[\r\n]/g;
    +  /**
    +   * Are newlines and adjacent spaces significant in the given node's innerHTML?
    +   */
    +  function isPreformatted(node, content) {
    +    // PRE means preformatted, and is a very common case, so don't create
    +    // unnecessary computed style objects.
    +    if ('PRE' === node.tagName) { return true; }
    +    if (!newlineRe.test(content)) { return true; }  // Don't care
    +    var whitespace = '';
    +    // For disconnected nodes, IE has no currentStyle.
    +    if (node.currentStyle) {
    +      whitespace = node.currentStyle.whiteSpace;
    +    } else if (window.getComputedStyle) {
    +      // Firefox makes a best guess if node is disconnected whereas Safari
    +      // returns the empty string.
    +      whitespace = window.getComputedStyle(node, null).whiteSpace;
    +    }
    +    return !whitespace || whitespace === 'pre';
    +  }
    +
    +  function normalizedHtml(node, out) {
    +    switch (node.nodeType) {
    +      case 1:  // an element
    +        var name = node.tagName.toLowerCase();
    +        out.push('<', name);
    +        for (var i = 0; i < node.attributes.length; ++i) {
    +          var attr = node.attributes[i];
    +          if (!attr.specified) { continue; }
    +          out.push(' ');
    +          normalizedHtml(attr, out);
    +        }
    +        out.push('>');
    +        for (var child = node.firstChild; child; child = child.nextSibling) {
    +          normalizedHtml(child, out);
    +        }
    +        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
    +          out.push('<\/', name, '>');
    +        }
    +        break;
    +      case 2: // an attribute
    +        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
    +        break;
    +      case 3: case 4: // text
    +        out.push(textToHtml(node.nodeValue));
    +        break;
    +    }
    +  }
    +
    +  /**
    +   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    +   * matches the union o the sets o strings matched d by the input RegExp.
    +   * Since it matches globally, if the input strings have a start-of-input
    +   * anchor (/^.../), it is ignored for the purposes of unioning.
    +   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    +   * @return {RegExp} a global regex.
    +   */
    +  function combinePrefixPatterns(regexs) {
    +    var capturedGroupIndex = 0;
    +
    +    var needToFoldCase = false;
    +    var ignoreCase = false;
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.ignoreCase) {
    +        ignoreCase = true;
    +      } else if (/[a-z]/i.test(regex.source.replace(
    +                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    +        needToFoldCase = true;
    +        ignoreCase = false;
    +        break;
    +      }
    +    }
    +
    +    function decodeEscape(charsetPart) {
    +      if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
    +      switch (charsetPart.charAt(1)) {
    +        case 'b': return 8;
    +        case 't': return 9;
    +        case 'n': return 0xa;
    +        case 'v': return 0xb;
    +        case 'f': return 0xc;
    +        case 'r': return 0xd;
    +        case 'u': case 'x':
    +          return parseInt(charsetPart.substring(2), 16)
    +              || charsetPart.charCodeAt(1);
    +        case '0': case '1': case '2': case '3': case '4':
    +        case '5': case '6': case '7':
    +          return parseInt(charsetPart.substring(1), 8);
    +        default: return charsetPart.charCodeAt(1);
    +      }
    +    }
    +
    +    function encodeEscape(charCode) {
    +      if (charCode < 0x20) {
    +        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    +      }
    +      var ch = String.fromCharCode(charCode);
    +      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
    +        ch = '\\' + ch;
    +      }
    +      return ch;
    +    }
    +
    +    function caseFoldCharset(charSet) {
    +      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    +          new RegExp(
    +              '\\\\u[0-9A-Fa-f]{4}'
    +              + '|\\\\x[0-9A-Fa-f]{2}'
    +              + '|\\\\[0-3][0-7]{0,2}'
    +              + '|\\\\[0-7]{1,2}'
    +              + '|\\\\[\\s\\S]'
    +              + '|-'
    +              + '|[^-\\\\]',
    +              'g'));
    +      var groups = [];
    +      var ranges = [];
    +      var inverse = charsetParts[0] === '^';
    +      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    +        var p = charsetParts[i];
    +        switch (p) {
    +          case '\\B': case '\\b':
    +          case '\\D': case '\\d':
    +          case '\\S': case '\\s':
    +          case '\\W': case '\\w':
    +            groups.push(p);
    +            continue;
    +        }
    +        var start = decodeEscape(p);
    +        var end;
    +        if (i + 2 < n && '-' === charsetParts[i + 1]) {
    +          end = decodeEscape(charsetParts[i + 2]);
    +          i += 2;
    +        } else {
    +          end = start;
    +        }
    +        ranges.push([start, end]);
    +        // If the range might intersect letters, then expand it.
    +        if (!(end < 65 || start > 122)) {
    +          if (!(end < 65 || start > 90)) {
    +            ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    +          }
    +          if (!(end < 97 || start > 122)) {
    +            ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    +          }
    +        }
    +      }
    +
    +      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    +      // -> [[1, 12], [14, 14], [16, 17]]
    +      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    +      var consolidatedRanges = [];
    +      var lastRange = [NaN, NaN];
    +      for (var i = 0; i < ranges.length; ++i) {
    +        var range = ranges[i];
    +        if (range[0] <= lastRange[1] + 1) {
    +          lastRange[1] = Math.max(lastRange[1], range[1]);
    +        } else {
    +          consolidatedRanges.push(lastRange = range);
    +        }
    +      }
    +
    +      var out = ['['];
    +      if (inverse) { out.push('^'); }
    +      out.push.apply(out, groups);
    +      for (var i = 0; i < consolidatedRanges.length; ++i) {
    +        var range = consolidatedRanges[i];
    +        out.push(encodeEscape(range[0]));
    +        if (range[1] > range[0]) {
    +          if (range[1] + 1 > range[0]) { out.push('-'); }
    +          out.push(encodeEscape(range[1]));
    +        }
    +      }
    +      out.push(']');
    +      return out.join('');
    +    }
    +
    +    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    +      // Split into character sets, escape sequences, punctuation strings
    +      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    +      // include any of the above.
    +      var parts = regex.source.match(
    +          new RegExp(
    +              '(?:'
    +              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    +              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    +              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    +              + '|\\\\[0-9]+'  // a back-reference or octal escape
    +              + '|\\\\[^ux0-9]'  // other escape sequence
    +              + '|\\(\\?[:!=]'  // start of a non-capturing group
    +              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
    +              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    +              + ')',
    +              'g'));
    +      var n = parts.length;
    +
    +      // Maps captured group numbers to the number they will occupy in
    +      // the output or to -1 if that has not been determined, or to
    +      // undefined if they need not be capturing in the output.
    +      var capturedGroups = [];
    +
    +      // Walk over and identify back references to build the capturedGroups
    +      // mapping.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          // groups are 1-indexed, so max group index is count of '('
    +          ++groupIndex;
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            capturedGroups[decimalValue] = -1;
    +          }
    +        }
    +      }
    +
    +      // Renumber groups and reduce capturing groups to non-capturing groups
    +      // where possible.
    +      for (var i = 1; i < capturedGroups.length; ++i) {
    +        if (-1 === capturedGroups[i]) {
    +          capturedGroups[i] = ++capturedGroupIndex;
    +        }
    +      }
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          ++groupIndex;
    +          if (capturedGroups[groupIndex] === undefined) {
    +            parts[i] = '(?:';
    +          }
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            parts[i] = '\\' + capturedGroups[groupIndex];
    +          }
    +        }
    +      }
    +
    +      // Remove any prefix anchors so that the output will match anywhere.
    +      // ^^ really does mean an anchored match though.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    +      }
    +
    +      // Expand letters to groupts to handle mixing of case-sensitive and
    +      // case-insensitive patterns if necessary.
    +      if (regex.ignoreCase && needToFoldCase) {
    +        for (var i = 0; i < n; ++i) {
    +          var p = parts[i];
    +          var ch0 = p.charAt(0);
    +          if (p.length >= 2 && ch0 === '[') {
    +            parts[i] = caseFoldCharset(p);
    +          } else if (ch0 !== '\\') {
    +            // TODO: handle letters in numeric escapes.
    +            parts[i] = p.replace(
    +                /[a-zA-Z]/g,
    +                function (ch) {
    +                  var cc = ch.charCodeAt(0);
    +                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    +                });
    +          }
    +        }
    +      }
    +
    +      return parts.join('');
    +    }
    +
    +    var rewritten = [];
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    +      rewritten.push(
    +          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    +    }
    +
    +    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    +  }
    +
    +  var PR_innerHtmlWorks = null;
    +  function getInnerHtml(node) {
    +    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    +    // an html description of well formed XML and the containing tag is a PRE
    +    // tag, so we detect that case and emulate innerHTML.
    +    if (null === PR_innerHtmlWorks) {
    +      var testNode = document.createElement('PRE');
    +      testNode.appendChild(
    +          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
    +      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    +    }
    +
    +    if (PR_innerHtmlWorks) {
    +      var content = node.innerHTML;
    +      // XMP tags contain unescaped entities so require special handling.
    +      if (isRawContent(node)) {
    +        content = textToHtml(content);
    +      } else if (!isPreformatted(node, content)) {
    +        content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1')
    +            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
    +      }
    +      return content;
    +    }
    +
    +    var out = [];
    +    for (var child = node.firstChild; child; child = child.nextSibling) {
    +      normalizedHtml(child, out);
    +    }
    +    return out.join('');
    +  }
    +
    +  /** returns a function that expand tabs to spaces.  This function can be fed
    +    * successive chunks of text, and will maintain its own internal state to
    +    * keep track of how tabs are expanded.
    +    * @return {function (string) : string} a function that takes
    +    *   plain text and return the text with tabs expanded.
    +    * @private
    +    */
    +  function makeTabExpander(tabWidth) {
    +    var SPACES = '                ';
    +    var charInLine = 0;
    +
    +    return function (plainText) {
    +      // walk over each character looking for tabs and newlines.
    +      // On tabs, expand them.  On newlines, reset charInLine.
    +      // Otherwise increment charInLine
    +      var out = null;
    +      var pos = 0;
    +      for (var i = 0, n = plainText.length; i < n; ++i) {
    +        var ch = plainText.charAt(i);
    +
    +        switch (ch) {
    +          case '\t':
    +            if (!out) { out = []; }
    +            out.push(plainText.substring(pos, i));
    +            // calculate how much space we need in front of this part
    +            // nSpaces is the amount of padding -- the number of spaces needed
    +            // to move us to the next column, where columns occur at factors of
    +            // tabWidth.
    +            var nSpaces = tabWidth - (charInLine % tabWidth);
    +            charInLine += nSpaces;
    +            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
    +              out.push(SPACES.substring(0, nSpaces));
    +            }
    +            pos = i + 1;
    +            break;
    +          case '\n':
    +            charInLine = 0;
    +            break;
    +          default:
    +            ++charInLine;
    +        }
    +      }
    +      if (!out) { return plainText; }
    +      out.push(plainText.substring(pos));
    +      return out.join('');
    +    };
    +  }
    +
    +  var pr_chunkPattern = new RegExp(
    +      '[^<]+'  // A run of characters other than '<'
    +      + '|<\!--[\\s\\S]*?--\>'  // an HTML comment
    +      + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // a CDATA section
    +      // a probable tag that should not be highlighted
    +      + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
    +      + '|<',  // A '<' that does not begin a larger chunk
    +      'g');
    +  var pr_commentPrefix = /^<\!--/;
    +  var pr_cdataPrefix = /^<!\[CDATA\[/;
    +  var pr_brPrefix = /^<br\b/i;
    +  var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
    +
    +  /** split markup into chunks of html tags (style null) and
    +    * plain text (style {@link #PR_PLAIN}), converting tags which are
    +    * significant for tokenization (<br>) into their textual equivalent.
    +    *
    +    * @param {string} s html where whitespace is considered significant.
    +    * @return {Object} source code and extracted tags.
    +    * @private
    +    */
    +  function extractTags(s) {
    +    // since the pattern has the 'g' modifier and defines no capturing groups,
    +    // this will return a list of all chunks which we then classify and wrap as
    +    // PR_Tokens
    +    var matches = s.match(pr_chunkPattern);
    +    var sourceBuf = [];
    +    var sourceBufLen = 0;
    +    var extractedTags = [];
    +    if (matches) {
    +      for (var i = 0, n = matches.length; i < n; ++i) {
    +        var match = matches[i];
    +        if (match.length > 1 && match.charAt(0) === '<') {
    +          if (pr_commentPrefix.test(match)) { continue; }
    +          if (pr_cdataPrefix.test(match)) {
    +            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
    +            sourceBuf.push(match.substring(9, match.length - 3));
    +            sourceBufLen += match.length - 12;
    +          } else if (pr_brPrefix.test(match)) {
    +            // <br> tags are lexically significant so convert them to text.
    +            // This is undone later.
    +            sourceBuf.push('\n');
    +            ++sourceBufLen;
    +          } else {
    +            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
    +              // A <span class="nocode"> will start a section that should be
    +              // ignored.  Continue walking the list until we see a matching end
    +              // tag.
    +              var name = match.match(pr_tagNameRe)[2];
    +              var depth = 1;
    +              var j;
    +              end_tag_loop:
    +              for (j = i + 1; j < n; ++j) {
    +                var name2 = matches[j].match(pr_tagNameRe);
    +                if (name2 && name2[2] === name) {
    +                  if (name2[1] === '/') {
    +                    if (--depth === 0) { break end_tag_loop; }
    +                  } else {
    +                    ++depth;
    +                  }
    +                }
    +              }
    +              if (j < n) {
    +                extractedTags.push(
    +                    sourceBufLen, matches.slice(i, j + 1).join(''));
    +                i = j;
    +              } else {  // Ignore unclosed sections.
    +                extractedTags.push(sourceBufLen, match);
    +              }
    +            } else {
    +              extractedTags.push(sourceBufLen, match);
    +            }
    +          }
    +        } else {
    +          var literalText = htmlToText(match);
    +          sourceBuf.push(literalText);
    +          sourceBufLen += literalText.length;
    +        }
    +      }
    +    }
    +    return { source: sourceBuf.join(''), tags: extractedTags };
    +  }
    +
    +  /** True if the given tag contains a class attribute with the nocode class. */
    +  function isNoCodeTag(tag) {
    +    return !!tag
    +        // First canonicalize the representation of attributes
    +        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
    +                 ' $1="$2$3$4"')
    +        // Then look for the attribute we want.
    +        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
    +  }
    +
    +  /**
    +   * Apply the given language handler to sourceCode and add the resulting
    +   * decorations to out.
    +   * @param {number} basePos the index of sourceCode within the chunk of source
    +   *    whose decorations are already present on out.
    +   */
    +  function appendDecorations(basePos, sourceCode, langHandler, out) {
    +    if (!sourceCode) { return; }
    +    var job = {
    +      source: sourceCode,
    +      basePos: basePos
    +    };
    +    langHandler(job);
    +    out.push.apply(out, job.decorations);
    +  }
    +
    +  /** Given triples of [style, pattern, context] returns a lexing function,
    +    * The lexing function interprets the patterns to find token boundaries and
    +    * returns a decoration list of the form
    +    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    +    * where index_n is an index into the sourceCode, and style_n is a style
    +    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    +    * all characters in sourceCode[index_n-1:index_n].
    +    *
    +    * The stylePatterns is a list whose elements have the form
    +    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    +    *
    +    * Style is a style constant like PR_PLAIN, or can be a string of the
    +    * form 'lang-FOO', where FOO is a language extension describing the
    +    * language of the portion of the token in $1 after pattern executes.
    +    * E.g., if style is 'lang-lisp', and group 1 contains the text
    +    * '(hello (world))', then that portion of the token will be passed to the
    +    * registered lisp handler for formatting.
    +    * The text before and after group 1 will be restyled using this decorator
    +    * so decorators should take care that this doesn't result in infinite
    +    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    +    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    +    * '<script>foo()<\/script>', which would cause the current decorator to
    +    * be called with '<script>' which would not match the same rule since
    +    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    +    * the generic tag rule.  The handler registered for the 'js' extension would
    +    * then be called with 'foo()', and finally, the current decorator would
    +    * be called with '<\/script>' which would not match the original rule and
    +    * so the generic tag rule would identify it as a tag.
    +    *
    +    * Pattern must only match prefixes, and if it matches a prefix, then that
    +    * match is considered a token with the same style.
    +    *
    +    * Context is applied to the last non-whitespace, non-comment token
    +    * recognized.
    +    *
    +    * Shortcut is an optional string of characters, any of which, if the first
    +    * character, gurantee that this pattern and only this pattern matches.
    +    *
    +    * @param {Array} shortcutStylePatterns patterns that always start with
    +    *   a known character.  Must have a shortcut string.
    +    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    +    *   order if the shortcut ones fail.  May have shortcuts.
    +    *
    +    * @return {function (Object)} a
    +    *   function that takes source code and returns a list of decorations.
    +    */
    +  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    +    var shortcuts = {};
    +    var tokenizer;
    +    (function () {
    +      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    +      var allRegexs = [];
    +      var regexKeys = {};
    +      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    +        var patternParts = allPatterns[i];
    +        var shortcutChars = patternParts[3];
    +        if (shortcutChars) {
    +          for (var c = shortcutChars.length; --c >= 0;) {
    +            shortcuts[shortcutChars.charAt(c)] = patternParts;
    +          }
    +        }
    +        var regex = patternParts[1];
    +        var k = '' + regex;
    +        if (!regexKeys.hasOwnProperty(k)) {
    +          allRegexs.push(regex);
    +          regexKeys[k] = null;
    +        }
    +      }
    +      allRegexs.push(/[\0-\uffff]/);
    +      tokenizer = combinePrefixPatterns(allRegexs);
    +    })();
    +
    +    var nPatterns = fallthroughStylePatterns.length;
    +    var notWs = /\S/;
    +
    +    /**
    +     * Lexes job.source and produces an output array job.decorations of style
    +     * classes preceded by the position at which they start in job.source in
    +     * order.
    +     *
    +     * @param {Object} job an object like {@code
    +     *    source: {string} sourceText plain text,
    +     *    basePos: {int} position of job.source in the larger chunk of
    +     *        sourceCode.
    +     * }
    +     */
    +    var decorate = function (job) {
    +      var sourceCode = job.source, basePos = job.basePos;
    +      /** Even entries are positions in source in ascending order.  Odd enties
    +        * are style markers (e.g., PR_COMMENT) that run from that position until
    +        * the end.
    +        * @type {Array.<number|string>}
    +        */
    +      var decorations = [basePos, PR_PLAIN];
    +      var pos = 0;  // index into sourceCode
    +      var tokens = sourceCode.match(tokenizer) || [];
    +      var styleCache = {};
    +
    +      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    +        var token = tokens[ti];
    +        var style = styleCache[token];
    +        var match = void 0;
    +
    +        var isEmbedded;
    +        if (typeof style === 'string') {
    +          isEmbedded = false;
    +        } else {
    +          var patternParts = shortcuts[token.charAt(0)];
    +          if (patternParts) {
    +            match = token.match(patternParts[1]);
    +            style = patternParts[0];
    +          } else {
    +            for (var i = 0; i < nPatterns; ++i) {
    +              patternParts = fallthroughStylePatterns[i];
    +              match = token.match(patternParts[1]);
    +              if (match) {
    +                style = patternParts[0];
    +                break;
    +              }
    +            }
    +
    +            if (!match) {  // make sure that we make progress
    +              style = PR_PLAIN;
    +            }
    +          }
    +
    +          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    +          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    +            isEmbedded = false;
    +            style = PR_SOURCE;
    +          }
    +
    +          if (!isEmbedded) { styleCache[token] = style; }
    +        }
    +
    +        var tokenStart = pos;
    +        pos += token.length;
    +
    +        if (!isEmbedded) {
    +          decorations.push(basePos + tokenStart, style);
    +        } else {  // Treat group 1 as an embedded block of source code.
    +          var embeddedSource = match[1];
    +          var embeddedSourceStart = token.indexOf(embeddedSource);
    +          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    +          if (match[2]) {
    +            // If embeddedSource can be blank, then it would match at the
    +            // beginning which would cause us to infinitely recurse on the
    +            // entire token, so we catch the right context in match[2].
    +            embeddedSourceEnd = token.length - match[2].length;
    +            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    +          }
    +          var lang = style.substring(5);
    +          // Decorate the left of the embedded source
    +          appendDecorations(
    +              basePos + tokenStart,
    +              token.substring(0, embeddedSourceStart),
    +              decorate, decorations);
    +          // Decorate the embedded source
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceStart,
    +              embeddedSource,
    +              langHandlerForExtension(lang, embeddedSource),
    +              decorations);
    +          // Decorate the right of the embedded section
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceEnd,
    +              token.substring(embeddedSourceEnd),
    +              decorate, decorations);
    +        }
    +      }
    +      job.decorations = decorations;
    +    };
    +    return decorate;
    +  }
    +
    +  /** returns a function that produces a list of decorations from source text.
    +    *
    +    * This code treats ", ', and ` as string delimiters, and \ as a string
    +    * escape.  It does not recognize perl's qq() style strings.
    +    * It has no special handling for double delimiter escapes as in basic, or
    +    * the tripled delimiters used in python, but should work on those regardless
    +    * although in those cases a single string literal may be broken up into
    +    * multiple adjacent string literals.
    +    *
    +    * It recognizes C, C++, and shell style comments.
    +    *
    +    * @param {Object} options a set of optional parameters.
    +    * @return {function (Object)} a function that examines the source code
    +    *     in the input job and builds the decoration list.
    +    */
    +  function sourceDecorator(options) {
    +    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    +    if (options['tripleQuotedStrings']) {
    +      // '''multi-line-string''', 'single-line-string', and double-quoted
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    +           null, '\'"']);
    +    } else if (options['multiLineStrings']) {
    +      // 'multi-line-string', "multi-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    +           null, '\'"`']);
    +    } else {
    +      // 'single-line-string', "single-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,
    +           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    +           null, '"\'']);
    +    }
    +    if (options['verbatimStrings']) {
    +      // verbatim-string-literal production from the C# grammar.  See issue 93.
    +      fallthroughStylePatterns.push(
    +          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    +    }
    +    if (options['hashComments']) {
    +      if (options['cStyleComments']) {
    +        // Stop C preprocessor declarations at an unclosed open comment
    +        shortcutStylePatterns.push(
    +            [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
    +             null, '#']);
    +        fallthroughStylePatterns.push(
    +            [PR_STRING,
    +             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
    +             null]);
    +      } else {
    +        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +      }
    +    }
    +    if (options['cStyleComments']) {
    +      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    +      fallthroughStylePatterns.push(
    +          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    +    }
    +    if (options['regexLiterals']) {
    +      var REGEX_LITERAL = (
    +          // A regular expression literal starts with a slash that is
    +          // not followed by * or / so that it is not confused with
    +          // comments.
    +          '/(?=[^/*])'
    +          // and then contains any number of raw characters,
    +          + '(?:[^/\\x5B\\x5C]'
    +          // escape sequences (\x5C),
    +          +    '|\\x5C[\\s\\S]'
    +          // or non-nesting character sets (\x5B\x5D);
    +          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
    +          // finally closed by a /.
    +          + '/');
    +      fallthroughStylePatterns.push(
    +          ['lang-regex',
    +           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    +           ]);
    +    }
    +
    +    var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
    +    if (keywords.length) {
    +      fallthroughStylePatterns.push(
    +          [PR_KEYWORD,
    +           new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
    +    }
    +
    +    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    +    fallthroughStylePatterns.push(
    +        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    +        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
    +        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_LITERAL,
    +         new RegExp(
    +             '^(?:'
    +             // A hex number
    +             + '0x[a-f0-9]+'
    +             // or an octal or decimal number,
    +             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    +             // possibly in scientific notation
    +             + '(?:e[+\\-]?\\d+)?'
    +             + ')'
    +             // with an optional modifier like UL for unsigned long
    +             + '[a-z]*', 'i'),
    +         null, '0123456789'],
    +        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
    +
    +    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    +  }
    +
    +  var decorateSource = sourceDecorator({
    +        'keywords': ALL_KEYWORDS,
    +        'hashComments': true,
    +        'cStyleComments': true,
    +        'multiLineStrings': true,
    +        'regexLiterals': true
    +      });
    +
    +  /** Breaks {@code job.source} around style boundaries in
    +    * {@code job.decorations} while re-interleaving {@code job.extractedTags},
    +    * and leaves the result in {@code job.prettyPrintedHtml}.
    +    * @param {Object} job like {
    +    *    source: {string} source as plain text,
    +    *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
    +    *                   html preceded by their position in {@code job.source}
    +    *                   in order
    +    *    decorations: {Array.<number|string} an array of style classes preceded
    +    *                 by the position at which they start in job.source in order
    +    * }
    +    * @private
    +    */
    +  function recombineTagsAndDecorations(job) {
    +    var sourceText = job.source;
    +    var extractedTags = job.extractedTags;
    +    var decorations = job.decorations;
    +
    +    var html = [];
    +    // index past the last char in sourceText written to html
    +    var outputIdx = 0;
    +
    +    var openDecoration = null;
    +    var currentDecoration = null;
    +    var tagPos = 0;  // index into extractedTags
    +    var decPos = 0;  // index into decorations
    +    var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
    +
    +    var adjacentSpaceRe = /([\r\n ]) /g;
    +    var startOrSpaceRe = /(^| ) /gm;
    +    var newlineRe = /\r\n?|\n/g;
    +    var trailingSpaceRe = /[ \r\n]$/;
    +    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
    +
    +    // A helper function that is responsible for opening sections of decoration
    +    // and outputing properly escaped chunks of source
    +    function emitTextUpTo(sourceIdx) {
    +      if (sourceIdx > outputIdx) {
    +        if (openDecoration && openDecoration !== currentDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        if (!openDecoration && currentDecoration) {
    +          openDecoration = currentDecoration;
    +          html.push('<span class="', openDecoration, '">');
    +        }
    +        // This interacts badly with some wikis which introduces paragraph tags
    +        // into pre blocks for some strange reason.
    +        // It's necessary for IE though which seems to lose the preformattedness
    +        // of <pre> tags when their innerHTML is assigned.
    +        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
    +        // and it serves to undo the conversion of <br>s to newlines done in
    +        // chunkify.
    +        var htmlChunk = textToHtml(
    +            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
    +            .replace(lastWasSpace
    +                     ? startOrSpaceRe
    +                     : adjacentSpaceRe, '$1&nbsp;');
    +        // Keep track of whether we need to escape space at the beginning of the
    +        // next chunk.
    +        lastWasSpace = trailingSpaceRe.test(htmlChunk);
    +        // IE collapses multiple adjacient <br>s into 1 line break.
    +        // Prefix every <br> with '&nbsp;' can prevent such IE's behavior.
    +        var lineBreakHtml = window['_pr_isIE6']() ? '&nbsp;<br />' : '<br />';
    +        html.push(htmlChunk.replace(newlineRe, lineBreakHtml));
    +        outputIdx = sourceIdx;
    +      }
    +    }
    +
    +    while (true) {
    +      // Determine if we're going to consume a tag this time around.  Otherwise
    +      // we consume a decoration or exit.
    +      var outputTag;
    +      if (tagPos < extractedTags.length) {
    +        if (decPos < decorations.length) {
    +          // Pick one giving preference to extractedTags since we shouldn't open
    +          // a new style that we're going to have to immediately close in order
    +          // to output a tag.
    +          outputTag = extractedTags[tagPos] <= decorations[decPos];
    +        } else {
    +          outputTag = true;
    +        }
    +      } else {
    +        outputTag = false;
    +      }
    +      // Consume either a decoration or a tag or exit.
    +      if (outputTag) {
    +        emitTextUpTo(extractedTags[tagPos]);
    +        if (openDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        html.push(extractedTags[tagPos + 1]);
    +        tagPos += 2;
    +      } else if (decPos < decorations.length) {
    +        emitTextUpTo(decorations[decPos]);
    +        currentDecoration = decorations[decPos + 1];
    +        decPos += 2;
    +      } else {
    +        break;
    +      }
    +    }
    +    emitTextUpTo(sourceText.length);
    +    if (openDecoration) {
    +      html.push('</span>');
    +    }
    +    job.prettyPrintedHtml = html.join('');
    +  }
    +
    +  /** Maps language-specific file extensions to handlers. */
    +  var langHandlerRegistry = {};
    +  /** Register a language handler for the given file extensions.
    +    * @param {function (Object)} handler a function from source code to a list
    +    *      of decorations.  Takes a single argument job which describes the
    +    *      state of the computation.   The single parameter has the form
    +    *      {@code {
    +    *        source: {string} as plain text.
    +    *        decorations: {Array.<number|string>} an array of style classes
    +    *                     preceded by the position at which they start in
    +    *                     job.source in order.
    +    *                     The language handler should assigned this field.
    +    *        basePos: {int} the position of source in the larger source chunk.
    +    *                 All positions in the output decorations array are relative
    +    *                 to the larger source chunk.
    +    *      } }
    +    * @param {Array.<string>} fileExtensions
    +    */
    +  function registerLangHandler(handler, fileExtensions) {
    +    for (var i = fileExtensions.length; --i >= 0;) {
    +      var ext = fileExtensions[i];
    +      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    +        langHandlerRegistry[ext] = handler;
    +      } else if ('console' in window) {
    +        console.warn('cannot override language handler %s', ext);
    +      }
    +    }
    +  }
    +  function langHandlerForExtension(extension, source) {
    +    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    +      // Treat it as markup if the first non whitespace character is a < and
    +      // the last non-whitespace character is a >.
    +      extension = /^\s*</.test(source)
    +          ? 'default-markup'
    +          : 'default-code';
    +    }
    +    return langHandlerRegistry[extension];
    +  }
    +  registerLangHandler(decorateSource, ['default-code']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [],
    +          [
    +           [PR_PLAIN,       /^[^<?]+/],
    +           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    +           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    +           // Unescaped content in an unknown language
    +           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    +           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    +           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    +           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    +           // Unescaped content in javascript.  (Or possibly vbscript).
    +           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    +           // Contains unescaped stylesheet content
    +           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    +           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    +          ]),
    +      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [
    +           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    +           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    +           ],
    +          [
    +           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    +           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    +           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    +           [PR_PUNCTUATION,  /^[=<>\/]+/],
    +           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    +           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    +           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    +           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    +           ]),
    +      ['in.tag']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CPP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true
    +        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': 'null true false'
    +        }), ['json']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CSHARP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true,
    +          'verbatimStrings': true
    +        }), ['cs']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JAVA_KEYWORDS,
    +          'cStyleComments': true
    +        }), ['java']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': SH_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true
    +        }), ['bsh', 'csh', 'sh']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PYTHON_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'tripleQuotedStrings': true
    +        }), ['cv', 'py']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PERL_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['perl', 'pl', 'pm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': RUBY_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['rb']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JSCRIPT_KEYWORDS,
    +          'cStyleComments': true,
    +          'regexLiterals': true
    +        }), ['js']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    +
    +  function applyDecorator(job) {
    +    var sourceCodeHtml = job.sourceCodeHtml;
    +    var opt_langExtension = job.langExtension;
    +
    +    // Prepopulate output in case processing fails with an exception.
    +    job.prettyPrintedHtml = sourceCodeHtml;
    +
    +    try {
    +      // Extract tags, and convert the source code to plain text.
    +      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
    +      /** Plain text. @type {string} */
    +      var source = sourceAndExtractedTags.source;
    +      job.source = source;
    +      job.basePos = 0;
    +
    +      /** Even entries are positions in source in ascending order.  Odd entries
    +        * are tags that were extracted at that position.
    +        * @type {Array.<number|string>}
    +        */
    +      job.extractedTags = sourceAndExtractedTags.tags;
    +
    +      // Apply the appropriate language handler
    +      langHandlerForExtension(opt_langExtension, source)(job);
    +      // Integrate the decorations and tags back into the source code to produce
    +      // a decorated html string which is left in job.prettyPrintedHtml.
    +      recombineTagsAndDecorations(job);
    +    } catch (e) {
    +      if ('console' in window) {
    +        console.log(e);
    +        console.trace();
    +      }
    +    }
    +  }
    +
    +  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
    +    var job = {
    +      sourceCodeHtml: sourceCodeHtml,
    +      langExtension: opt_langExtension
    +    };
    +    applyDecorator(job);
    +    return job.prettyPrintedHtml;
    +  }
    +
    +  function prettyPrint(opt_whenDone) {
    +    var isIE678 = window['_pr_isIE6']();
    +    var ieNewline = isIE678 === 6 ? '\r\n' : '\r';
    +    // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
    +
    +    // fetch a list of nodes to rewrite
    +    var codeSegments = [
    +        document.getElementsByTagName('pre'),
    +        document.getElementsByTagName('code'),
    +        document.getElementsByTagName('td'),  /* ND Change: Add tables to support prototypes. */
    +        document.getElementsByTagName('xmp') ];
    +    var elements = [];
    +    for (var i = 0; i < codeSegments.length; ++i) {
    +      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    +        elements.push(codeSegments[i][j]);
    +      }
    +    }
    +    codeSegments = null;
    +
    +    var clock = Date;
    +    if (!clock['now']) {
    +      clock = { 'now': function () { return (new Date).getTime(); } };
    +    }
    +
    +    // The loop is broken into a series of continuations to make sure that we
    +    // don't make the browser unresponsive when rewriting a large page.
    +    var k = 0;
    +    var prettyPrintingJob;
    +
    +    function doWork() {
    +      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    +                     clock.now() + 250 /* ms */ :
    +                     Infinity);
    +      for (; k < elements.length && clock.now() < endTime; k++) {
    +        var cs = elements[k];
    +        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    +          // If the classes includes a language extensions, use it.
    +          // Language extensions can be specified like
    +          //     <pre class="prettyprint lang-cpp">
    +          // the language extension "cpp" is used to find a language handler as
    +          // passed to PR_registerLangHandler.
    +          var langExtension = cs.className.match(/\blang-(\w+)\b/);
    +          if (langExtension) { langExtension = langExtension[1]; }
    +
    +          // make sure this is not nested in an already prettified element
    +          var nested = false;
    +          for (var p = cs.parentNode; p; p = p.parentNode) {
    +            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    +                 p.tagName === 'xmp' || p.tagName === 'td') &&  /* ND Change: Add tables to support prototypes */
    +                p.className && p.className.indexOf('prettyprint') >= 0) {
    +              nested = true;
    +              break;
    +            }
    +          }
    +          if (!nested) {
    +            // fetch the content as a snippet of properly escaped HTML.
    +            // Firefox adds newlines at the end.
    +            var content = getInnerHtml(cs);
    +            content = content.replace(/(?:\r\n?|\n)$/, '');
    +
    +	  		/* ND Change: we need to preserve &nbsp;s so change them to a special character instead of a space. */
    +			content = content.replace(/&nbsp;/g, '\x11');
    +
    +            // do the pretty printing
    +            prettyPrintingJob = {
    +              sourceCodeHtml: content,
    +              langExtension: langExtension,
    +              sourceNode: cs
    +            };
    +            applyDecorator(prettyPrintingJob);
    +            replaceWithPrettyPrintedHtml();
    +          }
    +        }
    +      }
    +      if (k < elements.length) {
    +        // finish up in a continuation
    +        setTimeout(doWork, 250);
    +      } else if (opt_whenDone) {
    +        opt_whenDone();
    +      }
    +    }
    +
    +    function replaceWithPrettyPrintedHtml() {
    +      var newContent = prettyPrintingJob.prettyPrintedHtml;
    +      if (!newContent) { return; }
    +
    +      /* ND Change: Restore the preserved &nbsp;s.  */
    +	  newContent = newContent.replace(/\x11/g, '&nbsp;');
    +
    +      var cs = prettyPrintingJob.sourceNode;
    +
    +      // push the prettified html back into the tag.
    +      if (!isRawContent(cs)) {
    +        // just replace the old html with the new
    +        cs.innerHTML = newContent;
    +      } else {
    +        // we need to change the tag to a <pre> since <xmp>s do not allow
    +        // embedded tags such as the span tags used to attach styles to
    +        // sections of source code.
    +        var pre = document.createElement('PRE');
    +        for (var i = 0; i < cs.attributes.length; ++i) {
    +          var a = cs.attributes[i];
    +          if (a.specified) {
    +            var aname = a.name.toLowerCase();
    +            if (aname === 'class') {
    +              pre.className = a.value;  // For IE 6
    +            } else {
    +              pre.setAttribute(a.name, a.value);
    +            }
    +          }
    +        }
    +        pre.innerHTML = newContent;
    +
    +        // remove the old
    +        cs.parentNode.replaceChild(pre, cs);
    +        cs = pre;
    +      }
    +
    +      // Replace <br>s with line-feeds so that copying and pasting works
    +      // on IE 6.
    +      // Doing this on other browsers breaks lots of stuff since \r\n is
    +      // treated as two newlines on Firefox, and doing this also slows
    +      // down rendering.
    +      if (isIE678 && cs.tagName === 'PRE') {
    +        var lineBreaks = cs.getElementsByTagName('br');
    +        for (var j = lineBreaks.length; --j >= 0;) {
    +          var lineBreak = lineBreaks[j];
    +          lineBreak.parentNode.replaceChild(
    +              document.createTextNode(ieNewline), lineBreak);
    +        }
    +      }
    +    }
    +
    +    doWork();
    +  }
    +
    +  window['PR_normalizedHtml'] = normalizedHtml;
    +  window['prettyPrintOne'] = prettyPrintOne;
    +  window['prettyPrint'] = prettyPrint;
    +  window['PR'] = {
    +        'combinePrefixPatterns': combinePrefixPatterns,
    +        'createSimpleLexer': createSimpleLexer,
    +        'registerLangHandler': registerLangHandler,
    +        'sourceDecorator': sourceDecorator,
    +        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    +        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    +        'PR_COMMENT': PR_COMMENT,
    +        'PR_DECLARATION': PR_DECLARATION,
    +        'PR_KEYWORD': PR_KEYWORD,
    +        'PR_LITERAL': PR_LITERAL,
    +        'PR_NOCODE': PR_NOCODE,
    +        'PR_PLAIN': PR_PLAIN,
    +        'PR_PUNCTUATION': PR_PUNCTUATION,
    +        'PR_SOURCE': PR_SOURCE,
    +        'PR_STRING': PR_STRING,
    +        'PR_TAG': PR_TAG,
    +        'PR_TYPE': PR_TYPE
    +      };
    +})();
    +
    +
    +// ____________________________________________________________________________
    +
    +
    +
    +// Lua extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],[PR.PR_STRING,/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],[PR.PR_KEYWORD,/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_]\w*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),['lua'])
    +
    +
    +// Haskell extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\x0B\x0C\r ]+/,null,'	\n\r '],[PR.PR_STRING,/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'\"'],[PR.PR_STRING,/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,'\''],[PR.PR_LITERAL,/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,'0123456789']],[[PR.PR_COMMENT,/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],[PR.PR_KEYWORD,/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,null],[PR.PR_PLAIN,/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],[PR.PR_PUNCTUATION,/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),['hs'])
    +
    +
    +// ML extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_COMMENT,/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,'#'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],[PR.PR_KEYWORD,/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],[PR.PR_LITERAL,/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],[PR.PR_PUNCTUATION,/^[^\t\n\r \xA0\"\'\w]+/]]),['fs','ml'])
    +
    +
    +// SQL extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],[PR.PR_KEYWORD,/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_][\w-]*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),['sql'])
    +
    +
    +// VB extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0\u2028\u2029]+/,null,'	\n\r \xa0\u2028\u2029'],[PR.PR_STRING,/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'\"\u201c\u201d'],[PR.PR_COMMENT,/^[\'\u2018\u2019][^\r\n\u2028\u2029]*/,null,'\'\u2018\u2019']],[[PR.PR_KEYWORD,/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,null],[PR.PR_COMMENT,/^REM[^\r\n\u2028\u2029]*/i],[PR.PR_LITERAL,/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],[PR.PR_PLAIN,/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],[PR.PR_PUNCTUATION,/^(?:\[|\])/]]),['vb','vbs'])
    diff --git a/docs/javascript/searchdata.js b/docs/javascript/searchdata.js
    new file mode 100644
    index 000000000..ff39bc80d
    --- /dev/null
    +++ b/docs/javascript/searchdata.js
    @@ -0,0 +1,122 @@
    +var indexSectionsWithContent = {
    +   "General": {
    +      "Symbols": false,
    +      "Numbers": false,
    +      "A": false,
    +      "B": true,
    +      "C": true,
    +      "D": false,
    +      "E": true,
    +      "F": true,
    +      "G": true,
    +      "H": false,
    +      "I": true,
    +      "J": false,
    +      "K": false,
    +      "L": true,
    +      "M": false,
    +      "N": true,
    +      "O": false,
    +      "P": false,
    +      "Q": false,
    +      "R": true,
    +      "S": true,
    +      "T": false,
    +      "U": false,
    +      "V": true,
    +      "W": false,
    +      "X": false,
    +      "Y": false,
    +      "Z": false
    +      },
    +   "Variables": {
    +      "Symbols": false,
    +      "Numbers": false,
    +      "A": false,
    +      "B": true,
    +      "C": true,
    +      "D": false,
    +      "E": false,
    +      "F": false,
    +      "G": false,
    +      "H": false,
    +      "I": false,
    +      "J": false,
    +      "K": false,
    +      "L": false,
    +      "M": false,
    +      "N": false,
    +      "O": false,
    +      "P": false,
    +      "Q": false,
    +      "R": false,
    +      "S": false,
    +      "T": false,
    +      "U": false,
    +      "V": false,
    +      "W": false,
    +      "X": false,
    +      "Y": false,
    +      "Z": false
    +      },
    +   "Functions": {
    +      "Symbols": false,
    +      "Numbers": false,
    +      "A": false,
    +      "B": false,
    +      "C": true,
    +      "D": false,
    +      "E": true,
    +      "F": false,
    +      "G": true,
    +      "H": false,
    +      "I": true,
    +      "J": false,
    +      "K": false,
    +      "L": true,
    +      "M": false,
    +      "N": true,
    +      "O": false,
    +      "P": false,
    +      "Q": false,
    +      "R": true,
    +      "S": true,
    +      "T": false,
    +      "U": false,
    +      "V": false,
    +      "W": false,
    +      "X": false,
    +      "Y": false,
    +      "Z": false
    +      },
    +   "Classes": {
    +      "Symbols": false,
    +      "Numbers": false,
    +      "A": false,
    +      "B": false,
    +      "C": false,
    +      "D": false,
    +      "E": false,
    +      "F": false,
    +      "G": true,
    +      "H": false,
    +      "I": false,
    +      "J": false,
    +      "K": false,
    +      "L": true,
    +      "M": false,
    +      "N": false,
    +      "O": false,
    +      "P": false,
    +      "Q": false,
    +      "R": false,
    +      "S": false,
    +      "T": false,
    +      "U": false,
    +      "V": false,
    +      "W": false,
    +      "X": false,
    +      "Y": false,
    +      "Z": false
    +      }
    +   }
    \ No newline at end of file
    diff --git a/docs/search/ClassesG.html b/docs/search/ClassesG.html
    new file mode 100644
    index 000000000..7c2a0193b
    --- /dev/null
    +++ b/docs/search/ClassesG.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="../files/blob-h.html#GitBlob" target=_parent class=ISymbol>GitBlob</a></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="../files/error-h.html#GitError" target=_parent class=ISymbol>GitError</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/ClassesL.html b/docs/search/ClassesL.html
    new file mode 100644
    index 000000000..beb6a60d5
    --- /dev/null
    +++ b/docs/search/ClassesL.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_lookup_undrequest><div class=IEntry><a href="../files/blob-h.html#lookup_request" target=_parent class=ISymbol>lookup_request</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsC.html b/docs/search/FunctionsC.html
    new file mode 100644
    index 000000000..03341b746
    --- /dev/null
    +++ b/docs/search/FunctionsC.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Close><div class=IEntry><a href="../files/blob-h.html#GitBlob.Close" target=_parent class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromBuffer><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" target=_parent class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromFile><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" target=_parent class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsE.html b/docs/search/FunctionsE.html
    new file mode 100644
    index 000000000..eecff1530
    --- /dev/null
    +++ b/docs/search/FunctionsE.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_EIO_undAfterLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" target=_parent class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_EIO_undLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" target=_parent class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsG.html b/docs/search/FunctionsG.html
    new file mode 100644
    index 000000000..b7550956c
    --- /dev/null
    +++ b/docs/search/FunctionsG.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="../files/blob-h.html#GitBlob.GitBlob" target=_parent class=ISymbol>GitBlob</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="../files/error-h.html#GitError.GitError" target=_parent class=ISymbol>GitError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsI.html b/docs/search/FunctionsI.html
    new file mode 100644
    index 000000000..fc527d73c
    --- /dev/null
    +++ b/docs/search/FunctionsI.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Initialize><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Initialize')" class=ISymbol>Initialize</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsL.html b/docs/search/FunctionsL.html
    new file mode 100644
    index 000000000..11870557d
    --- /dev/null
    +++ b/docs/search/FunctionsL.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Lookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" target=_parent class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsN.html b/docs/search/FunctionsN.html
    new file mode 100644
    index 000000000..9fbc82308
    --- /dev/null
    +++ b/docs/search/FunctionsN.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_New><div class=IEntry><a href="javascript:searchResults.Toggle('SR_New')" class=ISymbol>New</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsR.html b/docs/search/FunctionsR.html
    new file mode 100644
    index 000000000..b9dacba56
    --- /dev/null
    +++ b/docs/search/FunctionsR.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_RawContent><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" target=_parent class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_RawSize><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" target=_parent class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/FunctionsS.html b/docs/search/FunctionsS.html
    new file mode 100644
    index 000000000..4bfff336b
    --- /dev/null
    +++ b/docs/search/FunctionsS.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_StrError><div class=IEntry><a href="../files/error-h.html#GitError.StrError" target=_parent class=ISymbol>StrError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralB.html b/docs/search/GeneralB.html
    new file mode 100644
    index 000000000..b0706df19
    --- /dev/null
    +++ b/docs/search/GeneralB.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_blob><div class=IEntry><a href="../files/blob-h.html#GitBlob.blob" target=_parent class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralC.html b/docs/search/GeneralC.html
    new file mode 100644
    index 000000000..1cb2ab199
    --- /dev/null
    +++ b/docs/search/GeneralC.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Close><div class=IEntry><a href="../files/blob-h.html#GitBlob.Close" target=_parent class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_constructor_undtemplate><div class=IEntry><a href="javascript:searchResults.Toggle('SR_constructor_undtemplate')" class=ISymbol>constructor_template</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" target=_parent class=IParent>GitError</a></div></div></div><div class=SRResult id=SR_CreateFromBuffer><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" target=_parent class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromFile><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" target=_parent class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralE.html b/docs/search/GeneralE.html
    new file mode 100644
    index 000000000..eecff1530
    --- /dev/null
    +++ b/docs/search/GeneralE.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_EIO_undAfterLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" target=_parent class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_EIO_undLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" target=_parent class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralF.html b/docs/search/GeneralF.html
    new file mode 100644
    index 000000000..c04f7272f
    --- /dev/null
    +++ b/docs/search/GeneralF.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Functions><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Functions')" class=ISymbol>Functions</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Functions" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Functions" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralG.html b/docs/search/GeneralG.html
    new file mode 100644
    index 000000000..c3e5bdcb8
    --- /dev/null
    +++ b/docs/search/GeneralG.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="javascript:searchResults.Toggle('SR_GitBlob')" class=ISymbol>GitBlob</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob" target=_parent class=IParent>Global</a><a href="../files/blob-h.html#GitBlob.GitBlob" target=_parent class=IParent>GitBlob</a></div></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="javascript:searchResults.Toggle('SR_GitError')" class=ISymbol>GitError</a><div class=ISubIndex><a href="../files/error-h.html#GitError" target=_parent class=IParent>Global</a><a href="../files/error-h.html#GitError.GitError" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralI.html b/docs/search/GeneralI.html
    new file mode 100644
    index 000000000..fc527d73c
    --- /dev/null
    +++ b/docs/search/GeneralI.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Initialize><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Initialize')" class=ISymbol>Initialize</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralL.html b/docs/search/GeneralL.html
    new file mode 100644
    index 000000000..35f682b94
    --- /dev/null
    +++ b/docs/search/GeneralL.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Lookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" target=_parent class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_lookup_undrequest><div class=IEntry><a href="../files/blob-h.html#lookup_request" target=_parent class=ISymbol>lookup_request</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralN.html b/docs/search/GeneralN.html
    new file mode 100644
    index 000000000..9fbc82308
    --- /dev/null
    +++ b/docs/search/GeneralN.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_New><div class=IEntry><a href="javascript:searchResults.Toggle('SR_New')" class=ISymbol>New</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralR.html b/docs/search/GeneralR.html
    new file mode 100644
    index 000000000..b9dacba56
    --- /dev/null
    +++ b/docs/search/GeneralR.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_RawContent><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" target=_parent class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_RawSize><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" target=_parent class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralS.html b/docs/search/GeneralS.html
    new file mode 100644
    index 000000000..4bfff336b
    --- /dev/null
    +++ b/docs/search/GeneralS.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_StrError><div class=IEntry><a href="../files/error-h.html#GitError.StrError" target=_parent class=ISymbol>StrError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/GeneralV.html b/docs/search/GeneralV.html
    new file mode 100644
    index 000000000..c2ed22dc4
    --- /dev/null
    +++ b/docs/search/GeneralV.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Variables><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Variables')" class=ISymbol>Variables</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Variables" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Variables" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/NoResults.html b/docs/search/NoResults.html
    new file mode 100644
    index 000000000..5ce771767
    --- /dev/null
    +++ b/docs/search/NoResults.html
    @@ -0,0 +1,15 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=NoMatches>No Matches</div></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/VariablesB.html b/docs/search/VariablesB.html
    new file mode 100644
    index 000000000..b0706df19
    --- /dev/null
    +++ b/docs/search/VariablesB.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_blob><div class=IEntry><a href="../files/blob-h.html#GitBlob.blob" target=_parent class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/search/VariablesC.html b/docs/search/VariablesC.html
    new file mode 100644
    index 000000000..6d5825810
    --- /dev/null
    +++ b/docs/search/VariablesC.html
    @@ -0,0 +1,20 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.51 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +
    +
    +<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_constructor_undtemplate><div class=IEntry><a href="javascript:searchResults.Toggle('SR_constructor_undtemplate')" class=ISymbol>constructor_template</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    +document.getElementById("Loading").style.display="none";
    +document.getElementById("NoMatches").style.display="none";
    +var searchResults = new SearchResults("searchResults", "HTML");
    +searchResults.Search();
    +--></script></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/docs/styles/main.css b/docs/styles/main.css
    new file mode 100644
    index 000000000..111dd6573
    --- /dev/null
    +++ b/docs/styles/main.css
    @@ -0,0 +1,796 @@
    +@import('http://fonts.googleapis.com/css?family=EB+Garamond');
    +
    +body {
    +  background-color: #FFFFFF;
    +  font-family: Georgia, sans-serif;
    +  font-size: 14px;
    +  margin: 40px;
    +}
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +img { border: 0;  }
    +
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Opera doesn't break with just wbr, but will if you add this.  */
    +.Opera wbr:after {
    +	content: "\00200B";
    +	}
    +
    +
    +/*  Blockquotes are used as containers for things that may need to scroll.  */
    +blockquote {
    +    padding: 0;
    +    margin: 0;
    +    overflow: auto;
    +    }
    +
    +
    +.Firefox1 blockquote {
    +    padding-bottom: .5em;
    +    }
    +
    +/*  Turn off scrolling when printing.  */
    +@media print {
    +    blockquote {
    +        overflow: visible;
    +        }
    +    .IE blockquote {
    +        width: auto;
    +        }
    +    }
    +
    +
    +
    +#Menu {
    +    padding: 10px 0 0 0;
    +    }
    +.ContentPage #Menu,
    +.IndexPage #Menu {
    +    position: absolute;
    +    top: 0;
    +    left: 0;
    +    width: 31ex;
    +    overflow: hidden;
    +    }
    +.ContentPage .Firefox #Menu,
    +.IndexPage .Firefox #Menu {
    +    width: 27ex;
    +    }
    +
    +
    +    .MTitle {
    +        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 9pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px;
    +        }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0;
    +        }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px;
    +        }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Firefox .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +    #MSearchPanel {
    +        padding: 0px 6px;
    +        margin: .25em 0;
    +        }
    +
    +
    +    #MSearchField {
    +        font: italic 9pt Verdana, sans-serif;
    +        color: #606060;
    +        background-color: #E8E8E8;
    +        border: none;
    +        padding: 2px 4px;
    +        width: 100%;
    +        }
    +    /* Only Opera gets it right. */
    +    .Firefox #MSearchField,
    +    .IE #MSearchField,
    +    .Safari #MSearchField {
    +        width: 94%;
    +        }
    +    .Opera9 #MSearchField,
    +    .Konqueror #MSearchField {
    +        width: 97%;
    +        }
    +    .FramedMenuPage .Firefox #MSearchField,
    +    .FramedMenuPage .Safari #MSearchField,
    +    .FramedMenuPage .Konqueror #MSearchField {
    +        width: 98%;
    +        }
    +
    +    /* Firefox doesn't do this right in frames without #MSearchPanel added on.
    +        It's presence doesn't hurt anything other browsers. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        padding: 1px 3px;
    +        }
    +    .MSearchPanelActive #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        font-style: normal;
    +        padding: 1px 3px;
    +        }
    +
    +    #MSearchType {
    +        visibility: hidden;
    +        font: 8pt Verdana, sans-serif;
    +        width: 98%;
    +        padding: 0;
    +        border: 1px solid #C0C0C0;
    +        }
    +    .MSearchPanelActive #MSearchType,
    +    /*  As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchType,
    +    #MSearchType:focus {
    +        visibility: visible;
    +        color: #606060;
    +        }
    +    #MSearchType option#MSearchEverything {
    +        font-weight: bold;
    +        }
    +
    +    .Opera8 .MSearchPanelInactive:hover,
    +    .Opera8 .MSearchPanelActive {
    +        margin-left: -1px;
    +        }
    +
    +
    +    iframe#MSearchResults {
    +        width: 60ex;
    +        height: 15em;
    +        }
    +    #MSearchResultsWindow {
    +        display: none;
    +        position: absolute;
    +        left: 0; top: 0;
    +        border: 1px solid #000000;
    +        background-color: #E8E8E8;
    +        }
    +    #MSearchResultsWindowClose {
    +        font-weight: bold;
    +        font-size: 8pt;
    +        display: block;
    +        padding: 2px 5px;
    +        }
    +    #MSearchResultsWindowClose:link,
    +    #MSearchResultsWindowClose:visited {
    +        color: #000000;
    +        text-decoration: none;
    +        }
    +    #MSearchResultsWindowClose:active,
    +    #MSearchResultsWindowClose:hover {
    +        color: #800000;
    +        text-decoration: none;
    +        background-color: #F4F4F4;
    +        }
    +
    +
    +
    +
    +#Content {
    +    padding-bottom: 15px;
    +    }
    +
    +.ContentPage #Content {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    background-color: #FFFFFF;
    +    font-size: 9pt;  /* To make 31ex match the menu's 31ex. */
    +    margin-left: 31ex;
    +    }
    +.ContentPage .Firefox #Content {
    +    margin-left: 27ex;
    +    }
    +
    +
    +
    +    .CTopic {
    +        font-size: 10pt;
    +        margin-bottom: 3em;
    +        }
    +
    +
    +    .CTitle {
    +        font-size: 12pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 16pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 18pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 8pt;
    +        }
    +
    +    .Opera .CToolTip {
    +        max-width: 98%;
    +        }
    +
    +    /*  Scrollbars would be useless.  */
    +    .CToolTip blockquote {
    +        overflow: hidden;
    +        }
    +    .IE6 .CToolTip blockquote {
    +        overflow: visible;
    +        }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 10pt;
    +        margin: 1.5em 0 .5em 0;
    +        }
    +
    +    .CBody pre {
    +        font: 10pt "Courier New", Courier, monospace;
    +	    background-color: #FCFCFC;
    +	    margin: 1em 35px;
    +	    padding: 10px 15px 10px 10px;
    +	    border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4;
    +	    border-width: 1px 1px 1px 6px;
    +	    border-style: dashed dashed dashed solid;
    +        }
    +
    +    .CBody ul {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +        .CDLEntry {
    +            font: 10pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 10pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +    .CTopic img {
    +        text-align: center;
    +        display: block;
    +        margin: 1em auto;
    +        }
    +    .CImageCaption {
    +        font-variant: small-caps;
    +        font-size: 8pt;
    +        color: #808080;
    +        text-align: center;
    +        position: relative;
    +        top: 1em;
    +        }
    +
    +    .CImageLink {
    +        color: #808080;
    +        font-style: italic;
    +        }
    +    a.CImageLink:link,
    +    a.CImageLink:visited,
    +    a.CImageLink:hover { color: #808080 }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 10pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 10pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PDefaultValuePrefix,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +    .PAfterParameters {
    +        vertical-align: bottom;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CClass .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        background-color: #F4F4F4;
    +        }
    +    .CInterface .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0;
    +        background-color: #F4F4FF;
    +        }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype,
    +    .CEnumeration .Prototype {
    +        background-color: #FAF0F0; border-color: #E0B0B0;
    +        }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 12pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* In a frame IE 6 will make them too long unless you set the width to 100%.  Without frames it will be correct without a width
    +        or slightly too long (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  IE 7 has the same
    +        problem with frames, haven't tested it without.  */
    +    .FramedContentPage .IE .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 9pt; width: 100% }
    +
    +    .SEntry {
    +        width: 30% }
    +    .SDescription {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +    .SDescription { padding-left: 2ex }
    +    .SIndent1 .SEntry { padding-left: 1.5ex }   .SIndent1 .SDescription { padding-left: 3.5ex }
    +    .SIndent2 .SEntry { padding-left: 3.0ex }   .SIndent2 .SDescription { padding-left: 5.0ex }
    +    .SIndent3 .SEntry { padding-left: 4.5ex }   .SIndent3 .SDescription { padding-left: 6.5ex }
    +    .SIndent4 .SEntry { padding-left: 6.0ex }   .SIndent4 .SDescription { padding-left: 8.0ex }
    +    .SIndent5 .SEntry { padding-left: 7.5ex }   .SIndent5 .SDescription { padding-left: 9.5ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +    .SGroup td {
    +        padding-top: .5em; padding-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain td,
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        font-size: 10pt;
    +        padding-bottom: .25em }
    +
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        padding-top: 1em }
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold;
    +        }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 10pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Firefox .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +#Index {
    +    background-color: #FFFFFF;
    +    }
    +
    +/*  As opposed to .PopupSearchResultsPage #Index  */
    +.IndexPage #Index,
    +.FramedIndexPage #Index,
    +.FramedSearchResultsPage #Index {
    +    padding: 15px;
    +    }
    +
    +.IndexPage #Index {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    font-size: 9pt;  /* To make 27ex match the menu's 27ex. */
    +    margin-left: 27ex;
    +    }
    +
    +
    +    .IPageTitle {
    +        font-size: 20pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .FramedSearchResultsPage .IPageTitle {
    +        margin-bottom: 15px;
    +        }
    +
    +    .INavigationBar {
    +        font-size: 10pt;
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px;
    +        }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 16pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        font-size: 10pt;
    +        padding-left: 1ex;
    +        }
    +    .PopupSearchResultsPage .IEntry {
    +        font-size: 8pt;
    +        padding: 1px 5px;
    +        }
    +    .PopupSearchResultsPage .Opera9 .IEntry,
    +    .FramedSearchResultsPage .Opera9 .IEntry {
    +        text-align: left;
    +        }
    +    .FramedSearchResultsPage .IEntry {
    +        padding: 0;
    +        }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +    .PopupSearchResultsPage .ISubIndex {
    +        display: none;
    +        }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .IndexPage .ISymbolPrefix,
    +    .FramedIndexPage .ISymbolPrefix {
    +        font-size: 10pt;
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix,
    +    .FramedSearchResultsPage .ISymbolPrefix {
    +        color: #900000;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix {
    +        font-size: 8pt;
    +        }
    +
    +    .IndexPage #IFirstSymbolPrefix,
    +    .FramedIndexPage #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #ILastSymbolPrefix,
    +    .FramedIndexPage #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #IOnlySymbolPrefix,
    +    .FramedIndexPage #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +    .PopupSearchResultsPage .SRStatus {
    +        padding: 2px 5px;
    +        font-size: 8pt;
    +        font-style: italic;
    +        }
    +    .FramedSearchResultsPage .SRStatus {
    +        font-size: 10pt;
    +        font-style: italic;
    +        }
    +
    +    .SRResult {
    +        display: none;
    +        }
    +
    +
    +
    +#Footer {
    +    font-size: 8pt;
    +    color: #989898;
    +    text-align: right;
    +    }
    +
    +#Footer p {
    +    text-indent: 0;
    +    margin-bottom: .5em;
    +    }
    +
    +.ContentPage #Footer,
    +.IndexPage #Footer {
    +    text-align: right;
    +    margin: 2px;
    +    }
    +
    +.FramedMenuPage #Footer {
    +    text-align: center;
    +    margin: 5em 10px 10px 10px;
    +    padding-top: 1em;
    +    border-top: 1px solid #C8C8C8;
    +    }
    +
    +    #Footer a:link,
    +    #Footer a:hover,
    +    #Footer a:visited { color: #989898 }
    +    #Footer a:active { color: #A00000 }
    +
    +
    +
    +.prettyprint .kwd { color: #800000; }  /* keywords */
    +
    +    .prettyprint.PDefaultValue .kwd,
    +    .prettyprint.PDefaultValuePrefix .kwd,
    +    .prettyprint.PTypePrefix .kwd {
    +        color: #C88F8F;
    +        }
    +
    +.prettyprint .com { color: #008000; }  /* comments */
    +
    +    .prettyprint.PDefaultValue .com,
    +    .prettyprint.PDefaultValuePrefix .com,
    +    .prettyprint.PTypePrefix .com {
    +        color: #8FC88F;
    +        }
    +
    +.prettyprint .str { color: #0000B0; }  /* strings */
    +.prettyprint .lit { color: #0000B0; }  /* literals */
    +
    +    .prettyprint.PDefaultValue .str,
    +    .prettyprint.PDefaultValuePrefix .str,
    +    .prettyprint.PTypePrefix .str,
    +    .prettyprint.PDefaultValue .lit,
    +    .prettyprint.PDefaultValuePrefix .lit,
    +    .prettyprint.PTypePrefix .lit {
    +        color: #8F8FC0;
    +        }
    +
    +.prettyprint .typ { color: #000000; }  /* types */
    +.prettyprint .pun { color: #000000; }  /* punctuation */
    +.prettyprint .pln { color: #000000; }  /* punctuation */
    +
    +    .prettyprint.PDefaultValue .typ,
    +    .prettyprint.PDefaultValuePrefix .typ,
    +    .prettyprint.PTypePrefix .typ,
    +    .prettyprint.PDefaultValue .pun,
    +    .prettyprint.PDefaultValuePrefix .pun,
    +    .prettyprint.PTypePrefix .pun,
    +    .prettyprint.PDefaultValue .pln,
    +    .prettyprint.PDefaultValuePrefix .pln,
    +    .prettyprint.PTypePrefix .pln {
    +        color: #8F8F8F;
    +        }
    +
    +.prettyprint .tag { color: #008; }
    +.prettyprint .atn { color: #606; }
    +.prettyprint .atv { color: #080; }
    +.prettyprint .dec { color: #606; }
    +
    diff --git a/include/blob.h b/include/blob.h
    index f90f453f7..31b2b697e 100755
    --- a/include/blob.h
    +++ b/include/blob.h
    @@ -16,158 +16,210 @@
     
     using namespace node;
     
    -namespace {
    -  /**
    -   * Class: GitBlob
    -   *   Wrapper for libgit2 git_blob.
    -   */
    -  class GitBlob : public ObjectWrap {
    -    public:
    -      /**
    -       * Variable: constructor_template
    -       *   Used to create Node.js constructor.
    -       */
    -      static v8::Persistent<v8::FunctionTemplate> constructor_template;
    -      /**
    -       * Function: Initialize
    -       *   Used to intialize the EventEmitter from Node.js
    -       *
    -       * Parameters:
    -       *   target - v8::Object the Node.js global module object
    -       */
    -      static void Initialize(v8::Handle<v8::Object> target);
    -      /**
    -       * Accessor for GitBlob
    -       *
    -       * @return the internal git_blob reference
    -       */
    -      git_blob* GetValue();
    -      /**
    -       * Mutator for Object
    -       *
    -       * @param obj a git_object object
    -       */
    -      void SetValue(git_blob* blob);
    -      /**
    -       * Function: Lookup
    -       *   Lookup a blob object from a repository.
    -       *
    -       * Parameters:
    -       *   repo the repo to use when locating the blob.
    -       *   id identity of the blob to locate.
    -       *
    -       * Returns:
    -       *   0 on success; error code otherwise
    -       */
    -      int Lookup(git_repository* repo, const git_oid *id);
    -      /**
    -       * Function: RawContent
    -       *   Get a read-only buffer with the raw content of a blob.
    -       *
    -       * Returns:
    -       *   raw content buffer; NULL if the blob has no contents
    -       */
    -      const void* RawContent();
    -      /**
    -       * Function: RawSize
    -       *   Lookup a blob object from a repository.
    -       *
    -       * Returns:
    -       *   size in bytes
    -       */
    -      int RawSize();
    +/**
    + * Class: GitBlob
    + *   Wrapper for libgit2 git_blob.
    + */
    +class GitBlob : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +    /**
    +     * Accessor for GitBlob
    +     *
    +     * @return the internal git_blob reference
    +     */
    +    git_blob* GetValue();
    +    /**
    +     * Mutator for Object
    +     *
    +     * @param obj a git_object object
    +     */
    +    void SetValue(git_blob* blob);
    +    /**
    +     * Function: Lookup
    +     *   Lookup a blob object from a repository.
    +     *
    +     * Parameters:
    +     *   repo the repo to use when locating the blob.
    +     *   id identity of the blob to locate.
    +     *
    +     * Returns:
    +     *   0 on success; error code otherwise
    +     */
    +    int Lookup(git_repository* repo, const git_oid *id);
    +    /**
    +     * Function: RawContent
    +     *   Get a read-only buffer with the raw content of a blob.
    +     *
    +     * Returns:
    +     *   raw content buffer; NULL if the blob has no contents
    +     */
    +    const void* RawContent();
    +    /**
    +     * Function: RawSize
    +     *   Lookup a blob object from a repository.
    +     *
    +     * Returns:
    +     *   size in bytes
    +     */
    +    int RawSize();
    +    /**
    +     *
    +     * Function: Close
    +     *   Free a blob object.
    +     */
    +    void Close();
    +    /**
    +     *
    +     * Function: CreateFromFile
    +     *   Read a file into the ODB.
    +     *
    +     *   Returns:
    +     *     0 on success, error code otherwise
    +     */
    +    int CreateFromFile(git_oid* oid, git_repository* repo, const char* path);
    +    /**
    +     *
    +     * Function: CreateFromBuffer
    +     *   Read a buffer into the ODB.
    +     *
    +     *   Returns:
    +     *     0 on success, error code otherwise
    +     */
    +    int CreateFromFile(git_oid* oid, git_repository* repo, const void* buffer, size_t len);
     
    -    protected:
    -      /**
    -       * Constructor: GitBlob
    -       */
    -      GitBlob() {};
    -      /**
    -       * Deconstructor: GitBlob
    -       */
    -      ~GitBlob() {};
    -      /**
    -       * Function: New
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> New(const v8::Arguments& args);
    -      /**
    -       * Function: Lookup
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> Lookup(const v8::Arguments& args);
    -      /**
    -       * Function: EIO_Lookup
    -       *
    -       * Parameters:
    -       *   req - an eio_req pointer
    -       *
    -       * Returns:
    -       *   completion code integer
    -       */
    -      static int EIO_Lookup(eio_req* req);
    -      /**
    -       * Function: EIO_AfterLookup
    -       *
    -       * Parameters:
    -       *   req - an eio_req pointer
    -       *
    -       * Returns:
    -       *   completion code integer
    -       */
    -      static int EIO_AfterLookup(eio_req* req);
    -      /**
    -       * Function: RawContent
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> RawContent(const v8::Arguments& args);
    -      /**
    -       * Function: RawSize
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> RawSize(const v8::Arguments& args);
    +  protected:
    +    /**
    +     * Constructor: GitBlob
    +     */
    +    GitBlob() {};
    +    /**
    +     * Deconstructor: GitBlob
    +     */
    +    ~GitBlob() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +    /**
    +     * Function: Lookup
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> Lookup(const v8::Arguments& args);
    +    /**
    +     * Function: EIO_Lookup
    +     *
    +     * Parameters:
    +     *   req - an eio_req pointer
    +     *
    +     * Returns:
    +     *   completion code integer
    +     */
    +    static int EIO_Lookup(eio_req* req);
    +    /**
    +     * Function: EIO_AfterLookup
    +     *
    +     * Parameters:
    +     *   req - an eio_req pointer
    +     *
    +     * Returns:
    +     *   completion code integer
    +     */
    +    static int EIO_AfterLookup(eio_req* req);
    +    /**
    +     * Function: RawContent
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> RawContent(const v8::Arguments& args);
    +    /**
    +     * Function: RawSize
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> RawSize(const v8::Arguments& args);
    +    /**
    +     * Function: Close
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> Close(const v8::Arguments& args);
    +    /**
    +     * Function: CreateFromFile
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> CreateFromFile(const v8::Arguments& args);
    +    /**
    +     * Function: CreateFromBuffer
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> CreateFromBuffer(const v8::Arguments& args);
     
    -    private:
    -      /**
    -       * Variable: blob
    -       *   Internal reference to git_blob object
    -       */
    -      git_blob* blob;
    +  private:
    +    /**
    +     * Variable: blob
    +     *   Internal reference to git_blob object
    +     */
    +    git_blob* blob;
     
    -      /**
    -       * Struct: lookup_request
    -       *   Contains references to the current blob, repo, and oid for a
    -       *   commit lookup, also contains references to an error code post
    -       *   lookup, and a callback function to execute.
    -       */
    -      struct lookup_request {
    -        GitBlob* blob;
    -        Repo* repo;
    -        Oid* oid;
    -        int err;
    -        v8::Persistent<v8::Function> callback;
    -      };
    -  };
    -}
    +    /**
    +     * Struct: lookup_request
    +     *   Contains references to the current blob, repo, and oid for a
    +     *   commit lookup, also contains references to an error code post
    +     *   lookup, and a callback function to execute.
    +     */
    +    struct lookup_request {
    +      GitBlob* blob;
    +      Repo* repo;
    +      Oid* oid;
    +      int err;
    +      v8::Persistent<v8::Function> callback;
    +    };
    +};
     
     #endif
    diff --git a/include/error.h b/include/error.h
    index e1143acac..9f730ed41 100755
    --- a/include/error.h
    +++ b/include/error.h
    @@ -13,68 +13,66 @@
     
     using namespace node;
     
    -namespace {
    -  /**
    -   * Class: GitError
    -   *   Wrapper for libgit2 git_error.
    -   */
    -  class GitError : public ObjectWrap {
    -    public:
    -      /**
    -       * Variable: constructor_template
    -       *   Used to create Node.js constructor.
    -       */
    -      static v8::Persistent<v8::FunctionTemplate> constructor_template;
    -      /**
    -       * Function: Initialize
    -       *   Used to intialize the EventEmitter from Node.js
    -       *
    -       * Parameters:
    -       *   target - v8::Object the Node.js global module object
    -       */
    -      static void Initialize(v8::Handle<v8::Object> target);
    -      /**
    -       * Function: StrError
    -       *   Get a read-only buffer with the raw content of a blob.
    -       *
    -       * Parameters:
    -       *   err - A signed int error code
    -       *
    -       * Returns:
    -       *   a string explaining the error code.
    -       */
    -      const char* StrError(int err);
    -
    -    protected:
    -      /**
    -       * Constructor: GitBlob
    -       */
    -      GitError() {};
    -      /**
    -       * Deconstructor: GitBlob
    -       */
    -      ~GitError() {};
    -      /**
    -       * Function: New
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> New(const v8::Arguments& args);
    -      /**
    -       * Function: StrError
    -       *
    -       * Parameters:
    -       *   args v8::Arguments function call
    -       *
    -       * Returns:
    -       *   v8::Object args.This()
    -       */
    -      static v8::Handle<v8::Value> StrError(const v8::Arguments& args);
    -  };
    -}
    +/**
    + * Class: GitError
    + *   Wrapper for libgit2 git_error.
    + */
    +class GitError : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +    /**
    +     * Function: StrError
    +     *   Get a read-only buffer with the raw content of a blob.
    +     *
    +     * Parameters:
    +     *   err - A signed int error code
    +     *
    +     * Returns:
    +     *   a string explaining the error code.
    +     */
    +    const char* StrError(int err);
     
    +  protected:
    +    /**
    +     * Constructor: GitError
    +     */
    +    GitError() {};
    +    /**
    +     * Deconstructor: GitError
    +     */
    +    ~GitError() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +    /**
    +     * Function: StrError
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> StrError(const v8::Arguments& args);
    +};
    + 
     #endif
    diff --git a/package.json b/package.json
    index 32e7bcf2c..24a88aa5a 100644
    --- a/package.json
    +++ b/package.json
    @@ -22,5 +22,8 @@
       "scripts": {
         "preinstall": "./configure",
         "install": "make"
    +  },
    +  "dependencies": { 
    +    "diff": "1.0.0"
       }
     }
    diff --git a/src/base.cc b/src/base.cc
    index 6215185bd..09fc59f4d 100755
    --- a/src/base.cc
    +++ b/src/base.cc
    @@ -20,6 +20,8 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     #include "tree.h"
     #include "tree_entry.h"
     
    +namespace {
    +
     extern "C" void init(Handle<v8::Object> target) {
       HandleScope scope;
     
    @@ -35,3 +37,5 @@ extern "C" void init(Handle<v8::Object> target) {
       GitTree::Initialize(target);
       GitTreeEntry::Initialize(target);
     }
    +
    +}
    diff --git a/src/blob.cc b/src/blob.cc
    index a4821e3c5..1ba9205ad 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -15,133 +15,166 @@
     using namespace v8;
     using namespace node;
     
    -namespace {
    -  void GitBlob::Initialize (Handle<v8::Object> target) {
    -    HandleScope scope;
    +void GitBlob::Initialize (Handle<v8::Object> target) {
    +  HandleScope scope;
    +
    +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
    +  
    +  constructor_template = Persistent<FunctionTemplate>::New(t);
    +  constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    +  constructor_template->SetClassName(String::NewSymbol("Blob"));
    +
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawContent", RawContent);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawSize", RawSize);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "createFromFile", CreateFromFile);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "createFromBuffer", CreateFromBuffer);
    +
    +  target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction());
    +}
     
    -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    -    
    -    constructor_template = Persistent<FunctionTemplate>::New(t);
    -    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -    constructor_template->SetClassName(String::NewSymbol("Blob"));
    +git_blob* GitBlob::GetValue() {
    +  return this->blob;
    +}
     
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawContent", RawContent);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawSize", RawSize);
    +void GitBlob::SetValue(git_blob* blob) {
    +  this->blob = blob;
    +}
     
    -    target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction());
    -  }
    +int GitBlob::Lookup(git_repository* repo, const git_oid* id) {
    +  return git_blob_lookup(&this->blob, repo, id);
    +}
     
    -  git_blob* GitBlob::GetValue() {
    -    return this->blob;
    -  }
    +const void* GitBlob::RawContent() {
    +  return git_blob_rawcontent(this->blob);
    +}
     
    -  void GitBlob::SetValue(git_blob* blob) {
    -    this->blob = blob;
    -  }
    +int GitBlob::RawSize() {
    +  return git_blob_rawsize(this->blob);
    +}
     
    -  int GitBlob::Lookup(git_repository* repo, const git_oid* id) {
    -    return git_blob_lookup(&this->blob, repo, id);
    -  }
    +void GitBlob::Close() {
    +  git_blob_close(this->blob);
    +}
    +
    +Handle<Value> GitBlob::New(const Arguments& args) {
    +  HandleScope scope;
     
    -  const void* GitBlob::RawContent() {
    -    return git_blob_rawcontent(this->blob);
    +  GitBlob* blob = new GitBlob();
    +  blob->Wrap(args.This());
    +
    +  return args.This();
    +}
    +
    +Handle<Value> GitBlob::RawContent(const Arguments& args) {
    +  HandleScope scope;
    +
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    +
    +  return String::New((const char*)blob->RawContent());
    +}
    +
    +Handle<Value> GitBlob::Lookup(const Arguments& args) {
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    +  Local<Function> callback;
    +
    +  HandleScope scope;
    +
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Repo is required and must be a Object.")));
       }
     
    -  int GitBlob::RawSize() {
    -    return git_blob_rawsize(this->blob);
    +  if(args.Length() == 1 || !args[1]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Oid is required and must be a Object.")));
       }
     
    -  Handle<Value> GitBlob::New(const Arguments& args) {
    -    HandleScope scope;
    +  if(args.Length() == 3 || !args[3]->IsFunction()) {
    +    return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
    +  }
     
    -    GitBlob* blob = new GitBlob();
    -    blob->Wrap(args.This());
    +  callback = Local<Function>::Cast(args[3]);
     
    -    return args.This();
    -  }
    +  lookup_request* ar = new lookup_request();
    +  ar->blob = blob;
    +  ar->repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    +  ar->oid = ObjectWrap::Unwrap<Oid>(args[1]->ToObject());
    +  ar->callback = Persistent<Function>::New(callback);
     
    -  Handle<Value> GitBlob::RawContent(const Arguments& args) {
    -    HandleScope scope;
    +  blob->Ref();
     
    -    GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    +  eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar);
    +  ev_ref(EV_DEFAULT_UC);
     
    -    return String::New((const char*)blob->RawContent());
    -  }
    +  return Undefined();
    +}
     
    -  Handle<Value> GitBlob::Lookup(const Arguments& args) {
    -    GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    -    Local<Function> callback;
    +int GitBlob::EIO_Lookup(eio_req* req) {
    +  lookup_request* ar = static_cast<lookup_request* >(req->data);
     
    -    HandleScope scope;
    +  ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue());
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Repo is required and must be a Object.")));
    -    }
    +  return 0;
    +}
     
    -    if(args.Length() == 1 || !args[1]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Oid is required and must be a Object.")));
    -    }
    +int GitBlob::EIO_AfterLookup(eio_req* req) {
    +  HandleScope scope;
     
    -    if(args.Length() == 3 || !args[3]->IsFunction()) {
    -      return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
    -    }
    +  lookup_request* ar = static_cast<lookup_request* >(req->data);
    +  ev_unref(EV_DEFAULT_UC);
    +  ar->blob->Unref();
     
    -    callback = Local<Function>::Cast(args[3]);
    +  Local<Value> argv[1];
    +  argv[0] = Integer::New(ar->err);
     
    -    lookup_request* ar = new lookup_request();
    -    ar->blob = blob;
    -    ar->repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    -    ar->oid = ObjectWrap::Unwrap<Oid>(args[1]->ToObject());
    -    ar->callback = Persistent<Function>::New(callback);
    +  TryCatch try_catch;
     
    -    blob->Ref();
    +  ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
     
    -    eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar);
    -    ev_ref(EV_DEFAULT_UC);
    +  if(try_catch.HasCaught())
    +    FatalException(try_catch);
    +    
    +  ar->callback.Dispose();
     
    -    return Undefined();
    -  }
    +  delete ar;
     
    -  int GitBlob::EIO_Lookup(eio_req* req) {
    -    lookup_request* ar = static_cast<lookup_request* >(req->data);
    +  return 0;
    +}
     
    -    ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue());
    +Handle<Value> GitBlob::RawSize(const Arguments& args) {
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -    return 0;
    -  }
    +  HandleScope scope;
     
    -  int GitBlob::EIO_AfterLookup(eio_req* req) {
    -    HandleScope scope;
    +  return Integer::New(blob->RawSize());
    +}
     
    -    lookup_request* ar = static_cast<lookup_request* >(req->data);
    -    ev_unref(EV_DEFAULT_UC);
    -    ar->blob->Unref();
    +Handle<Value> GitBlob::Close(const Arguments& args) {
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -    Local<Value> argv[1];
    -    argv[0] = Integer::New(ar->err);
    +  HandleScope scope;
     
    -    TryCatch try_catch;
    +  blob->Close();
     
    -    ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
    +  return Undefined();
    +}
     
    -    if(try_catch.HasCaught())
    -      FatalException(try_catch);
    -      
    -    ar->callback.Dispose();
    +Handle<Value> GitBlob::CreateFromFile(const Arguments& args) {
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -    delete ar;
    +  HandleScope scope;
     
    -    return 0;
    -  }
    +  blob->Close();
     
    -  Handle<Value> GitBlob::RawSize(const Arguments& args) {
    -    HandleScope scope;
    +  return Undefined();
    +}
     
    -    GitBlob* blob = new GitBlob();
    +Handle<Value> GitBlob::CreateFromBuffer(const Arguments& args) {
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -    return Integer::New(blob->RawSize());
    -  }
    +  HandleScope scope;
     
    -  Persistent<FunctionTemplate> GitBlob::constructor_template;
    +  return Undefined();
     }
    +
    +Persistent<FunctionTemplate> GitBlob::constructor_template;
    diff --git a/src/commit.cc b/src/commit.cc
    index a123b3ca9..a12991791 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -1,6 +1,7 @@
     /*
    -Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    -*/
    + * Copyright 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    + * Dual licensed under the MIT and GPL licenses.
    + */
     
     #include <string.h>
     #include <v8.h>
    @@ -19,349 +20,347 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -namespace {
    -  void GitCommit::Initialize(Handle<Object> target) {
    -    HandleScope scope;
    +void GitCommit::Initialize(Handle<Object> target) {
    +  HandleScope scope;
    +
    +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
    +  
    +  constructor_template = Persistent<FunctionTemplate>::New(t);
    +  constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    +  constructor_template->SetClassName(String::NewSymbol("Commit"));
    +
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "messageShort", MessageShort);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "message", Message);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "time", Time);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "timeOffset", TimeOffset);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "author", Author);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "tree", Tree);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "parentCount", ParentCount);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "parent", Parent);
    +
    +  target->Set(String::NewSymbol("Commit"), constructor_template->GetFunction());
    +}
     
    -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    -    
    -    constructor_template = Persistent<FunctionTemplate>::New(t);
    -    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -    constructor_template->SetClassName(String::NewSymbol("Commit"));
    -
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "messageShort", MessageShort);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "message", Message);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "time", Time);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "timeOffset", TimeOffset);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "author", Author);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "tree", Tree);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "parentCount", ParentCount);
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "parent", Parent);
    -
    -    target->Set(String::NewSymbol("Commit"), constructor_template->GetFunction());
    -  }
    +git_commit* GitCommit::GetValue() {
    +  return this->commit;
    +}
     
    -  git_commit* GitCommit::GetValue() {
    -    return this->commit;
    -  }
    +void GitCommit::SetValue(git_commit* commit) {
    +  this->commit = commit;
    +}
     
    -  void GitCommit::SetValue(git_commit* commit) {
    -    this->commit = commit;
    -  }
    +int GitCommit::Lookup(git_oid* oid) {
    +  git_commit* commit;
     
    -  int GitCommit::Lookup(git_oid* oid) {
    -    git_commit* commit;
    +  //this->oid = oid;
     
    -    //this->oid = oid;
    +  //int err = git_commit_lookup(&commit, this->repo, oid);
     
    -    //int err = git_commit_lookup(&commit, this->repo, oid);
    +  //this->commit = commit;
     
    -    //this->commit = commit;
    +  //return err;
    +  return 0;
    +}
     
    -    //return err;
    -    return 0;
    -  }
    +const git_oid* GitCommit::Id() {
    +  return git_commit_id(this->commit);
    +}
     
    -  const git_oid* GitCommit::Id() {
    -    return git_commit_id(this->commit);
    -  }
    +const char* GitCommit::MessageShort() {
    +  return git_commit_message_short(this->commit);
    +}
     
    -  const char* GitCommit::MessageShort() {
    -    return git_commit_message_short(this->commit);
    -  }
    +const char* GitCommit::Message() {
    +  return git_commit_message(this->commit);
    +}
     
    -  const char* GitCommit::Message() {
    -    return git_commit_message(this->commit);
    -  }
    +time_t GitCommit::Time() {
    +  return git_commit_time(this->commit);
    +}
     
    -  time_t GitCommit::Time() {
    -    return git_commit_time(this->commit);
    -  }
    +int GitCommit::TimeOffset() {
    +  return git_commit_time_offset(this->commit);
    +}
     
    -  int GitCommit::TimeOffset() {
    -    return git_commit_time_offset(this->commit);
    -  }
    +const git_signature* GitCommit::Committer() {
    +  return git_commit_author(this->commit);
    +}
     
    -  const git_signature* GitCommit::Committer() {
    -    return git_commit_author(this->commit);
    -  }
    +const git_signature* GitCommit::Author() {
    +  return git_commit_author(this->commit);
    +}
     
    -  const git_signature* GitCommit::Author() {
    -    return git_commit_author(this->commit);
    -  }
    +int GitCommit::Tree(git_tree** tree) {
    +  return git_commit_tree(tree, this->commit);
    +}
     
    -  int GitCommit::Tree(git_tree** tree) {
    -    return git_commit_tree(tree, this->commit);
    -  }
    +unsigned int GitCommit::ParentCount() {
    +  return git_commit_parentcount(this->commit);
    +}
     
    -  unsigned int GitCommit::ParentCount() {
    -    return git_commit_parentcount(this->commit);
    -  }
    +int GitCommit::Parent(git_commit** commit, int pos) {
    +  return git_commit_parent(commit, this->commit, pos);
    +}
     
    -  int GitCommit::Parent(git_commit** commit, int pos) {
    -    return git_commit_parent(commit, this->commit, pos);
    -  }
    +Handle<Value> GitCommit::New(const Arguments& args) {
    +  HandleScope scope;
    +
    +  GitCommit *commit = new GitCommit();
    +  commit->Wrap(args.This());
     
    -  Handle<Value> GitCommit::New(const Arguments& args) {
    -    HandleScope scope;
    +  return args.This();
    +}
    +
    +Handle<Value> GitCommit::Lookup(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  Local<Function> callback;
     
    -    GitCommit *commit = new GitCommit();
    -    commit->Wrap(args.This());
    +  HandleScope scope;
     
    -    return args.This();
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Handle<Value> GitCommit::Lookup(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -    Local<Function> callback;
    +  if(args.Length() == 1 || !args[1]->IsFunction()) {
    +    return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
    +  }
     
    -    HandleScope scope;
    +  callback = Local<Function>::Cast(args[1]);
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
    -    }
    +  lookup_request *ar = new lookup_request();
    +  ar->commit = commit;
    +  ar->oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  ar->callback = Persistent<Function>::New(callback);
     
    -    if(args.Length() == 1 || !args[1]->IsFunction()) {
    -      return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
    -    }
    +  commit->Ref();
     
    -    callback = Local<Function>::Cast(args[1]);
    +  eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar);
    +  ev_ref(EV_DEFAULT_UC);
     
    -    lookup_request *ar = new lookup_request();
    -    ar->commit = commit;
    -    ar->oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    -    ar->callback = Persistent<Function>::New(callback);
    +  return Undefined();
    +}
     
    -    commit->Ref();
    +int GitCommit::EIO_Lookup(eio_req *req) {
    +  lookup_request *ar = static_cast<lookup_request *>(req->data);
     
    -    eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar);
    -    ev_ref(EV_DEFAULT_UC);
    +  ar->err = ar->commit->Lookup(ar->oid->GetValue());
     
    -    return Undefined();
    -  }
    +  return 0;
    +}
     
    -  int GitCommit::EIO_Lookup(eio_req *req) {
    -    lookup_request *ar = static_cast<lookup_request *>(req->data);
    +int GitCommit::EIO_AfterLookup(eio_req *req) {
    +  HandleScope scope;
     
    -    ar->err = ar->commit->Lookup(ar->oid->GetValue());
    +  lookup_request *ar = static_cast<lookup_request *>(req->data);
    +  ev_unref(EV_DEFAULT_UC);
    +  ar->commit->Unref();
     
    -    return 0;
    -  }
    +  Local<Value> argv[0];
    +  argv[0] = Integer::New(ar->err);
     
    -  int GitCommit::EIO_AfterLookup(eio_req *req) {
    -    HandleScope scope;
    +  TryCatch try_catch;
     
    -    lookup_request *ar = static_cast<lookup_request *>(req->data);
    -    ev_unref(EV_DEFAULT_UC);
    -    ar->commit->Unref();
    +  ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
     
    -    Local<Value> argv[0];
    -    argv[0] = Integer::New(ar->err);
    +  if(try_catch.HasCaught())
    +    FatalException(try_catch);
    +    
    +  ar->callback.Dispose();
     
    -    TryCatch try_catch;
    +  delete ar;
     
    -    ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
    +  return 0;
    +}
     
    -    if(try_catch.HasCaught())
    -      FatalException(try_catch);
    -      
    -    ar->callback.Dispose();
    +Handle<Value> GitCommit::Id(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -    delete ar;
    +  HandleScope scope;
     
    -    return 0;
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Handle<Value> GitCommit::Id(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
     
    -    HandleScope scope;
    +  oid->SetValue(const_cast<git_oid *>(commit->Id()));
    +  
    +  return Undefined();
    +}
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
    -    }
    +Handle<Value> GitCommit::MessageShort(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -    Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  HandleScope scope;
    +  
    +  return String::New(commit->MessageShort());
    +}
     
    -    oid->SetValue(const_cast<git_oid *>(commit->Id()));
    -    
    -    return Undefined();
    -  }
    +Handle<Value> GitCommit::Message(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -  Handle<Value> GitCommit::MessageShort(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  HandleScope scope;
    +  
    +  return String::New(commit->Message());
    +}
     
    -    HandleScope scope;
    -    
    -    return String::New(commit->MessageShort());
    -  }
    +Handle<Value> GitCommit::Time(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -  Handle<Value> GitCommit::Message(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  HandleScope scope;
    +  
    +  return Integer::New(commit->Time());
    +}
     
    -    HandleScope scope;
    -    
    -    return String::New(commit->Message());
    -  }
    +Handle<Value> GitCommit::TimeOffset(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -  Handle<Value> GitCommit::Time(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  HandleScope scope;
    +  
    +  return Integer::New(commit->TimeOffset());
    +}
     
    -    HandleScope scope;
    -    
    -    return Integer::New(commit->Time());
    -  }
    +Handle<Value> GitCommit::Committer(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -  Handle<Value> GitCommit::TimeOffset(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  HandleScope scope;
     
    -    HandleScope scope;
    -    
    -    return Integer::New(commit->TimeOffset());
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
     
    -  Handle<Value> GitCommit::Committer(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
     
    -    HandleScope scope;
    +  sig->SetValue(const_cast<git_signature *>(commit->Committer()));
    +  
    +  return Undefined();
    +}
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
    -    }
    +Handle<Value> GitCommit::Author(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -    Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
    +  HandleScope scope;
     
    -    sig->SetValue(const_cast<git_signature *>(commit->Committer()));
    -    
    -    return Undefined();
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
     
    -  Handle<Value> GitCommit::Author(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
     
    -    HandleScope scope;
    +  sig->SetValue(const_cast<git_signature *>(commit->Author()));
    +  
    +  return Undefined();
    +}
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
    -    }
    +Handle<Value> GitCommit::Tree(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -    Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
    +  HandleScope scope;
     
    -    sig->SetValue(const_cast<git_signature *>(commit->Author()));
    -    
    -    return Undefined();
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
       }
     
    -  Handle<Value> GitCommit::Tree(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  git_tree* in;
    +  GitTree* tree = ObjectWrap::Unwrap<GitTree>(args[0]->ToObject());
     
    -    HandleScope scope;
    +  int err = commit->Tree(&in);
    +  tree->SetValue(in);
     
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
    -    }
    +  return Integer::New(err);
    +}
    +//Handle<Value> GitCommit::Tree(const Arguments& args) {
    +//  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +//  Local<Function> callback;
    +//
    +//  HandleScope scope;
    +//
    +//  if(args.Length() == 0 || !args[0]->IsObject()) {
    +//    return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
    +//  }
    +//
    +//  callback = Local<Function>::Cast(args[1]);
    +//
    +//  tree_request *ar = new tree_request();
    +//  ar->commit = commit;
    +//  ar->repo = ObjectWrap::Unwrap<Tree>(args[0]->ToObject());
    +//  ar->callback = Persistent<Function>::New(callback);
    +//
    +//  commit->Ref();
    +//
    +//  eio_custom(EIO_Tree, EIO_PRI_DEFAULT, EIO_AfterTree, ar);
    +//  ev_ref(EV_DEFAULT_UC);
    +//
    +//  return Undefined();
    +//}
    +//
    +//int GitCommit::EIO_Tree(eio_req *req) {
    +//  tree_request *ar = static_cast<tree_request *>(req->data);
    +//
    +//  git_tree *tree = ar->commit->Tree();
    +//
    +//  ar->tree->SetValue(tree);
    +//
    +//  return 0;
    +//}
    +//
    +//int GitCommit::EIO_AfterTree(eio_req *req) {
    +//  HandleScope scope;
    +//
    +//  tree_request *ar = static_cast<tree_request *>(req->data);
    +//  ev_unref(EV_DEFAULT_UC);
    +//  ar->commit->Unref();
    +//
    +//  Local<Value> argv[1];
    +//
    +//  TryCatch try_catch;
    +//
    +//  ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
    +//
    +//  if(try_catch.HasCaught())
    +//    FatalException(try_catch);
    +//    
    +//  ar->err.Dispose();
    +//  ar->callback.Dispose();
    +//
    +//  delete ar;
    +//
    +//  return 0;
    +//}
    +
    +Handle<Value> GitCommit::ParentCount(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
    +  HandleScope scope;
    +
    +  unsigned int count = commit->ParentCount();
    +
    +  return Integer::New(count);
    +}
     
    -    git_tree* in;
    -    GitTree* tree = ObjectWrap::Unwrap<GitTree>(args[0]->ToObject());
    +Handle<Value> GitCommit::Parent(const Arguments& args) {
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    -    int err = commit->Tree(&in);
    -    tree->SetValue(in);
    +  HandleScope scope;
     
    -    return Integer::New(err);
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Commit is required and must be an Object.")));
       }
    -  //Handle<Value> GitCommit::Tree(const Arguments& args) {
    -  //  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -  //  Local<Function> callback;
    -  //
    -  //  HandleScope scope;
    -  //
    -  //  if(args.Length() == 0 || !args[0]->IsObject()) {
    -  //    return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
    -  //  }
    -  //
    -  //  callback = Local<Function>::Cast(args[1]);
    -  //
    -  //  tree_request *ar = new tree_request();
    -  //  ar->commit = commit;
    -  //  ar->repo = ObjectWrap::Unwrap<Tree>(args[0]->ToObject());
    -  //  ar->callback = Persistent<Function>::New(callback);
    -  //
    -  //  commit->Ref();
    -  //
    -  //  eio_custom(EIO_Tree, EIO_PRI_DEFAULT, EIO_AfterTree, ar);
    -  //  ev_ref(EV_DEFAULT_UC);
    -  //
    -  //  return Undefined();
    -  //}
    -  //
    -  //int GitCommit::EIO_Tree(eio_req *req) {
    -  //  tree_request *ar = static_cast<tree_request *>(req->data);
    -  //
    -  //  git_tree *tree = ar->commit->Tree();
    -  //
    -  //  ar->tree->SetValue(tree);
    -  //
    -  //  return 0;
    -  //}
    -  //
    -  //int GitCommit::EIO_AfterTree(eio_req *req) {
    -  //  HandleScope scope;
    -  //
    -  //  tree_request *ar = static_cast<tree_request *>(req->data);
    -  //  ev_unref(EV_DEFAULT_UC);
    -  //  ar->commit->Unref();
    -  //
    -  //  Local<Value> argv[1];
    -  //
    -  //  TryCatch try_catch;
    -  //
    -  //  ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
    -  //
    -  //  if(try_catch.HasCaught())
    -  //    FatalException(try_catch);
    -  //    
    -  //  ar->err.Dispose();
    -  //  ar->callback.Dispose();
    -  //
    -  //  delete ar;
    -  //
    -  //  return 0;
    -  //}
    -
    -  Handle<Value> GitCommit::ParentCount(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
    -    HandleScope scope;
    -
    -    unsigned int count = commit->ParentCount();
    -
    -    return Integer::New(count);
    -  }
    -
    -  Handle<Value> GitCommit::Parent(const Arguments& args) {
    -    GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
    -    HandleScope scope;
    -
    -    if(args.Length() == 0 || !args[0]->IsObject()) {
    -      return ThrowException(Exception::Error(String::New("Commit is required and must be an Object.")));
    -    }
     
    -    if(args.Length() == 1 || !args[1]->IsNumber()) {
    -      return ThrowException(Exception::Error(String::New("Position is required and must be a Number.")));
    -    }
    -
    -    GitCommit* out = ObjectWrap::Unwrap<GitCommit>(args[0]->ToObject());
    -    git_commit* in;
    -    int index = args[1]->ToInteger()->Value();
    +  if(args.Length() == 1 || !args[1]->IsNumber()) {
    +    return ThrowException(Exception::Error(String::New("Position is required and must be a Number.")));
    +  }
     
    -    int err = commit->Parent(&in, index);
    -    out->SetValue(in);
    +  GitCommit* out = ObjectWrap::Unwrap<GitCommit>(args[0]->ToObject());
    +  git_commit* in;
    +  int index = args[1]->ToInteger()->Value();
     
    -    return Integer::New(err);
    -  }
    +  int err = commit->Parent(&in, index);
    +  out->SetValue(in);
     
    -  Persistent<FunctionTemplate> GitCommit::constructor_template;
    +  return Integer::New(err);
     }
    +
    +Persistent<FunctionTemplate> GitCommit::constructor_template;
    diff --git a/src/commit.h b/src/commit.h
    index 2b0b2d69a..445c98421 100755
    --- a/src/commit.h
    +++ b/src/commit.h
    @@ -20,80 +20,78 @@
     using namespace node;
     using namespace v8;
     
    -namespace {
    -  /**
    -   * Class wrapper for libgit2 git_commit
    -   */
    -  class GitCommit : public EventEmitter {
    -    public:
    -      /**
    -       * v8::FunctionTemplate used to create Node.js constructor
    -       */
    -      static Persistent<FunctionTemplate> constructor_template;
    -
    -      /**
    -       * Used to intialize the EventEmitter from Node.js
    -       *
    -       * @param target v8::Object the Node.js module object
    -       */
    -      static void Initialize (Handle<v8::Object> target);
    -
    -      git_commit* GetValue();
    -      void SetValue(git_commit* commit);
    -      int Lookup(git_oid* oid);
    -      const git_oid* Id();
    -      const char* MessageShort();
    -      const char* Message();
    -      time_t Time();
    -      int TimeOffset();
    -      const git_signature* Committer();
    -      const git_signature* Author();
    -      int Tree(git_tree** tree);
    -      unsigned int ParentCount();
    -      int Parent(git_commit** commit, int pos);
    -
    -    protected:
    -      GitCommit() {}
    -      ~GitCommit() {}
    -
    -      static Handle<Value> New(const Arguments& args);
    -
    -      static Handle<Value> Lookup(const Arguments& args);
    -      static int EIO_Lookup(eio_req *req);
    -      static int EIO_AfterLookup(eio_req *req);
    -
    -      static Handle<Value> Id(const Arguments& args);
    -      static Handle<Value> MessageShort(const Arguments& args);
    -      static Handle<Value> Message(const Arguments& args);
    -      static Handle<Value> Time(const Arguments& args);
    -      static Handle<Value> TimeOffset(const Arguments& args);
    -      static Handle<Value> Committer(const Arguments& args);
    -      static Handle<Value> Author(const Arguments& args);
    -
    -      static Handle<Value> Tree(const Arguments& args);
    -      static int EIO_Tree(eio_req* req);
    -      static int EIO_AfterTree(eio_req* req);
    -
    -      static Handle<Value> ParentCount(const Arguments& args);
    -      static Handle<Value> Parent(const Arguments& args);
    -
    -    private:
    -      git_commit* commit;
    -      git_repository* repo;
    -      git_oid* oid;
    -
    -      struct lookup_request {
    -        GitCommit* commit;
    -        Oid* oid;
    -        int err;
    -        Persistent<Function> callback;
    -      };
    -
    -      //struct tree_request {
    -      //  GitCommit* commit;
    -      //  Tree* tree;
    -      //  Persistent<Function> callback;
    -      //};
    -  };
    -}
    +/**
    + * Class wrapper for libgit2 git_commit
    + */
    +class GitCommit : public EventEmitter {
    +  public:
    +    /**
    +     * v8::FunctionTemplate used to create Node.js constructor
    +     */
    +    static Persistent<FunctionTemplate> constructor_template;
    +
    +    /**
    +     * Used to intialize the EventEmitter from Node.js
    +     *
    +     * @param target v8::Object the Node.js module object
    +     */
    +    static void Initialize (Handle<v8::Object> target);
    +
    +    git_commit* GetValue();
    +    void SetValue(git_commit* commit);
    +    int Lookup(git_oid* oid);
    +    const git_oid* Id();
    +    const char* MessageShort();
    +    const char* Message();
    +    time_t Time();
    +    int TimeOffset();
    +    const git_signature* Committer();
    +    const git_signature* Author();
    +    int Tree(git_tree** tree);
    +    unsigned int ParentCount();
    +    int Parent(git_commit** commit, int pos);
    +
    +  protected:
    +    GitCommit() {}
    +    ~GitCommit() {}
    +
    +    static Handle<Value> New(const Arguments& args);
    +
    +    static Handle<Value> Lookup(const Arguments& args);
    +    static int EIO_Lookup(eio_req *req);
    +    static int EIO_AfterLookup(eio_req *req);
    +
    +    static Handle<Value> Id(const Arguments& args);
    +    static Handle<Value> MessageShort(const Arguments& args);
    +    static Handle<Value> Message(const Arguments& args);
    +    static Handle<Value> Time(const Arguments& args);
    +    static Handle<Value> TimeOffset(const Arguments& args);
    +    static Handle<Value> Committer(const Arguments& args);
    +    static Handle<Value> Author(const Arguments& args);
    +
    +    static Handle<Value> Tree(const Arguments& args);
    +    static int EIO_Tree(eio_req* req);
    +    static int EIO_AfterTree(eio_req* req);
    +
    +    static Handle<Value> ParentCount(const Arguments& args);
    +    static Handle<Value> Parent(const Arguments& args);
    +
    +  private:
    +    git_commit* commit;
    +    git_repository* repo;
    +    git_oid* oid;
    +
    +    struct lookup_request {
    +      GitCommit* commit;
    +      Oid* oid;
    +      int err;
    +      Persistent<Function> callback;
    +    };
    +
    +    //struct tree_request {
    +    //  GitCommit* commit;
    +    //  Tree* tree;
    +    //  Persistent<Function> callback;
    +    //};
    +};
     #endif
    diff --git a/src/error.cc b/src/error.cc
    index d46974f5d..f4d86eba1 100755
    --- a/src/error.cc
    +++ b/src/error.cc
    @@ -14,47 +14,45 @@
     using namespace v8;
     using namespace node;
     
    -namespace {
    -  void GitError::Initialize (Handle<v8::Object> target) {
    -    HandleScope scope;
    +void GitError::Initialize (Handle<v8::Object> target) {
    +  HandleScope scope;
     
    -    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    -    
    -    constructor_template = Persistent<FunctionTemplate>::New(t);
    -    constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -    constructor_template->SetClassName(String::NewSymbol("Error"));
    +  Local<FunctionTemplate> t = FunctionTemplate::New(New);
    +  
    +  constructor_template = Persistent<FunctionTemplate>::New(t);
    +  constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    +  constructor_template->SetClassName(String::NewSymbol("Error"));
     
    -    NODE_SET_PROTOTYPE_METHOD(constructor_template, "strError", StrError);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "strError", StrError);
     
    -    target->Set(String::NewSymbol("Error"), constructor_template->GetFunction());
    -  }
    -
    -  const char* GitError::StrError(int err) {
    -    return git_strerror(err);
    -  }
    -
    -  Handle<Value> GitError::New(const Arguments& args) {
    -    HandleScope scope;
    +  target->Set(String::NewSymbol("Error"), constructor_template->GetFunction());
    +}
     
    -    GitError *error = new GitError();
    -    error->Wrap(args.This());
    +const char* GitError::StrError(int err) {
    +  return git_strerror(err);
    +}
     
    -    return args.This();
    -  }
    +Handle<Value> GitError::New(const Arguments& args) {
    +  HandleScope scope;
     
    -  Handle<Value> GitError::StrError(const Arguments& args) {
    -    GitError* error = ObjectWrap::Unwrap<GitError>(args.This());
    +  GitError *error = new GitError();
    +  error->Wrap(args.This());
     
    -    HandleScope scope;
    +  return args.This();
    +}
     
    -    if(args.Length() == 0 || !args[0]->IsNumber()) {
    -      return ThrowException(Exception::Error(String::New("Error is required and must be a Number.")));
    -    }
    +Handle<Value> GitError::StrError(const Arguments& args) {
    +  GitError* error = ObjectWrap::Unwrap<GitError>(args.This());
     
    -    Local<Integer> err = Local<Integer>::Cast(args[0]);
    +  HandleScope scope;
     
    -    return String::New(error->StrError(err->Value()));
    +  if(args.Length() == 0 || !args[0]->IsNumber()) {
    +    return ThrowException(Exception::Error(String::New("Error is required and must be a Number.")));
       }
     
    -  Persistent<FunctionTemplate> GitError::constructor_template;
    +  Local<Integer> err = Local<Integer>::Cast(args[0]);
    +
    +  return String::New(error->StrError(err->Value()));
     }
    +
    +Persistent<FunctionTemplate> GitError::constructor_template;
    diff --git a/src/tree.cc b/src/tree.cc
    index 1904cf7d9..8f1989120 100755
    --- a/src/tree.cc
    +++ b/src/tree.cc
    @@ -28,7 +28,6 @@ void GitTree::Initialize (Handle<v8::Object> target) {
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "entryByIndex", EntryByIndex);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "entryByName", EntryByName);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "sortEntries", EntryCount);
    -  NODE_SET_PROTOTYPE_METHOD(constructor_template, "clearEntries", ClearEntries);
     
       target->Set(String::NewSymbol("Tree"), constructor_template->GetFunction());
     }
    @@ -41,10 +40,6 @@ void GitTree::SetValue(git_tree* tree) {
       this->tree = tree;
     }
     
    -int GitTree::New(git_repository* repo) {
    -  return git_tree_new(&this->tree, repo);
    -}
    -
     size_t GitTree::EntryCount() {
       return git_tree_entrycount(this->tree);
     }
    @@ -62,26 +57,13 @@ int GitTree::SortEntries() {
       return 0;
     }
     
    -void GitTree::ClearEntries() {
    -  git_tree_clear_entries(this->tree);
    -}
    -
     Handle<Value> GitTree::New(const Arguments& args) {
       HandleScope scope;
     
       GitTree *tree = new GitTree();
     
    -  if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
    -  }
    -
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    -  int err = tree->New((git_repository *)repo);
    -    
       tree->Wrap(args.This());
     
    -  args.This()->Set(String::New("error"), Integer::New(err));
    -
       return args.This();
     }
     
    diff --git a/src/tree.h b/src/tree.h
    index c4d0579d8..2d6da237f 100755
    --- a/src/tree.h
    +++ b/src/tree.h
    @@ -33,29 +33,18 @@ class GitTree : public EventEmitter {
          * @param target v8::Object the Node.js module object
          */
         static void Initialize(Handle<v8::Object> target);
    -
    -    /**
    -     * Creates new internal git_tree reference
    -     *
    -     * @param repo the repo to use when creating the tree.
    -     * @return 0 on success; error code otherwise
    -     */
    -    int New(git_repository* repo);
    -
         /**
          * Accessor for GitTree
          *
          * @return the internal git_tree reference
          */
         git_tree* GetValue();
    -
         /**
          * Mutator for GitTree
          *
          * @param obj a git_tree object
          */
         void SetValue(git_tree* tree);
    -
         /**
          * Lookup a tree object from a repository.
          *
    @@ -66,14 +55,12 @@ class GitTree : public EventEmitter {
          * @return 0 on success; error code otherwise
          */
         int Lookup(git_tree** tree, git_repository* repo, const git_oid* id);
    -
         /**
          * Get number of entries in the looked up tree.
          *
          * @return number of entries
          */
         size_t EntryCount();
    -
         /**
          * Get entry by index in the looked up tree.
          *
    @@ -93,12 +80,10 @@ class GitTree : public EventEmitter {
          * Constructor
          */
         GitTree() {};
    -
         /**
          * Deconstructor
          */
         ~GitTree() {};
    -
         /**
          * Creates a new instance of GitTree to Node.js
          *
    diff --git a/src/tree_entry.cc b/src/tree_entry.cc
    index f3c4f46d7..e5e34c7f9 100644
    --- a/src/tree_entry.cc
    +++ b/src/tree_entry.cc
    @@ -44,7 +44,8 @@ const git_oid* GitTreeEntry::Id() {
     }
     
     int GitTreeEntry::ToObject(git_object** obj) {
    -  return git_tree_entry_2object(obj, this->entry);
    +  //TODO: Implement correct arguments
    +  //return git_tree_entry_2object(obj, this->entry);
     }
     
     Handle<Value> GitTreeEntry::New(const Arguments& args) {
    @@ -90,7 +91,7 @@ Handle<Value> GitTreeEntry::ToObject(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Blob is required and must be an Object.")));
       }
     
    -  Blob* blob = ObjectWrap::Unwrap<Blob>(args[0]->ToObject());
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args[0]->ToObject());
     
       git_object* out;
       entry->ToObject(&out);
    diff --git a/vendor/libgit2/README.md b/vendor/libgit2/README.md
    index 05378685b..1254adcd9 100644
    --- a/vendor/libgit2/README.md
    +++ b/vendor/libgit2/README.md
    @@ -127,7 +127,9 @@ Here are the bindings to libgit2 that are currently available:
     * GitForDelphi (Delphi bindings) <https://github.com/libgit2/GitForDelphi>
     * node-gitteh (Node.js bindings) <https://github.com/libgit2/node-gitteh>
     * nodegit (Node.js bindings) <https://github.com/tbranyen/nodegit>
    +* go-git (Go bindings) <https://github.com/str1ngs/go-git>
     * libqgit2 (C++ QT bindings) <https://projects.kde.org/projects/playground/libs/libqgit2/>
    +* libgit2-ocaml (ocaml bindings) <https://github.com/burdges/libgit2-ocaml>
     * Geef (Erlang bindings) <https://github.com/schacon/geef>
     
     If you start another language binding to libgit2, please let us know so
    diff --git a/vendor/libgit2/include/git2.h b/vendor/libgit2/include/git2.h
    index 248c2ba39..29fa98e18 100644
    --- a/vendor/libgit2/include/git2.h
    +++ b/vendor/libgit2/include/git2.h
    @@ -26,7 +26,7 @@
     #ifndef INCLUDE_git_git_h__
     #define INCLUDE_git_git_h__
     
    -#define LIBGIT2_VERSION "0.10.0"
    +#define LIBGIT2_VERSION "0.11.0"
     #define LIBGIT2_VER_MAJOR 0
     #define LIBGIT2_VER_MINOR 10
     #define LIBGIT2_VER_REVISION 0
    diff --git a/vendor/libgit2/include/git2/blob.h b/vendor/libgit2/include/git2/blob.h
    index 3cd1467bf..0e05d6f89 100644
    --- a/vendor/libgit2/include/git2/blob.h
    +++ b/vendor/libgit2/include/git2/blob.h
    @@ -52,6 +52,24 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
     	return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB);
     }
     
    +/**
    + * Close an open blob
    + *
    + * This is a wrapper around git_object_close()
    + *
    + * IMPORTANT:
    + * It *is* necessary to call this method when you stop
    + * using a blob. Failure to do so will cause a memory leak.
    + *
    + * @param blob the blob to close
    + */
    +
    +GIT_INLINE(void) git_blob_close(git_blob *blob)
    +{
    +	git_object_close((git_object *) blob);
    +}
    +
    +
     /**
      * Get a read-only buffer with the raw content of a blob.
      *
    diff --git a/vendor/libgit2/include/git2/commit.h b/vendor/libgit2/include/git2/commit.h
    index ba18a5b39..c09b34843 100644
    --- a/vendor/libgit2/include/git2/commit.h
    +++ b/vendor/libgit2/include/git2/commit.h
    @@ -53,6 +53,23 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
     	return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT);
     }
     
    +/**
    + * Close an open commit
    + *
    + * This is a wrapper around git_object_close()
    + *
    + * IMPORTANT:
    + * It *is* necessary to call this method when you stop
    + * using a commit. Failure to do so will cause a memory leak.
    + *
    + * @param commit the commit to close
    + */
    +
    +GIT_INLINE(void) git_commit_close(git_commit *commit)
    +{
    +	git_object_close((git_object *) commit);
    +}
    +
     /**
      * Get the id of a commit.
      *
    @@ -83,7 +100,7 @@ GIT_EXTERN(const char *) git_commit_message(git_commit *commit);
      * @param commit a previously loaded commit.
      * @return the time of a commit
      */
    -GIT_EXTERN(time_t) git_commit_time(git_commit *commit);
    +GIT_EXTERN(git_time_t) git_commit_time(git_commit *commit);
     
     /**
      * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
    diff --git a/vendor/libgit2/include/git2/common.h b/vendor/libgit2/include/git2/common.h
    index 7cfb8982e..57ab9c1ff 100644
    --- a/vendor/libgit2/include/git2/common.h
    +++ b/vendor/libgit2/include/git2/common.h
    @@ -119,13 +119,13 @@
     /** The object type is invalid or doesn't match */
     #define GIT_EINVALIDTYPE (GIT_ERROR - 8)
     
    -/** The object cannot be written that because it's missing internal data */
    +/** The object cannot be written because it's missing internal data */
     #define GIT_EMISSINGOBJDATA (GIT_ERROR - 9)
     
     /** The packfile for the ODB is corrupted */
     #define GIT_EPACKCORRUPTED (GIT_ERROR - 10)
     
    -/** Failed to adquire or release a file lock */
    +/** Failed to acquire or release a file lock */
     #define GIT_EFLOCKFAIL (GIT_ERROR - 11)
     
     /** The Z library failed to inflate/deflate an object's data */
    @@ -146,7 +146,7 @@
     /** The specified symbolic reference is too deeply nested */
     #define GIT_ETOONESTEDSYMREF (GIT_ERROR - 17)
     
    -/** The pack-refs file is either corrupted of its format is not currently supported */
    +/** The pack-refs file is either corrupted or its format is not currently supported */
     #define GIT_EPACKEDREFSCORRUPTED (GIT_ERROR - 18)
     
     /** The path is invalid */
    diff --git a/vendor/libgit2/include/git2/index.h b/vendor/libgit2/include/git2/index.h
    index 605740c10..599512f8a 100644
    --- a/vendor/libgit2/include/git2/index.h
    +++ b/vendor/libgit2/include/git2/index.h
    @@ -91,8 +91,8 @@ GIT_EXTERN(int) git_index_open_bare(git_index **index, const char *index_path);
      * Open the Index inside the git repository pointed
      * by 'repo'.
      *
    + * @param index the pointer for the new index
      * @param repo the git repo which owns the index
    - * @param index_path the path to the index file in disk
      * @return 0 on success; error code otherwise
      */
     GIT_EXTERN(int) git_index_open_inrepo(git_index **index, git_repository *repo);
    @@ -132,7 +132,7 @@ GIT_EXTERN(int) git_index_read(git_index *index);
     GIT_EXTERN(int) git_index_write(git_index *index);
     
     /**
    - * Find the first index of any entires which point to given
    + * Find the first index of any entries which point to given
      * path in the Git index.
      *
      * @param index an existing index object
    diff --git a/vendor/libgit2/include/git2/odb.h b/vendor/libgit2/include/git2/odb.h
    index 8926b446c..1d351beea 100644
    --- a/vendor/libgit2/include/git2/odb.h
    +++ b/vendor/libgit2/include/git2/odb.h
    @@ -156,6 +156,26 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *d
      */
     GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
     
    +/**
    + * Write an object directly into the ODB
    + *
    + * This method writes a full object straight into the ODB.
    + * For most cases, it is preferred to write objects through a write
    + * stream, which is both faster and less memory intensive, specially
    + * for big objects.
    + *
    + * This method is provided for compatibility with custom backends
    + * which are not able to support streaming writes
    + *
    + * @param oid pointer to store the OID result of the write
    + * @param odb object database where to store the object
    + * @param data buffer with the data to storr
    + * @param len size of the buffer
    + * @param type type of the data to store
    + * @return 0 on success; error code otherwise
    + */
    +GIT_EXTERN(int) git_odb_write(git_oid *oid, git_odb *odb, const void *data, size_t len, git_otype type);
    +
     /**
      * Open a stream to write an object into the ODB
      *
    @@ -180,7 +200,7 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
      * @param stream pointer where to store the stream
      * @param db object database where the stream will write
      * @param size final size of the object that will be written
    - * @para type type of the object that will be written
    + * @param type type of the object that will be written
      * @return 0 if the stream was created; error code otherwise
      */
     GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type);
    @@ -235,6 +255,48 @@ GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otyp
      */
     GIT_EXTERN(void) git_odb_object_close(git_odb_object *object);
     
    +/**
    + * Return the OID of an ODB object
    + *
    + * This is the OID from which the object was read from
    + *
    + * @param object the object
    + * @return a pointer to the OID
    + */
    +GIT_EXTERN(const git_oid *) git_odb_object_id(git_odb_object *object);
    +
    +/**
    + * Return the data of an ODB object
    + *
    + * This is the uncompressed, raw data as read from the ODB,
    + * without the leading header.
    + *
    + * This pointer is owned by the object and shall not be free'd.
    + *
    + * @param object the object
    + * @return a pointer to the data
    + */
    +GIT_EXTERN(const void *) git_odb_object_data(git_odb_object *object);
    +
    +/**
    + * Return the size of an ODB object
    + *
    + * This is the real size of the `data` buffer, not the
    + * actual size of the object.
    + *
    + * @param object the object
    + * @return the size
    + */
    +GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object);
    +
    +/**
    + * Return the type of an ODB object
    + *
    + * @param object the object
    + * @return the type
    + */
    +GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object);
    +
     /** @} */
     GIT_END_DECL
     #endif
    diff --git a/vendor/libgit2/include/git2/odb_backend.h b/vendor/libgit2/include/git2/odb_backend.h
    index 3875ec7f6..ba41f726c 100644
    --- a/vendor/libgit2/include/git2/odb_backend.h
    +++ b/vendor/libgit2/include/git2/odb_backend.h
    @@ -28,7 +28,6 @@
     #include "common.h"
     #include "types.h"
     #include "oid.h"
    -#include "odb.h"
     
     /**
      * @file git2/backend.h
    @@ -55,6 +54,13 @@ struct git_odb_backend {
     			struct git_odb_backend *,
     			const git_oid *);
     
    +	int (* write)(
    +			git_oid *,
    +			struct git_odb_backend *,
    +			const void *,
    +			size_t,
    +			git_otype);
    +
     	int (* writestream)(
     			struct git_odb_stream **,
     			struct git_odb_backend *,
    diff --git a/vendor/libgit2/include/git2/refs.h b/vendor/libgit2/include/git2/refs.h
    index 4ffc5ce5b..da55eaa3b 100644
    --- a/vendor/libgit2/include/git2/refs.h
    +++ b/vendor/libgit2/include/git2/refs.h
    @@ -241,6 +241,29 @@ GIT_EXTERN(int) git_reference_packall(git_repository *repo);
      */
     GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags);
     
    +
    +/**
    + * List all the references in the repository, calling a custom
    + * callback for each one.
    + *
    + * The listed references may be filtered by type, or using
    + * a bitwise OR of several types. Use the magic value
    + * `GIT_REF_LISTALL` to obtain all references, including
    + * packed ones.
    + *
    + * The `callback` function will be called for each of the references
    + * in the repository, and will receive the name of the reference and
    + * the `payload` value passed to this method.
    + *
    + * @param repo Repository where to find the refs
    + * @param list_flags Filtering flags for the reference
    + *		listing.
    + * @param callback Function which will be called for every listed ref
    + * @param payload Additional data to pass to the callback
    + * @return 0 on success; error code otherwise
    + */
    +GIT_EXTERN(int) git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
    +
     /** @} */
     GIT_END_DECL
     #endif
    diff --git a/vendor/libgit2/include/git2/repository.h b/vendor/libgit2/include/git2/repository.h
    index 317b367d2..00c1f20d0 100644
    --- a/vendor/libgit2/include/git2/repository.h
    +++ b/vendor/libgit2/include/git2/repository.h
    @@ -155,20 +155,16 @@ GIT_EXTERN(int) git_repository_index(git_index **index, git_repository *repo);
     /**
      * Free a previously allocated repository
      *
    + * Note that after a repository is free'd, all the objects it has spawned
    + * will still exist until they are manually closed by the user
    + * with `git_object_close`, but accessing any of the attributes of
    + * an object without a backing repository will result in undefined
    + * behavior
    + *
      * @param repo repository handle to close. If NULL nothing occurs.
      */
     GIT_EXTERN(void) git_repository_free(git_repository *repo);
     
    -/**
    - * Force a garbage collector pass on the repository
    - *
    - * This will force-free any cached objects that have been
    - * previously marked by the user as closed (`git_object_close`).
    - *
    - * @param repo repository handle to collect. If NULL nothing occurs.
    - */
    -GIT_EXTERN(int) git_repository_gc(git_repository *repo);
    -
     /**
      * Creates a new Git repository in the given folder.
      *
    diff --git a/vendor/libgit2/include/git2/signature.h b/vendor/libgit2/include/git2/signature.h
    index 96275aa07..40412a45f 100644
    --- a/vendor/libgit2/include/git2/signature.h
    +++ b/vendor/libgit2/include/git2/signature.h
    @@ -45,9 +45,9 @@ GIT_BEGIN_DECL
      * @email email of the person
      * @time time when the action happened
      * @offset timezone offset in minutes for the time
    - * @return the new sig, NULl on out of memory
    + * @return the new sig, NULL on out of memory
      */
    -GIT_EXTERN(git_signature *) git_signature_new(const char *name, const char *email, time_t time, int offset);
    +GIT_EXTERN(git_signature *) git_signature_new(const char *name, const char *email, git_time_t time, int offset);
     
     /**
      * Create a copy of an existing signature.
    diff --git a/vendor/libgit2/include/git2/tag.h b/vendor/libgit2/include/git2/tag.h
    index c343f6bf4..ee92cd5c2 100644
    --- a/vendor/libgit2/include/git2/tag.h
    +++ b/vendor/libgit2/include/git2/tag.h
    @@ -52,6 +52,24 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi
     	return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG);
     }
     
    +/**
    + * Close an open tag
    + *
    + * This is a wrapper around git_object_close()
    + *
    + * IMPORTANT:
    + * It *is* necessary to call this method when you stop
    + * using a tag. Failure to do so will cause a memory leak.
    + *
    + * @param tag the tag to close
    + */
    +
    +GIT_INLINE(void) git_tag_close(git_tag *tag)
    +{
    +	git_object_close((git_object *) tag);
    +}
    +
    +
     /**
      * Get the id of a tag.
      *
    diff --git a/vendor/libgit2/include/git2/tree.h b/vendor/libgit2/include/git2/tree.h
    index ec2b51646..164aec9e2 100644
    --- a/vendor/libgit2/include/git2/tree.h
    +++ b/vendor/libgit2/include/git2/tree.h
    @@ -52,6 +52,24 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git
     	return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE);
     }
     
    +/**
    + * Close an open tree
    + *
    + * This is a wrapper around git_object_close()
    + *
    + * IMPORTANT:
    + * It *is* necessary to call this method when you stop
    + * using a tree. Failure to do so will cause a memory leak.
    + *
    + * @param tree the tree to close
    + */
    +
    +GIT_INLINE(void) git_tree_close(git_tree *tree)
    +{
    +	git_object_close((git_object *) tree);
    +}
    +
    +
     /**
      * Get the id of a tree.
      *
    diff --git a/vendor/libgit2/include/git2/types.h b/vendor/libgit2/include/git2/types.h
    index 64f7fc72e..88f6b7d55 100644
    --- a/vendor/libgit2/include/git2/types.h
    +++ b/vendor/libgit2/include/git2/types.h
    @@ -52,12 +52,12 @@ GIT_BEGIN_DECL
     #if defined(_MSC_VER)
     
     typedef __int64 git_off_t;
    -typedef __time64_t git_time_t;
    +typedef __time64_t  git_time_t;
     
     #elif defined(__MINGW32__)
     
     typedef off64_t git_off_t;
    -typedef time_t git_time_t;
    +typedef __time64_t git_time_t;
     
     #else  /* POSIX */
     
    @@ -66,8 +66,8 @@ typedef time_t git_time_t;
      * before us (directly or indirectly), they'll get 32 bit off_t in their client
      * app, even though /we/ define _FILE_OFFSET_BITS=64.
      */
    -typedef long long git_off_t;
    -typedef time_t git_time_t;
    +typedef int64_t git_off_t;
    +typedef int64_t git_time_t;
     
     #endif
     
    @@ -129,7 +129,7 @@ typedef struct git_index git_index;
     
     /** Time in a signature */
     typedef struct git_time {
    -	time_t time; /** time in seconds from epoch */
    +	git_time_t time; /** time in seconds from epoch */
     	int offset; /** timezone offset, in minutes */
     } git_time;
     
    diff --git a/vendor/libgit2/src/backends/sqlite.c b/vendor/libgit2/src/backends/sqlite.c
    index 72d7b4d8e..a4c6d4825 100644
    --- a/vendor/libgit2/src/backends/sqlite.c
    +++ b/vendor/libgit2/src/backends/sqlite.c
    @@ -44,21 +44,20 @@ typedef struct {
     	sqlite3_stmt *st_read_header;
     } sqlite_backend;
     
    -int sqlite_backend__read_header(git_rawobj *obj, git_odb_backend *_backend, const git_oid *oid)
    +int sqlite_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid)
     {
     	sqlite_backend *backend;
     	int error;
     
    -	assert(obj && _backend && oid);
    +	assert(len_p && type_p && _backend && oid);
     
     	backend = (sqlite_backend *)_backend;
     	error = GIT_ERROR;
    -	obj->data = NULL;
     
     	if (sqlite3_bind_text(backend->st_read_header, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) {
     		if (sqlite3_step(backend->st_read_header) == SQLITE_ROW) {
    -			obj->type = sqlite3_column_int(backend->st_read_header, 0);
    -			obj->len = sqlite3_column_int(backend->st_read_header, 1);
    +			*type_p = (git_otype)sqlite3_column_int(backend->st_read_header, 0);
    +			*len_p = (size_t)sqlite3_column_int(backend->st_read_header, 1);
     			assert(sqlite3_step(backend->st_read_header) == SQLITE_DONE);
     			error = GIT_SUCCESS;
     		} else {
    @@ -71,26 +70,26 @@ int sqlite_backend__read_header(git_rawobj *obj, git_odb_backend *_backend, cons
     }
     
     
    -int sqlite_backend__read(git_rawobj *obj, git_odb_backend *_backend, const git_oid *oid)
    +int sqlite_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid)
     {
     	sqlite_backend *backend;
     	int error;
     
    -	assert(obj && _backend && oid);
    +	assert(data_p && len_p && type_p && _backend && oid);
     
     	backend = (sqlite_backend *)_backend;
     	error = GIT_ERROR;
     
     	if (sqlite3_bind_text(backend->st_read, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) {
     		if (sqlite3_step(backend->st_read) == SQLITE_ROW) {
    -			obj->type = sqlite3_column_int(backend->st_read, 0);
    -			obj->len = sqlite3_column_int(backend->st_read, 1);
    -			obj->data = git__malloc(obj->len);
    +			*type_p = (git_otype)sqlite3_column_int(backend->st_read, 0);
    +			*len_p = (size_t)sqlite3_column_int(backend->st_read, 1);
    +			*data_p = git__malloc(*len_p);
     
    -			if (obj->data == NULL) {
    +			if (*data_p == NULL) {
     				error = GIT_ENOMEM;
     			} else {
    -				memcpy(obj->data, sqlite3_column_blob(backend->st_read, 2), obj->len);
    +				memcpy(*data_p, sqlite3_column_blob(backend->st_read, 2), *len_p);
     				error = GIT_SUCCESS;
     			}
     
    @@ -126,27 +125,24 @@ int sqlite_backend__exists(git_odb_backend *_backend, const git_oid *oid)
     }
     
     
    -int sqlite_backend__write(git_oid *id, git_odb_backend *_backend, git_rawobj *obj)
    +int sqlite_backend__write(git_oid *id, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
     {
    -	char hdr[64];
    -	int  hdrlen;
    -
     	int error;
     	sqlite_backend *backend;
     
    -	assert(id && _backend && obj);
    +	assert(id && _backend && data);
     
     	backend = (sqlite_backend *)_backend;
     
    -	if ((error = git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0)
    +	if ((error = git_odb_hash(id, data, len, type)) < 0)
     		return error;
     
     	error = SQLITE_ERROR;
     
     	if (sqlite3_bind_text(backend->st_write, 1, (char *)id->id, 20, SQLITE_TRANSIENT) == SQLITE_OK &&
    -		sqlite3_bind_int(backend->st_write, 2, (int)obj->type) == SQLITE_OK &&
    -		sqlite3_bind_int(backend->st_write, 3, obj->len) == SQLITE_OK &&
    -		sqlite3_bind_blob(backend->st_write, 4, obj->data, obj->len, SQLITE_TRANSIENT) == SQLITE_OK) {
    +		sqlite3_bind_int(backend->st_write, 2, (int)type) == SQLITE_OK &&
    +		sqlite3_bind_int(backend->st_write, 3, len) == SQLITE_OK &&
    +		sqlite3_bind_blob(backend->st_write, 4, data, len, SQLITE_TRANSIENT) == SQLITE_OK) {
     		error = sqlite3_step(backend->st_write);
     	}
     
    diff --git a/vendor/libgit2/src/commit.c b/vendor/libgit2/src/commit.c
    index d5d6ebd8a..03b111da5 100644
    --- a/vendor/libgit2/src/commit.c
    +++ b/vendor/libgit2/src/commit.c
    @@ -315,7 +315,7 @@ GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
     GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
     GIT_COMMIT_GETTER(const char *, message, commit->message)
     GIT_COMMIT_GETTER(const char *, message_short, commit->message_short)
    -GIT_COMMIT_GETTER(time_t, time, commit->committer->when.time)
    +GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
     GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
     GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)
     
    diff --git a/vendor/libgit2/src/filebuf.c b/vendor/libgit2/src/filebuf.c
    index 607ad618d..dff9373f6 100644
    --- a/vendor/libgit2/src/filebuf.c
    +++ b/vendor/libgit2/src/filebuf.c
    @@ -151,8 +151,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
     	int error;
     	size_t path_len;
     
    -	if (file == NULL)
    -		return GIT_ERROR;
    +	assert(file && path);
     
     	memset(file, 0x0, sizeof(git_filebuf));
     
    @@ -203,7 +202,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
     		char tmp_path[GIT_PATH_MAX];
     
     		/* Open the file as temporary for locking */
    -		file->fd = gitfo_creat_tmp(tmp_path, "_filebuf_"); 
    +		file->fd = gitfo_mktemp(tmp_path, path); 
     		if (file->fd < 0) {
     			error = GIT_EOSERR;
     			goto cleanup;
    @@ -218,12 +217,6 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
     			goto cleanup;
     		}
     	} else {
    -		/* If the file is not temporary, make sure we have a path */
    -		if (path == NULL) {
    -			error = GIT_ERROR;
    -			goto cleanup;
    -		}
    -
     		path_len = strlen(path);
     
     		/* Save the original path of the file */
    diff --git a/vendor/libgit2/src/fileops.c b/vendor/libgit2/src/fileops.c
    index 237a16a9f..5dd4a3806 100644
    --- a/vendor/libgit2/src/fileops.c
    +++ b/vendor/libgit2/src/fileops.c
    @@ -25,14 +25,14 @@ int gitfo_mkdir_2file(const char *file_path)
     	return GIT_SUCCESS;
     }
     
    -static int creat_tempfile(char *path_out, const char *tmp_dir, const char *filename)
    +int gitfo_mktemp(char *path_out, const char *filename)
     {
     	int fd;
     
    -	git__joinpath(path_out, tmp_dir, filename);
    +	strcpy(path_out, filename);
     	strcat(path_out, "_git2_XXXXXX");
     
    -#ifdef GIT_WIN32
    +#if defined(_MSC_VER)
     	/* FIXME: there may be race conditions when multi-threading
     	 * with the library */
     	if (_mktemp_s(path_out, GIT_PATH_MAX) != 0)
    @@ -46,66 +46,6 @@ static int creat_tempfile(char *path_out, const char *tmp_dir, const char *filen
     	return fd >= 0 ? fd : GIT_EOSERR;
     }
     
    -static const char *find_tmpdir(void)
    -{
    -	static int tmpdir_not_found = 0;
    -	static char temp_dir[GIT_PATH_MAX];
    -	static const char *env_vars[] = {
    -		"TEMP", "TMP", "TMPDIR"
    -	};
    -
    -	unsigned int i, j;
    -	char test_file[GIT_PATH_MAX];
    -
    -	if (tmpdir_not_found)
    -		return NULL;
    -
    -	if (temp_dir[0] != '\0')
    -		return temp_dir;
    -
    -	for (i = 0; i < ARRAY_SIZE(env_vars); ++i) {
    -		char *env_path;
    -
    -		env_path = getenv(env_vars[i]);
    -		if (env_path == NULL)
    -			continue;
    -
    -		strcpy(temp_dir, env_path);
    -
    -		/* Fix backslashes because Windows environment vars
    -		 * are probably fucked up */
    -		for (j = 0; j < strlen(temp_dir); ++j)
    -			if (temp_dir[j] == '\\')
    -				temp_dir[j] = '/';
    -
    -		if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) {
    -			gitfo_unlink(test_file);
    -			return temp_dir;
    -		}
    -	}
    -
    -	/* last resort: current folder. */
    -	strcpy(temp_dir, "./");
    -	if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) {
    -		gitfo_unlink(test_file);
    -		return temp_dir;
    -	}
    -
    -	tmpdir_not_found = 1;
    -	return NULL;
    -}
    -
    -int gitfo_creat_tmp(char *path_out, const char *filename)
    -{
    -	const char *tmp_dir;
    -
    -	tmp_dir = find_tmpdir();
    -	if (tmp_dir == NULL)
    -		return GIT_EOSERR;
    -
    -	return creat_tempfile(path_out, tmp_dir, filename);
    -}
    -
     int gitfo_open(const char *path, int flags)
     {
     	int fd = open(path, flags | O_BINARY);
    @@ -263,7 +203,7 @@ int gitfo_mv(const char *from, const char *to)
     	 * file exists, the `rename` call fails. This is as
     	 * close as it gets with the Win32 API.
     	 */
    -	return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING) ? GIT_SUCCESS : GIT_EOSERR;
    +	return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
     #else
     	/* Don't even try this on Win32 */
     	if (!link(from, to)) {
    @@ -443,22 +383,29 @@ int gitfo_dirent(
     	return GIT_SUCCESS;
     }
     
    -#ifdef GIT_WIN32
     
    -static int is_windows_rooted_path(const char *path)
    +int retrieve_path_root_offset(const char *path)
     {
    +	int offset = 0;
    +
    +#ifdef GIT_WIN32
    +
     	/* Does the root of the path look like a windows drive ? */
     	if (isalpha(path[0]) && (path[1] == ':'))
    -		return GIT_SUCCESS;
    +		offset += 2;
    +
    +#endif
    +
    +	if (*(path + offset) == '/')
    +		return offset;
     
     	return GIT_ERROR;
     }
     
    -#endif
     
     int gitfo_mkdir_recurs(const char *path, int mode)
     {
    -	int error;
    +	int error, root_path_offset;
     	char *pp, *sp;
         char *path_copy = git__strdup(path);
     
    @@ -468,12 +415,9 @@ int gitfo_mkdir_recurs(const char *path, int mode)
     	error = GIT_SUCCESS;
     	pp = path_copy;
     
    -#ifdef GIT_WIN32
    -
    -	if (!is_windows_rooted_path(pp))
    -		pp += 2; /* Skip the drive name (eg. C: or D:) */
    -
    -#endif
    +	root_path_offset = retrieve_path_root_offset(pp);
    +	if (root_path_offset > 0)
    +		pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
     
         while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != 0) {
     		if (sp != pp && gitfo_isdir(path_copy) < GIT_SUCCESS) {
    @@ -499,8 +443,12 @@ int gitfo_mkdir_recurs(const char *path, int mode)
     
     static int retrieve_previous_path_component_start(const char *path)
     {
    -	int offset, len, start = 0;
    -	
    +	int offset, len, root_offset, start = 0;
    +
    +	root_offset = retrieve_path_root_offset(path);
    +	if (root_offset > -1)
    +		start += root_offset;
    +
     	len = strlen(path);
     	offset = len - 1;
     
    @@ -512,7 +460,7 @@ static int retrieve_previous_path_component_start(const char *path)
     	if (path[offset] == '/')
     		offset--;
     
    -	if (offset < 0)
    +	if (offset < root_offset)
     		return GIT_ERROR;
     
     	while (offset > start && path[offset-1] != '/') {
    @@ -522,15 +470,25 @@ static int retrieve_previous_path_component_start(const char *path)
     	return offset;
     }
     
    -int gitfo_prettify_dir_path(char *buffer_out, const char *path)
    +int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
     {
    -	int len = 0, segment_len, only_dots;
    +	int len = 0, segment_len, only_dots, root_path_offset, error = GIT_SUCCESS;
     	char *current;
     	const char *buffer_out_start, *buffer_end;
     
    -	buffer_out_start = buffer_out;
     	current = (char *)path;
     	buffer_end = path + strlen(path);
    +	buffer_out_start = buffer_out;
    +
    +	root_path_offset = retrieve_path_root_offset(path);
    +	if (root_path_offset < 0) {
    +		error = gitfo_getcwd(buffer_out, size);
    +		if (error < GIT_SUCCESS)
    +			return error;
    +
    +		len = strlen(buffer_out);
    +		buffer_out += len;
    +	}
     
     	while (current < buffer_end) {
     		/* Prevent multiple slashes from being added to the output */
    @@ -543,7 +501,7 @@ int gitfo_prettify_dir_path(char *buffer_out, const char *path)
     		segment_len = 0;
     
     		/* Copy path segment to the output */
    -		while (current < buffer_end && *current !='/')
    +		while (current < buffer_end && *current != '/')
     		{
     			only_dots &= (*current == '.');
     			*buffer_out++ = *current++;
    @@ -568,7 +526,9 @@ int gitfo_prettify_dir_path(char *buffer_out, const char *path)
     
     			*buffer_out ='\0';
     			len = retrieve_previous_path_component_start(buffer_out_start);
    -			if (len < GIT_SUCCESS)
    +
    +			/* Are we escaping out of the root dir? */
    +			if (len < 0)
     				return GIT_EINVALIDPATH;
     
     			buffer_out = (char *)buffer_out_start + len;
    @@ -576,7 +536,7 @@ int gitfo_prettify_dir_path(char *buffer_out, const char *path)
     		}
     
     		/* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
    -		if (only_dots &&segment_len > 0)
    +		if (only_dots && segment_len > 0)
     			return GIT_EINVALIDPATH;
     
     		*buffer_out++ = '/';
    @@ -588,20 +548,24 @@ int gitfo_prettify_dir_path(char *buffer_out, const char *path)
     	return GIT_SUCCESS;
     }
     
    -int gitfo_prettify_file_path(char *buffer_out, const char *path)
    +int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path)
     {
     	int error, path_len, i;
     	const char* pattern = "/..";
     
     	path_len = strlen(path);
     
    +	/* Let's make sure the filename isn't empty nor a dot */
    +	if (path_len == 0 || (path_len == 1 && *path == '.'))
    +		return GIT_EINVALIDPATH;
    +
     	/* Let's make sure the filename doesn't end with "/", "/." or "/.." */
     	for (i = 1; path_len > i && i < 4; i++) {
     		if (!strncmp(path + path_len - i, pattern, i))
     			return GIT_EINVALIDPATH;
     	}
     
    -	error =  gitfo_prettify_dir_path(buffer_out, path);
    +	error =  gitfo_prettify_dir_path(buffer_out, size, path);
     	if (error < GIT_SUCCESS)
     		return error;
     
    @@ -633,3 +597,34 @@ int gitfo_cmp_path(const char *name1, int len1, int isdir1,
     	return 0;
     }
     
    +static void posixify_path(char *path)
    +{
    +	while (*path) {
    +		if (*path == '\\')
    +			*path = '/';
    +
    +		path++;
    +	}
    +}
    +
    +int gitfo_getcwd(char *buffer_out, size_t size)
    +{
    +	char *cwd_buffer;
    +
    +	assert(buffer_out && size > 0);
    +
    +#ifdef GIT_WIN32
    +	cwd_buffer = _getcwd(buffer_out, size);
    +#else
    +	cwd_buffer = getcwd(buffer_out, size); //TODO: Fixme. Ensure the required headers are correctly included
    +#endif
    +
    +	if (cwd_buffer == NULL)
    +		return GIT_EOSERR;
    +
    +	posixify_path(buffer_out);
    +
    +	git__joinpath(buffer_out, buffer_out, "");	//Ensure the path ends with a trailing slash
    +
    +	return GIT_SUCCESS;
    +}
    diff --git a/vendor/libgit2/src/fileops.h b/vendor/libgit2/src/fileops.h
    index bc636fc38..6e0fd9d14 100644
    --- a/vendor/libgit2/src/fileops.h
    +++ b/vendor/libgit2/src/fileops.h
    @@ -58,7 +58,7 @@ extern int gitfo_exists(const char *path);
     extern int gitfo_open(const char *path, int flags);
     extern int gitfo_creat(const char *path, int mode);
     extern int gitfo_creat_force(const char *path, int mode);
    -extern int gitfo_creat_tmp(char *path_out, const char *filename);
    +extern int gitfo_mktemp(char *path_out, const char *filename);
     extern int gitfo_isdir(const char *path);
     extern int gitfo_mkdir_recurs(const char *path, int mode);
     extern int gitfo_mkdir_2file(const char *path);
    @@ -144,6 +144,8 @@ extern int gitfo_close_cached(gitfo_cache *ioc);
     extern int gitfo_cmp_path(const char *name1, int len1, int isdir1,
     		const char *name2, int len2, int isdir2);
     
    +extern int gitfo_getcwd(char *buffer_out, size_t size);
    +
     /**
      * Clean up a provided absolute or relative directory path.
      * 
    @@ -161,12 +163,13 @@ extern int gitfo_cmp_path(const char *name1, int len1, int isdir1,
      * the file system perspective.
      *
      * @param buffer_out buffer to populate with the normalized path.
    + * @param size buffer size.
      * @param path directory path to clean up.
      * @return
      * - GIT_SUCCESS on success;
      * - GIT_ERROR when the input path is invalid or escapes the current directory.
      */
    -GIT_EXTERN(int) gitfo_prettify_dir_path(char *buffer_out, const char *path);
    +int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path);
     
     /**
      * Clean up a provided absolute or relative file path.
    @@ -183,11 +186,13 @@ GIT_EXTERN(int) gitfo_prettify_dir_path(char *buffer_out, const char *path);
      * the file system perspective.
      *
      * @param buffer_out buffer to populate with the normalized path.
    + * @param size buffer size.
      * @param path file path to clean up.
      * @return
      * - GIT_SUCCESS on success;
      * - GIT_ERROR when the input path is invalid or escapes the current directory.
      */
    -GIT_EXTERN(int) gitfo_prettify_file_path(char *buffer_out, const char *path);
    +int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path);
     
    +int retrieve_path_root_offset(const char *path);
     #endif /* INCLUDE_fileops_h__ */
    diff --git a/vendor/libgit2/src/index.c b/vendor/libgit2/src/index.c
    index 6a355e11b..6a31dd5cb 100644
    --- a/vendor/libgit2/src/index.c
    +++ b/vendor/libgit2/src/index.c
    @@ -74,7 +74,7 @@ struct entry_short {
     	uint32_t file_size;
     	git_oid oid;
     	uint16_t flags;
    -	char path[1]; /* arbritrary length */
    +	char path[1]; /* arbitrary length */
     };
     
     struct entry_long {
    @@ -89,7 +89,7 @@ struct entry_long {
     	git_oid oid;
     	uint16_t flags;
     	uint16_t flags_extended;
    -	char path[1]; /* arbritrary length */
    +	char path[1]; /* arbitrary length */
     };
     
     /* local declarations */
    @@ -148,7 +148,7 @@ static int index_initialize(git_index **index_out, git_repository *owner, const
     		index->on_disk = 1;
     
     	*index_out = index;
    -	return GIT_SUCCESS;
    +	return git_index_read(index);
     }
     
     int git_index_open_bare(git_index **index_out, const char *index_path)
    @@ -312,8 +312,8 @@ int git_index_add(git_index *index, const char *rel_path, int stage)
     
     	memset(&entry, 0x0, sizeof(git_index_entry));
     
    -	entry.ctime.seconds = st.st_ctime;
    -	entry.mtime.seconds = st.st_mtime;
    +	entry.ctime.seconds = (git_time_t)st.st_ctime;
    +	entry.mtime.seconds = (git_time_t)st.st_mtime;
     	/* entry.mtime.nanoseconds = st.st_mtimensec; */
     	/* entry.ctime.nanoseconds = st.st_ctimensec; */
     	entry.dev= st.st_rdev;
    @@ -491,10 +491,10 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
     
     	source = (const struct entry_short *)(buffer);
     
    -	dest->ctime.seconds = (time_t)ntohl(source->ctime.seconds);
    -	dest->ctime.nanoseconds = (time_t)ntohl(source->ctime.nanoseconds);
    -	dest->mtime.seconds = (time_t)ntohl(source->mtime.seconds);
    -	dest->mtime.nanoseconds = (time_t)ntohl(source->mtime.nanoseconds);
    +	dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
    +	dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
    +	dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
    +	dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
     	dest->dev = ntohl(source->dev);
     	dest->ino = ntohl(source->ino);
     	dest->mode = ntohl(source->mode);
    diff --git a/vendor/libgit2/src/odb.c b/vendor/libgit2/src/odb.c
    index 9aeaa8a23..33d5468d9 100644
    --- a/vendor/libgit2/src/odb.c
    +++ b/vendor/libgit2/src/odb.c
    @@ -109,6 +109,26 @@ static void free_odb_object(void *o)
     	}
     }
     
    +const git_oid *git_odb_object_id(git_odb_object *object)
    +{
    +	return &object->cached.oid;
    +}
    +
    +const void *git_odb_object_data(git_odb_object *object)
    +{
    +	return object->raw.data;
    +}
    +
    +size_t git_odb_object_size(git_odb_object *object)
    +{
    +	return object->raw.len;
    +}
    +
    +git_otype git_odb_object_type(git_odb_object *object)
    +{
    +	return object->raw.type;
    +}
    +
     void git_odb_object_close(git_odb_object *object)
     {
     	git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
    @@ -395,6 +415,41 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
     	return error;
     }
     
    +int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
    +{
    +	unsigned int i;
    +	int error = GIT_ERROR;
    +
    +	assert(oid && db);
    +
    +	for (i = 0; i < db->backends.length && error < 0; ++i) {
    +		backend_internal *internal = git_vector_get(&db->backends, i);
    +		git_odb_backend *b = internal->backend;
    +
    +		/* we don't write in alternates! */
    +		if (internal->is_alternate)
    +			continue;
    +
    +		if (b->write != NULL)
    +			error = b->write(oid, b, data, len, type);
    +	}
    +
    +	/* if no backends were able to write the object directly, we try a streaming
    +	 * write to the backends; just write the whole object into the stream in one
    +	 * push */
    +	if (error < GIT_SUCCESS) {
    +		git_odb_stream *stream;
    +
    +		if ((error = git_odb_open_wstream(&stream, db, len, type)) == GIT_SUCCESS) {
    +			stream->write(stream, data, len);
    +			error = stream->finalize_write(oid, stream);
    +			stream->free(stream);
    +		}
    +	}
    +
    +	return error;
    +}
    +
     int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
     {
     	unsigned int i;
    diff --git a/vendor/libgit2/src/odb_loose.c b/vendor/libgit2/src/odb_loose.c
    index 4ab1128f3..8ee01cd2c 100644
    --- a/vendor/libgit2/src/odb_loose.c
    +++ b/vendor/libgit2/src/odb_loose.c
    @@ -579,7 +579,7 @@ int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend
     	loose_backend *backend;
     	loose_writestream *stream;
     
    -	char hdr[64];
    +	char hdr[64], tmp_path[GIT_PATH_MAX];
     	int  hdrlen;
     	int error;
     
    @@ -603,7 +603,9 @@ int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend
     	stream->stream.free = &loose_backend__stream_free;
     	stream->stream.mode = GIT_STREAM_WRONLY;
     
    -	error = git_filebuf_open(&stream->fbuf, NULL,
    +	git__joinpath(tmp_path, backend->objects_dir, "tmp_object");
    +
    +	error = git_filebuf_open(&stream->fbuf, tmp_path,
     		GIT_FILEBUF_HASH_CONTENTS |
     		GIT_FILEBUF_DEFLATE_CONTENTS |
     		GIT_FILEBUF_TEMPORARY);
    diff --git a/vendor/libgit2/src/odb_pack.c b/vendor/libgit2/src/odb_pack.c
    index 65210f0b0..8c527bcf3 100644
    --- a/vendor/libgit2/src/odb_pack.c
    +++ b/vendor/libgit2/src/odb_pack.c
    @@ -93,7 +93,7 @@ struct pack_file {
     	git_oid *bad_object_sha1; /* array of git_oid */
     
     	int index_version;
    -	time_t mtime;
    +	git_time_t mtime;
     	int pack_fd;
     	unsigned pack_local:1, pack_keep:1;
     	git_oid sha1;
    @@ -827,7 +827,7 @@ static int packfile_check(struct pack_file **pack_out, const char *path, int loc
     	 */
     	p->pack_size = (off_t)st.st_size;
     	p->pack_local = local;
    -	p->mtime = st.st_mtime;
    +	p->mtime = (git_time_t)st.st_mtime;
     
     	/* see if we can parse the sha1 oid in the packfile name */
     	if (path_len < 40 ||
    diff --git a/vendor/libgit2/src/refs.c b/vendor/libgit2/src/refs.c
    index 40b80ec9b..16bd74149 100644
    --- a/vendor/libgit2/src/refs.c
    +++ b/vendor/libgit2/src/refs.c
    @@ -333,6 +333,7 @@ static int loose_lookup(
     
     	ref->mtime = ref_time;
     	*ref_out = ref;
    +	gitfo_free_buf(&ref_file);
     	return GIT_SUCCESS;
     
     cleanup:
    @@ -605,10 +606,12 @@ static int packed_load(git_repository *repo)
     
     
     struct dirent_list_data {
    -	git_vector ref_list;
     	git_repository *repo;
     	size_t repo_path_len;
     	unsigned int list_flags;
    +
    +	int (*callback)(const char *, void *);
    +	void *callback_payload;
     };
     
     static int _dirent_loose_listall(void *_data, char *full_path)
    @@ -624,10 +627,12 @@ static int _dirent_loose_listall(void *_data, char *full_path)
     		git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
     		return GIT_SUCCESS;
     
    -	if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
    -		return GIT_SUCCESS; /* we are filtering out this reference */
    +	if (data->list_flags != GIT_REF_LISTALL) {
    +		if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
    +			return GIT_SUCCESS; /* we are filtering out this reference */
    +	}
     
    -	return git_vector_insert(&data->ref_list, git__strdup(file_path));
    +	return data->callback(file_path, data->callback_payload);
     }
     
     static int _dirent_loose_load(void *data, char *full_path)
    @@ -1401,47 +1406,67 @@ int git_reference_packall(git_repository *repo)
     	return packed_write(repo);
     }
     
    -int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags)
    +int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload)
     {
     	int error;
     	struct dirent_list_data data;
     	char refs_path[GIT_PATH_MAX];
     
    -	array->strings = NULL;
    -	array->count = 0;
    -
    -	git_vector_init(&data.ref_list, 8, NULL);
    -	data.repo_path_len = strlen(repo->path_repository);
    -	data.list_flags = list_flags;
    -	data.repo = repo;
    -
     	/* list all the packed references first */
     	if (list_flags & GIT_REF_PACKED) {
     		const char *ref_name;
     		void *_unused;
     
    -		if ((error = packed_load(repo)) < GIT_SUCCESS) {
    -			git_vector_free(&data.ref_list);
    +		if ((error = packed_load(repo)) < GIT_SUCCESS)
     			return error;
    -		}
     
     		GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
    -			git_vector_insert(&data.ref_list, git__strdup(ref_name));
    +			if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
    +				return error;
     		);
     	}
     
     	/* now list the loose references, trying not to
     	 * duplicate the ref names already in the packed-refs file */
    +
    +	data.repo_path_len = strlen(repo->path_repository);
    +	data.list_flags = list_flags;
    +	data.repo = repo;
    +	data.callback = callback;
    +	data.callback_payload = payload;
    +
    +
     	git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR);
    -	error = gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
    +	return gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
    +}
    +
    +int cb__reflist_add(const char *ref, void *data)
    +{
    +	return git_vector_insert((git_vector *)data, git__strdup(ref));
    +}
    +
    +int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags)
    +{
    +	int error;
    +	git_vector ref_list;
    +
    +	assert(array && repo);
    +
    +	array->strings = NULL;
    +	array->count = 0;
    +
    +	if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
    +		return GIT_ENOMEM;
    +
    +	error = git_reference_listcb(repo, list_flags, &cb__reflist_add, (void *)&ref_list);
     
     	if (error < GIT_SUCCESS) {
    -		git_vector_free(&data.ref_list);
    +		git_vector_free(&ref_list);
     		return error;
     	}
     
    -	array->strings = (char **)data.ref_list.contents;
    -	array->count = data.ref_list.length;
    +	array->strings = (char **)ref_list.contents;
    +	array->count = ref_list.length;
     	return GIT_SUCCESS;
     }
     
    diff --git a/vendor/libgit2/src/repository.c b/vendor/libgit2/src/repository.c
    index 132969402..91b95a881 100644
    --- a/vendor/libgit2/src/repository.c
    +++ b/vendor/libgit2/src/repository.c
    @@ -66,7 +66,7 @@ static int assign_repository_dirs(
     	if (git_dir == NULL)
     		return GIT_ENOTFOUND;
     
    -	error = gitfo_prettify_dir_path(path_aux, git_dir);
    +	error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_dir);
     	if (error < GIT_SUCCESS)
     		return error;
     
    @@ -81,7 +81,7 @@ static int assign_repository_dirs(
     	if (git_object_directory == NULL)
     		git__joinpath(path_aux, repo->path_repository, GIT_OBJECTS_DIR);
     	else {
    -		error = gitfo_prettify_dir_path(path_aux, git_object_directory);
    +		error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_object_directory);
     		if (error < GIT_SUCCESS)
     			return error;
     	}
    @@ -95,7 +95,7 @@ static int assign_repository_dirs(
     	if (git_work_tree == NULL)
     		repo->is_bare = 1;
     	else {
    -		error = gitfo_prettify_dir_path(path_aux, git_work_tree);
    +		error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_work_tree);
     		if (error < GIT_SUCCESS)
     			return error;
     
    @@ -108,7 +108,7 @@ static int assign_repository_dirs(
     		if (git_index_file == NULL)
     			git__joinpath(path_aux, repo->path_repository, GIT_INDEX_FILE);
     		else {
    -			error = gitfo_prettify_file_path(path_aux, git_index_file);
    +			error = gitfo_prettify_file_path(path_aux, sizeof(path_aux), git_index_file);
     			if (error < GIT_SUCCESS)
     				return error;
     		}
    @@ -403,7 +403,7 @@ static int repo_init_find_dir(repo_init *results, const char* path)
     	char temp_path[GIT_PATH_MAX];
     	int error = GIT_SUCCESS;
     
    -	error = gitfo_prettify_dir_path(temp_path, path);
    +	error = gitfo_prettify_dir_path(temp_path, sizeof(temp_path), path);
     	if (error < GIT_SUCCESS)
     		return error;
     
    diff --git a/vendor/libgit2/src/revwalk.c b/vendor/libgit2/src/revwalk.c
    index a9d4f8734..73bb060f5 100644
    --- a/vendor/libgit2/src/revwalk.c
    +++ b/vendor/libgit2/src/revwalk.c
    @@ -482,11 +482,22 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
     void git_revwalk_free(git_revwalk *walk)
     {
     	unsigned int i;
    +	const void *_unused;
    +	commit_object *commit;
     
     	if (walk == NULL)
     		return;
     
     	git_revwalk_reset(walk);
    +
    +	/* if the parent has more than PARENTS_PER_COMMIT parents,
    +	 * we had to allocate a separate array for those parents.
    +	 * make sure it's being free'd */
    +	GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, {
    +		if (commit->out_degree > PARENTS_PER_COMMIT)
    +			free(commit->parents);
    +	});
    +
     	git_hashtable_free(walk->commits);
     	git_pqueue_free(&walk->iterator_time);
     
    diff --git a/vendor/libgit2/src/signature.c b/vendor/libgit2/src/signature.c
    index 13816c396..412637600 100644
    --- a/vendor/libgit2/src/signature.c
    +++ b/vendor/libgit2/src/signature.c
    @@ -38,7 +38,7 @@ void git_signature_free(git_signature *sig)
     	free(sig);
     }
     
    -git_signature *git_signature_new(const char *name, const char *email, time_t time, int offset)
    +git_signature *git_signature_new(const char *name, const char *email, git_time_t time, int offset)
     {
     	git_signature *p = NULL;
     
    diff --git a/vendor/libgit2/src/thread-utils.h b/vendor/libgit2/src/thread-utils.h
    index e542639c8..20d6022ea 100644
    --- a/vendor/libgit2/src/thread-utils.h
    +++ b/vendor/libgit2/src/thread-utils.h
    @@ -5,7 +5,11 @@
     
     /* Common operations even if threading has been disabled */
     typedef struct {
    +#if defined(_MSC_VER)
    +	volatile long val;
    +#else
     	volatile int val;
    +#endif
     } git_atomic;
     
     GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
    diff --git a/vendor/libgit2/src/util.c b/vendor/libgit2/src/util.c
    index bfc4f7b27..995daf314 100644
    --- a/vendor/libgit2/src/util.c
    +++ b/vendor/libgit2/src/util.c
    @@ -223,6 +223,9 @@ void git__joinpath_n(char *buffer_out, int count, ...)
     		int len;
     
     		path = va_arg(ap, const char *);
    +
    +		assert((i == 0) || path != buffer_start);
    +
     		if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/')
     			path++;
     
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/HEAD b/vendor/libgit2/tests/resources/empty_bare.git/HEAD
    new file mode 100644
    index 000000000..cb089cd89
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_bare.git/HEAD
    @@ -0,0 +1 @@
    +ref: refs/heads/master
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/config b/vendor/libgit2/tests/resources/empty_bare.git/config
    new file mode 100644
    index 000000000..90e16477b
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_bare.git/config
    @@ -0,0 +1,7 @@
    +[core]
    +	repositoryformatversion = 0
    +	filemode = false
    +	bare = true
    +	symlinks = false
    +	ignorecase = true
    +	hideDotFiles = dotGitOnly
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/description b/vendor/libgit2/tests/resources/empty_bare.git/description
    new file mode 100644
    index 000000000..498b267a8
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_bare.git/description
    @@ -0,0 +1 @@
    +Unnamed repository; edit this file 'description' to name the repository.
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/info/exclude b/vendor/libgit2/tests/resources/empty_bare.git/info/exclude
    new file mode 100644
    index 000000000..a5196d1be
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_bare.git/info/exclude
    @@ -0,0 +1,6 @@
    +# git ls-files --others --exclude-from=.git/info/exclude
    +# Lines that start with '#' are comments.
    +# For a project mostly in C, the following would be a good set of
    +# exclude patterns (uncomment them if you want to use them):
    +# *.[oa]
    +# *~
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/objects/info/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/HEAD b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/HEAD
    new file mode 100644
    index 000000000..cb089cd89
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/HEAD
    @@ -0,0 +1 @@
    +ref: refs/heads/master
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/config b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/config
    new file mode 100644
    index 000000000..78387c50b
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/config
    @@ -0,0 +1,8 @@
    +[core]
    +	repositoryformatversion = 0
    +	filemode = false
    +	bare = false
    +	logallrefupdates = true
    +	symlinks = false
    +	ignorecase = true
    +	hideDotFiles = dotGitOnly
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/description b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/description
    new file mode 100644
    index 000000000..498b267a8
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/description
    @@ -0,0 +1 @@
    +Unnamed repository; edit this file 'description' to name the repository.
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/info/exclude b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/info/exclude
    new file mode 100644
    index 000000000..a5196d1be
    --- /dev/null
    +++ b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/info/exclude
    @@ -0,0 +1,6 @@
    +# git ls-files --others --exclude-from=.git/info/exclude
    +# Lines that start with '#' are comments.
    +# For a project mostly in C, the following would be a good set of
    +# exclude patterns (uncomment them if you want to use them):
    +# *.[oa]
    +# *~
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt b/vendor/libgit2/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
    new file mode 100644
    index 000000000..e69de29bb
    diff --git a/vendor/libgit2/tests/t00-core.c b/vendor/libgit2/tests/t00-core.c
    index 08e42ecf4..4cb111428 100644
    --- a/vendor/libgit2/tests/t00-core.c
    +++ b/vendor/libgit2/tests/t00-core.c
    @@ -151,187 +151,204 @@ BEGIN_TEST(path2, "get the latest component in a path")
     #undef TOPDIR_TEST
     END_TEST
     
    -typedef int (normalize_path)(char *, const char *);
    +typedef int (normalize_path)(char *, size_t, const char *);
     
    -static int ensure_normalized(const char *input_path, const char *expected_path, normalize_path normalizer)
    +/* Assert flags */
    +#define CWD_AS_PREFIX 1
    +#define PATH_AS_SUFFIX 2
    +#define ROOTED_PATH 4
    +
    +static int ensure_normalized(const char *input_path, const char *expected_path, normalize_path normalizer, int assert_flags)
     {
     	int error = GIT_SUCCESS;
     	char buffer_out[GIT_PATH_MAX];
    +	char current_workdir[GIT_PATH_MAX];
    +
    +	error = gitfo_getcwd(current_workdir, sizeof(current_workdir));
    +	if (error < GIT_SUCCESS)
    +		return error;
     
    -	error = normalizer(buffer_out, input_path);
    +	error = normalizer(buffer_out, sizeof(buffer_out), input_path);
     	if (error < GIT_SUCCESS)
     		return error;
     
     	if (expected_path == NULL)
     		return error;
     
    -	if (strcmp(buffer_out, expected_path))
    -		error = GIT_ERROR;
    +	if ((assert_flags & PATH_AS_SUFFIX) != 0)
    +		if (git__suffixcmp(buffer_out, expected_path))
    +			return GIT_ERROR;
    +
    +	if ((assert_flags & CWD_AS_PREFIX) != 0)
    +		if (git__prefixcmp(buffer_out, current_workdir))
    +			return GIT_ERROR;
    +
    +	if ((assert_flags & ROOTED_PATH) != 0) {
    +		error = strcmp(expected_path, buffer_out);
    +	}
     
     	return error;
     }
     
    -static int ensure_dir_path_normalized(const char *input_path, const char *expected_path)
    +static int ensure_dir_path_normalized(const char *input_path, const char *expected_path, int assert_flags)
     {
    -	return ensure_normalized(input_path, expected_path, gitfo_prettify_dir_path);
    +	return ensure_normalized(input_path, expected_path, gitfo_prettify_dir_path, assert_flags);
     }
     
    -static int ensure_file_path_normalized(const char *input_path, const char *expected_path)
    +static int ensure_file_path_normalized(const char *input_path, const char *expected_path, int assert_flags)
     {
    -	return ensure_normalized(input_path, expected_path, gitfo_prettify_file_path);
    +	return ensure_normalized(input_path, expected_path, gitfo_prettify_file_path, assert_flags);
     }
     
     BEGIN_TEST(path3, "prettify and validate a path to a file")
    -	must_pass(ensure_file_path_normalized("a", "a"));
    -	must_pass(ensure_file_path_normalized("./testrepo.git", "testrepo.git"));
    -	must_pass(ensure_file_path_normalized("./.git", ".git"));
    -	must_pass(ensure_file_path_normalized("./git.", "git."));
    -	must_fail(ensure_file_path_normalized("git./", NULL));
    -	must_fail(ensure_file_path_normalized("", NULL));
    -	must_fail(ensure_file_path_normalized(".", NULL));
    -	must_fail(ensure_file_path_normalized("./", NULL));
    -	must_fail(ensure_file_path_normalized("./.", NULL));
    -	must_fail(ensure_file_path_normalized("./..", NULL));
    -	must_fail(ensure_file_path_normalized("../.", NULL));
    -	must_fail(ensure_file_path_normalized("./.././/", NULL));
    -	must_fail(ensure_file_path_normalized("dir/..", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/../..", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/..///..", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub///../..", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub///..///..", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/../../..", NULL));
    -	must_pass(ensure_file_path_normalized("dir", "dir"));
    -	must_fail(ensure_file_path_normalized("dir//", NULL));
    -	must_pass(ensure_file_path_normalized("./dir", "dir"));
    -	must_fail(ensure_file_path_normalized("dir/.", NULL));
    -	must_fail(ensure_file_path_normalized("dir///./", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/..", NULL));
    -	must_fail(ensure_file_path_normalized("dir//sub/..",NULL));
    -	must_fail(ensure_file_path_normalized("dir//sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("dir/sub/../.", NULL));
    -	must_fail(ensure_file_path_normalized("dir/s1/../s2/", NULL));
    -	must_fail(ensure_file_path_normalized("d1/s1///s2/..//../s3/", NULL));
    -	must_pass(ensure_file_path_normalized("d1/s1//../s2/../../d2", "d2"));
    -	must_fail(ensure_file_path_normalized("dir/sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("....", NULL));
    -	must_fail(ensure_file_path_normalized("...", NULL));
    -	must_fail(ensure_file_path_normalized("./...", NULL));
    -	must_fail(ensure_file_path_normalized("d1/...", NULL));
    -	must_fail(ensure_file_path_normalized("d1/.../", NULL));
    -	must_fail(ensure_file_path_normalized("d1/.../d2", NULL));
    +	must_pass(ensure_file_path_normalized("a", "a", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_file_path_normalized("./testrepo.git", "testrepo.git", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_file_path_normalized("./.git", ".git", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_file_path_normalized("./git.", "git.", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_fail(ensure_file_path_normalized("git./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("", NULL, 0));
    +	must_fail(ensure_file_path_normalized(".", NULL, 0));
    +	must_fail(ensure_file_path_normalized("./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("./.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("./..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("../.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("./.././/", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/../..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/..///..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub///../..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub///..///..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/../../..", NULL, 0));
    +	must_pass(ensure_file_path_normalized("dir", "dir", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_fail(ensure_file_path_normalized("dir//", NULL, 0));
    +	must_pass(ensure_file_path_normalized("./dir", "dir", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_fail(ensure_file_path_normalized("dir/.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir///./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir//sub/..",NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir//sub/../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/sub/../.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("dir/s1/../s2/", NULL, 0));
    +	must_fail(ensure_file_path_normalized("d1/s1///s2/..//../s3/", NULL, 0));
    +	must_pass(ensure_file_path_normalized("d1/s1//../s2/../../d2", "d2", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_fail(ensure_file_path_normalized("dir/sub/../", NULL, 0));
    +	must_pass(ensure_file_path_normalized("../../a/../../b/c/d/../../e", "b/e", PATH_AS_SUFFIX));
    +	must_fail(ensure_file_path_normalized("....", NULL, 0));
    +	must_fail(ensure_file_path_normalized("...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("./...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("d1/...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("d1/.../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("d1/.../d2", NULL, 0));
     	
    -	must_pass(ensure_file_path_normalized("/a", "/a"));
    -	must_pass(ensure_file_path_normalized("/./testrepo.git", "/testrepo.git"));
    -	must_pass(ensure_file_path_normalized("/./.git", "/.git"));
    -	must_pass(ensure_file_path_normalized("/./git.", "/git."));
    -	must_fail(ensure_file_path_normalized("/git./", NULL));
    -	must_fail(ensure_file_path_normalized("/", NULL));
    -	must_fail(ensure_file_path_normalized("/.", NULL));
    -	must_fail(ensure_file_path_normalized("/./", NULL));
    -	must_fail(ensure_file_path_normalized("/./.", NULL));
    -	must_fail(ensure_file_path_normalized("/./..", NULL));
    -	must_fail(ensure_file_path_normalized("/../.", NULL));
    -	must_fail(ensure_file_path_normalized("/./.././/", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/../..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/..///..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub///../..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub///..///..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/../../..", NULL));
    -	must_pass(ensure_file_path_normalized("/dir", "/dir"));
    -	must_fail(ensure_file_path_normalized("/dir//", NULL));
    -	must_pass(ensure_file_path_normalized("/./dir", "/dir"));
    -	must_fail(ensure_file_path_normalized("/dir/.", NULL));
    -	must_fail(ensure_file_path_normalized("/dir///./", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/..", NULL));
    -	must_fail(ensure_file_path_normalized("/dir//sub/..",NULL));
    -	must_fail(ensure_file_path_normalized("/dir//sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/sub/../.", NULL));
    -	must_fail(ensure_file_path_normalized("/dir/s1/../s2/", NULL));
    -	must_fail(ensure_file_path_normalized("/d1/s1///s2/..//../s3/", NULL));
    -	must_pass(ensure_file_path_normalized("/d1/s1//../s2/../../d2", "/d2"));
    -	must_fail(ensure_file_path_normalized("/dir/sub/../", NULL));
    -	must_fail(ensure_file_path_normalized("/....", NULL));
    -	must_fail(ensure_file_path_normalized("/...", NULL));
    -	must_fail(ensure_file_path_normalized("/./...", NULL));
    -	must_fail(ensure_file_path_normalized("/d1/...", NULL));
    -	must_fail(ensure_file_path_normalized("/d1/.../", NULL));
    -	must_fail(ensure_file_path_normalized("/d1/.../d2", NULL));
    +	must_pass(ensure_file_path_normalized("/a", "/a", ROOTED_PATH));
    +	must_pass(ensure_file_path_normalized("/./testrepo.git", "/testrepo.git", ROOTED_PATH));
    +	must_pass(ensure_file_path_normalized("/./.git", "/.git", ROOTED_PATH));
    +	must_pass(ensure_file_path_normalized("/./git.", "/git.", ROOTED_PATH));
    +	must_fail(ensure_file_path_normalized("/git./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/./.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/./..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/../.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/./.././/", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/../..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/..///..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub///../..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub///..///..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/../../..", NULL, 0));
    +	must_pass(ensure_file_path_normalized("/dir", "/dir", 0));
    +	must_fail(ensure_file_path_normalized("/dir//", NULL, 0));
    +	must_pass(ensure_file_path_normalized("/./dir", "/dir", 0));
    +	must_fail(ensure_file_path_normalized("/dir/.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir///./", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/..", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir//sub/..",NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir//sub/../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/../.", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/dir/s1/../s2/", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/d1/s1///s2/..//../s3/", NULL, 0));
    +	must_pass(ensure_file_path_normalized("/d1/s1//../s2/../../d2", "/d2", 0));
    +	must_fail(ensure_file_path_normalized("/dir/sub/../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/....", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/./...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/d1/...", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/d1/.../", NULL, 0));
    +	must_fail(ensure_file_path_normalized("/d1/.../d2", NULL, 0));
     END_TEST
     
     BEGIN_TEST(path4, "validate and prettify a path to a folder")
    -	must_pass(ensure_dir_path_normalized("./testrepo.git", "testrepo.git/"));
    -	must_pass(ensure_dir_path_normalized("./.git", ".git/"));
    -	must_pass(ensure_dir_path_normalized("./git.", "git./"));
    -	must_pass(ensure_dir_path_normalized("git./", "git./"));
    -	must_pass(ensure_dir_path_normalized("", ""));
    -	must_pass(ensure_dir_path_normalized(".", ""));
    -	must_pass(ensure_dir_path_normalized("./", ""));
    -	must_pass(ensure_dir_path_normalized("./.", ""));
    -	must_fail(ensure_dir_path_normalized("./..", NULL));
    -	must_fail(ensure_dir_path_normalized("../.", NULL));
    -	must_fail(ensure_dir_path_normalized("./.././/", NULL));
    -	must_pass(ensure_dir_path_normalized("dir/..", ""));
    -	must_pass(ensure_dir_path_normalized("dir/sub/../..", ""));
    -	must_pass(ensure_dir_path_normalized("dir/sub/..///..", ""));
    -	must_pass(ensure_dir_path_normalized("dir/sub///../..", ""));
    -	must_pass(ensure_dir_path_normalized("dir/sub///..///..", ""));
    -	must_fail(ensure_dir_path_normalized("dir/sub/../../..", NULL));
    -	must_pass(ensure_dir_path_normalized("dir", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir//", "dir/"));
    -	must_pass(ensure_dir_path_normalized("./dir", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir/.", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir///./", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir/sub/..", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir//sub/..", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir//sub/../", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir/sub/../.", "dir/"));
    -	must_pass(ensure_dir_path_normalized("dir/s1/../s2/", "dir/s2/"));
    -	must_pass(ensure_dir_path_normalized("d1/s1///s2/..//../s3/", "d1/s3/"));
    -	must_pass(ensure_dir_path_normalized("d1/s1//../s2/../../d2", "d2/"));
    -	must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/"));
    -	must_fail(ensure_dir_path_normalized("....", NULL));
    -	must_fail(ensure_dir_path_normalized("...", NULL));
    -	must_fail(ensure_dir_path_normalized("./...", NULL));
    -	must_fail(ensure_dir_path_normalized("d1/...", NULL));
    -	must_fail(ensure_dir_path_normalized("d1/.../", NULL));
    -	must_fail(ensure_dir_path_normalized("d1/.../d2", NULL));
    -
    -	must_pass(ensure_dir_path_normalized("/./testrepo.git", "/testrepo.git/"));
    -	must_pass(ensure_dir_path_normalized("/./.git", "/.git/"));
    -	must_pass(ensure_dir_path_normalized("/./git.", "/git./"));
    -	must_pass(ensure_dir_path_normalized("/git./", "/git./"));
    -	must_pass(ensure_dir_path_normalized("/", "/"));
    -	must_pass(ensure_dir_path_normalized("//", "/"));
    -	must_pass(ensure_dir_path_normalized("///", "/"));
    -	must_pass(ensure_dir_path_normalized("/.", "/"));
    -	must_pass(ensure_dir_path_normalized("/./", "/"));
    -	must_fail(ensure_dir_path_normalized("/./..", NULL));
    -	must_fail(ensure_dir_path_normalized("/../.", NULL));
    -	must_fail(ensure_dir_path_normalized("/./.././/", NULL));
    -	must_pass(ensure_dir_path_normalized("/dir/..", "/"));
    -	must_pass(ensure_dir_path_normalized("/dir/sub/../..", "/"));
    -	must_fail(ensure_dir_path_normalized("/dir/sub/../../..", NULL));
    -	must_pass(ensure_dir_path_normalized("/dir", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir//", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/./dir", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir/.", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir///./", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir//sub/..", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir/sub/../", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("//dir/sub/../.", "/dir/"));
    -	must_pass(ensure_dir_path_normalized("/dir/s1/../s2/", "/dir/s2/"));
    -	must_pass(ensure_dir_path_normalized("/d1/s1///s2/..//../s3/", "/d1/s3/"));
    -	must_pass(ensure_dir_path_normalized("/d1/s1//../s2/../../d2", "/d2/"));
    -	must_fail(ensure_dir_path_normalized("/....", NULL));
    -	must_fail(ensure_dir_path_normalized("/...", NULL));
    -	must_fail(ensure_dir_path_normalized("/./...", NULL));
    -	must_fail(ensure_dir_path_normalized("/d1/...", NULL));
    -	must_fail(ensure_dir_path_normalized("/d1/.../", NULL));
    -	must_fail(ensure_dir_path_normalized("/d1/.../d2", NULL));
    +	must_pass(ensure_dir_path_normalized("./testrepo.git", "testrepo.git/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("./.git", ".git/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("./git.", "git./", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("git./", "git./", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized(".", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("./", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("./.", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/../..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/..///..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub///../..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub///..///..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir//", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("./dir", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/.", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir///./", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/..", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir//sub/..", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir//sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/../.", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/s1/../s2/", "dir/s2/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("d1/s1///s2/..//../s3/", "d1/s3/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("d1/s1//../s2/../../d2", "d2/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX));
    +	must_pass(ensure_dir_path_normalized("../../a/../../b/c/d/../../e", "b/e/", PATH_AS_SUFFIX));
    +	must_fail(ensure_dir_path_normalized("....", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("./...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("d1/...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("d1/.../", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("d1/.../d2", NULL, 0));
    +
    +	must_pass(ensure_dir_path_normalized("/./testrepo.git", "/testrepo.git/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/./.git", "/.git/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/./git.", "/git./", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/git./", "/git./", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/", "/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("//", "/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("///", "/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/.", "/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/./", "/", ROOTED_PATH));
    +	must_fail(ensure_dir_path_normalized("/./..", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/../.", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/./.././/", NULL, 0));
    +	must_pass(ensure_dir_path_normalized("/dir/..", "/", 0));
    +	must_pass(ensure_dir_path_normalized("/dir/sub/../..", "/", 0));
    +	must_fail(ensure_dir_path_normalized("/dir/sub/../../..", NULL, 0));
    +	must_pass(ensure_dir_path_normalized("/dir", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir//", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/./dir", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir/.", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir///./", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir//sub/..", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir/sub/../", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("//dir/sub/../.", "/dir/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/dir/s1/../s2/", "/dir/s2/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/d1/s1///s2/..//../s3/", "/d1/s3/", ROOTED_PATH));
    +	must_pass(ensure_dir_path_normalized("/d1/s1//../s2/../../d2", "/d2/", ROOTED_PATH));
    +	must_fail(ensure_dir_path_normalized("/....", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/./...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/d1/...", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/d1/.../", NULL, 0));
    +	must_fail(ensure_dir_path_normalized("/d1/.../d2", NULL, 0));
     END_TEST
     
     static int ensure_joinpath(const char *path_a, const char *path_b, const char *expected_path)
    @@ -372,6 +389,37 @@ BEGIN_TEST(path6, "properly join path components for more than one path")
     	must_pass(ensure_joinpath_n("a", "b", "", "/c/d", "a/b/c/d"));
     END_TEST
     
    +static int count_number_of_path_segments(const char *path)
    +{
    +	int number = 0;
    +	char *current = (char *)path;
    +
    +	while (*current)
    +	{
    +		if (*current++ == '/')
    +			number++;
    +	}
    +
    +	assert (number > 0);
    +
    +	return --number;
    +}
    +
    +BEGIN_TEST(path7, "prevent a path which escapes the root directory from being prettified")
    +	char current_workdir[GIT_PATH_MAX];
    +	char prettified[GIT_PATH_MAX];
    +	int i = 0, number_to_escape;
    +
    +	must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir)));
    +
    +	number_to_escape = count_number_of_path_segments(current_workdir);
    +
    +	for (i = 0; i < number_to_escape + 1; i++)
    +		git__joinpath(current_workdir, current_workdir, "../");
    +
    +	must_fail(gitfo_prettify_dir_path(prettified, sizeof(prettified), current_workdir));
    +END_TEST
    +
     typedef struct name_data {
     	int  count;  /* return count */
     	char *name;  /* filename     */
    @@ -628,6 +676,7 @@ BEGIN_SUITE(core)
     	ADD_TEST(path4);
     	ADD_TEST(path5);
     	ADD_TEST(path6);
    +	ADD_TEST(path7);
     
     	ADD_TEST(dirent0);
     	ADD_TEST(dirent1);
    diff --git a/vendor/libgit2/tests/t04-commit.c b/vendor/libgit2/tests/t04-commit.c
    index 1140d3319..e92842435 100644
    --- a/vendor/libgit2/tests/t04-commit.c
    +++ b/vendor/libgit2/tests/t04-commit.c
    @@ -366,7 +366,7 @@ BEGIN_TEST(details0, "query the details on a parsed commit")
     
     		const git_signature *author, *committer;
     		const char *message, *message_short;
    -		time_t commit_time;
    +		git_time_t commit_time;
     		unsigned int parents, p;
     		git_commit *parent;
     
    diff --git a/vendor/libgit2/tests/t06-index.c b/vendor/libgit2/tests/t06-index.c
    index 19b4da5c2..93ca2c04e 100644
    --- a/vendor/libgit2/tests/t06-index.c
    +++ b/vendor/libgit2/tests/t06-index.c
    @@ -34,7 +34,7 @@ struct test_entry {
     	unsigned int index;
     	char path[128];
     	git_off_t file_size;
    -	time_t mtime;
    +	git_time_t mtime;
     };
     
     struct test_entry TEST_ENTRIES[] = {
    diff --git a/vendor/libgit2/tests/t11-sqlite.c b/vendor/libgit2/tests/t11-sqlite.c
    index fecf7886f..61ecf98ac 100644
    --- a/vendor/libgit2/tests/t11-sqlite.c
    +++ b/vendor/libgit2/tests/t11-sqlite.c
    @@ -23,7 +23,7 @@
      * Boston, MA 02110-1301, USA.
      */
     #include "test_lib.h"
    -
    +#include "odb.h"
     
     #ifdef GIT2_SQLITE_BACKEND
     #include "t03-data.h"
    @@ -31,14 +31,17 @@
     #include "git2/odb_backend.h"
     
     
    -static int cmp_objects(raw_object *o1, raw_object *o2)
    +static int cmp_objects(git_odb_object *odb_obj, git_rawobj *raw)
     {
    -	if (o1->type != o2->type)
    +	if (raw->type != git_odb_object_type(odb_obj))
     		return -1;
    -	if (o1->len != o2->len)
    +
    +	if (raw->len != git_odb_object_size(odb_obj))
     		return -1;
    -	if ((o1->len > 0) && (memcmp(o1->data, o2->data, o1->len) != 0))
    +
    +	if ((raw->len > 0) && (memcmp(raw->data, git_odb_object_data(odb_obj), raw->len) != 0))
     		return -1;
    +
     	return 0;
     }
     
    @@ -62,15 +65,15 @@ static git_odb *open_sqlite_odb(void)
     #define TEST_WRITE(PTR) {\
         git_odb *db; \
     	git_oid id1, id2; \
    -    raw_object obj; \
    +    git_odb_object *obj; \
     	db = open_sqlite_odb(); \
     	must_be_true(db != NULL); \
         must_pass(git_oid_mkstr(&id1, PTR.id)); \
    -    must_pass(git_odb_write(&id2, db, &PTR##_obj)); \
    +    must_pass(git_odb_write(&id2, db, PTR##_obj.data, PTR##_obj.len, PTR##_obj.type)); \
         must_be_true(git_oid_cmp(&id1, &id2) == 0); \
         must_pass(git_odb_read(&obj, db, &id1)); \
    -    must_pass(cmp_objects(&obj, &PTR##_obj)); \
    -    git_rawobj_close(&obj); \
    +    must_pass(cmp_objects(obj, &PTR##_obj)); \
    +    git_odb_object_close(obj); \
         git_odb_close(db); \
     }
     
    diff --git a/vendor/libgit2/tests/t12-repo.c b/vendor/libgit2/tests/t12-repo.c
    index a9a93d147..adf20cfd7 100644
    --- a/vendor/libgit2/tests/t12-repo.c
    +++ b/vendor/libgit2/tests/t12-repo.c
    @@ -113,22 +113,18 @@ static int ensure_repository_init(
     		return GIT_ERROR;
     
     	if (repo->path_workdir != NULL || expected_working_directory != NULL) {
    -		if (strcmp(repo->path_workdir, expected_working_directory) != 0)
    -			//return GIT_ERROR;
    +		if (git__suffixcmp(repo->path_workdir, expected_working_directory) != 0)
     			goto cleanup;
     	}
     
    -	if (strcmp(repo->path_odb, path_odb) != 0)
    -		//return GIT_ERROR;
    +	if (git__suffixcmp(repo->path_odb, path_odb) != 0)
     		goto cleanup;
     
    -	if (strcmp(repo->path_repository, expected_path_repository) != 0)
    -		//return GIT_ERROR;
    +	if (git__suffixcmp(repo->path_repository, expected_path_repository) != 0)
     		goto cleanup;
     
     	if (repo->path_index != NULL || expected_path_index != NULL) {
    -		if (strcmp(repo->path_index, expected_path_index) != 0)
    -			//return GIT_ERROR;
    +		if (git__suffixcmp(repo->path_index, expected_path_index) != 0)
     			goto cleanup;
     	}
     
    @@ -162,11 +158,100 @@ BEGIN_TEST(init1, "initialize a bare repo")
     	must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, BARE_REPOSITORY, NULL, path_repository, NULL));
     END_TEST
     
    +BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping out of the current working directory")
    +	char path_repository[GIT_PATH_MAX];
    +	char current_workdir[GIT_PATH_MAX];
    +	const int mode = 0755; /* or 0777 ? */
    +	git_repository* repo;
    +
    +	must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir)));
    +
    +	git__joinpath(path_repository, TEMP_REPO_FOLDER, "a/b/c/");
    +	must_pass(gitfo_mkdir_recurs(path_repository, mode));
    +
    +	must_pass(chdir(path_repository));
    +
    +	must_pass(git_repository_init(&repo, "../d/e.git", 1));
    +	must_pass(git__suffixcmp(repo->path_repository, "/a/b/d/e.git/"));
    +
    +	git_repository_free(repo);
    +
    +	must_pass(git_repository_open(&repo, "../d/e.git"));
    +
    +	git_repository_free(repo);
    +
    +	must_pass(chdir(current_workdir));
    +	rmdir_recurs(TEMP_REPO_FOLDER);
    +END_TEST
    +
    +#define EMPTY_BARE_REPOSITORY_NAME		"empty_bare.git"
    +#define EMPTY_BARE_REPOSITORY_FOLDER	TEST_RESOURCES "/" EMPTY_BARE_REPOSITORY_NAME "/"
    +
    +BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git")
    +	git_repository *repo;
    +
    +	must_pass(copydir_recurs(EMPTY_BARE_REPOSITORY_FOLDER, TEMP_REPO_FOLDER));
    +	must_pass(remove_placeholders(TEMP_REPO_FOLDER, "dummy-marker.txt"));
    +
    +	must_pass(git_repository_open(&repo, TEMP_REPO_FOLDER));
    +
    +	git_repository_free(repo);
    +	must_pass(rmdir_recurs(TEMP_REPO_FOLDER));
    +END_TEST
    +
    +#define SOURCE_EMPTY_REPOSITORY_NAME	"empty_standard_repo/.gitted"
    +#define EMPTY_REPOSITORY_NAME			"empty_standard_repo/.git"
    +#define EMPTY_REPOSITORY_FOLDER			TEST_RESOURCES "/" SOURCE_EMPTY_REPOSITORY_NAME "/"
    +#define DEST_REPOSITORY_FOLDER			TEMP_REPO_FOLDER DOT_GIT "/"
    +
    +BEGIN_TEST(open1, "Open a standard repository that has just been initialized by git")
    +	git_repository *repo;
    +
    +	must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, DEST_REPOSITORY_FOLDER));
    +	must_pass(remove_placeholders(DEST_REPOSITORY_FOLDER, "dummy-marker.txt"));
    +
    +	must_pass(git_repository_open(&repo, DEST_REPOSITORY_FOLDER));
    +
    +	git_repository_free(repo);
    +	must_pass(rmdir_recurs(TEMP_REPO_FOLDER));
    +END_TEST
    +
    +
    +BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of the current working directory")
    +	char new_current_workdir[GIT_PATH_MAX];
    +	char current_workdir[GIT_PATH_MAX];
    +	char path_repository[GIT_PATH_MAX];
    +
    +	const int mode = 0755; /* or 0777 ? */
    +	git_repository* repo;
    +
    +	/* Setup the repository to open */
    +	must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir)));
    +	strcpy(path_repository, current_workdir);
    +	git__joinpath_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git");
    +	must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository));
    +
    +	/* Change the current working directory */
    +	git__joinpath(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/");
    +	must_pass(gitfo_mkdir_recurs(new_current_workdir, mode));
    +	must_pass(chdir(new_current_workdir));
    +
    +	must_pass(git_repository_open(&repo, "../../d/e.git"));
    +
    +	git_repository_free(repo);
    +
    +	must_pass(chdir(current_workdir));
    +	rmdir_recurs(TEMP_REPO_FOLDER);
    +END_TEST
     
     BEGIN_SUITE(repository)
     	ADD_TEST(odb0);
     	ADD_TEST(odb1);
     	ADD_TEST(init0);
     	ADD_TEST(init1);
    +	ADD_TEST(init2);
    +	ADD_TEST(open0);
    +	ADD_TEST(open1);
    +	ADD_TEST(open2);
     END_SUITE
     
    diff --git a/vendor/libgit2/tests/test_helpers.c b/vendor/libgit2/tests/test_helpers.c
    index 588461135..760de238b 100644
    --- a/vendor/libgit2/tests/test_helpers.c
    +++ b/vendor/libgit2/tests/test_helpers.c
    @@ -141,7 +141,7 @@ int copy_file(const char *src, const char *dst)
     	if (gitfo_read_file(&source_buf, src) < GIT_SUCCESS)
     		return GIT_ENOTFOUND;
     
    -	dst_fd = gitfo_creat(dst, 0644);
    +	dst_fd = gitfo_creat_force(dst, 0644);
     	if (dst_fd < 0)
     		goto cleanup;
     
    @@ -211,18 +211,13 @@ typedef struct {
     
     static int copy_filesystem_element_recurs(void *_data, char *source)
     {
    -	const int mode = 0755; /* or 0777 ? */
     	copydir_data *data = (copydir_data *)_data;
     
     	data->dst[data->dst_len] = 0;
     	git__joinpath(data->dst, data->dst, source + data->src_len);
     
    -	if (gitfo_isdir(source) == GIT_SUCCESS) {
    -		if (gitfo_mkdir(data->dst, mode) < GIT_SUCCESS)
    -			return GIT_EOSERR;
    -
    +	if (gitfo_isdir(source) == GIT_SUCCESS)
     		return gitfo_dirent(source, GIT_PATH_MAX, copy_filesystem_element_recurs, _data);
    -	}
     
     	return copy_file(source, data->dst);
     }
    @@ -261,3 +256,31 @@ void close_temp_repo(git_repository *repo)
     	git_repository_free(repo);
     	rmdir_recurs(TEMP_REPO_FOLDER);
     }
    +
    +static int remove_placeholders_recurs(void *filename, char *path)
    +{
    +	char passed_filename[GIT_PATH_MAX];
    +	char *data = (char *)filename;
    +
    +	if (!gitfo_isdir(path))
    +		return gitfo_dirent(path, GIT_PATH_MAX, remove_placeholders_recurs, data);
    +
    +	 if (git__basename_r(passed_filename, sizeof(passed_filename), path) < GIT_SUCCESS)
    +		 return GIT_EINVALIDPATH;
    +
    +	if (!strcmp(data, passed_filename))
    +		return gitfo_unlink(path);
    +
    +	return GIT_SUCCESS;
    +}
    +
    +int remove_placeholders(char *directory_path, char *filename)
    +{
    +	char buffer[GIT_PATH_MAX];
    +
    +	if (gitfo_isdir(directory_path))
    +		return GIT_EINVALIDPATH;
    +
    +	strcpy(buffer, directory_path);
    +	return remove_placeholders_recurs(filename, buffer);
    +}
    diff --git a/vendor/libgit2/tests/test_helpers.h b/vendor/libgit2/tests/test_helpers.h
    index 9a24ebccf..19c8ae55c 100644
    --- a/vendor/libgit2/tests/test_helpers.h
    +++ b/vendor/libgit2/tests/test_helpers.h
    @@ -67,6 +67,7 @@ extern int cmp_files(const char *a, const char *b);
     extern int copy_file(const char *source, const char *dest);
     extern int rmdir_recurs(const char *directory_path);
     extern int copydir_recurs(const char *source_directory_path, const char *destination_directory_path);
    +extern int remove_placeholders(char *directory_path, char *filename);
     
     extern int open_temp_repo(git_repository **repo, const char *path);
     extern void close_temp_repo(git_repository *repo);
    diff --git a/vendor/naturaldocs/Config/Languages.txt b/vendor/naturaldocs/Config/Languages.txt
    new file mode 100644
    index 000000000..28adf6186
    --- /dev/null
    +++ b/vendor/naturaldocs/Config/Languages.txt
    @@ -0,0 +1,286 @@
    +Format: 1.51
    +
    +# This is the main Natural Docs languages file.  If you change anything here,
    +# it will apply to EVERY PROJECT you use Natural Docs on.  If you'd like to
    +# change something for just one project, edit the Languages.txt in its project
    +# directory instead.
    +
    +
    +#-------------------------------------------------------------------------------
    +# SYNTAX:
    +#
    +# Unlike other Natural Docs configuration files, in this file all comments
    +# MUST be alone on a line.  Some languages deal with the # character, so you
    +# cannot put comments on the same line as content.
    +#
    +# Also, all lists are separated with spaces, not commas, again because some
    +# languages may need to use them.
    +#
    +# Language: [name]
    +#    Defines a new language.  Its name can use any characters.
    +#
    +#    The language Shebang Script is special.  It's entry is only used for
    +#    extensions, and files with those extensions have their shebang (#!) lines
    +#    read to determine the real language of the file.  Extensionless files are
    +#    always treated this way.
    +#
    +#    The language Text File is also special.  It's treated as one big comment
    +#    so you can put Natural Docs content in them without special symbols.  Also,
    +#    if you don't specify a package separator, ignored prefixes, or enum value
    +#    behavior, it will copy those settings from the language that is used most
    +#    in the source tree.
    +#
    +# Extensions: [extension] [extension] ...
    +#    Defines the file extensions of the language's source files.  You can use *
    +#    to mean any undefined extension.
    +#
    +# Shebang Strings: [string] [string] ...
    +#    Defines a list of strings that can appear in the shebang (#!) line to
    +#    designate that it's part of the language.
    +#
    +# Ignore Prefixes in Index: [prefix] [prefix] ...
    +# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    +#    Specifies prefixes that should be ignored when sorting symbols in an
    +#    index.  Can be specified in general or for a specific topic type.
    +#
    +#------------------------------------------------------------------------------
    +# For basic language support only:
    +#
    +# Line Comments: [symbol] [symbol] ...
    +#    Defines a space-separated list of symbols that are used for line comments,
    +#    if any.
    +#
    +# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...
    +#    Defines a space-separated list of symbol pairs that are used for block
    +#    comments, if any.
    +#
    +# Package Separator: [symbol]
    +#    Defines the default package separator symbol.  The default is a dot.
    +#
    +# [Topic Type] Prototype Enders: [symbol] [symbol] ...
    +#    When defined, Natural Docs will attempt to get a prototype from the code
    +#    immediately following the topic type.  It stops when it reaches one of
    +#    these symbols.  Use \n for line breaks.
    +#
    +# Line Extender: [symbol]
    +#    Defines the symbol that allows a prototype to span multiple lines if
    +#    normally a line break would end it.
    +#
    +# Enum Values: [global|under type|under parent]
    +#    Defines how enum values are referenced.  The default is global.
    +#    global       - Values are always global, referenced as 'value'.
    +#    under type   - Values are under the enum type, referenced as
    +#               'package.enum.value'.
    +#    under parent - Values are under the enum's parent, referenced as
    +#               'package.value'.
    +#
    +# Perl Package: [perl package]
    +#    Specifies the Perl package used to fine-tune the language behavior in ways
    +#    too complex to do in this file.
    +#
    +#------------------------------------------------------------------------------
    +# For full language support only:
    +#
    +# Full Language Support: [perl package]
    +#    Specifies the Perl package that has the parsing routines necessary for full
    +#    language support.
    +#
    +#-------------------------------------------------------------------------------
    +
    +# The following languages MUST be defined in this file:
    +#
    +#    Text File, Shebang Script
    +
    +# If you add a language that you think would be useful to other developers
    +# and should be included in Natural Docs by default, please e-mail it to
    +# languages [at] naturaldocs [dot] org.
    +
    +
    +Language: Text File
    +
    +   Extension: txt
    +
    +
    +Language: Shebang Script
    +
    +   Extension: cgi
    +
    +
    +Language: C/C++
    +
    +   Extensions: c cpp h hpp cxx hxx
    +   Ignore Function Prefix in Index: ~
    +   Line Comment: //
    +   Block Comment: /* */
    +   Package Separator: ::
    +   Enum Values: Under parent
    +   Class Prototype Enders: ; {
    +   Function Prototype Enders: ; {
    +   Variable Prototype Enders: ; =
    +
    +
    +Language: C#
    +
    +   Extension: cs
    +   Ignore Prefix in Index: @
    +   Full Language Support: NaturalDocs::Languages::CSharp
    +
    +
    +Language: Java
    +
    +   Extension: java
    +   Line Comment: //
    +   Block Comment: /* */
    +   Enum Values: Under type
    +   Function Prototype Ender: {
    +   Variable Prototype Enders: ; =
    +
    +
    +Language: JavaScript
    +
    +   Extension: js
    +   Line Comment: //
    +   Block Comment: /* */
    +   Enum Values: Under type
    +   Function Prototype Ender: {
    +   Variable Prototype Enders: ; = , }
    +
    +
    +Language: Perl
    +
    +   Extensions: pl pm
    +   Shebang String: perl
    +   Ignore Variable Prefixes in Index: $ @ % *
    +   Full Language Support: NaturalDocs::Languages::Perl
    +
    +
    +Language: Python
    +
    +   Extension: py
    +   Shebang String: python
    +   Line Comment: #
    +   Function Prototype Ender: :
    +   Variable Prototype Ender: =
    +   Line Extender: \
    +
    +
    +Language: PHP
    +
    +   Extensions: inc php php3 php4 phtml
    +   Shebang String: php
    +   Ignore Variable Prefix in Index: $
    +   Line Comments: // #
    +   Block Comment: /* */
    +   Function Prototype Enders: ; {
    +   Variable Prototype Enders: ; =
    +
    +
    +Language: SQL
    +
    +   Extension: sql
    +   Line Comment: --
    +   Block Comment: /* */
    +   Enum Values: Global
    +   Function Prototype Enders: , ; ) as As AS is Is IS
    +   Variable Prototype Enders: , ; ) := default Default DEFAULT
    +   Database Index Prototype Enders: , ; )
    +   Database Trigger Prototype Enders: begin Begin BEGIN as As AS
    +   Perl Package: NaturalDocs::Languages::PLSQL
    +
    +
    +Language: Visual Basic
    +
    +   Extensions: vb vbs bas cls frm
    +   Line Comment: '
    +   Enum Values: Under type
    +   Function Prototype Ender: \n
    +   Variable Prototype Enders: \n =
    +   Line Extender: _
    +
    +
    +Language: Pascal
    +
    +   Extension: pas
    +   Line Comment: //
    +   Block Comments: { } (* *)
    +   Function Prototype Ender: ;
    +   Variable Prototype Enders: ; =
    +   Perl Package: NaturalDocs::Languages::Pascal
    +
    +
    +Language: Assembly
    +
    +   Extension: asm
    +   Line Comment: ;
    +   Variable Prototype Ender: \n
    +   Line Extender: \
    +
    +
    +Language: Ada
    +
    +   Extensions: ada ads adb
    +   Line Comment: --
    +   Function Prototype Enders: ; is Is IS
    +   Variable Prototype Enders: ; :=
    +   Perl Package: NaturalDocs::Languages::Ada
    +
    +
    +Language: Tcl
    +
    +   Extensions: tcl exp
    +   Shebang Strings: tclsh wish expect
    +   Line Comment: #
    +   Package Separator: ::
    +   Function Prototype Enders: ; {
    +   Variable Prototype Enders: ; \n
    +   Line Extender: \
    +   Perl Package: NaturalDocs::Languages::Tcl
    +
    +
    +Language: Ruby
    +
    +   Extension: rb
    +   Shebang String: ruby
    +   Ignore Variable Prefixes in Index: $ @ @@
    +   Line Comment: #
    +   Enum Values: Under parent
    +   Function Prototype Enders: ; \n
    +   Variable Prototype Enders: ; \n =
    +   Line Extender: \
    +
    +
    +Language: Makefile
    +
    +   Extensions: mk mak make
    +   Line Comment: #
    +
    +
    +Language: ActionScript
    +
    +   Extensions: as mxml
    +   Full Language Support: NaturalDocs::Languages::ActionScript
    +
    +
    +Language: ColdFusion
    +
    +   Extensions: cfm cfml cfc
    +   Line Comment: //
    +   Block Comments: <!--- ---> /* */
    +   Function Prototype Enders: { <
    +
    +
    +Language: R
    +
    +   Extension: r
    +   Line Comment: #
    +   Function Prototype Enders: { ;
    +   Variable Prototype Enders: <- = ; \n
    +
    +
    +Language: Fortran
    +
    +   Extensions: f90 f95 f03
    +   Line Comment: !
    +   Function Prototype Ender: \n
    +   Variable Prototype Enders: \n = =>
    +   Line Extender: &
    diff --git a/vendor/naturaldocs/Config/Topics.txt b/vendor/naturaldocs/Config/Topics.txt
    new file mode 100644
    index 000000000..9a10469b6
    --- /dev/null
    +++ b/vendor/naturaldocs/Config/Topics.txt
    @@ -0,0 +1,382 @@
    +Format: 1.51
    +
    +# This is the main Natural Docs topics file.  If you change anything here, it
    +# will apply to EVERY PROJECT you use Natural Docs on.  If you'd like to
    +# change something for just one project, edit the Topics.txt in its project
    +# directory instead.
    +
    +
    +#-------------------------------------------------------------------------------
    +# SYNTAX:
    +#
    +# Topic Type: [name]
    +#    Creates a new topic type.  Each type gets its own index and behavior
    +#    settings.  Its name can have letters, numbers, spaces, and these
    +#    charaters: - / . '
    +#
    +#    The Enumeration type is special.  It's indexed with Types but its members
    +#    are indexed with Constants according to the rules in Languages.txt.
    +#
    +# Plural: [name]
    +#    Sets the plural name of the topic type, if different.
    +#
    +# Keywords:
    +#    [keyword]
    +#    [keyword], [plural keyword]
    +#    ...
    +#    Defines a list of keywords for the topic type.  They may only contain
    +#    letters, numbers, and spaces and are not case sensitive.  Plural keywords
    +#    are used for list topics.
    +#
    +# Index: [yes|no]
    +#    Whether the topics get their own index.  Defaults to yes.  Everything is
    +#    included in the general index regardless of this setting.
    +#
    +# Scope: [normal|start|end|always global]
    +#    How the topics affects scope.  Defaults to normal.
    +#    normal        - Topics stay within the current scope.
    +#    start         - Topics start a new scope for all the topics beneath it,
    +#                    like class topics.
    +#    end           - Topics reset the scope back to global for all the topics
    +#                    beneath it.
    +#    always global - Topics are defined as global, but do not change the scope
    +#                    for any other topics.
    +#
    +# Class Hierarchy: [yes|no]
    +#    Whether the topics are part of the class hierarchy.  Defaults to no.
    +#
    +# Page Title If First: [yes|no]
    +#    Whether the topic's title becomes the page title if it's the first one in
    +#    a file.  Defaults to no.
    +#
    +# Break Lists: [yes|no]
    +#    Whether list topics should be broken into individual topics in the output.
    +#    Defaults to no.
    +#
    +# Can Group With: [type], [type], ...
    +#    Defines a list of topic types that this one can possibly be grouped with.
    +#    Defaults to none.
    +#-------------------------------------------------------------------------------
    +
    +# The following topics MUST be defined in this file:
    +#
    +#    Generic, Class, Interface, Section, File, Group, Function, Variable,
    +#    Property, Type, Constant, Enumeration, Event, Delegate
    +
    +# If you add something that you think would be useful to other developers
    +# and should be included in Natural Docs by default, please e-mail it to
    +# topics [at] naturaldocs [dot] org.
    +
    +
    +Topic Type: Generic
    +
    +   Index: No
    +   Keywords:
    +      topic, topics
    +      about, list
    +
    +
    +Topic Type: Class
    +
    +   Plural: Classes
    +   Scope: Start
    +   Class Hierarchy: Yes
    +   Page Title If First: Yes
    +   Can Group With: Interfaces
    +
    +   Keywords:
    +      class, classes
    +      structure, structures
    +      struct, structs
    +      package, packages
    +      namespace, namespaces
    +
    +
    +Topic Type: Interface
    +
    +   Plural: Interfaces
    +   Scope: Start
    +   Class Hierarchy: Yes
    +   Page Title If First: Yes
    +   Can Group With: Classes
    +
    +   Keywords:
    +      interface, interfaces
    +
    +
    +Topic Type: Section
    +
    +   Plural: Sections
    +   Index: No
    +   Scope: End
    +   Page Title If First: Yes
    +
    +   Keywords:
    +      section
    +      title
    +
    +
    +Topic Type: File
    +
    +   Plural: Files
    +   Scope: Always global
    +   Page Title If First: Yes
    +
    +   Keywords:
    +      file, files
    +      program, programs
    +      script, scripts
    +      document, documents
    +      doc, docs
    +      header, headers
    +
    +
    +Topic Type: Group
    +
    +   Plural: Groups
    +   Index: No
    +
    +   Keywords:
    +      group
    +
    +
    +Topic Type: Function
    +
    +   Plural: Functions
    +   Break Lists: Yes
    +   Can Group With: Properties
    +
    +   Keywords:
    +      function, functions
    +      func, funcs
    +      procedure, procedures
    +      proc, procs
    +      routine, routines
    +      subroutine, subroutines
    +      sub, subs
    +      method, methods
    +      callback, callbacks
    +      constructor, constructors
    +      destructor, destructors
    +      operator, operators
    +
    +
    +Topic Type: Variable
    +
    +   Plural: Variables
    +   Can Group With: Types, Constants, Macros, Enumerations
    +
    +   Keywords:
    +      variable, variables
    +      var, vars
    +      integer, integers
    +      int, ints
    +      uint, uints
    +      long, longs
    +      ulong, ulongs
    +      short, shorts
    +      ushort, ushorts
    +      byte, bytes
    +      ubyte, ubytes
    +      sbyte, sbytes
    +      float, floats
    +      double, doubles
    +      real, reals
    +      decimal, decimals
    +      scalar, scalars
    +      array, arrays
    +      arrayref, arrayrefs
    +      hash, hashes
    +      hashref, hashrefs
    +      bool, bools
    +      boolean, booleans
    +      flag, flags
    +      bit, bits
    +      bitfield, bitfields
    +      field, fields
    +      pointer, pointers
    +      ptr, ptrs
    +      reference, references
    +      ref, refs
    +      object, objects
    +      obj, objs
    +      character, characters
    +      wcharacter, wcharacters
    +      char, chars
    +      wchar, wchars
    +      string, strings
    +      wstring, wstrings
    +      str, strs
    +      wstr, wstrs
    +      handle, handles
    +
    +
    +Topic Type: Property
    +
    +   Plural: Properties
    +   Can Group With: Functions
    +
    +   Keywords:
    +      property, properties
    +      prop, props
    +
    +
    +Topic Type: Type
    +
    +   Plural: Types
    +   Can Group With: Variables, Constants, Macros, Enumerations
    +
    +   Keywords:
    +      type, types
    +      typedef, typedefs
    +
    +
    +Topic Type: Constant
    +
    +   Plural: Constants
    +   Can Group With: Variables, Types, Macros, Enumerations
    +
    +   Keywords:
    +      constant, constants
    +      const, consts
    +
    +
    +Topic Type: Enumeration
    +
    +   Plural: Enumerations
    +   Index: No
    +   Can Group With: Variables, Types, Macros, Constants
    +
    +   Keywords:
    +      enum, enums
    +      enumeration, enumerations
    +
    +
    +Topic Type: Event
    +
    +   Plural: Events
    +   Keywords:
    +      event, events
    +
    +
    +Topic Type: Delegate
    +
    +   Plural: Delegates
    +   Keywords:
    +      delegate, delegates
    +
    +
    +Topic Type: Macro
    +
    +   Plural: Macros
    +   Can Group With: Variables, Types, Constants
    +
    +   Keywords:
    +      define, defines
    +      def, defs
    +      macro, macros
    +
    +
    +Topic Type: Database
    +
    +   Plural: Databases
    +   Page Title If First: Yes
    +
    +   Keywords:
    +      database, databases
    +      db, dbs
    +
    +
    +Topic Type: Database Table
    +
    +   Plural: Database Tables
    +   Scope: Start
    +   Page Title If First: Yes
    +
    +   Keywords:
    +      table, tables
    +      database table, database tables
    +      databasetable, databasetables
    +      db table, db tables
    +      dbtable, dbtables
    +
    +
    +Topic Type: Database View
    +
    +   Plural: Database Views
    +   Scope: Start
    +   Page Title If First: Yes
    +
    +   Keywords:
    +      view, views
    +      database view, database views
    +      databaseview, databaseviews
    +      db view, db views
    +      dbview, dbviews
    +
    +
    +Topic Type: Database Index
    +
    +   Plural: Database Indexes
    +   Keywords:
    +      index, indexes
    +      index, indices
    +      database index, database indexes
    +      database index, database indices
    +      databaseindex, databaseindexes
    +      databaseindex, databaseindices
    +      db index, db indexes
    +      db index, db indices
    +      dbindex, dbindexes
    +      dbindex, dbindices
    +      key, keys
    +      database key, database keys
    +      databasekey, databasekeys
    +      db key, db keys
    +      dbkey, dbkeys
    +      primary key, primary keys
    +      primarykey, primarykeys
    +      database primary key, database primary keys
    +      databaseprimarykey, databaseprimarykeys
    +      db primary key, db primary keys
    +      dbprimarykey, dbprimarykeys
    +
    +
    +Topic Type: Database Cursor
    +
    +   Plural: Database Cursors
    +   Keywords:
    +      cursor, cursors
    +      database cursor, database cursors
    +      databasecursor, databasecursors
    +      db cursor, db cursors
    +      dbcursor, dbcursors
    +
    +
    +Topic Type: Database Trigger
    +
    +   Plural: Database Triggers
    +   Keywords:
    +      trigger, triggers
    +      database trigger, database triggers
    +      databasetrigger, databasetriggers
    +      db trigger, db triggers
    +      dbtrigger, dbtriggers
    +
    +
    +Topic Type: Cookie
    +
    +   Plural: Cookies
    +   Scope: Always global
    +
    +   Keywords:
    +      cookie, cookies
    +
    +
    +Topic Type: Build Target
    +
    +   Plural: Build Targets
    +   Keywords:
    +      target, targets
    +      build target, build targets
    +      buildtarget, buildtargets
    diff --git a/vendor/naturaldocs/Help/customizinglanguages.html b/vendor/naturaldocs/Help/customizinglanguages.html
    new file mode 100644
    index 000000000..0b579d96c
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/customizinglanguages.html
    @@ -0,0 +1,52 @@
    +
    +
    +<html><head><title>Customizing Natural Docs Languages</title><link rel=stylesheet type="text/css" href="styles.css"><link rel=stylesheet type="text/css" href="examples.css"><style type="text/css"><!--
    +
    +
    +        .InMainFile {
    +            padding: 1ex 2ex;
    +            margin: 1em 0;
    +            font: italic 9pt Verdana, sans-serif;
    +            line-height: 150%;
    +            background-color: #F8F8F8;
    +            }
    +        .InMainFile code {
    +            font-size: 9pt;
    +            }
    +
    +        .EnumTable {
    +            margin: .5em 5ex;
    +            }
    +        .EnumOption {
    +            font-weight: bold;
    +            padding-right: 2ex;
    +            }
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Languages, Indexes,<br>and Prototypes</span></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Customizing Languages</div><div class=TOC><a href="#LanguagesTxt">Languages.txt</a> &middot; <a href="#FileExtensions">File Extensions</a> &middot; <a href="#AddingLanguages">Adding Languages</a> &middot; <a href="#Prototypes">Prototypes</a><br><a href="#IndexPrefixes">Index Prefixes</a> &middot; <a href="#SpecialLanguages">Special Languages</a> &middot; <a href="#SyntaxReference">Syntax Reference</a></div><div class=Topic><a name="LanguagesTxt"></a><div class=TopicTitle>Languages.txt</div><p>Natural Docs has two files called <code>Languages.txt</code>: one in its Config directory, and one in <a href="running.html#CommandLine">your project directory.</a>&nbsp; These control the language, index prefix, and prototype features of Natural Docs.</p><p>You should edit the one in your project directory whenever possible.&nbsp; It keeps your changes separate and easier to manage, plus you don&rsquo;t have to reapply them whenever you upgrade.&nbsp; Editing the one in Natural Docs&rsquo; Config directory would be better only if you&rsquo;re using Natural Docs with a lot of projects and would like the changes to apply everywhere.</p><p>Note that unlike other Natural Docs configuration files, comments can only appear on their own lines.&nbsp; They cannot appear after something else on a line because settings may need to use the <code>#</code> symbol.&nbsp; Also, all lists are space-separated instead of comma-separated, again because some settings may need to use the comma symbol.</p></div><div class=Topic><a name=FileExtensions></a><div class=TopicTitle>File Extensions</div><p>If Natural Docs doesn&rsquo;t recognize a file extension you use for your code, it&rsquo;s not a problem.&nbsp; You can alter the language definition from your project file to add them.</p><pre class=Example>Alter Language: <i>[your language]</i>
    +   Add Extensions: cxx hxx</pre><p>On the other hand, if it&rsquo;s scanning some files you don&rsquo;t want it to scan, you can exclude extensions as well.&nbsp; Just add this to the top of your file:</p><pre class=Example>Ignore Extensions: c cpp</pre><p>In this example, Natural Docs will ignore C++ source files, thus only scanning the headers.</p></div><div class=Topic><a name=AddingLanguages></a><div class=TopicTitle>Adding Languages</div><p>You can add <a href="languages.html">basic language support</a> for any programming language just by editing these configuration files.&nbsp; Here are the most important settings:</p><pre class=Example>Language: Fictional
    +
    +   Extensions: fsrc fhdr
    +   Shebang Strings: fictional
    +   Line Comment: //
    +   Block Comment: /* */
    +   Package Separator: ::</pre><p>This tells Natural Docs that any files with the .fsrc or .fhdr extensions are part of our fictional programming language.&nbsp; Also, any .cgi or extensionless files that have &ldquo;fictional&rdquo; in the shebang (<code>#!</code>) line are part of it as well.&nbsp; Line comments start with <code>//</code> and block comments appear between <code>/*</code> and <code>*/</code>.&nbsp; The default package separator is <code>::</code>.&nbsp; Not too hard, huh?</p><p>You can also add settings to <a href="#IndexPrefixes">ignore prefixes in the index</a> and <a href="#Prototypes">detect prototypes</a>, but those are dealt with in their own sections on this page.</p></div><div class=Topic><a name=Prototypes></a><div class=TopicTitle>Prototypes</div><p>So you&rsquo;ve <a href="#AddingLanguages">added a new language</a> and want to detect prototypes.&nbsp; Or perhaps you <a href="customizingtopics.html#AddingTopicTypes">added a custom topic type</a> and want to detect prototypes for that as well.&nbsp; Here&rsquo;s an example of the properties you need:</p><pre class=Example>Function Prototype Enders: ; {
    +Variable Prototype Enders: ; =</pre><p>The algorithm for finding prototypes is very simple, yet it works really well in practice.&nbsp; All the code following the comment is grabbed until it reaches an ender symbol or another comment.&nbsp; Ender symbols appearing inside parenthesis, brackets, braces, or angle brackets don&rsquo;t count.&nbsp; If it reaches an ender symbol and somewhere in that code is the topic title, the code is accepted as the prototype.</p><p>So in the example above, variables end at semicolons (the end of the declaration) or equal signs (the default value expression, which we don&rsquo;t want to include.)&nbsp; Since the Natural Docs comment for the variable should have appeared right before the definition, that leaves us with the name and type.&nbsp; Functions are handled similarly: they end at a semicolon (the end of a predeclaration) or an opening brace (the beginning of the body) leaving us with the name, parameters, and return type.</p><p>You can do this with any topic type, including custom ones.&nbsp; Any prototypes that look like they have parameters will be formatted as such automatically.</p><div class=SubTopic>Line Breaks</div><p>For some languages, line breaks are significant.&nbsp; To have them end a prototype, use <code>\n</code>.&nbsp; If it has an extender symbol that allows the code to continue on the next line, you can specify that as well.</p><pre class=Example>Function Prototype Ender: \n
    +Variable Prototype Ender: \n =
    +Line Extender: _</pre><div class=SubTopic>Colors</div><p>If you&rsquo;re collecting prototypes for a custom topic type, they will not automatically get their own background color like the other types have.&nbsp; <a href="styles.html#CommonCustomizations">You have to define it via CSS.</a></p></div><div class=Topic><a name=IndexPrefixes></a><div class=TopicTitle>Index Prefixes</div><p>Natural Docs has the ability to ignore prefixes in the indexes.&nbsp; This is necessary because in certain languages, variables are prefixed with <code>$</code> or other symbols and we don&rsquo;t want them to get all grouped together under the symbols heading.&nbsp; Instead, they appear in the sidebar and are sorted as if they&rsquo;re not there.</p><div class=NDIndex><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading>A</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>AddProperty</span>, <span class=IParent>SomeClass</span></td></tr><tr><td class=ISymbolPrefix>$</td><td class=IEntry><span class=ISymbol>amount</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Average</span></td></tr></table></div><p>However, we can take advantage of this simply to get around coding conventions.&nbsp; Suppose you prefix all your class names with C.&nbsp; They&rsquo;d all form one gigantic group under C in the index.&nbsp; If you want, you can have it ignored so CCat, CDog, and CMouse get filed under C, D, and M instead.&nbsp; Just add this to your languages file:</p><pre class=Example>Alter Language: <i>[your language]</i>
    +   Add Ignored Class Prefix in Index: C</pre><p>Now C is ignored in your indexes:</p><div class=NDIndex><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading>A</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>C</td><td class=IEntry><span class=ISymbol>Account</span></td></tr><tr><td class=ISymbolPrefix>C</td><td class=IEntry><span class=ISymbol>AccountHolder</span></td></tr><tr><td class=ISymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>AddProperty</span>, <span class=IParent>SomeClass</span></td></tr><tr><td class=ISymbolPrefix>$</td><td class=IEntry><span class=ISymbol>amount</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Average</span></td></tr></table></div><p>You can include any number of prefixes and can do this for any topic type.&nbsp; So if you have a bunch of functions that start with <code>COM_</code> and <code>DB_</code>, you can ignore them too:</p><pre class=Example>Alter Language: <i>[your language]</i>
    +   Add Ignored Class Prefix in Index: C
    +   Add Ignored Function Prefixes in Index: COM_ DB_</pre></div><div class=Topic><a name=SpecialLanguages></a><div class=TopicTitle>Special Languages</div><p>There are two languages with special properties: Shebang Script and Text File.</p><p>Shebang Script allows you to define the file extensions where the language is really determined by the shebang (<code>#!</code>) line within it.&nbsp; For example, .cgi files.&nbsp; If Natural Docs finds a .cgi file, it sees that it&rsquo;s a Shebang Script so it opens it up to parse it&rsquo;s shebang line.&nbsp; It then searches it for substrings defined by other languages&rsquo; <code>Shebang String</code> settings to find out what language it really is.&nbsp; Files with no extension are always treated this way.</p><p>With Text File, the entire file is treated like a comment.&nbsp; There are no comment symbols required, you can just put Natural Docs content there in plain text.&nbsp; The most important setting is <code>Extensions</code>.</p><p>However, since it is possible to document classes, functions, etc. in text files, they also have their own <code>Package Separator</code> and <code>Ignored <i>[type]</i> Prefixes in Index</code> settings.&nbsp; To make things easier on you, by default it copies these settings from whichever language has the most source files in your project.&nbsp; You can override this by manually setting them, but you shouldn&rsquo;t need to.</p></div><div class=Topic><a name=SyntaxReference></a><div class=TopicTitle>Syntax Reference</div><p>Unlike other Natural Docs configuration files, comments can only appear on their own lines.&nbsp; They cannot appear after something else on a line because settings may need to use the <code>#</code> symbol.&nbsp; Likewise, lists are separated with spaces instead of commas because commas themselves may need to appear on the list.</p><p>Singular and plural forms are generally both supported, so you can write <code>Extension</code> or <code>Extensions</code>.&nbsp; It doesn&rsquo;t matter if they match how many items are set.&nbsp; Also, you can use either <code>Ignore</code> or <code>Ignored</code>.</p><pre class=Example>Ignore Extensions: <i>[extension] [extension]</i> ...</pre><p>Causes the listed file extensions to be ignored, even if they were previously defined to be part of a language.&nbsp; The list is space-separated.&nbsp; ex. &ldquo;<code>Ignore Extensions: cvs txt</code>&rdquo;</p><pre class=Example>Language: <i>[name]</i>
    +Alter Language: <i>[name]</i></pre><p>Creates a new language or alters an existing one.&nbsp; Names can use any characters.&nbsp; Note the <a href="#SpecialLanguages">special behavior for languages named Shebang Script and Text File</a>.</p><p>If you&rsquo;re altering an existing language and a property has an <code>[Add/Replace]</code> form, you have to specify whether you&rsquo;re adding to or replacing the list if that property has already been defined.</p><div class=SubTopic>General Language Properties</div><pre class=Example>Extensions: <i>[extension] [extension]</i> ...
    +<i>[Add/Replace]</i> Extensions: <i>[extension] [extension]</i> ...</pre><p>Defines file extensions for the language&rsquo;s source files.&nbsp; The list is space-separated.&nbsp; ex. &ldquo;<code>Extensions: c cpp</code>&rdquo;.&nbsp; You can use extensions that were previously used by another language to redefine them.&nbsp; You can use <code>*</code> to specify all undefined extensions.</p><pre class=Example>Shebang Strings: <i>[string] [string]</i> ...
    +<i>[Add/Replace]</i> Shebang Strings: <i>[string] [string]</i> ...</pre><p>Defines a list of strings that can appear in the shebang (<code>#!</code>) line to designate that it&rsquo;s part of this language.&nbsp; They can appear anywhere in the line, so <code>php</code> will work for &ldquo;<code>#!/user/bin/php4</code>&rdquo;.&nbsp; You can use strings that were previously used by another language to redefine them.</p><pre class=Example>Ignore Prefixes in Index: <i>[prefix] [prefix]</i> ...
    +Ignore <i>[type]</i> Prefixes in Index: <i>[prefix] [prefix]</i> ...
    +
    +<i>[Add/Replace]</i> Ignored Prefixes in Index: <i>[prefix] [prefix]</i> ...
    +<i>[Add/Replace]</i> Ignored <i>[type]</i> Prefixes in Index: <i>[prefix] [prefix]</i> ...</pre><p>Specifies prefixes that should be ignored when sorting symbols for an index.&nbsp; Can be specified in general or for a specific topic type.&nbsp; The prefixes will still appear, the symbols will just be sorted as if they&rsquo;re not there.&nbsp; For example, specifying <code>ADO_</code> for functions will mean that <code>ADO_DoSomething</code> will appear under D instead of A.</p><div class=SubTopic>Basic Language Support Properties</div><p>These attributes are only available for languages with basic language support.</p><pre class=Example>Line Comments: <i>[symbol] [symbol]</i> ...</pre><p>Defines a space-separated list of symbols that are used for line comments, if any.&nbsp; ex. &ldquo;<code>Line Comment: //</code>&rdquo;.</p><pre class=Example>Block Comments: <i>[opening symbol] [closing symbol] [o.s.] [c.s.]</i> ...</pre><p>Defines a space-separated list of symbol pairs that are used for block comments, if any.&nbsp; ex. &ldquo;<code>Block Comment: /* */</code>&rdquo;.</p><pre class=Example>Enum Values: <i>[global|under type|under parent]</i></pre><p>Defines the behavior of enum values.&nbsp; The default is global.</p><table border=0 cellspacing=0 cellpadding=0 class=EnumTable><tr><td class=EnumOption>Global</td><td>Enum values are always global and will be referenced as &ldquo;Value&rdquo;.</td></tr><tr><td class=EnumOption>Under Type</td><td>Enum values appear under the type and will be referenced as &ldquo;Package.Enum.Value&rdquo;.</td></tr><tr><td class=EnumOption>Under Parent</td><td>Enum values appear under the parent and will be referenced as &ldquo;Package.Value&rdquo;</td></tr></table><pre class=Example><i>[type]</i> Prototype Enders: <i>[symbol] [symbol]</i> ...</pre><p>When defined, Natural Docs will attempt to collect prototypes from the code following the specified topic type.&nbsp; It grabs code until the first ender symbol or the next Natural Docs comment, and if it contains the topic name, it serves as its prototype.&nbsp; Use <code>\n</code> to specify a line break.&nbsp; ex. &ldquo;<code>Function Prototype Enders: { ;</code>&rdquo;, &ldquo;<code>Variable Prototype Enders: = ;</code>&rdquo;. </p><pre class=Example>Line Extender: <i>[symbol]</i></pre><p>Defines the symbol that allows a prototype to span multiple lines if normally a line break would end it.</p><pre class=Example>Perl Package: <i>[perl package]</i></pre><p>Specifies the Perl package used to fine-tune the language behavior in ways too complex to do in this file.</p><div class=SubTopic>Full Language Support Properties</div><p>These attributes are only available for languages with full language support.</p><pre class=Example>Full Language Support: <i>[perl package]</i></pre><p>Specifies the Perl package that has the parsing routines necessary for full language support.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/customizingtopics.html b/vendor/naturaldocs/Help/customizingtopics.html
    new file mode 100644
    index 000000000..92a17f293
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/customizingtopics.html
    @@ -0,0 +1,74 @@
    +
    +
    +<html><head><title>Customizing Natural Docs Topics</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        .InMainFile {
    +            padding: 1ex 2ex;
    +            margin: 1em 0;
    +            font: italic 9pt Verdana, sans-serif;
    +            line-height: 150%;
    +            background-color: #F8F8F8;
    +            }
    +        .InMainFile code {
    +            font-size: 9pt;
    +            }
    +
    +        .ScopeTable {
    +            margin: .5em 5ex;
    +            }
    +        .ScopeOption {
    +            font-weight: bold;
    +            padding-right: 2ex;
    +            }
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Topics and Keywords</span><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Customizing Topics</div><div class=TOC><a href="#Topicstxt">Topics.txt</a> &middot; <a href="#TopicTypesVsKeywords">Topic Types vs. Keywords</a> &middot; <a href="#AddingTopicTypes">Adding Topic Types</a><br><a href="#ChangingKeywords">Changing Keywords</a> &middot; <a href="#AlteringBehavior">Altering Behavior</a> &middot; <a href="#SyntaxReference">Syntax Reference</a></div><div class=Topic><a name="Topicstxt"></a><div class=TopicTitle>Topics.txt</div><p>Natural Docs has two files called <code>Topics.txt</code>: one in its Config directory, and one in <a href="running.html#CommandLine">your project directory.</a>&nbsp; These control the topic behavior and keywords Natural Docs uses.</p><p>You should edit the one in your project directory whenever possible.&nbsp; It keeps your changes separate and easier to manage, plus you don&rsquo;t have to reapply them whenever you upgrade.&nbsp; Editing the one in Natural Docs&rsquo; Config directory would be better only if you&rsquo;re using Natural Docs with a lot of projects and would like the changes to apply everywhere.</p></div><div class=Topic><a name="TopicTypesVsKeywords"></a><div class=TopicTitle>Topic Types vs. Keywords</div><p>It&rsquo;s important to understand the difference between topic types and keywords.&nbsp; Topic types have their own indexes and behavior settings.&nbsp; You&rsquo;ll reference them by name when dealing with indexes in the <a href="menu.html">menu file</a> or prototype detection in the <a href="customizinglanguages.html">language file</a>, but not when documenting your code unless you make their names keywords as well.</p><p>You use keywords when documenting your code.&nbsp; There can be many keywords per topic type, and they are completely interchangable.</p><p>Suppose you document a class with the <code>Class</code> keyword and a struct with <code>Struct</code>.&nbsp; They are both keywords for the Class topic type by default, so they will appear in the same index.&nbsp; If you wanted structs to have their own index, you would <a href="#AddingTopicTypes">add a topic type for structs</a> and <a href="#ChangingKeywords">change the <code>Struct</code> keyword to point to it.</a></p></div><div class=Topic><a name="AddingTopicTypes"></a><div class=TopicTitle>Adding Topic Types</div><p>If you want to be able to document something in Natural Docs doesn&rsquo;t handle by default, you want to create your own topic type for it.&nbsp; Let&rsquo;s say you&rsquo;re working on a video game and you want to document all the sound effects because you want to keep track of what each one is for and have an index of them.&nbsp; You&rsquo;d add this to your topics file:</p><pre class=Example>Topic Type: Sound Effect
    +   Plural: Sound Effects
    +   Keywords:
    +      sound
    +      sound effect
    +</pre><p>Sound effects can now be documented with the <code>sound</code> or <code>sound effect</code> keywords, and they&rsquo;ll get their own index.&nbsp; The <code>Plural</code> line just specifies the plural name of the topic type.&nbsp; It isn&rsquo;t required, but Natural Docs will use it in some places where the plural would sound more natural, like when grouping topics or naming indexes on the menu.</p><p>Here are a couple of other things you may want to add:</p><pre class=Example>Topic Type: Sound Effect
    +   Plural: Sound Effects
    +   Scope: Always Global
    +   Keywords:
    +      sound, sounds
    +      sound effect, sound effects
    +</pre><p>You can set the <a href="documenting/reference.html#KeywordsTopicsAndScope">scope behavior</a> of the topic type.&nbsp; Your options are:</p><table border=0 cellspacing=0 cellpadding=0 class=ScopeTable><tr><td class=ScopeOption>Normal</td><td>Topics stay within the current scope.</td></tr><tr><td class=ScopeOption>Start</td><td>Topics start a new scope for all the topics beneath it, like class topics.</td></tr><tr><td class=ScopeOption>End</td><td>Topics reset the scope back to global for all the topics beneath it.</td></tr><tr><td class=ScopeOption>Always Global</td><td>Topics are defined as global, but do not change the scope for any other topics.</td></tr></table><p>Here we set it to <code>Always Global</code> so that if we document one as part of a class, it will still be global yet will not break the class&rsquo; scope.&nbsp; In other words, we can always link to it with just its name instead of needing something like <code>&lt;Class.Sound&gt;</code>.</p><p>The other thing we did was add plural keywords, which you do by using a comma after an existing keyword.&nbsp; These keywords are used for <a href="documenting/reference.html#ListTopics">list topics</a> so we don&rsquo;t have to document each one individually with the full syntax.</p><p>There are more options, these are just the most important ones.&nbsp; See the <a href="#SyntaxReference">full syntax reference</a> for the rest.</p><a name="Prototypes"></a><div class="SubTopic">Prototypes</div><p>If you&rsquo;d like to collect prototypes for your new topic type, you have to do that by <a href="customizinglanguages.html#Prototypes">editing <code>Languages.txt</code></a>.</p></div><div class=Topic><a name="ChangingKeywords"></a><div class=TopicTitle>Changing Keywords</div><a name="AddingAndChanging"></a><div class="SubTopic First">Adding and Changing</div><p>If you&rsquo;re <a href="#AddingTopicTypes">defining your own topic type</a> or editing the main topics file, you simply add to the keywords list:</p><pre class=Example>Topic Type: Sound Effect
    +   Keywords:
    +      sound, sounds
    +      sound effect, sound effects
    +</pre><p>It doesn&rsquo;t matter if the keyword was previously defined for a different topic type.&nbsp; Just define it again and the definition will change.</p><p>If you want to add keywords to one of the main topic types from the project file, use <code>Alter Topic Type</code> instead:</p><pre class=Example>Alter Topic Type: General
    +   Keywords:
    +      note
    +      notes
    +</pre><p>Natural Docs will keep a list of the main file&rsquo;s topic types in your project file so that you can do this easily.</p><a name="Ignoring"></a><div class="SubTopic">Ignoring</div><p>Sometimes a keyword just gets in the way.&nbsp; It&rsquo;s too common in your comments and Natural Docs keeps accidentally picking them up as topics when that isn&rsquo;t what you wanted.&nbsp; You can get rid of keywords completely by either deleting them from the main file or putting this in your project file:</p><pre class=Example>Ignore Keywords:
    +   about
    +   title
    +</pre><p>If you only have a few, you can use this syntax as well:</p><pre class=Example>Ignore Keywords: note, notes, title
    +</pre></div><div class=Topic><a name="AlteringBehavior"></a><div class=TopicTitle>Altering Behavior</div><p>You can change the behavior of any topic type defined in the main file via your project file.&nbsp; Just use <code>Alter Topic Type</code> and redefine any property.</p><pre class=Example>Alter Topic Type: Constant
    +   Scope: Always Global
    +</pre><p>Natural Docs will keep a list of the main file&rsquo;s topic types in your project file so you can do this easily.&nbsp; See the <a href="#SyntaxReference">syntax reference</a> below for a full list of your options.</p></div><div class=Topic><a name="SyntaxReference"></a><div class=TopicTitle>Syntax Reference</div><pre class=Example>Ignore Keywords: <i>[keyword]</i>, <i>[keyword]</i> ...
    +   <i>[keyword]</i>
    +   <i>[keyword]</i>, <i>[keyword]</i>
    +   ...
    +</pre><p>Ignores the keywords so that they&rsquo;re not recognized as Natural Docs topics anymore.&nbsp; Can be specified as a list on the same line and/or following like a normal Keywords section.</p><pre class=Example>Topic Type: <i>[name]</i>
    +Alter Topic Type: <i>[name]</i>
    +</pre><p>Creates a new topic type or alters an existing one.&nbsp; The name can only contain letters, numbers, spaces, and these characters: <code>. - &lsquo; /</code>.&nbsp; It isn&rsquo;t case sensitive, although the original case is remembered for presentation.</p><p>There are a number of default types that must be defined in the main file, but they will be listed there since it may change between versions.&nbsp; The default types can have their keywords or behaviors changed, though, either by editing the default file or by overriding them in the user file.</p><a name="Properties"></a><div class="SubTopic">Properties</div><pre class=Example>Plural: <i>[name]</i>
    +</pre><p>Specifies the plural name of the topic type.&nbsp; Defaults to the singular name.&nbsp; Has the same restrictions as the topic type name.</p><pre class=Example>Index: <i>[yes|no]</i>
    +</pre><p>Whether the topic type gets an index.&nbsp; Defaults to yes.</p><pre class=Example>Scope: <i>[normal|start|end|always global]</i>
    +</pre><p>How the topic affects scope.&nbsp; Defaults to normal.</p><table border=0 cellspacing=0 cellpadding=0 class=ScopeTable><tr><td class=ScopeOption>Normal</td><td>Topics stay within the current scope.</td></tr><tr><td class=ScopeOption>Start</td><td>Topics start a new scope for all the topics beneath it, like class topics.</td></tr><tr><td class=ScopeOption>End</td><td>Topics reset the scope back to global for all the topics beneath it.</td></tr><tr><td class=ScopeOption>Always Global</td><td>Topics are defined as global, but do not change the scope for any other topics.</td></tr></table><pre class=Example>Class Hierarchy: <i>[yes|no]</i>
    +</pre><p>Whether the topic is part of the class hierarchy.&nbsp; Defaults to no.</p><pre class=Example>Page Title if First: <i>[yes|no]</i>
    +</pre><p>Whether the title of this topic becomes the page title if it is the first topic in a file.&nbsp; Defaults to no.</p><pre class=Example>Break Lists: <i>[yes|no]</i>
    +</pre><p>Whether <a href="documenting/reference.html#ListTopics">list topics</a> should be broken into individual topics in the output.&nbsp; Defaults to no.</p><pre class=Example>Can Group With: <i>[topic type]</i>, <i>[topic type]</i>, ...
    +</pre><p>Lists the topic types that can be grouped with this one in the output.&nbsp; If two or more topic types often appear together, like Functions and Properties, this will allow them to be grouped together under one heading if it would cause too many groups otherwise.</p><pre class=Example>Keywords:
    +   <i>[keyword]</i>
    +   <i>[keyword]</i>, <i>[plural keyword]</i>
    +   ...
    +</pre><p>A list of the topic type&rsquo;s keywords.&nbsp; Each line after the heading is the keyword and optionally its plural form.&nbsp; This continues until the next line in &ldquo;<code>keyword: value</code>&rdquo; format.</p><ul><li>Keywords can only have letters, numbers, and spaces.&nbsp; No punctuation or symbols are allowed.</li><li>Keywords are not case sensitive.</li><li>Subsequent keyword sections add to the list.&nbsp; They don&rsquo;t replace it.</li><li>Keywords can be redefined by appearing in later keyword sections.</li></ul></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/documenting.html b/vendor/naturaldocs/Help/documenting.html
    new file mode 100644
    index 000000000..465ca9538
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/documenting.html
    @@ -0,0 +1,58 @@
    +
    +
    +<html><head><title>Documenting Your Code - Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><link rel=stylesheet type="text/css" href="examples.css"><style type="text/css"><!--
    +
    +
    +    #ReferenceTable {
    +        width: 100%;
    +        }
    +    #ReferenceTable #LeftSide {
    +        padding-right: 4ex;
    +        width: 40%;
    +        }
    +    #ReferenceTable #RightSide {
    +        width: 60%;
    +        }
    +
    +    #LeftSide a:link,
    +    #LeftSide a:hover,
    +    #LeftSide a:visited {
    +        color: #000000;
    +        }
    +
    +    #LeftSide .TopicTitle a:hover,
    +    #LeftSide .TopicTitle a:active {
    +        text-decoration: none;
    +        }
    +
    +    #RightSide .Example {
    +        margin-left: 0;
    +        margin-right: 0;
    +        }
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><span class=SideMenuEntry id=SelectedSideMenuEntry>Documenting<br>Your Code</span><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Documenting Your Code</div><div class=Topic><table id=ReferenceTable border=0 cellspacing=0 cellpadding=0><tr><td id=LeftSide><div class=Topic><div class=TopicTitle><a href="documenting/walkthrough.html">Walkthrough</a></div><ul><li><a href="documenting/walkthrough.html#OurFirstFunction">Our First Function</a></i><li><a href="documenting/walkthrough.html#ClassesAndScope">Classes and Scope</a></i><li><a href="documenting/walkthrough.html#MoreFormatting">More Formatting</a></i><li><a href="documenting/walkthrough.html#MoreOnLinking">More on Linking</a></i><li><a href="documenting/walkthrough.html#ExtraDocumentation">Extra Documentation</a></i><li><a href="documenting/walkthrough.html#AbbreviatedSyntax">Abbreviated Syntax</a></i></ul></div><div class=Topic><div class=TopicTitle><a href="documenting/reference.html">Reference</a></div><ul><li><a href="documenting/reference.html#Comments">Comments</a></i><li><a href="documenting/reference.html#TextFiles">Text Files</a></i><li><a href="documenting/reference.html#KeywordsTopicsAndScope">Keywords, Topics, and Scope</a></i><li><a href="documenting/reference.html#Linking">Linking</a></i><li><a href="documenting/reference.html#FormattingAndLayout">Formatting and Layout</a></i><li><a href="documenting/reference.html#PageTitles">Page Titles</a></i><li><a href="documenting/reference.html#Summaries">Summaries</a></i><li><a href="documenting/reference.html#JavadocCompatibility">Javadoc Compatibility</a></i></ul></div></td><td id=RightSide><p>Here&rsquo;s a quick example of how to document your code for Natural Docs.&nbsp; If you&rsquo;re a new user, we have <a href="documenting/walkthrough.html">a walkthrough</a> to get you started.&nbsp; Otherwise, visit <a href="documenting/reference.html">the reference</a> for the full details.</p><pre class=Example>/*
    +   Function: Multiply
    +
    +   Multiplies two integers.
    +
    +   Parameters:
    +
    +      x - The first integer.
    +      y - The second integer.
    +
    +   Returns:
    +
    +      The two integers multiplied together.
    +
    +   See Also:
    +
    +      &lt;Divide&gt;
    +*/
    +int Multiply (int x, int y)
    +   {  return x * y;  };</pre></td></tr></table></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/documenting/reference.html b/vendor/naturaldocs/Help/documenting/reference.html
    new file mode 100644
    index 000000000..3a6b5f34d
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/documenting/reference.html
    @@ -0,0 +1,147 @@
    +
    +
    +<html><head><title>Documenting Your Code: Reference - Natural Docs</title><link rel=stylesheet type="text/css" href="../styles.css"><link rel=stylesheet type="text/css" href="../examples.css"><style type="text/css"><!--
    +
    +
    +        .KeywordList                        { margin: 1em 5ex }
    +        .KeywordList td                { padding-bottom: 1em }
    +
    +                .KeywordListKeyword                {  font-weight: bold; white-space: nowrap  }
    +                .KeywordListDescription                {  padding-left: 5ex  }
    +                .KeywordListSynonyms                {  font-weight: normal; font-size: 8pt; font-style: italic }
    +
    +                .KeywordListSynonyms a:link,
    +                .KeywordListSynonyms a:visited,
    +                .KeywordListSynonyms a:hover                { color: #808080 }
    +
    +        
    +--></style><script language=JavaScript src="../javascript/PNGHandling.js"></script><script language=JavaScript src="../javascript/BrowserStyles.js"></script><script language=JavaScript src="../example/NaturalDocs.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="../images/header/leftside.png" width=30 height=75><a href="../index.html"><img src="../images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="../images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="../images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="../images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="../images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="../languages.html" class=SideMenuEntry>Language Support</a><a href="../output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="../documenting.html" class=SideMenuEntry id=SelectedSideMenuEntry>Documenting<br>Your Code</a><a href="../keywords.html" class=SideMenuEntry>Keywords</a><a href="../running.html" class=SideMenuEntry>Running</a><a href="../troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="../menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="../styles.html" class=SideMenuEntry>CSS Styles</a><a href="../customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="../customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Documenting Your Code</div><div class=TOC><a href="#Comments">Comments</a> &middot; <a href="#TextFiles">Text Files</a> &middot; <a href="#KeywordsTopicsAndScope">Keywords, Topics, and Scope</a> &middot; <a href="#Linking">Linking</a><br><a href="#FormattingAndLayout">Formatting and Layout</a> &middot; <a href="#PageTitles">Page Titles</a> &middot; <a href="#Summaries">Summaries</a> &middot; <a href="#JavadocCompatibility">Javadoc Compatibility</a></div><div class=CToolTip id="ttAdd"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Add (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Adds two integers.</div></div><div class=CToolTip id="ttSubtract"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Subtract (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Subtracts two integers.</div></div><div class=CToolTip id="ttMultiply"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Multiply (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Multiplies two integers.</div></div><div class=CToolTip id="ttDivide"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Divide (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Divides two integers.</div></div><div class=CToolTip id="ttIsEqual"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>bool IsEqual (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Returns whether two integers are equal.</div></div><div class=Topic><a name="Comments"></a><div class=TopicTitle>Comments</div><p>There is no special comment style for Natural Docs.&nbsp; You just embed Natural Docs topics into regular comments, and it&rsquo;s pretty tolerant as far as style goes.&nbsp; You can use block comments or string together line comments.&nbsp; The only requirement is that the comments are not on the same line as code.</p><pre class=Example>/* Function: Multiply
    +   Multiplies two integers and returns the result. */
    +
    +// Function: Multiply
    +// Multiplies two integers and returns the result.
    +</pre><p>Note that when stringing line comments together, blank lines that you want to include in the documentation must start with the comment symbol as well.&nbsp; If a line is completely blank, it&rsquo;s considered the end of the comment and thus the end of the Natural Docs topic.</p><a name="BoxesAndHorizontalLines"></a><div class="SubTopic">Boxes and Horizontal Lines</div><p>Natural Docs can also handle comment boxes and horizontal lines.&nbsp; It doesn&rsquo;t matter what symbols they use.&nbsp; The boxes don&rsquo;t need to be closed on the right side, and they can have different symbols for the edges and corners.</p><pre class=Example>/*
    + * Function: Multiply
    + * Multiplies two integers and returns the result.
    + */
    +
    +/* +-------------------------------------------------+
    +   | Function: Multiply                              |
    +   | Multiplies two integers and returns the result. |
    +   +-------------------------------------------------+ */
    +
    +//////////////////////////////////////////////////////////////
    +//
    +//  Function: Multiply
    +//  ------------------
    +//
    +//  Multiplies two integers together and returns the result.
    +//
    +//////////////////////////////////////////////////////////////
    +</pre><a name="JavadocStyle"></a><div class="SubTopic">Javadoc Style</div><p>If you have <a href="../languages.html">full language support</a>, you can also use Javadoc-style comments to write Natural Docs documentation.&nbsp; To do this you repeat the last symbol on the first line of the comment once.&nbsp; The comment must appear directly above what it&rsquo;s documenting, and you can omit the topic line if you want (the &ldquo;<code>Function: Multiply</code>&rdquo; part.)</p><pre class=Example>/**
    + * Multiplies two integers and returns the result.
    + */
    +
    +///
    +// Multiplies two integers together and returns the result.
    +</pre><p>If you omit the topic line and include any Javadoc tags in the comment, like <code>@param</code>, the entire comment will be treated as Javadoc and can only use Javadoc formatting.&nbsp; Otherwise it&rsquo;s treated as a Natural Docs comment and you can use all the formatting options described on this page.&nbsp; You can have both styles in the same source file, but not in the same comment.</p><a name="PerlPOD"></a><div class="SubTopic">Perl POD</div><p>Perl users can also use POD to do block comments.</p><pre class=Example>=begin nd
    +
    +Function: Multiply
    +Multiplies two integers and returns the result.
    +
    +=cut
    +</pre><p>You can also use <code>NaturalDocs</code> or <code>Natural Docs</code> in place of <code>ND</code>.&nbsp; None of them are case sensitive.&nbsp; If for some reason you want to go back to POD documentation instead of using <code>=cut</code>, you can write <code>=end nd</code>.</p><p>There&rsquo;s a second form of just <code>=nd</code> which is offered as a convenience.&nbsp; However, it is <b>not valid POD</b>.&nbsp; Perl will skip over it and execute fine, but POD parsers will give errors and possibly include the unformatted text in the output.&nbsp; Use the longer, valid form unless you know for certain that no one will ever try to run POD on your code.</p><pre class=Example>=nd
    +
    +Function: Multiply
    +Multiplies two integers and returns the result.
    +
    +=cut
    +</pre></div><div class=Topic><a name="TextFiles"></a><div class=TopicTitle>Text Files</div><p>Documentation can also be included in text files.&nbsp; Any file with a .txt extension appearing in the source tree and starting with a topic line will included in the documentation.&nbsp; It will be treated the same as a source file, meaning it will appear in the menu, its topics will be in the indexes, and its topics can be linked to from anywhere in the documentation.&nbsp; The only difference is you don&rsquo;t need comment symbols.</p><p>Remember that the topic line is required to be the first line of content.&nbsp; If the first non-blank line is not in the &ldquo;<code>Function: Multiply</code>&rdquo; format the file will be ignored.&nbsp; An easy way to do this is to use the <code>Title</code> keyword, although all of <a href="../keywords.html">the other ones</a> will work as well.</p><pre class=Example>Title: License
    +
    +This project is licensed under the GPL.
    +</pre><p>This method is convenient for documenting file formats, configuration settings, the general program architecture, or anything else that isn&rsquo;t directly tied to a source file.</p></div><div class=Topic><a name="KeywordsTopicsAndScope"></a><div class=TopicTitle>Keywords, Topics, and Scope</div><p>A topic in Natural Docs starts with a topic line in the format <code>&ldquo;keyword: name&rdquo;</code>.&nbsp; You can have multiple topics per comment as long as you separate them with a blank line.&nbsp; The keywords aren&rsquo;t case sensitive.</p><p>The list of keywords is pretty predictable: Function, Class, Variable, etc.&nbsp; Just use what you&rsquo;re documenting.&nbsp; There are many synonyms as well, so you can use keywords like Func, Procedure, Proc, Method and Constructor.&nbsp; Look at the <a href="../keywords.html">full list of keywords</a> to see everything that&rsquo;s available.</p><p>The <a href="../keywords.html">list of keywords</a> is separated into topic types.&nbsp; Each type gets its own index, and which specific keyword you use doesn&rsquo;t matter.&nbsp; Some also have scoping rules or other behavior as noted.</p><a name="Scope"></a><div class="SubTopic">Scope</div><p>Like the code it&rsquo;s documenting, Natural Docs topics have scope.&nbsp; This mostly has to do with <a href="#Linking">linking</a>: if you&rsquo;re in a class you can link to its members by their name alone, but if you&rsquo;re not, you have to use a notation like <code>class.member</code> or <code>class::member</code>.</p><p>If you have <a href="../languages.html">full language support</a> and are documenting something that appears in the code, the scope will be handled automatically.&nbsp; If you&rsquo;re using text files, have basic language support, or are including a topic that doesn&rsquo;t correspond to something in the code, scoping follows these rules:</p><ul><li>Everything after a class topic (or <a href="../keywords.html">anything that says &ldquo;Starts Scope&rdquo;</a>) is part of that class.</li><li>Everything after a section topic (or <a href="../keywords.html">anything that says &ldquo;Ends Scope&rdquo;</a>) is global again.</li><li>File topics (or <a href="../keywords.html">anything that says &ldquo;Always Global&rdquo;</a>) are global but do not change the scope for what follows.</li><p></ul></p><a name="ListTopics"></a><div class="SubTopic">List Topics</div><p>If you looked at the list, you saw that most of the keywords have plural forms.&nbsp; That&rsquo;s for list topics, which let you document many small things without using the full syntax.&nbsp; Anything that appears in <a href="#DefinitionLists">definition lists</a> within that topic will be treated as if it had its own topic.&nbsp; It will appear in the indexes and be linkable, just like normal topics.</p><p>Function list topics will automatically break apart in the output as well, so it will look the same as if you documented each one individually.</p></div><div class=Topic><a name="Linking"></a><div class=TopicTitle>Linking</div><p>Linking is the one place where Natural Docs has some negative effect on the readability of the comments.&nbsp; The alternative would be to automatically guess where links should be, but systems that do that can sometimes pepper your sentences with unintentional links to functions called &ldquo;is&rdquo; or &ldquo;on&rdquo;.&nbsp; However, the Natural Docs syntax is still as minimal as possible.&nbsp; Simply surround any topic you want to link to with angle brackets.&nbsp; Natural Docs will keep track off all the topics and where they are defined, so you don&rsquo;t need to use HTML-like syntax or remember what file anything is in.&nbsp; Also, if the link can&rsquo;t be resolved to anything, Natural Docs leaves the angle brackets in the output so if something wasn&rsquo;t intended to be a link (such as <code>#include &lt;somefile.h&gt;</code>) it won&rsquo;t be mangled.</p><pre class=Example>Let's link to function &lt;Multiply&gt;.
    +</pre><div class=NDContent><p class=CParagraph>Let&rsquo;s link to function <a href="#Example_Class.Multiply" class=LFunction id=link632 onMouseOver="ShowTip(event, 'ttMultiply', 'link632')" onMouseOut="HideTip('ttMultiply')">Multiply</a>.</p></div><p>Links and topic names are case sensitive, regardless of whether the language is or not.</p><p>When linking to functions, it doesn&rsquo;t matter if you include empty parenthesis or not.&nbsp; Both <code>&lt;Function&gt;</code> and <code>&lt;Function()&gt;</code> will work.&nbsp; However, if you documented the function with parameters as part of the name, you will need to include those parameters whenever linking to it.&nbsp; It is recommended that you only include parameters in the topic name if you need to distinguish between two functions with the same name.</p><p>If the topic has a <a href="#Summaries">summary sentence</a>, hovering over the link will give it to you as a tooltip.&nbsp; If the topic has a prototype, that will be included as well.&nbsp; You can try it above.</p><div class="SubTopic">Scope</div><p>If a topic is <a href="#Scope">considered part of a class</a>, they can be linked to using any of the three most common class/member notations:&nbsp; <code>class.member</code>, <code>class::member</code>, and <code>class-&gt;member</code>.&nbsp; Natural Docs will not be confused by <code>&lt;class-&gt;member&gt;</code>.&nbsp; Like in the language itself, if the topic you&rsquo;re writing is in that class&rsquo; scope you can link to it simply as <code>&lt;member&gt;</code>.</p><p>If you have multi-level classes and packages, links can be relative as well.&nbsp; So if you&rsquo;re in <code>Project::UI::Window::Base</code> and you want to link to <code>Project::UI::Button::Base</code>, just using <code>&lt;Button::Base&gt;</code> will work.</p><a name="PluralsAndPossessives"></a><div class="SubTopic">Plurals and Possessives</div><p>To make the documentation easier to write and easier to read in the source file, you can include plurals and possessives inside the angle brackets.&nbsp; In other words, you don&rsquo;t have to use awkward syntax like <code>&lt;Object&gt;s</code>, although that&rsquo;s supported as well.&nbsp; You can simply write <code>&lt;Objects&gt;</code> and it will link to the symbol <code>Object</code> just fine.&nbsp; It can handle any plural and/or possessive form you can throw at it.&nbsp; I&rsquo;m not kidding: <code>Foxes</code>, <code>Fox&rsquo;s</code>, <code>Foxes&rsquo;</code>, <code>Children</code>, <code>Mice</code>, <code>Alumni</code>, <code>Indices</code>, <code>Amoebae</code>, <code>Teeth</code>, just try to trip it up.</p><a name="URLsAndEMail"></a><div class="SubTopic">URLs and E-Mail</div><p>You can also link to URLs and e-mail addresses.&nbsp; It will detect them automatically, but you can also put them in angle brackets if you like.</p><pre class=Example>Visit &lt;http://www.website.com&gt; or send messages to
    +email@address.com.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Visit <a href="#" onClick="return false;" class=LURL>http://www.website.com</a> or send messages to <a href="#" onclick="location.href='mai' + 'lto:' + 'em' + 'ail' + '@' + 'addre' + 'ss.com'; return false;" class="LEMail">em<span style="display: none;">.nosp@m.</span>ail<span>@</span>addre<span style="display: none;">.nosp@m.</span>ss.com</a>.</p></div></div></div></div><p>E-mail addresses are protected in a way that should avoid spam crawlers.&nbsp; Although the link above looks and acts like a regular link (try it) the HTML code actually looks like this:</p><pre class=Example>&lt;a href="#"
    + onClick="location.href='mai' + 'lto:' + 'em' + 'ail' + '@'
    +          + 'addre' + 'ss.com'; return false;"&gt;
    +    em&lt;span style="display: none"&gt;.nosp@m.&lt;/span&gt;ail
    +    &lt;span&gt;@&lt;/span&gt;
    +    addre&lt;span style="display: none"&gt;.nosp@m.&lt;/span&gt;ss.com
    +&lt;/a&gt;
    +</pre><p>You can create named links by putting the text, &ldquo;at&rdquo;, and then the address in the angle brackets.&nbsp; This format lets it read naturally in a sentence.</p><pre class=Example>Visit &lt;the website at http://www.website.com&gt; or &lt;e-mail me at email@address.com&gt;.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Visit <a href="#" onClick="return false;" class=LURL>the website</a> or <a href="#" onclick="location.href='mai' + 'lto:' + 'em' + 'ail' + '@' + 'addre' + 'ss.com'; return false;" class="LEMail">e-mail me</a>.</p></div></div></div></div></div><div class=Topic><a name="FormattingAndLayout"></a><div class=TopicTitle>Formatting and Layout</div><p>You can apply additional formatting and layout to your Natural Docs content, all in ways that will appear very natural in the source code.</p><a name="Paragraphs"></a><div class="SubTopic">Paragraphs</div><p>You can break paragraphs by leaving blank lines between them.&nbsp; So we have this in our content:</p><pre class=Example>The first paragraph blah blah blah blah blah blah blah blah
    +blah blah blah blah blah blah blah blah blah blah blah blah
    +blah blah blah blah.
    +
    +The second paragraph blah blah blah blah blah blah blah
    +blah blah blah blah blah blah blah blah blah blah blah blah
    +blah blah blah blah.
    +</pre><p>and we get this in our output:</p><div class=NDContent><div class=CBody><div class=CFunction><div class=CTopic><p class=CParagraph>The first paragraph blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p><p class=CParagraph>The second paragraph blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah.</p></div></div></div></div><a name="BoldAndUnderline"></a><div class="SubTopic">Bold and Underline</div><p>You can apply bold to a stretch of text by surrounding it with asterisks.&nbsp; You can apply underlining by surrounding it with underscores instead.&nbsp; With underlining, it doesn&rsquo;t matter if you use an underscore for every space between words or not; they&rsquo;ll be converted to spaces if you do.</p><pre class=Example>Some *bold text* and some _underlined text_
    +and yet _more_underlined_text_.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Some <b>bold text</b> and some <u>underlined text</u> and yet <u>more underlined text</u>.</p></div></div></div></div><a name="Headings"></a><div class="SubTopic">Headings</div><p>You can add headings to your output just by ending a line with a colon and having a blank line above it.</p><pre class=Example>Some text before the heading.
    +
    +Heading:
    +Some text under the heading.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Some text before the heading.</p><h4 class=CHeading>Heading</h4><p class=CParagraph>Some text under the heading.</p></div></div></div></div><p>You <u>must</u> have a blank line above the heading or it will not work.&nbsp; You can skip the blank after it but not before.</p><a name="BulletLists"></a><div class="SubTopic">Bullet Lists</div><p>You can add bullet lists by starting a line with a dash, an asterisk, an o, or a plus.&nbsp; Bullets can have blank lines between them if you want, and subsequent lines don&rsquo;t have to be indented.&nbsp; You end a list by skipping a line and doing something else.</p><pre class=Example>- Bullet one.
    +- Bullet two.
    +  Bullet two continued.
    +- Bullet three.
    +
    +Some text after the bullet list.
    +
    +o Spaced bullet one.
    +
    +o Spaced bullet two.
    +Spaced bullet two continued.
    +
    +o Spaced bullet three.
    +
    +Some text after the spaced bullet list.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><ul class=CBulletList><li>Bullet one.</li><li>Bullet two.&nbsp; Bullet two continued.</li><li>Bullet three.</li></ul><p class=CParagraph>Some text after the bullet list.</p><ul class=CBulletList><li>Spaced bullet one.</li><li>Spaced bullet two.&nbsp; Spaced bullet two continued.</li><li>Spaced bullet three.</li></ul><p class=CParagraph>Some text after the spaced bullet list.</p></div></div></div></div><a name="DefinitionLists"></a><div class="SubTopic">Definition Lists</div><p>You can add a definition list by using the format below, specifically &ldquo;text space dash space text&rdquo;.&nbsp; Like bullet lists, you can have blank lines between them if you want, subsequent lines don&rsquo;t have to be indented, and you end the list by skipping a line and doing something else.</p><pre class=Example>First  - This is the first item.
    +Second - This is the second item.
    +         This is more of the second item.
    +Third  - This is the third item.
    +This is more of the third item.
    +
    +Some text after the definition list.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>First</td><td class=CDLDescription>This is the first item.</td></tr><tr><td class=CDLEntry>Second</td><td class=CDLDescription>This is the second item.&nbsp; This is more of the second item.</td></tr><tr><td class=CDLEntry>Third</td><td class=CDLDescription>This is the third item.&nbsp; This is more of the third item.</td></tr></table><p class=CParagraph>Some text after the definition list.</p></div></div></div></div><p>Remember that with definition lists, if you&rsquo;re using the plural form of the keywords each entry can be linked to as if it had its own topic.</p><a name="CodeAndTextDiagrams"></a><div class="SubTopic">Code and Text Diagrams</div><p>You can add example code or text diagrams by starting each line with <code>&gt;</code>, <code>|</code>, or <code>:</code>.&nbsp; If you have a vertical line or text box with the comment, you must separate these symbols from it with a space.</p><pre class=Example>: a = b + c;
    +
    +&gt;   +-----+     +-----+
    +&gt;   |  A  | --> |  B  |
    +&gt;   +-----+     +-----+
    +&gt;                  |
    +&gt;               +-----+
    +&gt;               |  C  |
    +&gt;               +-----+
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><pre class=CCode>a = b + c;</pre><pre class=CCode>+-----+     +-----+<br>|  A  | --> |  B  |<br>+-----+     +-----+<br>               |<br>            +-----+<br>            |  C  |<br>            +-----+</pre></div></div></div></div><p>For long stretches, this may be too tedious.&nbsp; You can start a code section by placing <code>(start code)</code> or just <code>(code)</code> alone on a line.&nbsp; You end it with either <code>(end code)</code> or just <code>(end)</code>.&nbsp; You can&rsquo;t put any other content on these lines.</p><pre class=Example>(start code)
    +
    +if (x == 0) {
    +   DoSomething();
    +}
    +
    +return x;
    +
    +(end)
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><pre class=CCode>if (x == 0) {<br>   DoSomething();<br>}<br><br>return x;</pre></div></div></div></div><p>You can also use <code>example</code>, <code>diagram</code>, or <code>table</code> instead of <code>code</code>.&nbsp; Just use whatever&rsquo;s appropriate.&nbsp; Always flexible, it will accept <code>begin</code> for <code>start</code> and it will accept <code>finish</code> or <code>done</code> for <code>end</code> so you don&rsquo;t have to remember the exact word.</p><p>Syntax highlighting will be applied to <code>(start code)</code> sections by default.&nbsp; If you&rsquo;d like to also apply it to <code>&gt;</code>, <code>:</code>, and <code>|</code> lines or turn it off completely, use the <a href="../running.html"><code>-hl</code> command line option</a>.</p><a name="Images"></a><div class="SubTopic">Images</div><p>You can include images in your documentation by writing &ldquo;<code>(see filename)</code>&rdquo;.&nbsp; If you put it alone on a line it will be embedded in place.</p><pre class=Example>This is the first paragraph.
    +
    +(see logo.gif)
    +
    +This is the second paragraph.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>This is the first paragraph.</p><img src="../images/logo.gif" width="268" height="61"><p class=CParagraph>This is the second paragraph.</p></div></div></div></div><p>If it&rsquo;s not alone on a line the image will appear after the end of the paragraph, the text will become a link to it, and the file name will be used as a caption.</p><pre class=Example>This is the first paragraph (see logo.gif)  This is
    +more of the first paragraph.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>This is the first paragraph <a href="#Image1" class=CImageLink>(see logo)</a>&nbsp; This is more of the first paragraph.</p><div class=CImage><a name="Image1"></a><div class=CImageCaption>logo</div><img src="../images/logo.gif" width="268" height="61"></div></div></div></div></div><p>The image file names are relative to the source file the comment appears in or any directories specified with the <a href="../running.html#CommandLine"><code>-img</code> command line option</a>.&nbsp; You can use relative paths like <code>(see images/logo.gif)</code> and <code>(see ../images/logo.gif)</code>, but you cannot use absolute paths, URLs, or specify a file that&rsquo;s not in a folder included by <a href="../running.html#CommandLine"><code>-i</code></a> or <a href="../running.html#CommandLine"><code>-img</code></a>.</p><p>Natural Docs supports gif, jpg, jpeg, png, and bmp files.</p></div><div class=Topic><a name="PageTitles"></a><div class=TopicTitle>Page Titles</div><p>Natural Docs automatically determines the page title as follows:</p><ul><li>If there&rsquo;s only one topic in the file, that topic&rsquo;s title becomes the page title.</li><li>Otherwise, if the first topic in the file is a class, section, or file, that topic&rsquo;s title becomes the page title.</li><li>Otherwise, the file name becomes the page title.</li><p></ul></p><p>This should be enough for most people.&nbsp; However, if you don&rsquo;t like the page title Natural Docs has chosen for you, add a &ldquo;<code>Title: [name]</code>&rdquo; comment to the top of the file to override it.&nbsp; <code>Title</code> is a synonym of Section, so that will satisfy the second rule and make it the page title.</p></div><div class=Topic><a name="Summaries"></a><div class=TopicTitle>Summaries</div><p>Summaries are automatically generated for every file, class, and section.&nbsp; You don&rsquo;t have to do anything special to get them.</p><p>There are two things you may want to keep in mind when documenting your code so that the summaries are nicer.&nbsp; The first is that they use the first sentence in the topic as the description, so long as it&rsquo;s plain text and not something like a bullet list.&nbsp; It will also appear in the tooltip whenever that topic is linked to.</p><p>The second is that you may want to manually add group topics to divide long lists and make the summaries easier to navigate.&nbsp; Natural Docs will automatically group them by type if you do not, but sometimes you want to be more specific.&nbsp; You don&rsquo;t need to provide a description, just adding a &ldquo;<code>Group: [name]</code>&rdquo; comment is sufficient.&nbsp; Note that once you manually add a group automatic grouping is completely turned off for that class.</p><p>Here&rsquo;s an example summary.&nbsp; Note that as before, when you hover over a link, you&rsquo;ll get the prototype and summary line as a tooltip.</p><div class=NDSummary><div class=Summary><div class=STitle>Summary</div><div class=SBorder><table border=0 cellspacing=0 cellpadding=0 class=STable><tr><td class=SEntrySize><div class=SMain><div class=SEntry><a href="#Example_Class" >Example Class</a></div></div></td><td class=SDescriptionSize><div class=SMain><div class=SDescription>A example class that does arithmetic with functions for people scared of operators.</div></div></td></tr><tr><td><div class=SGroup><div class=SEntry><div class=SIndent1><a href="#Example_Class.Arithmetic_Functions" >Arithmetic Functions</a></div></div></div></td><td><div class=SGroup><div class=SDescription><div class=SIndent1></div></div></div></td></tr><tr class=SMarked><td><div class=SFunction><div class=SEntry><div class=SIndent2><a href="#Example_Class.Add" id=link1 onMouseOver="ShowTip(event, 'ttAdd', 'link1')" onMouseOut="HideTip('ttAdd')">Add</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent2>Adds two integers.</div></div></div></td></tr><tr><td><div class=SFunction><div class=SEntry><div class=SIndent2><a href="#Example_Class.Subtract" id=link2 onMouseOver="ShowTip(event, 'ttSubtract', 'link2')" onMouseOut="HideTip('ttSubtract')">Subtract</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent2>Subtracts two integers.</div></div></div></td></tr><tr class=SMarked><td><div class=SFunction><div class=SEntry><div class=SIndent2><a href="#Example_Class.Multiply" id=link3 onMouseOver="ShowTip(event, 'ttMultiply', 'link3')" onMouseOut="HideTip('ttMultiply')">Multiply</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent2>Multiplies two integers.</div></div></div></td></tr><tr><td><div class=SFunction><div class=SEntry><div class=SIndent2><a href="#Example_Class.Divide" id=link4 onMouseOver="ShowTip(event, 'ttDivide', 'link4')" onMouseOut="HideTip('ttDivide')">Divide</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent2>Divides two integers.</div></div></div></td></tr><tr><td><div class=SGroup><div class=SEntry><div class=SIndent1><a href="#Example_Class.Comparison_Functions" >Comparison Functions</a></div></div></div></td><td><div class=SGroup><div class=SDescription><div class=SIndent1></div></div></div></td></tr><tr class=SMarked><td><div class=SFunction><div class=SEntry><div class=SIndent2><a href="#Example_Class.IsEqual" id=link5 onMouseOver="ShowTip(event, 'ttIsEqual', 'link5')" onMouseOut="HideTip('ttIsEqual')">IsEqual</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent2>Returns whether two integers are equal.</div></div></div></td></tr></table></div></div></div></div><div class=Topic><a name="JavadocCompatibility"></a><div class=TopicTitle>Javadoc Compatibility</div><p>If you have <a href="../languages.html">full language support</a> Natural Docs will also extract documentation from actual Javadoc comments, not just Natural Docs comments written with the Javadoc comment symbols.&nbsp; This provides you with a good method of migrating to Natural Docs as you don&rsquo;t have to convert all of your existing documentation.</p><pre class=Example>/**
    + * Multiplies two integers.
    + *
    + * @param x The first integer.
    + * @param y The second integer.
    + * @return The two integers multiplied together.
    + * @see Divide
    + */
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><h3 class=CTitle><a name="Example_Class.Multiply"></a>Multiply</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Multiply (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table><p class=CParagraph>Multiplies two integers.</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>x</td><td class=CDLDescription>The first integer.</td></tr><tr><td class=CDLEntry>y</td><td class=CDLDescription>The second integer.</td></tr></table><h4 class=CHeading>Returns</h4><p class=CParagraph>The two integers multiplied together.</p><h4 class=CHeading>See Also</h4><p class=CParagraph><a href="#Example_Class.Divide" class=LFunction id=link116 onMouseOver="ShowTip(event, 'ttDivide', 'link116')" onMouseOut="HideTip('ttDivide')">Divide</a></p></div></div></div></div><p>While most Javadoc tags and simple HTML formatting are supported, Natural Docs is not a full Javadoc parser and may not support some advanced tags and HTML.&nbsp; See <a href="../documentation/html/files/Modules/NaturalDocs/Parser/JavaDoc-pm.html">this page</a> for a list of what&rsquo;s supported and what isn&rsquo;t.</p><p>A source file can contain both Natural Docs and Javadoc comments.&nbsp; However, you cannot mix the two within a single comment.&nbsp; For example, you can&rsquo;t use asterisks for bold in a <code>@param</code> line.&nbsp; If a comment is written with the Javadoc comment symbols (repeat the last symbol of the first line once) doesn&rsquo;t have a topic line (like &ldquo;<code>Function: Multiply</code>&rdquo;) and uses Javadoc tags (like <code>@param</code>) the comment is treated as Javadoc.&nbsp; If any of those conditions aren&rsquo;t true it&rsquo;s treated as a Natural Docs comment.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="../images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="../images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/documenting/walkthrough.html b/vendor/naturaldocs/Help/documenting/walkthrough.html
    new file mode 100644
    index 000000000..055d0b82a
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/documenting/walkthrough.html
    @@ -0,0 +1,181 @@
    +
    +
    +<html><head><title>Documenting Your Code - Walkthrough - Natural Docs</title><link rel=stylesheet type="text/css" href="../styles.css"><link rel=stylesheet type="text/css" href="../examples.css"><script language=JavaScript src="../javascript/PNGHandling.js"></script><script language=JavaScript src="../javascript/BrowserStyles.js"></script><script language=JavaScript src="../example/NaturalDocs.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="../images/header/leftside.png" width=30 height=75><a href="../index.html"><img src="../images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="../images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="../images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="../images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="../images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="../languages.html" class=SideMenuEntry>Language Support</a><a href="../output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="../documenting.html" class=SideMenuEntry id=SelectedSideMenuEntry>Documenting<br>Your Code</a><a href="../keywords.html" class=SideMenuEntry>Keywords</a><a href="../running.html" class=SideMenuEntry>Running</a><a href="../troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="../menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="../styles.html" class=SideMenuEntry>CSS Styles</a><a href="../customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="../customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="../images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Documenting Your Code</div><div class=TOC><a href="#OurFirstFunction">Our First Function</a> &middot; <a href="#ClassesAndScope">Classes and Scope</a> &middot; <a href="#MoreFormatting">More Formatting</a><br><a href="#MoreOnLinking">More on Linking</a> &middot; <a href="#ExtraDocumentation">Extra Documentation</a> &middot; <a href="#AbbreviatedSyntax">Abbreviated Syntax</a></div><div class=Topic><a name="OurFirstFunction"></a><div class=TopicTitle>Our First Function</div><p>So you downloaded Natural Docs, you <a href="../running.html">figured out the command line</a>, and now it&rsquo;s time to start documenting your code.&nbsp; Natural Docs tries to make this very straightforward and painless, so let&rsquo;s just dive right in:</p><pre class=Example>/*
    +   Function: Multiply
    +   Multiplies two integers and returns the result.
    +*/
    +int Multiply (int x, int y)
    +   {  return x * y;  };
    +</pre><p>That&rsquo;s all you need.&nbsp; Run Natural Docs and here&rsquo;s what appears in your output:</p><div class=NDContent><div class=CFunction><div class=CTopic><h3 class=CTitle><a name="Multiply"></a>Multiply</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Multiply (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table><p class=CParagraph>Multiplies two integers and returns the result.</p></div></div></div></div><p>Okay, so that&rsquo;s all you need, but probably not all you want.&nbsp; After all, you&rsquo;ve got some real functions to document, not little one-liners.&nbsp; Here&rsquo;s something more elaborate:</p><pre class=Example>/*
    +   Function: Multiply
    +
    +   Multiplies two integers.
    +
    +   Parameters:
    +
    +      x - The first integer.
    +      y - The second integer.
    +
    +   Returns:
    +
    +      The two integers multiplied together.
    +
    +   See Also:
    +
    +      &lt;Divide&gt;
    +*/
    +int Multiply (int x, int y)
    +   {  return x * y;  };
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><h3 class=CTitle><a name="Example_Class.Multiply"></a>Multiply</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Multiply (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table><p class=CParagraph>Multiplies two integers.</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>x</td><td class=CDLDescription>The first integer.</td></tr><tr><td class=CDLEntry>y</td><td class=CDLDescription>The second integer.</td></tr></table><h4 class=CHeading>Returns</h4><p class=CParagraph>The two integers multiplied together.</p><h4 class=CHeading>See Also</h4><p class=CParagraph><a href="#Example_Class.Divide" class=LFunction id=link116 onMouseOver="ShowTip(event, 'ttDivide', 'link116')" onMouseOut="HideTip('ttDivide')">Divide</a></p></div></div></div></div><div class=CToolTip id="ttAdd"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Add (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Adds two integers.</div></div><div class=CToolTip id="ttSubtract"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Subtract (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Subtracts two integers.</div></div><div class=CToolTip id="ttMultiply"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Multiply (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Multiplies two integers.</div></div><div class=CToolTip id="ttDivide"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int Divide (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Divides two integers.</div></div><div class=CToolTip id="ttIsEqual"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>bool IsEqual (</td><td class=PType>int&nbsp;</td><td class=PParameter>x,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>y</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Returns whether two integers are equal.</div></div><p>Still not too scary, huh?&nbsp; Notice the comments are just as readable as the output.&nbsp; No tags littered about, and the structure is very natural.&nbsp; You probably get it just by looking at it, but let&rsquo;s go through the details anyway.</p><pre class=Example>Function: Multiply
    +</pre><p>Every one of these comments you write (called <i>topics</i>) are going to start with a <i>topic line</i> in the format <code>&ldquo;keyword: title&rdquo;</code>.&nbsp; There are <a href="../keywords.html">a lot of keywords</a>, but they&rsquo;re exactly what you&rsquo;d expect: Function, Class, Variable, etc.&nbsp; There are also a lot of synonyms so instead of Function you could use Func, Procedure, Proc, Method, Constructor, etc.&nbsp; It&rsquo;s designed so you can just use whatever it is you&rsquo;re describing without memorizing anything.&nbsp; You can glance over <a href="../keywords.html">the keyword list</a> but you shouldn&rsquo;t have to consult it very often.</p></p><p>The other part of the topic line is the title.&nbsp; It should match whatever it is you&rsquo;re documenting, in this case the function name Multiply.&nbsp; Natural Docs is case sensitive even if your programming language isn&rsquo;t, so make sure you match it closely or you might not get the prototype in your output, which is the little gray box.&nbsp; You don&rsquo;t need to include the parameters in the title.&nbsp; In fact, it&rsquo;s better if you don&rsquo;t.</p></p><pre class=Example>Parameters:
    +
    +Returns:
    +
    +See Also:
    +</pre><p>You can also define <a href="reference.html#Headings">headings</a> by skipping a line and ending the text with a colon.&nbsp; If you&rsquo;re used to other documentation systems you may think there&rsquo;s only a handful of headings to choose from, but any text formatted this way will become one.&nbsp; If you want a heading called Dependencies you can go right ahead and add it.</p><pre class=Example>x - The first integer.
    +y - The second integer.
    +</pre><p>This is what&rsquo;s called a <a href="reference.html#DefinitionLists">definition list.</a>&nbsp; You can use more than one line to finish the definition, as it won&rsquo;t stop until you skip a line.</p><pre class=Example>x - The first integer.
    +y - The second integer with a long description.
    +    This is still part of the description.
    +
    +This is a new paragraph because we skipped a line.
    +</pre><p>Indentation doesn&rsquo;t matter either, so even if the second description line wasn&rsquo;t indented to match the first, it would still be considered part of it.</p><pre class=Example>&lt;Divide&gt;
    +</pre><p>This is how we link in Natural Docs, with angle brackets.&nbsp; There will be a lot more to say about this later, but for now I&rsquo;ll just show you something cool.&nbsp; Hover over it in the output below:</p><div class=NDContent><div class=CTopic><div class=CBody><p class=CParagraph><a href="#Example_Class.Divide" class=LFunction id=link116 onMouseOver="ShowTip(event, 'ttDivide', 'link216')" onMouseOut="HideTip('ttDivide')">Divide</a></p></div></div></div><p>You get that <i>everywhere</i> in your generated documentation.</p></div><div class=Topic><a name="ClassesAndScope"></a><div class=TopicTitle>Classes and Scope</div><p>So that&rsquo;s good for our one function of questionable usefulness, but what if we have a whole class of questionable usefulness?&nbsp; We can document the class and it&rsquo;s members the same way we documented the individual function, with a Natural Docs comment right above each element.&nbsp; We&rsquo;ll go back to short descriptions to keep the example manageable.</p><pre class=Example>/*
    +   Class: Counter
    +   A class that manages an incrementing counter.
    +*/
    +class Counter
    +   {
    +   public:
    +
    +      /*
    +         Constructor: Counter
    +         Initializes the object.
    +      */
    +      Counter()
    +         {  value = 0;  };
    +
    +      /*
    +         Function: Value
    +         Returns the value of the counter.
    +      */
    +      int Value()
    +         {  return value;  };
    +
    +      /*
    +         Function: Increment
    +         Adds one to the counter.
    +      */
    +      void Increment()
    +         {  value++;  };
    +
    +   protected:
    +
    +      /*
    +         Variable: value
    +         The counter's value.
    +      */
    +      int value;
    +   };
    +</pre><p>Everything&rsquo;s the same, we just substituted Class and Variable for the Function keyword when it was appropriate.&nbsp; We also used Constructor, but we could have just as easily used Function there too.&nbsp; They&rsquo;re both keywords for the same thing so it doesn&rsquo;t matter.</p><a name="Scope"></a><div class="SubTopic">Scope</div><p>Like the source code itself, Natural Docs topics have <a href="reference.html#Scope">scope.</a>&nbsp; Value and Increment are seen as part of class Counter, just like they are in the code.&nbsp; Why is this important?&nbsp; Linking.&nbsp; Linking from one topic to another has similar rules to how one function can call another.&nbsp; Since Value is in the same class as Increment, it&rsquo;s topic can link to it with just <code>&lt;Increment&gt;</code>.&nbsp; However, linking to Increment from a different class would require <code>&lt;Counter.Increment&gt;</code> instead.&nbsp; You can actually use any of the three most common class/member notations: <code>&lt;Counter.Increment&gt;</code>, <code>&lt;Counter::Increment&gt;</code>, and <code>&lt;Counter-&gt;Increment&gt;</code>.</p><p>If your programming language has <a href="../languages.html">full language support</a>, the scope is determined by the code and applied automatically.&nbsp; However, if you only have <a href="../languages.html">basic language support</a> it follows these rules:</p><ul><li>Any topic that appears under a Class topic (or anything that says <a href="../keywords.html">Starts Scope</a>) is part of that class.</li><li>Any topic that appears under a Section topic (or anything that says <a href="../keywords.html">Ends Scope</a>) is global again.</li><li>Any File topic (or anything that says <a href="../keywords.html">Always Global</a>) is global no matter what and doesn&rsquo;t affect any other topics.</li><p></ul></p><p>Chances are you would have written the same thing even if you didn&rsquo;t know this and it would have just worked.&nbsp; You usually won&rsquo;t need to think about them at all.&nbsp; However, it&rsquo;s still good to be aware of them in case something doesn&rsquo;t behave the way you expected it to.</p><p>You actually know enough to go start documenting now.&nbsp; I know Mr. ScrollBar says there&rsquo;s more on this page you can learn, but if you want to skip out early, you can.&nbsp; Really.&nbsp; I don&rsquo;t mind.</p></div><div class=Topic><a name="MoreFormatting"></a><div class=TopicTitle>More Formatting</div><p>Okay then.&nbsp; On we go.</p><a name="ParagraphsBoldAndUnderline"></a><div class="SubTopic">Paragraphs, Bold, and Underline</div><p>The syntax for these three is exactly what you would expect it to be.</p><pre class=Example>*Bold text*
    +
    +_Underlined text_
    +
    +Paragraphs are broken by skipping lines.  So the two
    +lines above each have their own paragraph, but these
    +three lines are all part of the same one.
    +</pre><div class=NDContent><div class=CBody><div class=CFunction><div class=CTopic><p><b>Bold text</b></p><p><u>Underlined text</u></p><p>Paragraphs are broken by skipping lines.&nbsp; So the two lines above each have their own paragraph, but these three lines are all part of the same one.</p></div></div></div></div><p>When underlining multiple words, you can use an underscore for each space or only put them at the edges like we did above.&nbsp; Both ways will work.</p><a name="BulletLists"></a><div class="SubTopic">Bullet Lists</div><p>You can add <a href="reference.html#BulletLists">bullet lists</a> by starting a line with a dash, an asterisk, an o, or a plus.&nbsp; Like definition lists, bullets can span multiple lines and indentation doesn&rsquo;t matter.&nbsp; To end a bullet you have to skip a line before doing something else.</p><pre class=Example>- Bullet one.
    +- Bullet two.
    +  Bullet two continued.
    +- Bullet three.
    +
    +Some text after the bullet list.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><ul class=CBulletList><li>Bullet one.</li><li>Bullet two.&nbsp; Bullet two continued.</li><li>Bullet three.</li></ul><p class=CParagraph>Some text after the bullet list.</p></div></div></div></div><a name="CodeAndTextDiagrams"></a><div class="SubTopic">Code and Text Diagrams</div><p>You can add <a href="reference.html#CodeAndTextDiagrams">example code or text diagrams</a> by starting each line with <code>&gt;</code>, <code>|</code>, or <code>:</code>.</p><pre class=Example>&gt; a = b + c;
    +&gt; b++;
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><pre class=CCode>a = b + c;<br>b++;</pre></div></div></div></div><p>If you have a long stretch, you can use <code>(start code)</code> and <code>(end)</code> instead.</p><pre class=Example>(start code)
    +
    +if (x == 0) {
    +   DoSomething();
    +}
    +
    +return x;
    +
    +(end)
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><pre class=CCode>if (x == 0) {<br>   DoSomething();<br>}<br><br>return x;</pre></div></div></div></div><p>You can also use <code>example</code>, <code>diagram</code>, or <code>table</code> instead of <code>code</code>.&nbsp; Just use whatever&rsquo;s appropriate.</p><p>As I mentioned before, we don&rsquo;t want you to worry about memorizing minor details, so in that spirit it will also accept <code>begin</code> for <code>start</code> and <code>finish</code> or <code>done</code> for <code>end</code>.&nbsp; You can also write <code>(end code)</code> instead of just <code>(end)</code>.</p><p>Syntax highlighting will be applied to <code>(start code)</code> sections by default.&nbsp; If you&rsquo;d like to also apply it to <code>&gt;</code>, <code>:</code>, and <code>|</code> lines or turn it off completely, use the <a href="../running.html"><code>-hl</code> command line option</a>.</p><a name="Images"></a><div class="SubTopic">Images</div><p>You can include images in your documentation by writing &ldquo;<code>(see filename)</code>&rdquo;.&nbsp; If you put it alone on a line it will be embedded in place, or if you put it in a paragraph it will appear after it using the file name as a caption.</p><pre class=Example>This is the first paragraph.
    +
    +(see logo.gif)
    +
    +This is the second paragraph (see logo.gif)  This
    +is more of the second paragraph.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>This is the first paragraph.</p><img src="../images/logo.gif" width="268" height="61"><p class=CParagraph>This is the second paragraph <a href="#Image1" class=CImageLink>(see logo)</a>&nbsp; This is more of the second paragraph.</p><div class=CImage><a name="Image1"></a><div class=CImageCaption>logo</div><img src="../images/logo.gif" width="268" height="61"></div></div></div></div></div><p>The image file names are relative to the source file the comment appears in, so if your file is C:\Project\SourceFile.cpp and your image is C:\Project\Images\Logo.gif, you would write <code>(see Images/Logo.gif)</code>.&nbsp; However, you can also specify image directories in <a href="../running.html#CommandLine">the command line with <code>-img</code></a>, so if all your source files are in C:\Project\Source and all your images are in C:\Project\Images, you can put &ldquo;<code>-img C:\Project\Images</code>&rdquo; on the command line and just use <code>(see logo.gif)</code> again.</p></div><div class=Topic><a name="MoreOnLinking"></a><div class=TopicTitle>More on Linking</div><p>Yes, there&rsquo;s still more to linking.&nbsp; You can link to URLs and e-mail addresses, but in this case the angle brackets are optional.</p><pre class=Example>Visit &lt;http://www.website.com&gt; or send messages to
    +email@address.com.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Visit <a href="#" onClick="return false;" class=LURL>http://www.website.com</a> or send messages to <a href="#" onclick="location.href='mai' + 'lto:' + 'em' + 'ail' + '@' + 'addre' + 'ss.com'; return false;" class="LEMail">em<span style="display: none;">.nosp@m.</span>ail<span>@</span>addre<span style="display: none;">.nosp@m.</span>ss.com</a>.</p></div></div></div></div><p>You can create named links by putting the text, &ldquo;at&rdquo;, and then the address in the angle brackets.&nbsp; This format lets it read naturally in a sentence.</p><pre class=Example>Visit &lt;the website at http://www.website.com&gt; or &lt;e-mail me at email@address.com&gt;.
    +</pre><div class=NDContent><div class=CFunction><div class=CTopic><div class=CBody><p class=CParagraph>Visit <a href="#" onClick="return false;" class=LURL>the website</a> or <a href="#" onclick="location.href='mai' + 'lto:' + 'em' + 'ail' + '@' + 'addre' + 'ss.com'; return false;" class="LEMail">e-mail me</a>.</p></div></div></div></div><p>E-mail addresses are protected from spam crawlers.&nbsp; They look and act like regular links (try it above) but you can see the actual HTML that&rsquo;s generated for them <a href="reference.html#URLsAndEMail">here</a>.</p><p>As for regular links, to help them fit into sentences easily you can actually include plurals and possessives inside the angle brackets.&nbsp; In other words, you don&rsquo;t have to use awkward syntax like <code>&lt;Object&gt;s</code>, although that&rsquo;s supported as well.&nbsp; You can simply write <code>&lt;Objects&gt;</code> and it will link to the symbol <code>Object</code> just fine.&nbsp; It can handle any plural and/or possessive form you can throw at it.&nbsp; I&rsquo;m not kidding: <code>Foxes</code>, <code>Fox&rsquo;s</code>, <code>Foxes&rsquo;</code>, <code>Children</code>, <code>Mice</code>, <code>Alumni</code>, <code>Indices</code>, <code>Amoebae</code>, <code>Teeth</code>, just try to trip it up.</p></div><div class=Topic><a name="ExtraDocumentation"></a><div class=TopicTitle>Extra Documentation</div><p>Sometimes you want to include documentation that doesn&rsquo;t correspond directly to a code element.&nbsp; Maybe you want to include license information or architecture notes.&nbsp; There are two ways to do this.</p><a name="FreestandingTopics"></a><div class="SubTopic">Freestanding Topics</div><p>Just because most of the things you write will directly correspond to an element of your source code doesn&rsquo;t mean they have to.&nbsp; You can pick any of <a href="../keywords.html">the available keywords</a> and create a freestanding comment with it.&nbsp; For example:</p><pre class=Example>/*
    +   Class: Counter
    +   A class that manages an incrementing counter.
    +*/
    +class Counter
    +   {
    +   public:
    +
    +      /*
    +         About: License
    +         This file is licensed under the GPL.
    +      */
    +
    +      /*
    +         Constructor: Counter
    +         Initializes the object.
    +      */
    +      Counter()
    +         {  value = 0;  };
    +   ...
    +</pre><p>The extra license topic will be added to the output just like the functions.</p><div class=NDContent><div class=CFunction><div class=CTopic><h3 class=CTitle>License</h3><div class=CBody><p class=CParagraph>This file is licensed under the GPL.</p></div></div></div></div><p>Remember that because of <a href="#Scope">scope</a>, the License topic will actually be considered part of Counter the way it&rsquo;s listed above.&nbsp; You&rsquo;d link to it from outside Counter with <code>&lt;Counter.License&gt;</code>.&nbsp; That idea may take some getting used to, but if an extra topic only applies to one class that&rsquo;s actually the most appropriate way to do it.&nbsp; In this case it&rsquo;s a license, so if it applies to the entire project instead you could put the comment above the class to make it global, just like moving a function there would.</p><a name="TextFiles"></a><div class="SubTopic">Text Files</div><p>You can also add additional documentation with text files.&nbsp; If you put a file with a .txt extension in your source tree and start it with a topic line, it&rsquo;s contents will be treated the same as if it were in a comment in your source code.&nbsp; That means you can define multiple topics within it, you can link between them and topics in your source code, and you can use all available formatting options.</p><pre class=Example>Title: License
    +
    +This file is licensed under the GPL.
    +
    +I can link to &lt;Counter&gt; and &lt;Counter.Increment&gt;, and
    +the documentation in that class can even link back
    +with &lt;License&gt;.
    +
    +
    +About: Second Topic
    +
    +I can create a *second* topic in here too, complete
    +with formatting.
    +</pre><p>The thing some people forget though is that you <b>must</b> start it with a topic line, like &ldquo;<code>Title: License</code>&rdquo; above.&nbsp; This is how Natural Docs tells it apart from regular text files.</p></div><div class=Topic><a name="AbbreviatedSyntax"></a><div class=TopicTitle>Abbreviated Syntax</div><p>Here&rsquo;s another useful thing you may want to know about.&nbsp; Suppose you have a lot of little things to document, like constants.&nbsp; Writing a separate topic for each one can be very tedious, no matter how much you compress it:</p><pre class=Example>// Constant: COUNTER_NORMAL
    +// Causes the counter to increment normally.
    +#define COUNTER_NORMAL 0
    +
    +// Constant: COUNTER_ODD
    +// Causes the counter to only increment in odd numbers.
    +#define COUNTER_ODD 1
    +
    +// Constant: COUNTER_EVEN
    +// Causes the counter to only increment in even numbers.
    +#define COUNTER_EVEN 2
    +</pre><p>One thing you may have noticed in the <a href="../keywords.html">keyword list</a> is that they almost all have plural forms.&nbsp; These are used to create what are called <a href="reference.html#ListTopics">list topics.</a>&nbsp; You define a topic using a plural keyword, and then anything appearing in a <a href="reference.html#DefinitionLists">definition list</a> within it creates a linkable symbol as if they each had their own topic.&nbsp; For example:</p><pre class=Example>/*
    +   Constants: Counter Modes
    +
    +   COUNTER_NORMAL - Causes the counter to increment normally.
    +   COUNTER_ODD    - Causes the counter to only increment in odd numbers.
    +   COUNTER_EVEN   - Causes the counter to only increment in even numbers.
    +*/
    +#define COUNTER_NORMAL 0
    +#define COUNTER_ODD 1
    +#define COUNTER_EVEN 2
    +</pre><p>I would now be able to write <code>&lt;COUNTER_ODD&gt;</code> and have it work the same as it would with the first example.</p><p>Using the enum or enumeration keyword is special because it automatically behaves in a similar manner.&nbsp; This allows both the enum and its values to be documented in the same place.</p><pre class=Example>/*
    +   Enum: CounterMode
    +
    +   NORMAL - Causes the counter to increment normally.
    +   ODD    - Causes the counter to only increment in odd numbers.
    +   EVEN   - Causes the counter to only increment in even numbers.
    +*/
    +enum CounterMode { NORMAL, ODD, EVEN };
    +</pre><p>That&rsquo;s it, you&rsquo;re done with this walkthrough.&nbsp; You should know enough now to make very good use of Natural Docs.&nbsp; If you still want to know more, you can look in <a href="reference.html">the reference</a> for some of the smaller details we may have skipped over.&nbsp; Also, look at the Customizing pages on this web site for even more you can do.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="../images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="../images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/example/Default.css b/vendor/naturaldocs/Help/example/Default.css
    new file mode 100644
    index 000000000..7318fdcd9
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/example/Default.css
    @@ -0,0 +1,528 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2004 Greg Valure
    +   Natural Docs is licensed under the GPL
    +*/
    +
    +body {
    +    font-family: Verdana, Arial, sans-serif;
    +    color: #000000;
    +    margin: 0px; padding: 0px }
    +
    +body.UnframedPage {
    +    background-color: #E8E8E8 }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Can't use something like display: none or it won't break.  */
    +.HB {
    +    font-size: 1px }
    +
    +
    +
    +
    +body.FramedMenuPage,
    +.MenuSection {
    +    font-size: 9pt;
    +    background-color: #E8E8E8;
    +    padding: 10px 0 0 0 }
    +
    +.MenuSection {
    +    width: 27ex }
    +
    +
    +    .MTitle {
    +        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 9pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px }
    +
    +    /*  Konqueror just can't do margins.  */
    +    .KHTML .MGroup {
    +        margin-bottom: 0; padding-bottom: 1em }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0 }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Gecko .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +
    +
    +body.FramedContentPage,
    +.ContentSection {
    +    background-color: #FFFFFF;
    +    padding-bottom: 15px }
    +
    +.ContentSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +
    +    .CTopic {
    +        font-size: 10pt;
    +        /*  This should be a margin but Konq 3.1.1 sucks.  */
    +        padding-bottom: 3em }
    +
    +
    +    .CTitle {
    +        font-size: 12pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 16pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 18pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0; max-width: 50%;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 8pt }
    +
    +    /*  Opera 6 gives it a huge height otherwise.  */
    +    .Opera6 .CTooltip, .Opera5 .CTooltip {
    +        max-width: 100% }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 10pt;
    +        margin-top: 1.5em; margin-bottom: .5em }
    +
    +    .CCode {
    +        font: 10pt "Courier New", Courier, monospace;
    +        overflow: auto;
    +        }
    +
    +    .CBulletList {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +    /* IE 4 and Konqueror always makes it too long.  */
    +    .IE4 .CDescriptionList,
    +    .KHTML .CDescriptionList {
    +        width: 85% }
    +
    +        .CDLEntry {
    +            font: 10pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 10pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +    .CTopic img {
    +        text-align: center;
    +        display: block;
    +        margin: 1em auto;
    +        }
    +    .CImageCaption {
    +        font-variant: small-caps;
    +        font-size: 8pt;
    +        color: #808080;
    +        text-align: center;
    +        position: relative;
    +        top: 1em;
    +        }
    +
    +    .CImageLink {
    +        color: #808080;
    +        font-style: italic;
    +        }
    +    a.CImageLink:link,
    +    a.CImageLink:visited,
    +    a.CImageLink:hover { color: #808080 }
    +
    +
    +
    +.Prototype {
    +    font: 10pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 10pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype {
    +        background-color: #FFF8F8; border-color: #E8C8C8 }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 12pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* Let's observe the evolution of IE's brokeness, shall we?
    +        IE 4 always makes them too long, there's no way around it.  */
    +    .IE4 .SBorder {
    +        width: 85% }
    +    /* IE 5 will make them too long unless you set the width to 100%.  Isn't this implied for a div?  */
    +    .IE5 .SBorder {
    +        width: 100% }
    +    /* IE 6 behaves like 5 when it's in a frame, but without frames it will be correct without a width or slightly too long
    +        (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  */
    +    body.FramedContentPage .IE6 .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 9pt; width: 100% }
    +
    +    .SEntrySize {
    +        width: 30% }
    +    .SDescriptionSize {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +
    +    .SEntry .SIndent1 {
    +        margin-left: 1.5ex }
    +    .SEntry .SIndent2 {
    +        margin-left: 3ex }
    +    .SEntry .SIndent3 {
    +        margin-left: 4.5ex }
    +    .SEntry .SIndent4 {
    +        margin-left: 6ex }
    +    .SEntry .SIndent5 {
    +        margin-left: 7.5ex }
    +
    +    .SDescription {
    +        padding-left: 3ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +
    +    .SGroup {
    +        margin-top: .5em; margin-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold; font-size: 10pt;
    +        margin-bottom: .25em }
    +
    +    .SClass,
    +    .SDatabase,
    +    .SDatabaseTable,
    +    .SSection {
    +        margin-top: 1em }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 10pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Gecko .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +body.FramedIndexPage,
    +.IndexSection {
    +    background-color: #FFFFFF;
    +    font-size: 10pt;
    +    padding: 15px }
    +
    +.IndexSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +    .IPageTitle {
    +        font-size: 20pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .INavigationBar {
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 16pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        padding-left: 1ex;  }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .ISymbolPrefix {
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +
    +
    +
    +.Footer {
    +    font-size: 8pt; color: #909090 }
    +
    +body.UnframedPage .Footer {
    +    text-align: right;
    +    margin: 2px }
    +
    +body.FramedMenuPage .Footer {
    +    text-align: center;
    +    margin: 5em 10px 0 10px}
    +
    +    .Footer a:link,
    +    .Footer a:hover,
    +    .Footer a:visited { color: #909090 }
    +    .Footer a:active { color: #A00000 }
    diff --git a/vendor/naturaldocs/Help/example/NaturalDocs.js b/vendor/naturaldocs/Help/example/NaturalDocs.js
    new file mode 100644
    index 000000000..2af84cf54
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/example/NaturalDocs.js
    @@ -0,0 +1,204 @@
    +
    +//
    +//  Browser Styles
    +// ____________________________________________________________________________
    +
    +var agt=navigator.userAgent.toLowerCase();
    +var browserType;
    +var browserVer;
    +
    +if (agt.indexOf("opera") != -1)
    +    {
    +    browserType = "Opera";
    +
    +    if (agt.indexOf("opera 5") != -1 || agt.indexOf("opera/5") != -1)
    +        {  browserVer = "Opera5";  }
    +    else if (agt.indexOf("opera 6") != -1 || agt.indexOf("opera/6") != -1)
    +        {  browserVer = "Opera6";  }
    +    else if (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1)
    +        {  browserVer = "Opera7";  }
    +    }
    +
    +else if (agt.indexOf("khtml") != -1 || agt.indexOf("konq") != -1 || agt.indexOf("safari") != -1)
    +    {
    +    browserType = "KHTML";
    +    }
    +
    +else if (agt.indexOf("msie") != -1)
    +    {
    +    browserType = "IE";
    +
    +    if (agt.indexOf("msie 4") != -1)
    +        {  browserVer = "IE4";  }
    +    else if (agt.indexOf("msie 5") != -1)
    +        {  browserVer = "IE5";  }
    +    else if (agt.indexOf("msie 6") != -1)
    +        {  browserVer = "IE6";  }
    +    }
    +
    +else if (agt.indexOf("gecko") != -1)
    +    {
    +    browserType = "Gecko";
    +    }
    +
    +// Opera already taken care of.
    +else if (agt.indexOf("mozilla") != -1 && agt.indexOf("compatible") == -1 && agt.indexOf("spoofer") == -1 &&
    +           agt.indexOf("webtv") == -1 && agt.indexOf("hotjava") == -1)
    +    {
    +    browserType = "Netscape";
    +
    +    if (agt.indexOf("mozilla/4") != -1)
    +        {  browserVer = "Netscape4";  }
    +    }
    +
    +
    +//
    +//  Menu
    +// ____________________________________________________________________________
    +
    +
    +function ToggleMenu(id)
    +    {
    +    if (!window.document.getElementById)
    +        {  return;  };
    +
    +    var display = window.document.getElementById(id).style.display;
    +
    +    if (display == "none")
    +        {  display = "block";  }
    +    else
    +        {  display = "none";  }
    +
    +    window.document.getElementById(id).style.display = display;
    +    }
    +
    +
    +//
    +//  Tooltips
    +// ____________________________________________________________________________
    +
    +
    +var tooltipTimer = 0;
    +
    +function ShowTip(event, tooltipID, linkID)
    +    {
    +    if (tooltipTimer)
    +        {  clearTimeout(tooltipTimer);  };
    +
    +    var docX = event.clientX + window.pageXOffset;
    +    var docY = event.clientY + window.pageYOffset;
    +
    +    var showCommand = "ReallyShowTip('" + tooltipID + "', '" + linkID + "', " + docX + ", " + docY + ")";
    +
    +    // KHTML cant handle showing on a timer right now.
    +
    +    if (browserType != "KHTML")
    +        {  tooltipTimer = setTimeout(showCommand, 1000);  }
    +    else
    +        {  eval(showCommand);  };
    +    }
    +
    +function ReallyShowTip(tooltipID, linkID, docX, docY)
    +    {
    +    tooltipTimer = 0;
    +
    +    var tooltip;
    +    var link;
    +
    +    if (document.getElementById)
    +        {
    +        tooltip = document.getElementById(tooltipID);
    +        link = document.getElementById(linkID);
    +        }
    +    else if (document.all)
    +        {
    +        tooltip = eval("document.all['" + tooltipID + "']");
    +        link = eval("document.all['" + linkID + "']");
    +        }
    +
    +    if (tooltip)
    +        {
    +        var left = 0;
    +        var top = 0;
    +
    +        // Not everything supports offsetTop/Left/Width, and some, like Konqueror and Opera 5, think they do but do it badly.
    +
    +        if (link && link.offsetWidth != null && browserType != "KHTML" && browserVer != "Opera5")
    +            {
    +            var item = link;
    +            while (item != document.body)
    +                {
    +                left += item.offsetLeft;
    +                item = item.offsetParent;
    +                }
    +
    +            item = link;
    +            while (item != document.body)
    +                {
    +                top += item.offsetTop;
    +                item = item.offsetParent;
    +                }
    +            top += link.offsetHeight;
    +            }
    +
    +        // The fallback method is to use the mouse X and Y relative to the document.  We use a separate if and test if its a number
    +        // in case some browser snuck through the above if statement but didn't support everything.
    +
    +        if (!isFinite(top) || top == 0)
    +            {
    +            left = docX;
    +            top = docY;
    +            }
    +
    +        // Some spacing to get it out from under the cursor.
    +
    +        top += 10;
    +
    +        // Make sure the tooltip doesnt get smushed by being too close to the edge, or in some browsers, go off the edge of the
    +        // page.  We do it here because Konqueror does get offsetWidth right even if it doesnt get the positioning right.
    +
    +        if (tooltip.offsetWidth != null)
    +            {
    +            var width = tooltip.offsetWidth;
    +            var docWidth = document.body.clientWidth;
    +
    +            if (left + width > docWidth)
    +                {  left = docWidth - width - 1;  }
    +            }
    +
    +        // Opera 5 chokes on the px extension, so it can use the Microsoft one instead.
    +
    +        if (tooltip.style.left != null && browserVer != "Opera5")
    +            {
    +            tooltip.style.left = left + "px";
    +            tooltip.style.top = top + "px";
    +            }
    +        else if (tooltip.style.pixelLeft != null)
    +            {
    +            tooltip.style.pixelLeft = left;
    +            tooltip.style.pixelTop = top;
    +            }
    +
    +        tooltip.style.visibility = "visible";
    +        }
    +    }
    +
    +function HideTip(tooltipID)
    +    {
    +    if (tooltipTimer)
    +        {
    +        clearTimeout(tooltipTimer);
    +        tooltipTimer = 0;
    +        }
    +
    +    var tooltip;
    +
    +    if (document.getElementById)
    +        {  tooltip = document.getElementById(tooltipID); }
    +    else if (document.all)
    +        {  tooltip = eval("document.all['" + tooltipID + "']");  }
    +
    +    if (tooltip)
    +        {  tooltip.style.visibility = "hidden";  }
    +    }
    +
    diff --git a/vendor/naturaldocs/Help/example/Roman.css b/vendor/naturaldocs/Help/example/Roman.css
    new file mode 100644
    index 000000000..54acc6e31
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/example/Roman.css
    @@ -0,0 +1,507 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2004 Greg Valure
    +   Natural Docs is licensed under the GPL
    +*/
    +
    +body {
    +    font-family: "Times New Roman", Roman, serif;
    +    color: #000000;
    +    margin: 0px; padding: 0px }
    +
    +body.UnframedPage {
    +    background-color: #E8E8E8 }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Can't use something like display: none or it won't break.  */
    +.HB {
    +    font-size: 1px }
    +
    +
    +
    +
    +body.FramedMenuPage,
    +.MenuSection {
    +    font-size: 10pt;
    +    background-color: #E8E8E8;
    +    padding: 10px 0 0 0 }
    +
    +.MenuSection {
    +    width: 27ex }
    +
    +
    +    .MTitle {
    +        font-size: 18pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 10pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px }
    +
    +    /*  Konqueror just can't do margins.  */
    +    .KHTML .MGroup {
    +        margin-bottom: 0; padding-bottom: 1em }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0 }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Gecko .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +
    +
    +body.FramedContentPage,
    +.ContentSection {
    +    background-color: #FFFFFF;
    +    padding-bottom: 15px }
    +
    +.ContentSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +
    +    .CTopic {
    +        font-size: 12pt;
    +        /*  This should be a margin but Konq 3.1.1 sucks.  */
    +        padding-bottom: 3em }
    +
    +
    +    .CTitle {
    +        font-size: 16pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 18pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 24pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0; max-width: 50%;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 10pt }
    +
    +    /*  Opera 6 gives it a huge height otherwise.  */
    +    .Opera6 .CTooltip, .Opera5 .CTooltip {
    +        max-width: 100% }
    +
    +    .CHeading {
    +        font-weight: bold;
    +        margin-top: 1.5em; margin-bottom: .5em }
    +
    +    .CCode {
    +        font: 10pt "Courier New", Courier, monospace;
    +        overflow: auto;
    +        }
    +
    +    .CBulletList {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +    /* IE 4 and Konqueror always makes it too long.  */
    +    .IE4 .CDescriptionList,
    +    .KHTML .CDescriptionList {
    +        width: 85% }
    +
    +        .CDLEntry {
    +            font: 10pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 12pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 10pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 10pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype {
    +        background-color: #FFF8F8; border-color: #E8C8C8 }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 14pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* Let's observe the evolution of IE's brokeness, shall we?
    +        IE 4 always makes them too long, there's no way around it.  */
    +    .IE4 .SBorder {
    +        width: 85% }
    +    /* IE 5 will make them too long unless you set the width to 100%.  Isn't this implied for a div?  */
    +    .IE5 .SBorder {
    +        width: 100% }
    +    /* IE 6 behaves like 5 when it's in a frame, but without frames it will be correct without a width or slightly too long
    +        (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  */
    +    body.FramedContentPage .IE6 .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 10pt; width: 100% }
    +
    +    .SEntrySize {
    +        width: 30% }
    +    .SDescriptionSize {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +
    +    .SEntry .SIndent1 {
    +        margin-left: 1.5ex }
    +    .SEntry .SIndent2 {
    +        margin-left: 3ex }
    +    .SEntry .SIndent3 {
    +        margin-left: 4.5ex }
    +    .SEntry .SIndent4 {
    +        margin-left: 6ex }
    +    .SEntry .SIndent5 {
    +        margin-left: 7.5ex }
    +
    +    .SDescription {
    +        padding-left: 3ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +
    +    .SGroup {
    +        margin-top: .5em; margin-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold; font-size: 12pt;
    +        margin-bottom: .25em }
    +
    +    .SClass,
    +    .SDatabase,
    +    .SDatabaseTable,
    +    .SSection {
    +        margin-top: 1em }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 12pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Gecko .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +body.FramedIndexPage,
    +.IndexSection {
    +    background-color: #FFFFFF;
    +    font: 12pt "Times New Roman", serif;
    +    padding: 15px }
    +
    +.IndexSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +    .IPageTitle {
    +        font-size: 24pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .INavigationBar {
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 20pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        padding-left: 1ex;  }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .ISymbolPrefix {
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +
    +
    +.Footer {
    +    font-size: 8pt; color: #909090 }
    +
    +body.UnframedPage .Footer {
    +    text-align: right;
    +    margin: 2px }
    +
    +body.FramedMenuPage .Footer {
    +    text-align: center;
    +    margin: 5em 10px 0 10px}
    +
    +    .Footer a:link,
    +    .Footer a:hover,
    +    .Footer a:visited { color: #909090 }
    +    .Footer a:active { color: #A00000 }
    diff --git a/vendor/naturaldocs/Help/example/Small.css b/vendor/naturaldocs/Help/example/Small.css
    new file mode 100644
    index 000000000..0cb7be1c9
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/example/Small.css
    @@ -0,0 +1,507 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2004 Greg Valure
    +   Natural Docs is licensed under the GPL
    +*/
    +
    +body {
    +    font-family: Verdana, Arial, sans-serif;
    +    color: #000000;
    +    margin: 0px; padding: 0px }
    +
    +body.UnframedPage {
    +    background-color: #E8E8E8 }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Can't use something like display: none or it won't break.  */
    +.HB {
    +    font-size: 1px }
    +
    +
    +
    +
    +body.FramedMenuPage,
    +.MenuSection {
    +    font-size: 8pt;
    +    background-color: #E8E8E8;
    +    padding: 10px 0 0 0 }
    +
    +.MenuSection {
    +    width: 27ex }
    +
    +
    +    .MTitle {
    +        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 9pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px }
    +
    +    /*  Konqueror just can't do margins.  */
    +    .KHTML .MGroup {
    +        margin-bottom: 0; padding-bottom: 1em }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0 }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Gecko .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +
    +
    +body.FramedContentPage,
    +.ContentSection {
    +    background-color: #FFFFFF;
    +    padding-bottom: 15px }
    +
    +.ContentSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +
    +    .CTopic {
    +        font-size: 8pt;
    +        /*  This should be a margin but Konq 3.1.1 sucks.  */
    +        padding-bottom: 3em }
    +
    +
    +    .CTitle {
    +        font-size: 11pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 16pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 18pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0; max-width: 50%;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 8pt }
    +
    +    /*  Opera 6 gives it a huge height otherwise.  */
    +    .Opera6 .CTooltip, .Opera5 .CTooltip {
    +        max-width: 100% }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 9pt;
    +        margin-top: 1.5em; margin-bottom: .5em }
    +
    +    .CCode {
    +        font: 8pt "Courier New", Courier, monospace;
    +        overflow: auto;
    +        }
    +
    +    .CBulletList {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +    /* IE 4 and Konqueror always makes it too long.  */
    +    .IE4 .CDescriptionList,
    +    .KHTML .CDescriptionList {
    +        width: 85% }
    +
    +        .CDLEntry {
    +            font: 8pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 8pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 8pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 8pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype {
    +        background-color: #FFF8F8; border-color: #E8C8C8 }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 11pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* Let's observe the evolution of IE's brokeness, shall we?
    +        IE 4 always makes them too long, there's no way around it.  */
    +    .IE4 .SBorder {
    +        width: 85% }
    +    /* IE 5 will make them too long unless you set the width to 100%.  Isn't this implied for a div?  */
    +    .IE5 .SBorder {
    +        width: 100% }
    +    /* IE 6 behaves like 5 when it's in a frame, but without frames it will be correct without a width or slightly too long
    +        (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  */
    +    body.FramedContentPage .IE6 .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Gecko .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 8pt; width: 100% }
    +
    +    .SEntrySize {
    +        width: 30% }
    +    .SDescriptionSize {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +
    +    .SEntry .SIndent1 {
    +        margin-left: 1.5ex }
    +    .SEntry .SIndent2 {
    +        margin-left: 3ex }
    +    .SEntry .SIndent3 {
    +        margin-left: 4.5ex }
    +    .SEntry .SIndent4 {
    +        margin-left: 6ex }
    +    .SEntry .SIndent5 {
    +        margin-left: 7.5ex }
    +
    +    .SDescription {
    +        padding-left: 3ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +
    +    .SGroup {
    +        margin-top: .5em; margin-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold; font-size: 9pt;
    +        margin-bottom: .25em }
    +
    +    .SClass,
    +    .SDatabase,
    +    .SDatabaseTable,
    +    .SSection {
    +        margin-top: 1em }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 8pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Gecko .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +body.FramedIndexPage,
    +.IndexSection {
    +    background-color: #FFFFFF;
    +    font-size: 8pt;
    +    padding: 15px }
    +
    +.IndexSection {
    +    border-width: 0 0 1px 1px; border-style: solid; border-color: #000000 }
    +
    +    .IPageTitle {
    +        font-size: 20pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .INavigationBar {
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 14pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        padding-left: 1ex;  }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .ISymbolPrefix {
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +
    +
    +.Footer {
    +    font-size: 8pt; color: #909090 }
    +
    +body.UnframedPage .Footer {
    +    text-align: right;
    +    margin: 2px }
    +
    +body.FramedMenuPage .Footer {
    +    text-align: center;
    +    margin: 5em 10px 0 10px}
    +
    +    .Footer a:link,
    +    .Footer a:hover,
    +    .Footer a:visited { color: #909090 }
    +    .Footer a:active { color: #A00000 }
    diff --git a/vendor/naturaldocs/Help/example/showstyle.html b/vendor/naturaldocs/Help/example/showstyle.html
    new file mode 100644
    index 000000000..e71b8b9cd
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/example/showstyle.html
    @@ -0,0 +1,43 @@
    +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    +
    +<html><head><title>CSS Sample - Project</title><script language=JavaScript src="NaturalDocs.js"></script>
    +
    +<script language=JavaScript>
    +
    +// This is added
    +
    +var css = window.location.search;
    +css = css.substr(1);
    +document.write('<link rel="stylesheet" type="text/css" href="' + css + '.css">');
    +
    +
    +// --></script>
    +
    +
    +</head><body class=UnframedPage><script language=JavaScript><!--
    +if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    +
    +<!--  Generated by Natural Docs, version 1.3 -->
    +<!--  http://www.naturaldocs.org  -->
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +
    +<!-- This is added -->
    +
    +<div style="font: 10pt Verdana, Arial, sans-serif; background-color: white; padding: 5px; border-bottom: 1px solid black; text-align: center">This is the <b><script language=JavaScript>document.write(css);</script></b> style.&nbsp; <a href="javascript:history.back()">Back</a></div>
    +
    +
    +<table border=0 cellspacing=0 cellpadding=0 width=100%><tr><td class=MenuSection valign=top><!--START_ND_MENU--><div class=MTitle>Project<div class=MSubTitle>SubTitle</div></div><div class=MEntry><div class=MFile id=MSelected>CSS Sample</div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Group</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MText>Arbitrary Text</div></div><div class=MEntry><div class=MFile><a href="#">File 1</a></div></div><div class=MEntry><div class=MFile><a href="#">File 2</a></div></div></div></div></div><div class=MEntry><div class=MLink><a href="http://www.naturaldocs.org">External Link</a></div></div><div class=MEntry><div class=MIndex><a href="#">Index</a></div></div><!--END_ND_MENU--></td>
    +
    +<td class=ContentSection valign=top><div class=CSection id=MainTopic><div class=CTopic><h1 class=CTitle><a name="CSS_Sample"></a>CSS Sample</h1><div class=CBody><p class=CParagraph>Here’s what the output would look like with the currently selected CSS style.&nbsp;  The CSS structure is well-documented so you can easily alter it or make your own.</p><p class=CParagraph>Here&rsquo;s a paragraph break.&nbsp;  Natural Docs defaults to print-style paragraphs, where each one is indented rather than separated with a blank line.&nbsp;  If you open the CSS file it will tell you which line to remove to go back to web-style paragraphs.</p><h4 class=CHeading>Header</h4><p class=CParagraph>There&rsquo;s a header, just so you know what one looks like in this style.&nbsp;  As you can tell, the quality of the text here is going to go downhill fast as I&rsquo;m really just blathering on to fill up the page.&nbsp;  If you&rsquo;re actually reading this, you can safely stop now.&nbsp;  No, really.&nbsp;  I&rsquo;m not going to say anything important from here on down.&nbsp;  Reading it will be just as boring as writing it was.</p><ul class=CBulletList><li>Here&rsquo;s a bullet.&nbsp;  Thought you should see that.</li><li>Here&rsquo;s another one.&nbsp;  Well look at that.</li><li>And a third.&nbsp;  Looks just like all the others, but I&rsquo;m going to give it some more text.&nbsp;  So there you go.</li></ul><p class=CParagraph>Now lets look at a text diagram, shall we?&nbsp;  Are you still reading this?&nbsp;  What&rsquo;s wrong with you?</p><pre class=CCode>+------+     +------+
    +| Moby | --&gt; | Dick |
    ++------+     +------+
    +   |
    +   V
    ++----------+
    +| Musician |
    ++----------+</pre><div class=Summary><div class=STitle>Summary</div><div class=SBorder><table border=0 cellspacing=0 cellpadding=0 class=STable><tr><td class=SEntrySize><div class=SMain><div class=SEntry><a href="#CSS_Sample" >CSS Sample</a></div></div></td><td class=SDescriptionSize><div class=SMain><div class=SDescription>Here’s what the output would look like with the currently selected CSS style. </div></div></td></tr><tr class=SMarked><td><div class=SFunction><div class=SEntry><div class=SIndent1><a href="#DoSomething" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')">DoSomething</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent1>Ah, here&rsquo;s our first function. </div></div></div></td></tr><tr><td><div class=SFunction><div class=SEntry><div class=SIndent1><a href="#DoSomethingElse" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')">DoSomethingElse</a></div></div></div></td><td><div class=SFunction><div class=SDescription><div class=SIndent1>This is another function, much like <a href="#DoSomething" class=LFunction id=link3 onMouseOver="ShowTip(event, 'tt1', 'link3')" onMouseOut="HideTip('tt1')">DoSomething()</a>, but different, in that it does something else. </div></div></div></td></tr><tr><td><div class=SGroup><div class=SEntry><div class=SIndent1><a href="#Variables" >Variables</a></div></div></div></td><td><div class=SGroup><div class=SDescription><div class=SIndent1></div></div></div></td></tr><tr class=SMarked><td><div class=SVariable><div class=SEntry><div class=SIndent2><a href="#myVariable" id=link4 onMouseOver="ShowTip(event, 'tt3', 'link4')" onMouseOut="HideTip('tt3')">myVariable</a></div></div></div></td><td><div class=SVariable><div class=SDescription><div class=SIndent2>This is my variable. </div></div></div></td></tr></table></div></div></div></div></div><div class=CFunction><div class=CTopic><h3 class=CTitle><a name="DoSomething"></a>DoSomething</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int DoSomething(</td><td class=PType>int&nbsp;</td><td class=PParameter>one,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>two,</td><td></td></tr><tr><td></td><td class=PType>float&nbsp;</td><td class=PParameter>four</td><td class=PAfterParameters>)</td></tr></table></td></tr></table><p class=CParagraph>Ah, here&rsquo;s our first function.&nbsp;  I have nothing to say about it just like I had nothing to say about anything else.&nbsp;  Typing, typing, typing to fill up space.&nbsp;  Just a random stream-of-consciousness about nothing.</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>one</td><td class=CDLDescription>This is the first parameter, aptly named one.</td></tr><tr><td class=CDLEntry>two</td><td class=CDLDescription>Bet you can&rsquo;t guess what the next one is called?</td></tr><tr><td class=CDLEntry>four</td><td class=CDLDescription>Hah!&nbsp;  Did that just to screw you up.</td></tr></table><h4 class=CHeading>Returns</h4><p class=CParagraph>Sometimes it returns, sometimes it doesn&rsquo;t.&nbsp;  It&rsquo;s moody that way.</p></div></div></div><div class=CFunction><div class=CTopic><h3 class=CTitle><a name="DoSomethingElse"></a>DoSomethingElse</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>bool DoSomethingElse()</tr></td></table><p class=CParagraph>This is another function, much like <a href="#DoSomething" class=LFunction id=link5 onMouseOver="ShowTip(event, 'tt1', 'link5')" onMouseOut="HideTip('tt1')">DoSomething()</a>, but different, in that it does something else.&nbsp;  Hover over <a href="#DoSomething" class=LFunction id=link6 onMouseOver="ShowTip(event, 'tt1', 'link6')" onMouseOut="HideTip('tt1')">DoSomething()</a>, will ya?&nbsp;  See the nice DHTML tooltip goodness.&nbsp;  Here, here&rsquo;s a link to <a href="#myVariable" class=LVariable id=link7 onMouseOver="ShowTip(event, 'tt3', 'link7')" onMouseOut="HideTip('tt3')">myVariable</a> too.&nbsp;  Hover over that.</p></div></div></div><div class=CGroup><div class=CTopic><h3 class=CTitle><a name="Variables"></a>Variables</h3></div></div><div class=CVariable><div class=CTopic><h3 class=CTitle><a name="myVariable"></a>myVariable</h3><div class=CBody><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>int myVariable</tr></td></table><p class=CParagraph>This is my variable.&nbsp;  See how the prototype is colored differently on the default styles?&nbsp;  I thought that was cool too, since you can tell where you are in the documentation easier.&nbsp;  Or maybe you didn&rsquo;t think it was cool.&nbsp;  I shouldn&rsquo;t make assumptions like that.&nbsp;  See, now you went and hurt my feelings.&nbsp;  Shame on you.</p><p class=CParagraph>Um, why are you still reading this?&nbsp;  Didn&rsquo;t you learn by now?</p></div></div></div></td>
    +
    +</tr></table><div class=Footer><!--START_ND_FOOTER-->Generated by <a href="http://www.naturaldocs.org">Natural Docs</a><!--END_ND_FOOTER--></div><div class=CToolTip id="tt1"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class=PBeforeParameters>int DoSomething(</td><td class=PType>int&nbsp;</td><td class=PParameter>one,</td><td></td></tr><tr><td></td><td class=PType>int&nbsp;</td><td class=PParameter>two,</td><td></td></tr><tr><td></td><td class=PType>float&nbsp;</td><td class=PParameter>four</td><td class=PAfterParameters>)</td></tr></table></td></tr></table>Ah, here&rsquo;s our first function. </div></div><div class=CToolTip id="tt2"><div class=CFunction><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>bool DoSomethingElse()</tr></td></table>This is another function, much like DoSomething(), but different, in that it does something else. </div></div><div class=CToolTip id="tt3"><div class=CVariable><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>int myVariable</tr></td></table>This is my variable. </div></div><script language=JavaScript><!--
    +if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    diff --git a/vendor/naturaldocs/Help/examples.css b/vendor/naturaldocs/Help/examples.css
    new file mode 100644
    index 000000000..f61d06b6d
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/examples.css
    @@ -0,0 +1,90 @@
    +@import URL(example/Default.css);
    +
    +
    +.NDContent {
    +    color: #000000; background-color: #FFFFFF;
    +    padding: 15px 0;
    +    border-style: solid;
    +    border-width: 1px 3px 3px 1px;
    +    border-color: #c0c0c0 #808080 #808080 #c0c0c0;
    +    margin: 1em 5ex;
    +    -moz-border-radius: 12px;
    +    }
    +
    +    .NDContent p,
    +    .NDContent li,
    +    .NDContent td,
    +    .NDMenu td,
    +    .NDSummary td,
    +    .NDIndex td {
    +        font-size: 10pt;
    +        line-height: normal;
    +        }
    +    .NDContent .CTopic {
    +        padding-bottom: 0;
    +        }
    +    .Prototype td {
    +        font: 10pt Courier New, monospace;
    +        }
    +    .NDIndex .IHeading {
    +        font-size: 16pt;
    +        }
    +
    +
    +.NDMenu {
    +    font: 9pt Verdana, Arial, sans-serif;
    +    color: #000000; background-color: #E8E8E8;
    +    width: 27ex;
    +    padding: 10px 0;
    +    border-style: solid;
    +    border-width: 1px 3px 3px 1px;
    +    border-color: #808080 #606060 #606060 #808080;
    +    margin: 1em 0 1em 5ex;
    +    -moz-border-radius: 12px;
    +    }
    +
    +
    +.NDFooter {
    +    font: 8pt Verdana, Arial, sans-serif;
    +    color: #909090; background-color: #E8E8E8;
    +    border-style: solid;
    +    border-width: 1px 3px 3px 1px;
    +    border-color: #808080 #606060 #606060 #808080;
    +    margin: 1em 0 1em 5ex;
    +    -moz-border-radius: 12px;
    +    }
    +.NDFooter td {
    +	font-size: 8pt;
    +    padding: 0 2ex;
    +	}
    +
    +    .NDFooter a:link,
    +    .NDFooter a:hover,
    +    .NDFooter a:visited { color: #909090 }
    +    .NDFooter a:active { color: #A00000 }
    +
    +
    +.NDSummary {
    +    padding: 15px;
    +    border-style: solid;
    +    border-width: 1px 3px 3px 1px;
    +    border-color: #c0c0c0 #808080 #808080 #c0c0c0;
    +    margin: 1em 5ex;
    +    -moz-border-radius: 12px;
    +    }
    +
    +    .NDSummary .Summary {
    +        margin-top: 0;
    +        }
    +
    +
    +.NDIndex {
    +    color: #000000; background-color: #FFFFFF;
    +    padding: 15px;
    +    border-style: solid;
    +    border-width: 1px 3px 3px 1px;
    +    border-color: #c0c0c0 #808080 #808080 #c0c0c0;
    +    margin: 1em 5ex;
    +    -moz-border-radius: 12px;
    +    }
    +
    diff --git a/vendor/naturaldocs/Help/images/header/background.png b/vendor/naturaldocs/Help/images/header/background.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..09a2ea4652a448cdc6ce9743ea41c49acb53f43e
    GIT binary patch
    literal 229
    zcmeAS@N?(olHy`uVBq!ia0vp^0zmA|!2~3~-IxHPSkfJR9T^zbpD<_bdI{u9mbgZg
    z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-$B_jGX#@i_i=+G*Yf10LtzBmcM$
    zH1PzsOWr#ewBY64RrP^IG2fLx`9<?A&|=dFWLTQw!^s?z=yfB^T5lHH^QUD}R!n!+
    z-TPR1V3G8*hWZarKKM46=)JwP^x}jaKmR4H4*N69@GPTz#827l{>?J$%I68izgd;T
    Zr{8aV-e<F)tt8M144$rjF6*2UngFm9R+#_*
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/leftside.png b/vendor/naturaldocs/Help/images/header/leftside.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..7d0938657975206a25fa6031e42e2d9d36f8fd67
    GIT binary patch
    literal 1215
    zcmV;w1VH<VP)<h;3K|Lk000e1NJLTq0015U002t}0ssI2=n<f?00004XF*Lt006JZ
    zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$QAtEWRCwC#
    zm`!t=Mi|FgSPLW}%a-c;ZPKQPc6!Y*pE@5PZBJ<qZl)Joo{Sya6WOw(8QI1{AOy>j
    z2Nn?T0-|<u@DAgVHR!j0{?E(qf*i-;N)N{ys!A>{7n6yls#H;E9srjo>N+_Y%!i*%
    z<*Ae!Tn1_w<oH+{3`{~e4y_bY0!=BkZTr(F{pg>$VI+T9Q#B|Bu4R$;@26*HHhhCn
    z@xzrv0~f^3=Zio8Qt$4Zf*%)8gEP5?v3UE(lyPh5%|J8mVzGGl_tWidWxYjc@YgiW
    z)6?nsIjx6_>cCMhA>{FK_ThuYIjAZqS9K7&`O(qLG%-uDOqZRi$;Yf1kEg>SAC`U@
    z%8D?uEY@{>Fwku(A>@3E4@op+g{sbOZUl!`&WOAQ=c2ttHm<K#)09gVawabE9fq4A
    zF~E({$O2nXPKA_VB~`<3EX&d~O;K2zTaVE$8kB<3pjy*#9=BdWVz0wlfF@+ysa@2W
    zXow+XG=vd+hWuV9n$<UWLfzta){ll!kRCPBEGqz=NRMxyW?AToJcx%B)kk}OSH=)k
    zMu(e%659Y&8C}q2YSW{uj9o5tsE%qFYs|lhYG%m#=vBS0ho-@j$Q27O8_B3@S~^zf
    z`!VDh{lB5(ON{fJA<yW79s;uzc2@L#sYd^u*UMhK%9A=?uWjW`d%ZT{nPFT>FSUln
    zPP>s^<QlXmy=KxQ%Pz{Hqnddg+3Vyz`)W^c-eHK%<W1mGYS<E0ZTmt{Y&zuskE+^&
    zBWD0O7zpm&PIxfzcRUf(6PuqRTW}aOgcg)GBTE+wLK>yGQHa0KaPs8)XrAW<VQyJs
    z>}ip&G+>34v^g0QVB`%!5JbtC8#MEf3pb?1roTz42E7>Ljg1XiwjLjoebu+ypM+V&
    zh~euPlowJTEsA2d%MFJ*$4M#SC^`*$_GM<~Z!0iZ0g@$2>U3m57!&eSx;jvNny{Jz
    z$58q~eXkphMyu88bUNMc)%^srn@?9>^kX0yW$uTTZztUViXx!5wzhh`_V~+Zp8q+w
    zPe*VG$?4s`s~QYxx7&d3_xo-6WH#I7c`-Aqmb{eY^vcS^O%O(=`bT$c6LRd{2#wJ3
    zPS?xN%L`84;T7DIyj)ws69^+i@9*z#Z=0I_(Fq2PkMQg8${T()g59z#@9gXx9v<G`
    z-%qC>^|>fXFIK+a3R)vqROsMIuh%;`I8YQtRn_zJ6J2v!trvxRVpOnu@jD-&Aq6)#
    zH+Oe;HE$S(ae4W#@}zb*e=j`f<*xV3rV5TXn@teD@P=hswryWroPHVK^!u+N(kpjY
    z(Q=I~1+3yB3n7?<5a6;Gm!r|>?(U5w{@Uxm;t?+Y3NUsnvjxg>Z*Pxf4xk}<n$6qE
    z<m&qRtkwEWmVc3?e$}&#Z;RKey6ho9K0F6Z24Q%3cu<t9@%V&NXbdkU@rOoZtJ&Pi
    d3-VV11^}E<SW<3CBH#c3002ovPDHLkV1mR1L*)Pf
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/logo.png b/vendor/naturaldocs/Help/images/header/logo.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..9317a1b69a237a6a33678d1649048b39e9e75a91
    GIT binary patch
    literal 12146
    zcmV-&FOATNP)<h;3K|Lk000e1NJLTq00ImE002t}0{{R3E1ECB00004XF*Lt006JZ
    zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#08mU+MSy|R
    ze0<JPP?J|yoP~v_<>l;AQH(`IdunR8XlS)+YORQf+hJj;U0tAofy!lNt!HPIgoM_l
    zq`Y}~#9m#fR#lYE&Dv>cv07TAjE&u4VWpaxvsqc0WoD^%cA8F2h`6}aczC6Cb;3J4
    zX?l9gSy-Q8V61I!wSt1v^78d{b+%?^vsqb{va-cWNrhZoq-<=ue0<4CNr7>3xwW;)
    zZf>yI+2TS$c1%l)PELhnWU+>Z*>ZBjqN2fWZoP1Dz|hd%TwI=Xbj68@wRLvAR8)|F
    zfX{JpwN6ftVq&gcT&Jt6%zl2*WMqt4SfG@Yt4vIdT3Vo;ow<2=y>D;6y}irQ(&A@l
    zw0L;OXlS%}c){J>>WhorVPT?fZoW@Wk#uyrQ&WayWTt9rwydqxNlAQ{m&S>S)^KsW
    ztE<CUSDbfv$>`|tQ&W6ONr+TbnrCRNVq&Y1kgsuZvR+=Mdwa!fY`11+uTfBxgoV#{
    zcgA*h!&g?CUS6YFS%S;U*IHVeaB#YFbHJsg$VNthk&)tHU#W6(zI=SJetyeURF`gV
    zy1>BIY;2-iTb{$i*<4$sR#ulyOpbnj&Qns8TwIt>PK#Vzi%3U;SXi5smE~`5xN2&%
    zU0tSBQ<Zpm%6xsyhltWkOM=MB;B|Gzdwa}!d&gQ?pmui1b9BO1Rhn{hyY%$<b#}#!
    zjoDOFn0<c0P*09kRhN~OyHHP&Q&N^|Y_xH5zD`YyeSObPO^<bU#(R6nKR$6=TcK@k
    zxQmO~QBjnSkljp6hIe<wa&nM#biKd7&ykYgkdWX^ON{^j|8{q{mzU?5ndh0A=$oAB
    znVRX2j^LG+=8uozlau6;lH*cQmVtuQe}B@4huVpW+=YeMWo5E+bHs6R!ee8wZEd_-
    zS)y54pGQZ2TUw)EU!h}TrdwN^l9S<QXQDtraC>~reSXc8l;nAN$dZ!df`iqQl;dM#
    zse63DK0a$-Uz=H3p-D+~S67pdkKa>MmuqaWb91(Mc(P+-p?BK^N&o;W_(?=TRCwC#
    zd<#HRSGINx8UiFn3YcjKO{Ea<f_b0_h+=~T!AHkfW_UPMg>g)S#n2**NZJsYP@pnU
    zVj2brEmuUajtzrIeL$U9=T0;KtM}oEpi&UHb$Yqnw9e0rPXD#{IVU7MM%(G!Uu?gU
    zlfCy^d#$zi`p(|_9I&+i{T;po3EKbp4&Q+U?SFlT??8g~Kfl9wAVK>d-{Cuup#Af=
    z0KfRJfB&an|N7Uz{NNqHK_{U42fzIFum9=a|Kk_`{2ge&cwF%xK!9I<u=kyVi{JbF
    z^XH#`{&#Z&q@TaH_~604zx=2F`o*^xw10iv@Snf^^^+f93OxV(Kg@<H63i4m|6cjQ
    zy?^@8fBklX_HU03e)gv)_a0na{`vFMvkZ6s=WypQCiaEBA2_fVYOrlAUi=;`98UW0
    zEq>?8pZ(jn544{>2KepAzubGU+$EuC0U*|X$o|KF`t1WO{OM1>L>tQI4hz5geDU7j
    z{_I-@+P}<ee2f-@1Hgif4!}Zi0CC`hCqMr9YnXcn%n{b^KfieIzkI7e`*^<M$qz6M
    z?!Q6;Sdj2}IV=P}dh&0zy!YUN-)%%T?|**q$&bH1n3rUL^I^FY4PLqb56_n`KKQq@
    zzzIKkr~G;B;CGAne*Ez}*z4kVDF4v1_^X6Fz2zYBwfE}`eJ%#i90G3s(N`hEkLDGw
    zdmSu409lG8@yh*#1LdLpd;c!JynS|I9~lQ<LG@h;2S~~NgFia3vHYP8W!#_MvTHHA
    zh9)?-9XLpQd+q8E-9;43y=T}8J>dN#?~DV|>(ko37CVXuiC)(e(RJ@ZuZJHo?fSey
    zY-qX9u8kB8A~)XmktT-5di_0J_9Z3|&O)zWzaROElxXdL1uhBF>;3m%@wqO&|4PR7
    z>$^7Im);MRCR|V0m~j1i#>QP_2_xhF^;oI2JdvnL?`K@kNVx9<&G$pEOLry2UJu=s
    zAnm(<e^(z?NPxcA4<sD8-nT0up)XN--KYP$k2J$O(dPgtltU;!*F&Y(Ll1z#L}_`S
    zSGg2Q<&KF+q`tjFLEpO%&0POO4VUKiNTUxB8{5|pJh<Y-gDV;3q27J}EfFN*)gdxt
    z!Yh($x{v^ugbN_M0Bw<8yUKT!!&ib7F5c4nQlA8``_hd2(p?#ZzBIwBHUWSu2Qs`8
    z6EYIKe4sy!Vq>{aLTG|lsMO1+oHW1dK>04O*u;!PuLFt5k(V^lODc^G^-7dV!C0u5
    zmk<0TmV0@ZC)Rp}#wI3)f?lFGHD#0?Xn}jhJJze;+bi~=W{cMM=pr_@zkh-Tv$Znh
    z9tp$)X=wj{YZTIr*t1V0)pB4LTy`DklWyE~udgo>ijl-uU!?cOU6BW%aOJ|4jR^_m
    zguZuqpEM*SWF@Y^8ql{f;dq}kp|3APTDvhL<AOA!udlx^18XPt^_}cHxvM|H=Spqg
    zfm+m|??7VT1!zAm^?|-pC}s5ZA1H^+!48RLskZh4bPOe<^Oll@+Soq0V_>q-d_B~h
    z(PAG}01;}BUx^I(^6r;e0iK?o_d-@?NE0XiKSbi45$TDrQA*T9BV%JvMnW1luJn{j
    z?}bL5ltL*sW8=nq(vXw)PEKq*>3Mu2WY@;yg#MKfCu0*U6IPaQ3`w{GBT10PUP(v@
    zj9r<rD<k&8m5hv7pGcbb#>DcIv@1R<ePX47K0dJt2TmS`wd8ng#)X88SZRh&EiFOn
    zL-Rf^y%0L#Q+_=0fV3POUFfGgkWuzaYiWtGwA#dpep)Ea`(Xx3>-YBdo(K)4A%sp$
    zczgF#a1zi$LqRen`uoE>@xqnJPwyTLpFI5T5AOy<CLFJw=$|z-C*b)19#PV+U^bi#
    zC909N{TF<E;BuihG}Nd6LT!KTg?s(*eWAbBC$zRd)Mur%zaJC`gw{IS3O%0K|91Zc
    zWa{MG{r%<TP>?3VrB;eAP?_k5{JlLv>+eql;Y9faZKAd|5$=tN#QwzE+R%waNO1o|
    zVknv>?|yF@^q#oja3G}--xJ=TFae3{_nvsTNuj~4Ce})i`}kz|_*{_ICK40sji;Lw
    zfaExwf2JgVXN1=JoZNU%{ppu?mp$=wo6Yt^&xE%_ofRggLYsmQiV_Au`@6)Mf!Ppw
    z9Gj-i5J!4maH_4mx9cHHUI|I~1_PWL^F-*^wL$wmZOhv}SMaVJ8FKQ_@y}?#pTNKZ
    z*sdc%60-8jW$6}5G7ElBV&ZSa@3%+~t-R-{2EhKX&xY|s&nr@zi`G=kIBW{_D^Fgz
    zeEe+)^luJi2Bh~;Q0kSn6OS)wv}<pV|JX+2-S<vje)}2?=ha1Ev;Ct(FblxSmd`%h
    za*Z~p#zWxRXFgX#Fb@#A>O<~PWXM`MzQx7ewYSrrw#~%5KmPHr?g4KveD=3RGAlj{
    z!Et$B{{3SM$l`^-shsb7rT+3Cu3bYL@U<;Lhn&dp^DE<@U8Bu`Yg^vFFrIed4_oHc
    zcmO^d4=lN-4j|wea^><Lw!EFTG9diJ<>30+%#h1lT-`axJPl882ssJeuYKK6=EU1w
    zM0hK=&>mM@dw*OPl67R=a_TOu$1@3dKPWIH>*&j?5l0S>cwQM_aBVJZX-Ny*VGB%a
    z*>dfxW5MOJE{X?g&yc`0!vBKtE9#>$zd>ZUd+*TuE?rQ}qv7j_kbkv%@+ctu@Zo34
    zMeOwu-S{8APAGpJEnFhVhVg5UCumz11RbgmIJ$c~d1I{#D7oD7{uTfTO4N7dTWz-G
    zTPHKSfDBva!XJvRguH93*QL#^GAmlrboKYtSp)(+O9GvIEC>qh8ri)Hk>RLk;2&K2
    zEf_ykpLH|_@znO482Qo6FTczjIT|j1i{Qt*Kk+O}BL?w}VjMzM2SB8{EsrH=?=J|t
    zT*kfYAcJi?W&>bBP?4@HD?DbAZD~w+K>eZd1@F%VxKUrR+0^xcLGM2_#>+w)C>~^~
    zx%F*nEiUo}Etl)F<OCVGx@oeY%S!d#-78SMD`Jj*srKZm)mfQ$!*_4R1hK7CD=)Xa
    z|BXWjM5y<KczX{iAG%ZER4LH8{SdmO>Ar&d7V_gVjihM&8yFsK!GfUiwvf?DAOpEm
    zyslOj1ua;B1h6#7cJH%2E5GYmRut5-U=Fk#>Q%!%IohBbU+~aK%NoT9;D#szXYDtx
    zRELu*gD%Kb1TlAIVy4(u$wxeUOG-+5L%8b9qw?mZ#NOda4&s|d$ml3HtpP=yrJNx=
    zltm6BBp~44)F`qX#V#nqV^Tl}sx$?lt5U6VvQXcKE^2P??B46g7mU|WYf~2CucERv
    zDvGZ^Y+6f8OHh$geH4=UA;E@sU-48P8gD^@@j##nkRg0DBoMbiThMqK!uWVl3yGjK
    zrF!ItHrt50B(25K2xw`UwHh=&j{28mW{w1ekgjI4JFfFQipdbJE*o!gMNm;mz$8J2
    z$$<L6qM}2G+R9400`AK9;I-l1W%tU`Ts<P=E-^WaoZLD!&QKp$XJ#scLPj$)vqD;0
    zLb3uvTDVzUQdmM2vRVRxnIOpu=n6;+xEG*n(FLesZUS0b)GYzJE}dGfYf*Q>r6r^c
    zKI#y%XGlX)i>|k&x9na^eOXI=eV4LDd9S`?JfsU$o4Sxj6BO!;NPS&>$)Pl*5=^&2
    zQ(#$OOOUcm7ZenzBQr4`cnIdECGAjA%Y#8fSxSR_0?6>vhs4sb;|N!IY8(lIir_c_
    zWH=g7QiSViMS*Q)%98pLWm#KbQ5tF*PdlV+7<n1`g^zZ%;c11bED8*SFt7q>6di(U
    z5t>|73DlL9LHEFbyGOHh$PCt>ZWo26rpTZqo2HOPE*rL0N7ZFiAi7@IJl?{betI;d
    z&EXy!FG0o+6^%O?afo0B$@r)UabugKk%AIvkQSuO%FG-s3mS!NH3ZbF8wgp0C#lqs
    z6;KqE71Yp>RWw@9%?b%p2daaL0)m19kQ7~X%7CDN`XJB1pf2DRSJ}{e2p(HYf`SgI
    z%gWRt+%(T1U2l-?UXZS=J}5*NQm<<$3Tgv+P)S=*P)_e5GMd1k`oP}6Lm<$d3JNSM
    z(gg;!L8f3ZIwEdJ2nd3Tii(0B3|bSQ=qz^x$nfej-zOg<M}Q4ENOGz&U<}A03n)1S
    zwVYFJ%HA$6e%|Ib^eWp<<>cg?(p7RtmZ1bN#sUhIy7*J4;&sZ(-h$o|-Kiqn>1;`F
    zK|$r&z@nU-qIg|N2$#$K$xkXvy7+Q=CN?wEE)GjckYQ;!aIt9`C?O()l9c8YwL!cw
    z{&)~a)Hvp*qEosl<6V`M@o6ID7Ke)mSxGh`^`abSw>hvh^fooM1!QJsR`#k#yVRp<
    zwUUqpsEO}W%BC)L6PG*M1pn1@M^80zE4fWgS@;KEy42i&rhuZRh7$avZfGcLQYv8>
    zN_AzEk_-0KO=k<5&Vr}jvrWBa4F&a;MNPU&=v}F6YC2Iu##GSMq-^Utd-iNo)7khY
    zol>Ve-=u?4pVhUs5h^+Hfq_jhzfGr_9t;|!Gv~Zg%?2{O74^jmf(<J&8<g>ox}3mX
    zk_?rQ_w#Ma0&e8yH;Cn9^Iy2VX9Lg20~d}Q#!b!g5p~yM<yn|Zwf!$o+kV`8Hogct
    z^>PD7*X`KXrEEK26bL5%vKg|t876xSobKDuP^p}0w}=RfkI68J-MfMk+E($^%JYs^
    zUBT$&?&ZW7x^zW3DBoRR{2z&T5Ss(I1rP?-Ejo`xf7$#){DNikduklu`9O&GFPry~
    z>J61TSJ%pskpSp_Og%C(lBphTh#ysF6%e8VHTj*XwwL7OWOCy(m6=_mnOSO14ve-T
    zo}0;4=5TohIlu+pF<t;yox=@K_nr!9P?zAI4GrL|OI-;i;LCZgy5RizhJt{zx`y6{
    z^Ev0aNCli%Hju%z^(q^bXJP(wlyK`6Ri0G_=ClDNPAPTD6GXUeK#QES@i}cd4+iZ7
    zAU>rKkzs4|o~YLdHoVMhSRW6P8wGYgkYUW;8-L>1jY_Wl4d>T8oUZk2*QUJZ^3LaQ
    zL2q{9Pd4v>;^xBMoAEc4T^y8r+dg~O`uO;py+0@Q#@Mgp8Gn~o$en5T1`4Z^6Hbr;
    zxt|6~Xj>J|shpC=E4i{5OzG8Qg(>mJ;?MSS?>QUaZ0E+UMfzA2=_LBcNN(lY8*5V<
    zIL?h5E7u-#_Iu3EX2&5%g={w4u7pzddQvo)d4njd$4$HwCkk%ForsIOQ2@09=u}Wp
    zaN|Tu@68h@dT*wjNQv(~0kuJ>R+c$=SX;Rkt|{?nPo$)zpyV{%JQ0tx!M$<fMgx2|
    zoOKMh>{$GXc+kJG77YpyEFPjbc7qB!9yNjfu@esl?HJ%jB}WEiSi)e$ytIgzgk_@*
    z${WXyt<4@qWYF3R+K#oQa6M6um#>Rq#B4yvw8L3lDJccdyOM#+Eu@OPhLpAI3&?kc
    z``Cq*K%EA1rdKhRy-SpiWb%&8u)F>S3d>HCVHDg?10@79*eh>1n&WSF@#G7!-Mga&
    zH`|n5_6^9-i!ivi)}hi4&%)lb$htC{^Ex!VyY481v1~P#a4VIS4WqDB?AX9yFn)!|
    z%HyWoaB<wlW^=G7F0i36-dWV^td^O%_SiRp22O!v%;e`Q;)YWBaEi2ez}j9+h9i^w
    z$r$4JW!rbeo}IEb{$}=QIFNxqn!WbM`s`7>9Xu5ym&;@DTKp*|yEmI7W4Pn#|6&g#
    ze8gT@XeY_A4${9qj=Py8L%6-*=FK>6<|KalTd{lJzJ+*qU#1<KX&2TM2+NKi7?#Sx
    zef%^~LU@|6_pWtr=Y;RZ4mXT&Q&O_+uVWt!`Q)UW@hZ;wr-cPKZ>(KUaAEaZNJ6+h
    zJlasewL>tgV;GZu@|dmADC6|H`d-gwkKDv1#0Jicz~(V%Hvl&a>}dNwqSa{lF;9_?
    z6k5cF^$k4qXvybg-@G|kC_{JFYDoDQU*7x#n!QYJK|$e&d=DnWotR0sy^veTB8_b4
    za|%)hxtYY%-Xb|Kd;QHgFxZSZu;Pv7Tlc&Kv%M(BPd<y?l(i5R%a0%fxW6$Cl+d=y
    zk5%9#T^m;@i@}6mC*x#ib3R44_x2dMmd%z;GIpbd@8|5o-u3GXuxR;K#-vQfXY4@)
    z-NEJ*+R-2vg1$^EkJ)as!8ELQ^=y~Pm_#X^Et8GTh;06Xwid9SY1bk$$VNFlzh-~(
    zkz$O)Oo>Y;$<VNVJ(I&n?Mq`OHLQ_vd}e=ojFT;C80E*HCrg_<L(3}UHl)K+5rrmw
    z`=p()(2TgUhyi!%%@nYLyDowmj{(7JTlY*dX0bbX6T)(kp<(^nX`qCN3`}QfTo{{y
    zPd+d5?O_}ZdO^Kt4`YnS;jq|#1WSL#OP3TBYS4Q0a*Q9FWgnT0L1fs+hvG6QEJ3mG
    zv~pNgw4<=sxqv%1#vzN@vavB<%G$>Tw3~nwCW|10J*-e8-}3~y%F8%RX4r@ykfDZ`
    zo|3{uXGxf2xrUR@VKdPC>!p6I^g$+6Fd6pvjTADO5+>Lf#$@ngWyi%qH_Vkq3{866
    z`V>han}O-Jw3*>2XBgexhvo8F>@tBk**rNx22Fa(&1s;7wpFlhI7{QwHH_a7EYa9`
    zU!q0FKZegrXEHrFY+{kx$G4}4v5;u_9yyzn?!nQ>(NX4EzJ^!zE7UcH&nx8c#*V-?
    zVfTo0^~@a8j;trk=~Y@SZ$@nM6}0t$7qVG=Oa_hzJ^_%A6dTwmJBSREBk6H3!1D3>
    za@*1+{JeC?H%{6jNYmh8I!}(tVCCDHaWBLTda!Dm9b|Yx0u~7}u)_w|uOCd;$noz%
    zo?`IX8n#T{+{~DqX_p7YdBKAu1E$e5P(nloj~AQ-;DE7$5U@wID7A1h))Y!!NEt-D
    zB#tR;dmcI^p{(a+4-RJAwNLG{Eq_YO)9`VR?Q*nZ@MPL59*6mYtLH(PRyIgg(#Nz~
    z&5YpYDKHseDJRL0J(zCSQjZkNmuc)QnH-aW8<zrygGojV!w=2{g)A+x)-yER!IbRL
    zNlb<qSs|LFlmgCJ46#k1l){lwPLctM!51dOqCKz@6c%z=at4F%H=AADlOzLo#(Yyk
    z$a$uVG%kIF5{$_v(Sz*rJ(Hu^NM(?#kuM>35DgoPwnI=7cp@sy3}+Y_lOwP<5ND=m
    zkFa?x)+le3!!1a0^_()I)n+?t+%dE^5G=tI8O(N8hRPrsHnZ@-xj-QnJV<G9j`oz<
    z41PtH(%@M(Wso4lr*KLblyG_cm{-Y1in}s4ACbXNQ#cqWNw>54laqYe2#+@=kMJiG
    zEP?d;5o8$SNRT;6q0ETM0QdJ`3MF1-l%aVaq{t9aGYU&LlQ|*>9?WJJR7XfM0F6?n
    zVQ?zvBk9i4!7y@Vc#*;9Gf*6>W3+Jm!F@*k_8`$4axI^M#`u(c1lXJ+N#~4=X~#x5
    z9Qzo9e7N2`IX1#$anhNRxM{9O`TWAE8h4D(=V6tR5p)e3!_5-@*qAnjD+2{KFC~Q|
    zgDxzYjF&Pe(m|F!LRPXlb7<L_WFf4;WjdxJO3x+>l5A$mR{|{#AW7HMp!W=La)u?d
    zaKtZy{4K?5`J^8Mkpbmgl3mDRYs16&{KJRI$3;jm@W(+0m~&)~$*Ce%2G~R-ag=yb
    zOcfGK$ZcX#Gaq?_O{PZ9V9#bZP6BDA$RLTE21;mK#n6N~OVObO|K@T#Bit{B_=|@z
    zBCxn@J0EXgi;RALZz0iEKj3Rz+#oZZV`tep>FFGD?ZAhoNg0omo;|}ghtKCYRnxV6
    zzE;bM<G?4ba7?BpnzeDv5vFzo>1*Jkfi7cl9`?AfDmEuh!;__^vf1fz9NCDZaD;6K
    z4PIfKM3V>Kyb(#9hCPx>Sl2LNeVy{nV#axJ^1|W<S$XzB)G`RmIR^|4QYwXxiAYU{
    z=|;yk=FCCN1{%r-OspRw13%9LDju-O50k&8SS2t*UZ0htoP&TR<1bw2^oD7X0H#@z
    zsv*t{F>q!;=9ttrA%`>#QsPA@CD<2121FgCJ*a`DVK%#iD6Iq;WTdJSkdXL$21FGn
    z0Z`G1e{s2x;pc}FyTVvS_6IV=-~&-JgK>l)11lYMmM}e1Q&T;d*<tWpywLGCmA~R^
    z(9$r?HK(SA=UfltLj#*NXx9v;SHaT~(aPs*&=u*U!U$(@kf)U~)7h{S59afjVOl#!
    z=Aq@VG=o)xn$*ET8S2Fz9At4Mn!&<6hc_0-#W@_(cmx5+mekZi&`8auCdQ%e0aJ*M
    zbm%&L46}kJ0l>M9AOp+{)Kc@b40rO8V*BtgA_L5(gc+76TZUic+g{wh<cseU8O_T^
    z>BVGdhQk;#mz}4j$WX|XP+~$CKWl8Cssak-0_&`HnZQa+29!+6G$0{K2APW#5^lxD
    zcEi)kRzid7k0Qflh+qhokt;(gWe^^OB(OrT<q=z7qv-JL5zGcx02wqc4fdLv8W~%X
    zhkqn>L}Ohx2d;c1M%o%Xge+lc@}R<$Xh2q%pPyf-&Cj<>sw6z9SD{`qi3G?XlW<s+
    z@l;tVip${~YI=0#IGXappe@Y9F)>s-1kQxkR0%U%@=zei0M87V40<#(Oph=gUqJnq
    zV%eQVaBEiOc>qVMHsHNuVRKCL@Ex2qD=a3=DKfBdL>_s3D<XqGgN->vk^$*rU-;3I
    ztRn8oBNonP7gVcozLyFp-nfvViXww6RZ{ge_@-YSA+I6HV5~|e`g_><0z9rg0s%fK
    zF7e9`bICO@zDnL4_2Cbw#bKEyc{&?7etvA%7M`D<A1|L@Ba>)lbv48!)W|#}oSquC
    zhhAI9mayxz`n(!;6-UC?RB6`mt27c`l?K|`Y|TJjc2&Lv5oCbJ=P`TqVOm%~^!bFd
    zDi7$yqu6K9XAPuknEGVNKpuxo`+y!X!Vx4z2Ic^eBsD$NLz3!X$^*!N&Tz0X$PpQ8
    zU}g|fIU2ua;*UgZ%U?od(B`G4+GY4=dcwWg$Y5CAanhRWSgG06dxnS_eJbwGs%s|5
    z0MDvSN{n@}FM<q26}0WdVm`wz(<86SNRR<Nbxs2%M5zWsPPLP-Ds27&zdAX7G5s4L
    z6VX4F#kb<Op?d@|_{^{n9wA-KW~Xv^RdT`botMa8;O_I&a6DXH5BT|2O^ZeYY{=K^
    z^O#IMCy!`iv5-Hw4zO5x%sdur07?TO$SY)~=jEYyI7pMjff|zo6VH*spz@%c>XD3c
    z;6dgB8kkj7lwq8K^y~p<DvT#JjBEoF(9;o1UMg8gPX`ko_~XHR7+^BMJ&%^|B{iCX
    zY&0Prup!8Y$w!Js%VC}7vj%`5=&z4|5Ea3wsjFcO6B(`1hk1}M5`YXm9AB8eh9pB3
    z1m=-UzGpzX*cZcpstS{@F~2seU9??MWMHLE51bML8MJv*Qncv{@bkc148GPc0*zs{
    zmE>JEiVU^979kK|o;-m<%*L{8m@+sxguy0WRRg5F^AfQ(ZqTyAvR%nwmTR-8QL{%b
    z_v0}CHl)riV*QnFEW|<PRJuJ<zit3DGzDRK3Iig8pJt5*M(P?p92>S$JLghCou0$e
    zhT~m!*jU5P@2RnpNy{HdO|G&KWbn%$K)na@DKaSX!jN%_3`mTs{!>+tusQ7_kpSiU
    zHESJEZW<_|ZG`}`HPxKVCibiq!{(}<nr4*H<q?D{505n(Gr@sohG3ZFiGG0W88)y6
    zkj&EOYkIUbjOM6kUc{#+zx*|+F0R+e4gBfhq-qU@ngNgb4H_JxQwLZ>XlY$y7T-!G
    zFmNls&KN}<tCw1f^Yij+h~Gvvo5gu~`E>$+;$=bp8Y<hiVRIwyzlI`%aR@fJ0ezj|
    zWl9(OGWt(dkuvcs&lz?>wT>i1v3?+R8Ym%<p^oK{Lfkcz$oKMB1$B*$gf-g|gZNe$
    zNW=1a7}(hofq@Z$-&rp+h?571o_VbI{sEZqitP<?W4=b=CvblTZ7DArdXmFjT(b-Y
    zZR(78iVcRk8BxwJ(82&~^mP^>!&Y-oUKoirdEyT19rBUlHMmjrJqDcdJA&f;<YY}v
    zGm$iNWAYmPkdgRBm7$7-Z>~n-j`Rl(-2y{qJnxGl{4}?)FEZb_8kS7jeP)K;ur>L`
    z))l1tHDS|032iHYDx`E^O<sPn5&yx&vSCAyK7WXTPX<pI>#z#)jSUW4{LI#u37*$z
    z^41KXp5jmU?GX29kJxt9^}zn$<7d5t4w?QI1#7^?wVuHk3Y&psGc*r`&1cXC0JmTY
    z5g80HYzns^`w@43K$4-O5vut7{b93TPkz3ZF^tl^$ep3kC&MCwC-x;{jTmN;r5AT#
    zGCUDsP~_`1bw+%kTQqEjn-DEI1R03x0E@J{HDbEmTR<w33>}TP2Bra)fM$XWKouli
    zbL$pM-&146Z=$~MZmi3Ps$m$7VEJxC59`*NTi^@--NJ{>=DHdq@r&;55VT&e&+n<r
    z++o{LSM2w?&Gu<c4~$~S;*KI)2xM5};yRzf&<@Om-oRie2Iezpa3|jq_86?N2)t!1
    zZcM(lhQKW$+*iZ@7e7+G7GW0W!$jgIHg}`BuGlb%U-B)AvUZ5|jde!6_%0l_bkrT*
    zk)IEX$`klO#A+z6>tMWS`++0Dw}fIdar3)dhV;p|kZ)}Fhj*sg?a?DT5;wA;M@Yp*
    zMwg5dO^{*OpwP2ekk7^*m0=0)|NSVdxksNYZ0xY&WAG|4mk$Z=u)cwxTP$@wLxy4E
    zclFx^5M*7kdD#xzW^=Kb0gulw8bQ6W&Vn=+A@i=z#TLuZnpx0ju~@`w<}qlH4!yY8
    z=nfn0%Mo-{qh1IZLV&4M8QotZ4&UaU{2p@z@wbHwtw!|E9ACsc^VS_38pSFLe(k$_
    z;jmz{?Ql<HPqBb_bMTbNXj!+z=8y0FC~Gm?gZU~8exbGej<upkFBIzMwA-VAun>)2
    zi!!S6sj$e{VD%A1VW$2760cGyhE&BJh9xf&??$3Th9T$ye6|cPb7Z{OjMu2wqQG;$
    zxY$bcx2@i8H5y-sp3ho<3~0Z!y$mcaHdvRy;1^oWV&N?pD1Z#NX2X!h!pKL3{FyTS
    zw7T%tTo5)2|60eHNOq|tKS1HHfCls0sG=W1yZUl}t9b~qfdJ&G!y5G#MFuf>ml0V0
    z;j7eNIx2eV=);b8c00^N;vPu&qN$h5{$Jo13H}iVl~}AWTNe_4y}a83<Um=S-R?R_
    zM>BDReX1Gmb1EzpnJ~Yn*b+hh6^{R~fNn4t80*#%|AhYsueyuqb!Z%T1n!s(OE<u6
    zy8$g5zlp+}ZycgWkgrl4$7D+fkOA}Qjb%oQU^_Mr%cYZRgc_r<CwVpqJB&ty9tuXP
    z#ehD>4s;c}s1~b|lg%zIsz&%2%pI^f);ZfWCqD>7&XvZ-nPKH8KOAVl1;t^fzf4xZ
    zERui;Gl<Y5#Zs$Tk>9AY3~!}U{Ki{%o*~NH4JyTu*}9d?=jw1XWPHdFK{dd75p{?B
    zy_fCC&``1S{11tJ^>f*^n9cj9{#OAiu_G*iuQ+n9f06&h)~I2tK~?Qbjlk+Y^>!In
    z{e@8xmf~t(y>E4~vGn4KDap$b0<&tUn11((sWw>a;Qq)Ly24;IiiNX6Pd6HkW+B}o
    z6b92R9l{}_g)S6AO;}-R6b>0I#X?~RwBxo83%vu>%zA~P5w;==-Jn;fgsL84k4h*M
    zS9IuwjTOZTVTHKE$y>2lrI@M>^$3R)!s<pS_K-@xJ*XsZ?9q#bdWgKS(HClZ;llvP
    zP_1(M9nsU|G!kPItLPC?PuP|UDiprHiVA`I#qZ;1#;1md-J@QDCH%FgM26toiek&~
    zt7r~a`$rfnTE&gk?Z)9(zmMqf8-I7uCI7C{_Gxjk**SCV3dqVFb}a_F6B#-{b1JUJ
    z)+4hK!QcO>J5lZ;qZtN4Xv5&>*5RmE{qa#^x&OkbVUY!S6(YaJ;X5y3$F>#z?hyi@
    z_3hva!EltnKW6eOh#eZMr_H3^A~5>SF`d;$fuKStGz!tj(299LT+G3!U{JKvgU#(k
    zTPGyV(pqd5x|!XC!uD37P+>7ZqoP>oTOrg}_;v_=TZLvfXXZ7kf}KMN7Ak-<x1p%0
    zCdtsM*H;V0upw{q6+%I;M_;}V#Ri(*w+H6S{mwJbJoCk!VJqDYmIeoWA<fp|?Jrx3
    z75Vwq!Dg7Ki(h<k$34O-u!`KHF5Zbk$*-<%MN|02GZ*2eq!%m1zT#FhZukOPL;?Y%
    z<07&yFm$w=yCXmVxacmjbhL^|yLWJllU)JbOcxNUUw{|ToC+&n99&_Hz%iq4QSJ^5
    zM~J%V9l_O(F*GVv2BT=05<@if;#Ty`14ddaEF$+iU!Z{lh2T`tDux}-Ac$~}iXx0z
    zjRu$bzz}x}EWY|Vpb`iK3cV3ZM!i9W3#inpH>*&=tTMxj2l=w-@ke2)s8Hz@MulGA
    zF{IbGcIb;c^sVja2Rao)2K43%aOPW0Wuzh)u052yRxwn?U{6nlDF#!mdNcv>QRsce
    zzzbL<<_y>wG~Y(J8!T26F?fqvb-U5g(bu=SwS#W#My<Y}2qUpZl$OFAbsEfcqg8}w
    z>vpvmOH8F`s!=9LRnzdWNhGj9R*7zc&I&hG2fdrfjS13L+*(c9z4**C?#_1U73~$E
    zS_+z_NK@f79tf)#WF{iRr4b?$R%^F~UZHA*2z;p#Kx`FsBXA6f1;$RZ3i(8$#_C`y
    z7zi8=kI=8Ny4B5W!P+9EU7=D``%at4V5^lr_k^0QFbV-(r4opZbbTki(~T~mce@#k
    zmX2V7*kXX6PRt7+gPFbw4m;*zW3|yu??$hvsOYSq2dfP93VlUKg`1w<Zf@VySxpbt
    zR~QKE)n)~-D%e1Uw8^{)0=f+xsMtibDBN0GH*E@Ty{)fSD1v>1tE;PhTYYbfAC3&0
    zZo~NLN!<X*n_KYhO}<mubUWCs!d&6jTD=L)YjFD+x+SUe5+qgS24%Vdi#EZGw;MVw
    z(7!$Sw%C_YHFPGymU*V#4OF3wy9I+eL#GirOX@UKs2s{?L>DiXy4Y1YRDmL-St_dP
    zRu#w~W`O|2Nl6xrPP)O|u4=t4b{T;f4Z%R~1R%4)x2rcf2G{C_%@~s)Ocfp@GTm;z
    zL~6GOw^lnF!>F)YD}3j~CJV5kqP@Dh-B;D>>)WpKRkgRbqf0B<<OUyKgBk8LR0<CE
    zz3t}f1~*%<%J;Sk?mD;(`C?7qU}$bd*Un(ldh4d!w|y(zC~wuMgrW%!ru=V05hDut
    zR&Nrc3w)r}_aQ)o&YwKz*1ms#MaBO8?QX%JR6}y7P<^{KSfzUIlWMX8xq|FI_sQ+s
    zpFF2>LnW+VjcV;sZ@pbj_CpOwxAl`xkPsPHJ=fZbn!y)%Yj<cPyQfRtP3_KhaW53t
    zekV;wR1Px~_7e_1anU1`t4T{B4|b7Ll6GeY<RD!eok16g+&0aP=U`bmz3G9tz3Ho<
    z-KD0YqH;Tp1l60qA<$q?sqTTtbPR=}r&{ca>K>573Dv1aR(migeNs^E)KNjTkPZ|?
    z57HN|J9&wYi0E*!TRo-fq&XGG&~&)CpQeriP|Psnkht{3gPhY`kD%E)M2-%RaJoW8
    zbF)<?o{yk??@_@SlgaYEc>tBjBueUzj;0PO(Xau+HulFU4U5SX{oMEFA!vVmRM2iR
    zS%d%h=;P0(*&wne{aH0cXarkLrldd4L(qaB6{tj|ri$Q4AKj$@6M8lwATpJb5W#~v
    zR;hpot4qQ24>Y$&1~ALrZjU_LyQ4chFP%o`OQPPs{|r4Tnq-72`Vu*m(lc)J4z%`1
    z26T6KQTu$w{xhAqrTDdnhz>Ood;?$Fldi($^y!)UXa1Tk=y`@tsO^7%-)W}z<Dah!
    z+WtodXG+~m&+MPa_;aTS)4@dFe+h`u4MP%@Lc2&bL$%XH6>`Z!QmM&wi6}-#L)FxY
    zi`yJs=wLSY-1Ir?cCA9EOZ2IDzTyCArym`_RCb@9-=Ih1<w1028@fV+`{~os?zyM0
    z!VUrhEEN%IS5d)aD!qCdZUA!3rKiv3=59M<I-O)fEzwD*Pj{O{(Wj%~b2>L#M28m9
    z>7?A~GpD<|q1+iA-FXISo$20o=}dPwlujpIf{SAguhOZgF43<(O!Ty?j}CI9qC{8c
    zJLtLYQBhIuZ<^o|ef6q48kY&aJ9AB<(&$oZfY5Hr?JT_-ZK685m%5+ZCc0XB_3E3s
    zsMDKQqjRt30t?RNM#J|{cXsB&f2MQWa<87ty}I*i?#`X(Kn;F=dUe}RK@PQOWM~^@
    zapz95@Y9`Ff7<yFvC}R+I_QS^yEOmN2|S33LYLA@m!hB{7rx!$TIz0s1113&D3skV
    zMVl@;T9Qhk2D;HAsEVS|Pqav666Gdc%I$_GlPD?4WR2c-=^V0>6fJ^AP|bxNNx7Gj
    zpoXneE-ob{kp+4uMD-An)98;5I^mv5qCW=Mh870|QE60ZlE^)(bY~RoA3NdZ&&3MS
    z&<?fesGZ&Jrc$B_3Z`?VCiiV{5EaoyrKPY8z_qhfWQy*FXIVNu7c@m8QEsOQ7L{&O
    zv}hYV#YDq)0UL%WDH=>iqv^|~!q|C^ES&3pxM`*Be1zbB@nY`I#{uU`FJiz_VA>gN
    zGUe_xy%`Nd6Gap9HzB7xbEDrB?c5oi`zFyPHy0E>ivH-_N3b_QbM7{1I|m&=;T&p$
    zZt(f&Bj^(iwKt=oJsMTtgwIFk&VlsJTqJZvK~3hlbLSo^Y+BMI1k=TfrAdzmL{XRx
    zqW@;M=Ui&tU$Hms%|{5i7cWMA^ycG%T+D{3==l^gZQCP+Xqdp<ZI1~EHn`8HkZIA6
    o5@7yJ(T@uVHq58-{67H(0PZ;<tzvFLJpcdz07*qoM6N<$f)dbnc>n+a
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/overbody.png b/vendor/naturaldocs/Help/images/header/overbody.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..6b86af15f2758c27fb694034907e3c9236a89bae
    GIT binary patch
    literal 283
    zcmeAS@N?(olHy`uVBq!ia0vp^5<twx!2~43PM&lDQY`6?zK#qG>ra@ocD)4hB}-f*
    zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%t@U(q45_%)Ga-_%SwY0rRYp=I
    z*}zR`pJznl?|4D$7gMKCnzb$SJpbkDc5a=Pfb>~D*GnH;WUcKrymqVh{@mi@g*msk
    z>2wEivCb(z7E!wV<2`Zj``?dlTBkiLm#r&vRzZuD$o^=(z#c*KExF#21?n!JZD#YR
    z-8g8++PL(WQ}H}?Ri*0&JWd>%OHU+3-uq;DLRU~L?mx$bDd}}u9Mggh1=uh2`_)nZ
    ei<_U3jX@`kH!ZI3{8FI989ZJ6T-G@yGywo^0B8{a
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/overbodybg.png b/vendor/naturaldocs/Help/images/header/overbodybg.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..b4284ec199dac785dec8c6a3e61ca193805e4efc
    GIT binary patch
    literal 141
    zcmeAS@N?(olHy`uVBq!ia0vp^0zk~h!2~2-<vKS2DVB6cUq=Rp^(V|(yIunMk|nMY
    zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X<UL&+Lp07O2dE1g?3lnI!R5ii
    hqYcF2KrHXc!Z7*y+{UJ~*mFRo44$rjF6*2UngH2oBk2GD
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/overleftmargin.png b/vendor/naturaldocs/Help/images/header/overleftmargin.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..3d02af7fa1a626d2482302afd104f57e12a56e6d
    GIT binary patch
    literal 188
    zcmeAS@N?(olHy`uVBq!ia0vp^AT}EZ6Ob&OG-D!=Vo7)Ob!1?4ld(9uZyS)Wkn9oU
    z%fL`_nt`GA1Ovl;eFlauCkBRWPX>lt*$fQk#~B!Ow=OQ)EDTi3S>O>_%)r16WOEBM
    zGR&GI0TeXyba4!k2v7d=|Gz!67DLyczrVj<UmqX8uO{)xl(rQ=zQ4ae-@bl>w=+Wk
    ft6iqT0v{=c8hgzdWr}GmK-~<Uu6{1-oD!M<uZuPr
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/overmenu.png b/vendor/naturaldocs/Help/images/header/overmenu.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..d720d9860dcc3646056870d572679a04cff0d70e
    GIT binary patch
    literal 244
    zcmeAS@N?(olHy`uVBq!ia0vp^d_c^`!2~30DlJ|EDVB6cUq=Rp^(V|(yIunMk|nMY
    zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X+B{txLo|YY`yz#y1tnT`{=Ycq
    zP_S2ETFyq+mp^@%eX=#(d!SX|*bMQ7Q<}=o9hLCbT)UNj<F#4EMVhG`4{iKEACBra
    z(VMt#YoFnSeT$-A->W`nx%A22au$|FJ8s_QSa6{K@c}!La|RbGg$4fC?+E*7lAI&h
    r{NjjQby30l7l+^F@NZqqxJJKM@L}9x8N-V}7cqFc`njxgN@xNA5ujh7
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/overmenubg.png b/vendor/naturaldocs/Help/images/header/overmenubg.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..69339d8cb339beee2663a90ec886943a74653008
    GIT binary patch
    literal 141
    zcmeAS@N?(olHy`uVBq!ia0vp^0zk~h!2~2-<vKS2DVB6cUq=Rp^(V|(yIunMk|nMY
    zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X<UL&+Lp07O2dD}fSWMuMF!E&K
    j`Ru{MqYlLVo-7P^4o?YuI=lE2P$`3_tDnm{r-UW|^qwVF
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/header/rightside.png b/vendor/naturaldocs/Help/images/header/rightside.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..f8ef976a48c71cfd4515447f1df4a3f901ec3a99
    GIT binary patch
    literal 1186
    zcmV;T1YP@yP)<h;3K|Lk000e1NJLTq0015U002t}0ssI2=n<f?00004XF*Lt006JZ
    zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$G)Y83RCwC#
    zncHsLMi7QcilRi3lo-mA53(iOiGc!2F53rapR$KHX>WqG7Y%Zepm0&gD2mu|5Vu7o
    z>%g;iI?{qN#lw<R*cJsc;FY=h_UAvdL++aY`kPxWDR`dKbwO2yPN(|tw5DkyzX~pQ
    z@Lzs)n`fNf;`;;X@lb9w^3EZ00KqJ*apOA}zp{Tmw}(UN$rGhs&z?X8E<i)JcF*Tt
    zzH)xKa6bD~e)OnOsZ<0(NE<8w;o{eFn)J&uxu;L9XV1p2>n#=wfMii332uZR4stxf
    z#cO{u9ewe+Cd)OHA2&yUAP9+G?7#sxH{{#zCbsP)Y8Ti)W?VGhy(8cMaOXG)bP)%I
    zYk7;fyo9zf84g>u+R~YO)k|N43rO?|-(_4cUU;L?%=djlNG?Mno-hWuF8BQT%(ASo
    zEgOc!*LpbEWpi~U+}=(O535+|)RxeaiA9)|nFe&?{M<AQS&}5AV=angK~f}dz;YBi
    z_x5dNI-SX~Ov^lK$Ql@~q?`@Ubt~7`(`Hk{QbFNjq!=THQf6dK+Dtg?=EgfYF%?A-
    zF@Tw!M7tS38INH;pX<7g!ZXlCvpS!PrfK5hY+zr{kIqww0EnJ?o`-7((S$260C?NB
    zUDw66bKg`;3Lf2{D}GBu(p7`MigM5q0&;145rJ%nhV&Ir=ww@@5pq69Q9~fVKZVf6
    ztSe*)6iR8Cs$;L+lF)I9rKwIr=dJ5v+(K#G1`Qw4|9>=g!GEHGl&Jb6`oBhhXoaL{
    zD?#<YhKAD6Y1|ULW}~-P&EV!o7v^q{PUJ>E7V>$Wwv~bp?AK|MfxT-Ixq|30mn|#P
    zkaTY3Y3^?cc?^kjA@cV~K6!q@^EEaa(#?gmWG>G@1c&2lbUPeJGm-|Gc1w~a2zr3#
    z7Fm5K0h(rGt!~p}g@luZP2s>WB(a^Us#mR6aqCY{78Y5r4@mjsh-<Q2EpEB-r|8f_
    zvNH*h5=$s#@pnx#>h(GbM_a`38CUw3QJy|STCF=x8#EdXNs^Xc#}mSMWX2*O<c(n{
    zx~?Od9{Ay2W>Ivj9GaC6xQ?p6Z?_*KT2T}<WT`r>hiu}2xm<bQ|Eb%3(Cv1qYD^<7
    zK9alPC7(c<)<w7L?(gqYG^TNV&PNJ;+D7i$_I15})$4uI>-9RFj;g9?bg&u;Db%dj
    zqL+HjYhE}%JUs39`v(UHhGC!^^!zZ=LyDaPOr{q+|N3-ra(sL|7z}oIcbm;7J(>(U
    z-=-m%F9sCk`{ro$qb$#m_;3~I21;I^OBUUdu8Iu&(O)lJ@7H!)KRP-^=-JsBx`7F#
    z$A;nmO0bS=!{Ioi+3YvlzEl+HXzu`Jq7R5h?%v*>rfFd-*6+r&H1d}9hwI)s?w^+R
    zrc&`xSFhLI+1bI2^;dxtpkCzC<rg^+{ab(m0L}a7W>$($!T<mO07*qoM6N<$f=@s-
    A=l}o!
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/about.png b/vendor/naturaldocs/Help/images/menu/about.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..ca65ea4e726cd5ef9da9adbb4d2e1dfbf65e6ee5
    GIT binary patch
    literal 397
    zcmV;80doF{P)<h;3K|Lk000e1NJLTq001-q000dL0{{R3(bK)W00004XF*Lt006JZ
    zHwB960000mP)t-sFfcHnprBx2V6d>TaBy(Iz`)SZ(2$UjKtMnsARyr2;1CcHP*6~S
    zfPer10PyheJ}(!O0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy
    z<4Ht8R4C7tkxPyRAq)fgLx;q5|J%+Y-TQfwkPyhS%PG8Lmw!+BF0THws7m{1(bLp^
    z6%Fqy03k_ofL){;&?R-clz&pBXkIgA6QRXWg^<J6V1_ynF&b5sR#Q|09!-E!psLa}
    z0Q}|wp8-uqx6JeaM;kB=R%vjqea6=T9)MduCcBxnWsA}=NG@E~6~J_0rhNhY0Bjv#
    zXu-M5vP`>&&;%lYP<=)9F@UA}BGDCZrlE=p;7q_Xb74NY)g@Jy8myvDC@@z*Xh3to
    r?$?`?_LAfYSN8i*c5)?eNV%6ke;OhKPD65U00000NkvXXu0mjfsVSeu
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/background.png b/vendor/naturaldocs/Help/images/menu/background.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..db8b557bbd18d31b39e0c38908ccca1308ef847b
    GIT binary patch
    literal 187
    zcmeAS@N?(olHy`uVBq!ia0vp^0ze$k!3-p?DQ|xTq*&4&eH|GX)}JtE?Rp91O9c3Y
    zxW0Mw=Jo5>FJ8QO`}Xa-ckf=kdiDPO`<E|Yu2G7(3RELm;u=vBoS#-wo>-L1;Fyx1
    zl&avFo0y&&l$w}QS$Hzl2B=8W)5S5w;&gIKk^^gxvwK6W3+DztWyWI*wGSAiu*_&*
    h$8g#}C_&njiJ{{G<B@tHYZss<22WQ%mvv4FO#ql=L;L^$
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/bottomleft.png b/vendor/naturaldocs/Help/images/menu/bottomleft.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..0f608c8b99f8edc22ff951d230c5654b64d2b682
    GIT binary patch
    literal 235
    zcmeAS@N?(olHy`uVBq!ia0vp^LO?9c!3-o<`K&zvq*&4&eH|GX)}JtE?Rp91YX$g(
    zxc>Y1@8idh-@bkO^y$;ruU~)s`0@Ae-(SCeegFRb%a<>I{`~p;`Sbt(|37^AaJECL
    z6sSwG#5JNMI6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZZ84N#H0r;B5V#`)CKmO>2<
    zJVy=~ev@nX@xQ#2<AcT<EzNoJF0Gu?cKNA`RqpbaM;<OQNV41-$}nZJ-{Skf1+{xF
    iboecpJGa5|RUPB(3<2S8Rt`U)kqn-$elF{r5}E)v9BTmp
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/bottomright.png b/vendor/naturaldocs/Help/images/menu/bottomright.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..10c9e029163e921025dd5fb3eb937caea97a275a
    GIT binary patch
    literal 234
    zcmeAS@N?(olHy`uVBq!ia0vp^LO?9c!3-o<`K&zvq*&4&eH|GX)}JtE?Rp91YX$g(
    zxc>Y1@8idh-@bkO^y$;ruU~)s`0@Ae-(SCeegFRb%a<>I{`~p;`Sbt(|37^AaJECL
    z6sSwG#5JNMI6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZZ84N#Grr;B5V#`)9}mO>2%
    zJj@P1y_h=w*H53caqH7Zb9sUmPK{bx>dm^e@7koIb4DJy=ZemGo!ez_`efX;o2R*Q
    hM4$5u`Cm)=%(&>AfUvsd0~w%+44$rjF6*2UngD6!Y1jY&
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/community.png b/vendor/naturaldocs/Help/images/menu/community.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..0021013acc64af4eb00630f29bdb889030ddedf4
    GIT binary patch
    literal 507
    zcmV<X0R;YuP)<h;3K|Lk000e1NJLTq00341000dL0{{R31i@+X00004XF*Lt006JZ
    zHwB960000mP)t-su&}UDP*9MNkZ^EtprD{&U|`VD&_F;yARr*%;NUPYFc1(Bz`($O
    zfPer10Pyhe+(#_I0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz
    zQAtEWR5;76lS_^mF$e|m-`Mc#{<qDdJEKn4<5gL*>H#taP>ZD1D!+CNe5nLfo!`{@
    zQ3qh;Z)%HPUHeU~X@9O!Bn=1Y0+>`kn35o+Dh-&@Hc4I2S(1X3(uO`on$9Hl0i*!7
    z@{6&q1Iq|$GHpZ3`f*pIwtQY1wq-w7zXu={#8!vA|01^lY{;Vpy+OI8Q`MQ_cfjai
    zSMj6GC&E<8sz%&#`C=%6C{SX_X#H<ekpm2QCjV*IeeIp&_i*YdeDP^A6E>#9@i&R5
    zPo~xYu%&kk=H%?x?!GBEmn-?;n_Ge?u#^Xy0tmIo@+vIfE%4JR7oe-r3#mJ+;)~n`
    z_<jnWQiF3o<HKLQQM3Z`1eg}27L3G{Ed7J5Tk(^y4H)W74S@BGmiqQ?kXyP>NZry-
    xXj1rLOZ$T`k8~5ZWH99}DKY8!giCrx{Rd>DH#r+Uz<U4y002ovPDHLkV1jmM;3fb7
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/customizing.png b/vendor/naturaldocs/Help/images/menu/customizing.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..d56d25b3f87ef9261976591b4b346f5d64ca38b7
    GIT binary patch
    literal 575
    zcmV-F0>J%=P)<h;3K|Lk000e1NJLTq003YB000dL0{{R3VJw@D00004XF*Lt006JZ
    zHwB960000mP)t-sprD}8(9mFDV33fIaBy%yKtQmtupl5H5D*YhP*5;1FyP?ez`($O
    zfPer10PyhemqAaU0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz
    zl}SWFR5;7ElgpN6AqYd!;}bCZ|NpiJo_o7`-sQ+3i;#c@!0D29OaA{cAg5YDRr?)+
    zT(bZ$$L|nKy@maEEIQg>G$aj%^hB5Z!6ivr!I_dj(4_=osUqn-H{&4bbg4j<9%xfi
    zeeACQAdd!c4poEJV<zqtr|UqKac@+07&^2HWws00s*w93*F2EZtZ4lZv=_im_6o{l
    zNl^nkGJv(g)nF_gI7%9XcP?N~4e*HR0dR`tKLl$7OfcOHjtpXT0Qw}GJ29K`(+=C?
    z?Jf6EC!F)O#l}y9p#$Uw###elD?V{pPP46OwXJVE?JnWka1LE&jXi;oG>xV5U8tkH
    zBN$1z%B=~e=fm&i=vm<#Oy^_G3wAOzo6#o#+Sr{7kY3mV=t;G=YF<rOfv*yal&t7L
    zQ-H~UbHN0v6f_m?n-Q)Z(0ag8gSugQ&{f_MKwpD&4W16#*W)=q)O9l_JRK-<D9iKO
    zYe19a*W@j!0#PL&a912+OWKJ^hhD0r;!=<Gegl{KBXyuQAiJcR@CPuVJrQ^}Tqytm
    N002ovPDHLkV1n3{_CEjs
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/images/menu/using.png b/vendor/naturaldocs/Help/images/menu/using.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..1de988d2ca64727cba5d25d5dbacdc7680d91ae4
    GIT binary patch
    literal 390
    zcmV;10eSw3P)<h;3K|Lk000e1NJLTq001oj000dL0{{R33TPLe00004XF*Lt006JZ
    zHwB960000mP)t-s5D*aH;NZZ(z;JMIu&}ToARwTipiodyKtMn+Ffh>2&|qL-kdTmo
    zfPer10Pyhe>!A$-0000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy
    z+(|@1R4C7lkU4_HFbD*ZC0TdX|9?Ayy_4>cKu|Q@!~|$+hVY*XCmdDJ@E7`(T;~UR
    z^J@0;1HD*(qwau}?9e+*U9j~GpdG`?R<(4UbY~Q$Yh&rvwrP+xjciPqPspqnM^Acs
    ztrQ)}7ME%%#-_d@w>q9fhs&!Kx@kPCui3BdI#oMeZK3Dbx$o>@ssotO-py;MM>oqv
    z({@>?`F?48Xf*QI$Q<gZ-rQQ%1<9MOCbGs1j;6NM&9=mHG2LCA40XiT!>!@ffc|`O
    k4_IbkLswT;(*oL7Kiqm8s&!Ph^Z)<=07*qoM6N<$g6u)5r2qf`
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/Help/index.html b/vendor/naturaldocs/Help/index.html
    new file mode 100644
    index 000000000..84a7b633b
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/index.html
    @@ -0,0 +1,9 @@
    +
    +
    +<html><head><title>Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="."><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Version 1.51</div><div class=Topic><p>This is the Natural Docs help file, a subset of the documentation available at <a href="http://www.naturaldocs.org">the web site</a>.&nbsp; Everything you need is on the menu to the left.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/javascript/BrowserStyles.js b/vendor/naturaldocs/Help/javascript/BrowserStyles.js
    new file mode 100644
    index 000000000..71666418d
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/javascript/BrowserStyles.js
    @@ -0,0 +1,77 @@
    +
    +//
    +//  Browser Styles
    +// ____________________________________________________________________________
    +
    +var agt=navigator.userAgent.toLowerCase();
    +var browserType;
    +var browserVer;
    +
    +if (agt.indexOf("opera") != -1)
    +    {
    +    browserType = "Opera";
    +
    +    if (agt.indexOf("opera 5") != -1 || agt.indexOf("opera/5") != -1)
    +        {  browserVer = "Opera5";  }
    +    else if (agt.indexOf("opera 6") != -1 || agt.indexOf("opera/6") != -1)
    +        {  browserVer = "Opera6";  }
    +    else if (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1)
    +        {  browserVer = "Opera7";  }
    +    }
    +
    +else if (agt.indexOf("khtml") != -1 || agt.indexOf("konq") != -1 || agt.indexOf("safari") != -1)
    +    {
    +    browserType = "KHTML";
    +    }
    +
    +else if (agt.indexOf("msie") != -1)
    +    {
    +    browserType = "IE";
    +
    +    if (agt.indexOf("msie 4") != -1)
    +        {  browserVer = "IE4";  }
    +    else if (agt.indexOf("msie 5") != -1)
    +        {  browserVer = "IE5";  }
    +    else if (agt.indexOf("msie 6") != -1)
    +        {  browserVer = "IE6";  }
    +    else if (agt.indexOf("msie 7") != -1)
    +        {  browserVer = "IE7";  }
    +    }
    +
    +else if (agt.indexOf("gecko") != -1)
    +    {
    +    browserType = "Gecko";
    +    }
    +
    +// Opera already taken care of.
    +else if (agt.indexOf("mozilla") != -1 && agt.indexOf("compatible") == -1 && agt.indexOf("spoofer") == -1 &&
    +           agt.indexOf("webtv") == -1 && agt.indexOf("hotjava") == -1)
    +    {
    +    browserType = "Netscape";
    +
    +    if (agt.indexOf("mozilla/4") != -1)
    +        {  browserVer = "Netscape4";  }
    +    }
    +
    +
    +function OpeningBrowserTags()
    +    {
    +    if (browserType)
    +        {
    +        document.write('<div class='+browserType+'>');
    +
    +        if (browserVer)
    +            {  document.write('<div class='+browserVer+'>');  }
    +        }
    +    };
    +
    +function ClosingBrowserTags()
    +    {
    +    if (browserType)
    +        {
    +        document.write('</div>');
    +
    +        if (browserVer)
    +            {  document.write('</div>');  }
    +        }
    +    };
    diff --git a/vendor/naturaldocs/Help/javascript/PNGHandling.js b/vendor/naturaldocs/Help/javascript/PNGHandling.js
    new file mode 100644
    index 000000000..ab47a538e
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/javascript/PNGHandling.js
    @@ -0,0 +1,72 @@
    +// Parts derived from:
    +//    Opacity Displayer, Version 1.0
    +//    Copyright Michael Lovitt, 6/2002.
    +//    Distribute freely, but please leave this notice intact.
    +//    http://www.alistapart.com/articles/pngopacity/
    +
    +// Parts derived from:
    +//    Natural Docs
    +//    Copyright (C) 2003-2004 Greg Valure
    +//    http://www.naturaldocs.org/
    +
    +
    +var pngTransform;
    +var pngNormal;
    +
    +var agt=navigator.userAgent.toLowerCase();
    +
    +if (agt.indexOf("opera") != -1)
    +    {
    +    if ( (agt.indexOf("opera 5") != -1 || agt.indexOf("opera/5") != -1) &&
    +         agt.indexOf("mac") != -1)
    +        {
    +        pngNormal = 1;
    +        }
    +    else if (agt.indexOf("opera 6") != -1 || agt.indexOf("opera/6") != -1 ||
    +               agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1)
    +        {
    +        pngNormal = 1;
    +        }
    +    }
    +
    +else if (agt.indexOf("msie") != -1)
    +    {
    +    if (agt.indexOf("msie 5.5") != -1 || agt.indexOf("msie 6") != -1)
    +        {
    +        if (agt.indexOf("mac") != -1)
    +            {  pngNormal = 1;  }
    +        else if (agt.indexOf("win") != -1)
    +            {  pngTransform = 1;  };
    +        }
    +
    +    else if (agt.indexOf("msie 5") != -1)
    +        {
    +        if (agt.indexOf("mac") != -1)
    +            {  pngNormal = 1;  };
    +        }
    +
    +    else if (agt.indexOf("msie 7") != -1)
    +        {  pngNormal = 1;  }
    +    }
    +
    +else if (agt.indexOf("gecko") != -1)
    +    {
    +    pngNormal = 1;
    +    }
    +
    +
    +function PNGGIF(strPath, intWidth, intHeight, strAlt, strID)
    +    {
    +    if (pngTransform)
    +        {
    +        document.write('<div style="height:'+intHeight+'px;width:'+intWidth+'px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\''+strPath+'.png\', sizingMethod=\'scale\')" id="'+strID+'"></div>');
    +        }
    +    else if (pngNormal)
    +        {
    +        document.write('<img src="'+strPath+'.png" width="'+intWidth+'" height="'+intHeight+'" alt="'+strAlt+'" id="'+strID+'"/>');
    +        }
    +    else
    +        {
    +        document.write('<img src="'+strPath+'.gif" width="'+intWidth+'" height="'+intHeight+'" alt="'+strAlt+'" id="'+strID+'" />');
    +        }
    +    };
    diff --git a/vendor/naturaldocs/Help/keywords.html b/vendor/naturaldocs/Help/keywords.html
    new file mode 100644
    index 000000000..648250325
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/keywords.html
    @@ -0,0 +1,38 @@
    +
    +
    +<html><head><title>Natural Docs Topics and Keywords</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        .TopicType {
    +            margin-bottom: 3em;
    +            }
    +
    +        .TopicTypeName {
    +            font: bold 12pt Georgia, serif;
    +            margin-bottom: .5em;
    +            }
    +        .Behavior {
    +            font: italic 8pt Georgia, serif;
    +            margin-top: -.75em;
    +            margin-bottom: 1em;
    +            color: #808080;
    +            }
    +
    +
    +        .TopicTypes td {
    +            width: 33%;
    +            }
    +
    +        .Keywords td {
    +            padding-right: 2ex;
    +            width: auto;
    +            }
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Keywords</span><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Topics and Keywords</div><div class=TOC><a href="#General">General Topics</a> &middot; <a href="#Code">Code Topics</a> &middot; <a href="#Database">Database Topics</a> &middot; <a href="#Misc">Miscellaneous Topics</a></div><div class=Topic><p>Keywords are not case sensitive and are interchangable within their topic type.&nbsp; The plural forms denote <a href="documenting/reference.html#ListTopics">list topics</a> where every item in its <a href="documenting/reference.html#DefinitionLists">definition lists</a> are treated like they have their own topic.</p></div><div class=Topic><a name=General></a><div class=TopicTitle>General Topics</div><table width=100% border=0 cellspacing=0 cellpadding=0 class=TopicTypes><tr><td><div class=TopicType><div class=TopicTypeName>Generic</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>topic</td><td>topics</td></tr><tr><td>about</td><td>list</td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>Section</div><div class=Behavior>Ends Scope</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>section</td><td></td></tr><tr><td>title</td><td></td></tr></table></div><div class=TopicType><div class=TopicTypeName>Group</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>group</td><td></td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>File</div><div class=Behavior>Always Global</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>file</td><td>files</td></tr><tr><td>program</td><td>programs</td></tr><tr><td>script</td><td>scripts</td></tr><tr><td>document</td><td>documents</td></tr><tr><td>doc</td><td>docs</td></tr><tr><td>header</td><td>headers</td></tr></table></div></td></tr></table></div><div class=Topic><a name=Code></a><div class=TopicTitle>Code Topics</div><table width=100% border=0 cellspacing=0 cellpadding=0 class=TopicTypes><tr><td><div class=TopicType><div class=TopicTypeName>Class</div><div class=Behavior>Starts Scope</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>class</td><td>classes</td></tr><tr><td>structure</td><td>structures</td></tr><tr><td>struct</td><td>structs</td></tr><tr><td>package</td><td>packages</td></tr><tr><td>namespace</td><td>namespaces</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Interface</div><div class=Behavior>Starts Scope</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>interface</td><td>interfaces</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Type</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>type</td><td>types</td></tr><tr><td>typedef</td><td>typedefs</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Constant</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>constant</td><td>constants</td></tr><tr><td>const</td><td>consts</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Enumeration</div><div class=Behavior>Topic indexed under Types<br>Members indexed under Constants</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>enumeration</td><td>enumerations</td></tr><tr><td>enum</td><td>enums</td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>Function</div><div class=Behavior>List topics break apart</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>function</td><td>functions</td></tr><tr><td>func</td><td>funcs</td></tr><tr><td>procedure</td><td>procedures</td></tr><tr><td>proc</td><td>procs</td></tr><tr><td>routine</td><td>routines</td></tr><tr><td>subroutine</td><td>subroutines</td></tr><tr><td>sub</td><td>subs</td></tr><tr><td>method</td><td>methods</td></tr><tr><td>callback</td><td>callbacks</td></tr><tr><td>constructor</td><td>constructors</td></tr><tr><td>destructor</td><td>destructors</td></tr><tr><td>operator</td><td>operators</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Property</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>property</td><td>properties</td></tr><tr><td>prop</td><td>props</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Event</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>event</td><td>events</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Delegate</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>delegate</td><td>delegates</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Macro</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>macro</td><td>macros</td></tr><tr><td>define</td><td>defines</td></tr><tr><td>def</td><td>defs</td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>Variable</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>variable</td><td>variables</td></tr><tr><td>var</td><td>vars</td></tr><tr><td>integer</td><td>integers</td></tr><tr><td>int</td><td>ints</td></tr><tr><td>uint</td><td>uints</td></tr><tr><td>long</td><td>longs</td></tr><tr><td>ulong</td><td>ulongs</td></tr><tr><td>short</td><td>shorts</td></tr><tr><td>ushort</td><td>ushorts</td></tr><tr><td>byte</td><td>bytes</td></tr><tr><td>ubyte</td><td>ubytes</td></tr><tr><td>sbyte</td><td>sbytes</td></tr><tr><td>float</td><td>floats</td></tr><tr><td>double</td><td>doubles</td></tr><tr><td>real</td><td>reals</td></tr><tr><td>decimal</td><td>decimals</td></tr><tr><td>scalar</td><td>scalars</td></tr><tr><td>array</td><td>arrays</td></tr><tr><td>arrayref</td><td>arrayrefs</td></tr><tr><td>hash</td><td>hashes</td></tr><tr><td>hashref</td><td>hashrefs</td></tr><tr><td>bool</td><td>bools</td></tr><tr><td>boolean</td><td>booleans</td></tr><tr><td>flag</td><td>flags</td></tr><tr><td>bit</td><td>bits</td></tr><tr><td>bitfield</td><td>bitfields</td></tr><tr><td>field</td><td>fields</td></tr><tr><td>pointer</td><td>pointers</td></tr><tr><td>ptr</td><td>ptrs</td></tr><tr><td>reference</td><td>references</td></tr><tr><td>ref</td><td>refs</td></tr><tr><td>object</td><td>objects</td></tr><tr><td>obj</td><td>objs</td></tr><tr><td>character</td><td>characters</td></tr><tr><td>wcharacter</td><td>wcharacters</td></tr><tr><td>char</td><td>chars</td></tr><tr><td>wchar</td><td>wchars</td></tr><tr><td>string</td><td>strings</td></tr><tr><td>wstring</td><td>wstrings</td></tr><tr><td>str</td><td>strs</td></tr><tr><td>wstr</td><td>wstrs</td></tr><tr><td>handle</td><td>handles</td></tr></table></div></td></tr></table></div><div class=Topic><a name=Database></a><div class=TopicTitle>Database Topics</div><table width=100% border=0 cellspacing=0 cellpadding=0 class=TopicTypes><tr><td><div class=TopicType><div class=TopicTypeName>Database</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>database</td><td>databases</td></tr><tr><td>db</td><td>dbs</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Database Table</div><div class=Behavior>Starts Scope</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>table</td><td>tables</td></tr><tr><td>database table</td><td>database tables</td></tr><tr><td>db table</td><td>db tables</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Database View</div><div class=Behavior>Starts Scope</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>view</td><td>views</td></tr><tr><td>database view</td><td>database views</td></tr><tr><td>db view</td><td>db views</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Database Cursor</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>cursor</td><td>cursors</td></tr><tr><td>database cursor</td><td>database cursors</td></tr><tr><td>db cursor</td><td>db cursors</td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>Database Index</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>index</td><td>indexes</td></tr><tr><td></td><td>indices</td></tr><tr><td>database index</td><td>database indexes</td></tr><tr><td></td><td>database indices</td></tr><tr><td>db index</td><td>db indexes</td></tr><tr><td></td><td>db indices</td></tr><tr><td>key</td><td>keys</td></tr><tr><td>database key</td><td>database keys</td></tr><tr><td>db key</td><td>db keys</td></tr><tr><td>primary key</td><td>primary keys</td></tr><tr><td>database primary key</td><td>database primary keys</td></tr><tr><td>db primary key</td><td>db primary keys</td></tr></table></div><div class=TopicType><div class=TopicTypeName>Database Trigger</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>trigger</td><td>triggers</td></tr><tr><td>database trigger</td><td>database triggers</td></tr><tr><td>db trigger</td><td>db triggers</td></tr></table></div></td></tr></table></div><div class=Topic><a name=Misc></a><div class=TopicTitle>Miscellaneous Topics</div><table width=100% border=0 cellspacing=0 cellpadding=0 class=TopicTypes><tr><td><div class=TopicType><div class=TopicTypeName>Cookie</div><div class=Behavior>Always global</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>cookie</td><td>cookies</td></tr></table></div></td><td><div class=TopicType><div class=TopicTypeName>Build Target</div><table border=0 cellspacing=0 cellpadding=0 class=Keywords><tr><td>target</td><td>targets</td></tr><tr><td>build target</td><td>build targets</td></tr></table></div></td></tr></table></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/languages.html b/vendor/naturaldocs/Help/languages.html
    new file mode 100644
    index 000000000..02f21a09a
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/languages.html
    @@ -0,0 +1,32 @@
    +
    +
    +<html><head><title>Natural Docs Language Support</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        ul.LanguageList li {
    +           font: 12pt Georgia, serif;
    +           margin-bottom: .25em;
    +           }
    +
    +        ul.LanguageList .Subtle {
    +           font-size: 10pt;
    +           }
    +
    +        .NextUp {
    +           color: #808080;
    +           font: 9pt Verdana, sans-serif;
    +           margin-left: 1ex }
    +
    +        .LastUpdated {
    +            margin-left: 3.5ex;
    +            }
    +
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><span class=SideMenuEntry id=SelectedSideMenuEntry>Language Support</span><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Language Support</div><div class=TOC><a href="#FullLanguageSupport">Full Language Support</a> &middot; <a href="#BasicLanguageSupport">Basic Language Support</a></div><div class=Topic><a name=FullLanguageSupport></a><div class=TopicTitle>Full Language Support</div><p>The following languages have full language support, which means you get:</p><p><b>Full code documentation.</b>&nbsp; All functions, variables, and classes will appear in the output regardless of whether you wrote anything for them.&nbsp; This can be turned off with the <a href="running.html#CommandLine"><code>-do</code> command line option.</a></p><p><b>Inheritance diagrams.</b>&nbsp; They will appear in the output wherever appropriate.</p><p><b>Javadoc compatibility.</b>&nbsp; Natural Docs can read most Javadoc comments and include them in the output.&nbsp; You can also write Natural Docs documentation without topic lines by using the Javadoc comment symbols.</p><p><b>Auto-scoping.</b>&nbsp; The class a topic is part of is determined by the source code rather than class and section topics.</p><ul class=LanguageList><li>C# <i>(1.1, some 2.0)</i></li><li>Perl</li><li>ActionScript <i>(2 and 3)</i></li></ul></div><div class=Topic><a name=BasicLanguageSupport></a><div class=TopicTitle>Basic Language Support</div><p>The following languages have basic language support, which means you have:</p><p><b>Explicit documentation only.</b>&nbsp; Only things you write Natural Docs documentation for will appear in the output.</p><p><b>No inheritance diagrams.</b></p><p><b>Natural Docs comments only.</b>&nbsp; They also need to include a topic line.</p><p><b>Topic scoping.</b>&nbsp; The class a topic is part of is determined by the <a href="documenting/reference.html#KeywordsTopicsAndScope">topic scoping rules</a>.</p><ul class=LanguageList><li>C/C++</li><li>Java</li><li>PHP</li><li>Python</li><li>PL/SQL</li><li>Visual Basic</li><li>Pascal/Delphi</li><li>Ada</li><li>JavaScript</li><li>Ruby</li><li>Tcl</li><li>ColdFusion</li><li>Assembly</li><li>Fortran <i>(free-format only)</i></li><li>R</li><li>Makefiles</li><li>Plain Text</li><li><a href="customizinglanguages.html">Custom Languages</a></li></ul></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/menu.html b/vendor/naturaldocs/Help/menu.html
    new file mode 100644
    index 000000000..ef087d23a
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/menu.html
    @@ -0,0 +1,79 @@
    +
    +
    +<html><head><title>Organizing the Menu - Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><link rel=stylesheet type="text/css" href="examples.css"><style type="text/css"><!--
    +
    +
    +		.TimestampTable {
    +			margin: 1em 4ex;
    +			}
    +		.TimestampTable td {
    +			padding: 0 3ex 0 0;
    +			vertical-align: bottom;
    +			}
    +
    +	
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script><script language=JavaScript src="example/NaturalDocs.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><span class=SideMenuEntry id=SelectedSideMenuEntry>Organizing the Menu</span><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Organizing the Menu</div><div class=TOC><a href="#OrderAndTitles">Order and Titles</a> &middot; <a href="#Grouping">Grouping</a> &middot; <a href="#IndexesAndSearch">Indexes and Search</a> &middot; <a href="#AutomaticChanges">Automatic Changes</a><br><a href="#Extras">Extras</a> &middot; <a href="#Errors">Errors</a> &middot; <a href="#PortabilityAndVersioningSystems">Portability and Versioning Systems</a></div><div class=Topic><p>Natural Docs creates a file called <code>Menu.txt</code> in your <a href="running.html#CommandLine">project directory</a> that you can edit to organize the menu.&nbsp; It normally takes care of this on its own, but you have the option of improving it manually if you want to.</p></div><div class=Topic><a name="OrderAndTitles"></a><div class=TopicTitle>Order and Titles</div><p>If you&rsquo;ve never looked in it before, the menu file will have some comments explaining its syntax and a list like you see below.</p><pre class=Example>File: ClassA  (ClassA.h)
    +File: ClassB  (ClassB.h)
    +File: Globals  (Globals.h)
    +</pre><p>The list gets turned into a menu that looks like this:</p><table class=NDMenu><tr><td><div class=MEntry><div class=MFile><a href="#" onClick="return false;">ClassA</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">ClassB</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div></td></tr></table><p>The lines are in the format &ldquo;<code>File: <i>[title]</i> (<i>[filename]</i>)</code>&rdquo;.&nbsp; When Natural Docs made the menu, it decided on its own what the title of each file should be and then put them in alphabetical order.&nbsp; However, suppose we don&rsquo;t want this.&nbsp; We want Globals above the classes and we want spaces in the menu titles.&nbsp; So we edit the file.</p><pre class=Example>File: Globals  (Globals.h)
    +File: Class A  (ClassA.h)
    +File: Class B  (ClassB.h)
    +</pre><p>Run Natural Docs again and the menu is updated.</p><table class=NDMenu><tr><td><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class A</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class B</a></div></div></td></tr></table><p>However, open the menu file again and you&rsquo;ll see something interesting.</p><pre class=Example>File: Globals  (Globals.h)
    +File: Class A  (no auto-title, ClassA.h)
    +File: Class B  (no auto-title, ClassB.h)
    +</pre><p>Natural Docs noticed that you changed a couple of the titles and added a <code>no auto-title</code> attribute to each one.&nbsp; This tells it to never change them on it&rsquo;s own in the future, so your changes won&rsquo;t be lost.&nbsp; You don&rsquo;t have to worry about adding this, Natural Docs will always do it automatically.&nbsp; However, to go back to automatic titles you&rsquo;d have to manually remove it.</p></div><div class=Topic><a name="Grouping"></a><div class=TopicTitle>Grouping</div><p>This menu is good for our example, but in the real world they get much, much longer.&nbsp; We can add groups to organize it further.&nbsp; Natural Docs will create them automatically based on the each file&rsquo;s directory, but once again you can improve it manually if that&rsquo;s not good enough.</p><p>You can add groups as shown below.</p><pre class=Example>File: Globals  (Globals.h)
    +Group: Classes {
    +   File: Class A  (no auto-title, ClassA.h)
    +   File: Class B  (no auto-title, ClassB.h) }
    +</pre><table class=NDMenu><tr><td><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MenuGroup11');">Classes</a><div class=MGroupContent id=MenuGroup11><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class A</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class B</a></div></div></div></div></div></td></tr></table><p>You can also nest them inside each other.</p><pre class=Example>File: Globals  (Globals.h)
    +Group: Classes {
    +   File: Class A  (no auto-title, ClassA.h)
    +   Group: Nested Group {
    +      File: Class B  (no auto-title, ClassB.h)  }
    +   }
    +</pre><table class=NDMenu><tr><td><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MenuGroup21');">Classes</a><div class=MGroupContent id=MenuGroup21><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class A</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MenuGroup22');">Nested Group</a><div class=MGroupContent id=MenuGroup22><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class B</a></div></div></div></div></div></div></div></div></td></tr></table><p>We&rsquo;ll get rid of the nested group because it doesn&rsquo;t make sense for our example.&nbsp; Run Natural Docs, open up the menu file again and take a look.</p><pre class=Example>File: Globals  (Globals.h)
    +
    +Group: Classes  {
    +
    +   File: Class A  (no auto-title, ClassA.h)
    +   File: Class B  (no auto-title, ClassB.h)
    +   }  # Group: Classes
    +</pre><p>Natural Docs reformatted it.&nbsp; When you&rsquo;re organizing the menu, you don&rsquo;t have to worry about the indentation or otherwise keeping it neat.&nbsp; The file is reformatted every time it changes, so you can make quick and dirty edits and Natural Docs will keep it readable.</p><p>Besides breaking up long lists, groups also serve another purpose.&nbsp; Clicking on them will make it expand and collapse.&nbsp; Go ahead and try it in the examples above.&nbsp; When the menu gets too long its groups will start being collapsed by default, allowing easier navigation on large projects where it would just be impractical to show everything at once.</p></div><div class=Topic><a name="IndexesAndSearch"></a><div class=TopicTitle>Indexes and Search</div><p>Natural Docs will automatically determine what indexes your project needs and add them to the menu.&nbsp; Anything indexed will also be used for the search feature.&nbsp; The entries will look like this:</p><pre class=Example>Group: Index {
    +
    +   Index: Everything
    +   Class Index: Classes
    +   Function Index: Functions
    +   }  # Group: Index
    +</pre><p>Like the file entries we saw before, you can rename them by editing the title and reorder them by cutting and pasting.&nbsp; However, if you decide you don&rsquo;t want a particular index to be generated, just delete its entry and it will go away.&nbsp; Just like before, Natural Docs will detect this and add something new:</p><pre class=Example>Don't Index: Functions
    +</pre><p>As with <code>no auto-title</code>, Natural Docs adds this automatically to make sure it doesn&rsquo;t later undo your changes.</p></div><div class=Topic><a name="AutomaticChanges"></a><div class=TopicTitle>Automatic Changes</div><p>Natural Docs tries to manage the menu on its own as much as possible so you don&rsquo;t have to worry about it.&nbsp; This is just a peek into some of the things it does so you know what to expect.</p><p>You already saw that by default Natural Docs tries to guess what title should be for each file.&nbsp; If you leave it this way, Natural Docs will always update the menu for you if the file&rsquo;s content changes significantly enough to change its guess, such as if you rename the first class defined in it.&nbsp; If you&rsquo;d like to take advantage of this to define the menu title in each source file instead of in the menu itself, add a &ldquo;<code>Title: [title]</code>&rdquo; comment to the top of the file.</p><p>When you add and delete source files, Natural Docs will automatically add and remove them from the menu file.&nbsp; When adding one it will look for the best group to put it in by directory.&nbsp; If your grouping mirrors the source tree somewhat, this will be a lot more accurate.&nbsp; Also, if the group it&rsquo;s putting it in is alphabetized, Natural Docs will put it in the correct place to maintain that alphabetization.&nbsp; In fact, even if an existing file&rsquo;s automatic title changes, it will change it&rsquo;s position to make sure a previously alphabetized group stays that way.</p><p>There are exceptions in alphabetization for the indexes.&nbsp; If a group only contains indexes, it can be the last item on the menu or in its parent group without making it count as unsorted.&nbsp; Also, within groups that only contain indexes, the general index can be first, also without making the group count as unsorted.</p><p>Finally, if Natural Docs adds some files to a group that causes it to become too long, it will attempt to sub-group it based on directory.&nbsp; However, it will <i>only</i> do this when its adding files on its own, so you don&rsquo;t have to worry about it constantly messing up your groups.&nbsp; Since new files aren&rsquo;t added to a project that often, if you change the menu manually it should stay that way for quite some time.</p></div><div class=Topic><a name="Extras"></a><div class=TopicTitle>Extras</div><p>There&rsquo;s more you can do with the menu than just renaming and reorganizing its entries.&nbsp; Natural Docs has a few extras you can add to it as well.</p><a name="TitleAndSubtitle"></a><div class="SubTopic">Title and Subtitle</div><p>You can add a title and subtitle to your menu.</p><pre class=Example>Title: My Project
    +SubTitle: Something That Does Something
    +
    +File: Globals  (Globals.h)
    +Group: Classes
    +   File: Class A  (no auto-title, ClassA.h)
    +   File: Class B  (no auto-title, ClassB.h)
    +</pre><table class=NDMenu><tr><td><div class=MTitle>My Project<div class=MSubTitle>Something That Does Something</div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MenuGroup31');">Classes</a><div class=MGroupContent id=MenuGroup31><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class A</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class B</a></div></div></div></div></div></td></tr></table><p>In addition to adding the title to the menu, the Title tag will also change the HTML page titles from &ldquo;<i>Class A</i>&rdquo; to &ldquo;<i>Class A - My Project</i>&rdquo;, making bookmarks clearer.</p><a name="TextAndWebLinks"></a><div class="SubTopic">Text and Web Links</div><p>You can also add arbitrary text and web links to your menu.</p><pre class=Example>File: Globals  (Globals.h)
    +Group: Classes  {
    +   Text: I couldn't think of good names for these classes.
    +   File: Class A  (no auto-title, ClassA.h)
    +   File: Class B  (no auto-title, ClassB.h)
    +   }
    +Link: Built with Natural Docs  (http://www.naturaldocs.org)
    +</pre><table class=NDMenu><tr><td><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Globals</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MenuGroup51');">Classes</a><div class=MGroupContent id=MenuGroup51><div class=MEntry><div class=MText>I couldn&rsquo;t think of good names for these classes.</div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class A</a></div></div><div class=MEntry><div class=MFile><a href="#" onClick="return false;">Class B</a></div></div></div></div></div><div class=MEntry><div class=MLink><a href="#" onClick="return false;">Built with Natural Docs</a></div></div></td></tr></table><p>Even though comments use the # character, adding an anchor to a link (such as &ldquo;http://www.website.com/page.html#anchor&rdquo;) will still work.</p><a name="Footers"></a><div class="SubTopic">Footers</div><p>Finally, you can add a footer to all your pages, such as a copyright notice.&nbsp; Natural Docs will change any (c)&rsquo;s it finds into real copyright symbols.</p><pre class=Example>Footer: Copyright (C) 2010 Me
    +</pre><table class=NDFooter><tr><td>Copyright &copy; 2010 Me&nbsp; &middot;&nbsp; <a href="http://www.naturaldocs.org">Generated by Natural Docs</a></td></tr></table><p>You can also add a timestamp in any format you want.&nbsp; The tokens you can use in building it are:</p><p><table class=TimestampTable></p><p><tr><td><code>m</code></td><td>One or two digit month.</td><td>January is &ldquo;1&rdquo;</td></tr></p><p><tr><td><code>mm</code></td><td>Always two digit month.</td><td>January is &ldquo;01&rdquo;</td></tr></p><p><tr><td><code>mon</code></td><td>Short month word.</td><td>January is &ldquo;Jan&rdquo;</td></tr></p><p><tr><td><code>month</code></td><td>Long month word.</td><td>January is &ldquo;January&rdquo;</td></tr></p><p><tr><td><code>d</code></td><td>One or two digit day.</td><td>1 is &ldquo;1&rdquo;</td></tr></p><p><tr><td><code>dd</code></td><td>Always two digit day.</td><td>1 is &ldquo;01&rdquo;</td></tr></p><p><tr><td><code>day</code></td><td>Day with letter extension.</td><td>1 is &ldquo;1st&rdquo;</td></tr></p><p><tr><td><code>yy</code></td><td>Two digit year.</td><td>2010 is &ldquo;10&rdquo;</td></tr></p><p><tr><td><code>yyyy</code></td><td>Four digit year.</td><td>2010 is &ldquo;2010&rdquo;</td></tr></p><p><tr><td><code>year</code></td><td>Four digit year.</td><td>2010 is &ldquo;2010&rdquo;</td></tr></p><p></table></p><p>Everything else appears literally, so we can add:</p><pre class=Example>Timestamp: Updated month day, year
    +</pre><p>and get:</p><table class=NDFooter><tr><td>Copyright &copy; 2010 Me&nbsp; &middot;&nbsp; Updated January 1st, 2010&nbsp; &middot;&nbsp; <a href="http://www.naturaldocs.org">Generated by Natural Docs</a></td></tr></table></div><div class=Topic><a name="Errors"></a><div class=TopicTitle>Errors</div><p>If there&rsquo;s ever an error in the menu file, Natural Docs will tell you when it&rsquo;s run.&nbsp; It also adds a comment for each one in the menu file itself so that you can search for them in a text editor.</p><pre class=Example># There is an error in this file.  Search for ERROR to find it.
    +
    +File: Globals  (Globals.h)
    +Group: Classes  {
    +# ERROR: Txet is not a valid keyword.
    +   Txet: I couldn't think of good names for these classes.
    +   File: Class A  (no auto-title, ClassA.h)
    +   File: Class B  (no auto-title, ClassB.h)
    +   }
    +</pre><p>Remember that Natural Docs reformats the menu file whenever it&rsquo;s run, so you only need to correct the error.&nbsp; Natural Docs will remove the error comments on its own.</p></div><div class=Topic><a name="PortabilityAndVersioningSystems"></a><div class=TopicTitle>Portability and Versioning Systems</div><p>If you only use <a href="running.html">one input directory</a>, all the files in the menu will have relative paths.&nbsp; However, if you have more Natural Docs will use the absolute path instead.</p><p>This is not a problem.&nbsp; The menu file can still be shared between machines even if they don&rsquo;t keep the source tree in the exact same location.&nbsp; As long as you have the same layout within the source tree and point to the same base directories in the command line, Natural Docs will be able to convert the paths automatically for the new machine.</p><p>However, if you&rsquo;re putting the menu file in a versioning system like Subversion or SourceSafe, it might be very desirable to only have relative paths so anybody can check it in and only the real changes show.&nbsp; In that case, instead of using multiple input directories, see if it&rsquo;s possible to only have one input directory and use the <a href="running.html#CommandLine"><code>-xi</code> command line option</a> to exclude the subdirectories you don&rsquo;t want scanned.</p></div><div class=Topic><a name="ThatsIt"></a><div class=TopicTitle>That&rsquo;s It!</div><p>And we&rsquo;re done.&nbsp; The syntax to do all of this is included in the menu file itself, so you don&rsquo;t need to memorize everything.&nbsp; You shouldn&rsquo;t need to organize the menu very often, just after a lot of new files have been added and if you don&rsquo;t like the default.</p><p>Note that if you&rsquo;re using the non-framed HTML output format, changing the menu does require every output file to be updated.&nbsp; However, Natural Docs has a special process just for this so it won&rsquo;t take nearly as long as if it were rebuilding them all from scratch.&nbsp; Still, if you&rsquo;re working on a large project, it may be worth considering the framed HTML output format.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/output.html b/vendor/naturaldocs/Help/output.html
    new file mode 100644
    index 000000000..7d1bcda9b
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/output.html
    @@ -0,0 +1,84 @@
    +
    +
    +<html><head><title>Output Formats - Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        .FormatTable {
    +            margin-top: 1em;
    +            }
    +
    +        .FormatTable td {
    +            padding-bottom: .5em;
    +            line-height: 150%;
    +            }
    +
    +        .FormatName {
    +            font: bold 12pt Georgia, serif;
    +            padding-left: 5ex;
    +            }
    +
    +        .FormatExample
    +            {
    +            padding-left: 3ex;
    +            }
    +
    +        .FormatDescription {
    +            width: 100%;
    +            padding-left: 3ex;
    +            padding-right: 5ex;
    +            }
    +
    +
    +        .BrowserTable {
    +            margin-top: 1em;
    +            }
    +
    +        .BrowserTable td {
    +            padding-bottom: .5em;
    +            line-height: 150%;
    +            }
    +
    +        .BrowserName {
    +            font: bold 12pt Georgia, serif;
    +            padding-left: 5ex;
    +            }
    +        .BrowserSubNames {
    +            font: italic 8pt Georgia, serif;
    +            }
    +
    +        .BrowserVersion {
    +            padding-left: 3ex;
    +            }
    +
    +        .BrowserDescription {
    +            width: 100%;
    +            line-height: 150%;
    +            padding-left: 3ex;
    +            padding-right: 5ex;
    +            }
    +
    +        .FormatExample,
    +        .FormatDescription,
    +        .BrowserVersion,
    +        .BrowserDescription {
    +            padding-top: 4px;
    +            }
    +        .IE .FormatExample,
    +        .IE .FormatDescription,
    +        .IE .BrowserVersion,
    +        .IE .BrowserDescription {
    +            padding-top: 3px;
    +            }
    +        .IE .FormatTable,
    +        .IE .BrowserTable {
    +            }
    +
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Output Formats</span></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Output Formats</div><div class=Topic><p>These are the output formats that are currently implemented in Natural Docs.</p><table width=100% border=0 cellspacing=0 cellpadding=0 class=FormatTable><tr><td class=FormatName>HTML</td><td class=FormatDescription>HTML output.&nbsp; Each page is self-contained.&nbsp; Linking to specific pages is easy, but every file has to be updated whenever the menu changes.</td></tr><tr><td class=FormatName>FramedHTML</td><td class=FormatDescription>HTML output based on frames.&nbsp; The menu is updated quickly, but linking to individual pages is difficult and some people just plain hate frames.</td></tr></table></div><div class=Topic><div class=TopicTitle>HTML Compatibility</div><p>These are the browsers Natural Docs&rsquo; HTML output has been tested with.&nbsp; All browsers will be able to view and navigate it, some of the older ones just may not be able to use advanced features like search correctly.</p><table width=100% border=0 cellspacing=0 cellpadding=0 class=BrowserTable><tr><td class=BrowserName nowrap>FireFox</td><td class=BrowserDescription>Tested with 1.0, 1.5, and 2.0.</td></tr><tr><td class=BrowserName nowrap>Internet Explorer</td><td class=BrowserDescription>Tested with 6 and 7.</td></tr><tr><td class=BrowserName nowrap>Safari</td><td class=BrowserDescription>Tested with 2 and 3.&nbsp; Search doesn&rsquo;t work with unframed HTML in 2.</td></tr><tr><td class=BrowserName nowrap>Opera</td><td class=BrowserDescription>Tested with 7.0, 7.5, 8.0, 8.5, and 9.0.&nbsp; Search doesn&rsquo;t work with 7.0, and sometimes tooltips.</td></tr><tr><td class=BrowserName nowrap>Konqueror</td><td class=BrowserDescription>Tested with 3.5.5.&nbsp; Search doesn&rsquo;t work.</td></tr></table></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/running.html b/vendor/naturaldocs/Help/running.html
    new file mode 100644
    index 000000000..df186563c
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/running.html
    @@ -0,0 +1,40 @@
    +
    +
    +<html><head><title>Running Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        .OptionTable        { margin: 1em 3ex 0 3ex }
    +        .OptionTable td        { padding-bottom: 1em }
    +
    +        .Option                { font: 10pt Courier New, Courier, monospace; color: #808080; white-space: nowrap }
    +        .Description        { padding-left: 4ex }
    +        .Description ul { margin: 0 0 0 5ex; padding: 0 }
    +
    +        .ParameterGroup {
    +            font: bold 10pt Verdana, sans-serif;
    +            padding-top: 1em;
    +            }
    +        .ParameterGroupExtra {
    +            font: italic 8pt Verdana, sans-serif;
    +            color: #808080;
    +            }
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Running</span><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Running Natural Docs</div><div class=TOC><a href="#HowAndWhen">How and When</a> &middot; <a href="#CommandLine">Command Line</a> &middot; <a href="#Example">Examples</a></div></div><div class=Topic><a name=HowAndWhen></a>&nbsp; <div class=TopicTitle>How and When</div><p>Probably the best way to run Natural Docs is as part of the build process.&nbsp; This way every time you compile your code, your documentation is updated as well and you always have a current reference.&nbsp; Natural Docs has a differential build process so it will not rebuild the entire set of documentation every time it&rsquo;s run.</p><p>If you&rsquo;d like to run it manually instead, you should determine the command line you need and save it as a shortcut, batch file, or script since you should be running it often and will rarely need to fiddle with the parameters.</p><p></p></div><div class=Topic><a name=CommandLine></a><div class=TopicTitle>Command Line</div><pre>NaturalDocs -i [input (source) directory]
    +            -o [output format] [output directory]
    +            -p [project directory]
    +            [options]</pre><table class=OptionTable border=0 cellspacing=0 cellpadding=0><tr><td colspan=2 class="First ParameterGroup">Required Parameters:</td></tr><tr><td class=Option>-i <i>[dir]</i><br>--input <i>[dir]</i><br>--source <i>[dir]</i></td><td class=Description><p>The input (source) directory.&nbsp; Natural Docs will build the documentation from the files in this directory and all its subdirectories.&nbsp; You can specify it multiple times to include multiple directories.&nbsp; <a href="languages.html">See the list of supported programming languages.</a></p></tr><tr><td class=Option>-o <i>[fmt] [dir]</i><br>--output <i>[fmt] [dir]</i></td><td class=Description><p>The output format and directory.&nbsp; This can also be specified multiple times, so you can build the documentation in multiple formats in a single run.&nbsp; The supported formats are <a href="output.html"><code>HTML</code> and <code>FramedHTML</code></a>.</p></td></tr><tr><td class=Option>-p <i>[dir]</i><br>--project <i>[dir]</i></td><td class=Description><p>The project directory.&nbsp; Natural Docs needs a place to store configuration and data files for each project it&rsquo;s run on, so this is where it will put them.&nbsp; No two projects should share the same directory.</p></td></tr><tr><td colspan=2 class=ParameterGroup>Optional Parameters:</td></tr><tr><td class=Option>-xi <i>[dir]</i><br>--exclude-input <i>[dir]</i><br>--exclude-source <i>[dir]</i></td><td class=Description><p>Excludes a subdirectory from being scanned.&nbsp; The output and project directories are automatically excluded.</p></td></tr><tr><td class=Option>-img <i>[dir]</i><br>--images <i>[dir]</i></td><td class=Description><p>Adds a directory to search for image files when using <a href="documenting/reference.html#Images"><code>(see <i>[file]</i>)</code></a>.</p></td></tr><tr><td class=Option>-s <i>[style] (<i>[style]</i> ...)</i><br>--style <i>[style]</i> (<i>[style]</i> ...)</td><td class=Description><p>Selects the CSS style for HTML output.&nbsp; The default styles are <a href="styles.html"><code>Default</code>, <code>Small</code>, and <code>Roman</code></a>.</p><p>You can use any CSS file in your project directory or Natural Docs&rsquo; Styles directory just by using its name without the .css extension.&nbsp; If you include more than one, they will all be included in the HTML that order.</p></td></tr><tr><td class=Option>-r<br>--rebuild</td><td class=Description><p>Rebuilds everything from scratch.&nbsp; All source files will be rescanned and all output files will be rebuilt</p></td></tr><tr><td class=Option>-ro<br>--rebuild-output</td><td class=Description><p>Rebuilds all output files from scratch.</p></td></tr><tr><td class=Option>-t <i>[len]</i><br>--tab-length <i>[len]</i></td><td class=Description><p>Sets the number of spaces tabs should be expanded to.&nbsp; This only needs to be set if you use tabs in example code or text diagrams.&nbsp; The default is 4.</p></td></tr><tr><td class=Option>-hl <i>[opt]</i><br>--highlight <i>[opt]</i></td><td class=Description><p>Sets the syntax highlighting option used in the output.&nbsp; <code>Off</code> turns off all syntax highlighting.&nbsp; <code>Code</code> applies it to prototypes and <a href="documenting/reference.html#CodeAndTextDiagrams"><code>(start code)</code> segments</a>.&nbsp; <code>All</code> applies it to prototypes, <code>(start code)</code> segments, and <a href="documenting/reference.html#CodeAndTextDiagrams">lines prefixed with <code>&gt;</code>, <code>|</code>, or <code>:</code></a>.&nbsp; The default is <code>Code</code>.</p></td></tr><tr><td class=Option>-do<br>--documented-only</td><td class=Description><p>Tells Natural Docs to only include what you explicitly document in the output, and not to find undocumented classes, functions, and variables.&nbsp; This option is only relevant if you have <a href="languages.html">full language support</a>.</p></td></tr><tr><td class=Option>-oft<br>--only-file-titles</td><td class=Description><p>Tells Natural Docs to only use the file name for its menu and page titles.&nbsp; It won&rsquo;t try to determine one from the contents of the file.</p></td></tr><tr><td class=Option>-nag<br>--no-auto-group</td><td class=Description><p>Tells Natural Docs to not automatically create group topics if you don&rsquo;t add them yourself.</p></td></tr><tr><td class=Option>-q<br>--quiet</td><td class=Description><p>Suppresses all non-error output.</p></td></tr><tr><td class=Option>-?<br>-h<br>--help</td><td class=Description><p>Prints the syntax reference.</p></td></tr><tr><td colspan=2 class=ParameterGroup>No Longer Supported:<div class=ParameterGroupExtra>These parameters were part of previous Natural Docs releases, but are no longer supported.</div></td></tr><tr><td class=Option>-ho<br>--headers-only</td><td class=Description><p>This used to check only the headers and not the source files in C and C++.&nbsp; <a href="customizinglanguages.html">Edit <code>Languages.txt</code> instead</a> and add the line <code>&ldquo;Ignore Extensions: c cpp cxx&rdquo;</code>.</p></td></tr><tr><td class=Option>-s Custom<br>--style Custom</td><td class=Description><p>This used to tell Natural Docs not to alter the CSS file in the output directory.&nbsp; Copy your custom CSS file to your project directory and use it with <code>-s</code> instead.</p></td></tr><tr><td class=Option>-ag <i>[level]</i><br>--auto-group <i>[level]</i></td><td class=Description><p>This used to set the level of auto-grouping between Full, Basic, and None.&nbsp; The algorithm was improved so there&rsquo;s no need for separate levels anymore.&nbsp; You can use <code>-nag</code> if you want to turn it off completely.</p></td></tr><tr><td class=Option>-cs <i>[charset]</i><br>--charset <i>[charset]</i><br>--character-set <i>[charset]</i></td><td class=Description><p>This used to set the character set property of the generated HTML.&nbsp; Natural Docs now always uses UTF-8.</p></td></tr></table></div><div class=Topic><a name=Examples></a><div class=TopicTitle>Examples</div><pre>NaturalDocs -i C:\My Project\Source
    +            -o FramedHTML C:\My Project\Docs
    +            -p C:\My Project\Natural Docs
    +
    +NaturalDocs -i /project/src1
    +            -i /project/src2
    +            -o HTML /project/doc
    +            -p /project/nd
    +            -s Small -t 3</pre></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/styles.css b/vendor/naturaldocs/Help/styles.css
    new file mode 100644
    index 000000000..905f0140a
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/styles.css
    @@ -0,0 +1,292 @@
    +
    +body {
    +    background: #FFFFFF;
    +    margin: 18px 15px 25px 15px !important;
    +    }
    +
    +body,
    +td,
    +li {
    +     font: 9pt Verdana, sans-serif;
    +     }
    +p,
    +td,
    +li {
    +     line-height: 150%;
    +    }
    +
    +p {
    +    text-indent: 4ex;
    +    margin: 0;
    +    }
    +
    +td {
    +    vertical-align: top;
    +    }
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +.Opera wbr:after {
    +	content: "\00200B";
    +	}
    +
    +.NoIndent p
    +    {  text-indent: 0;  }
    +
    +img {
    +    border: none;
    +    }
    +
    +.First {
    +    margin-top: 0 !important;
    +    padding-top: 0 !important;
    +    }
    +.Last {
    +    margin-bottom: 0 !important;
    +    padding-bottom: 0 !important;
    +    }
    +
    +.PageTable {
    +	max-width: 950px;
    +	margin-left: auto;
    +	margin-right: auto;
    +	}
    +
    +.Header {
    +        background-image: URL("images/header/background.png");
    +        background-color: #7070C0;
    +        }
    +.SideMenuTop {
    +        background: URL("images/header/overmenubg.png");
    +        }
    +.SideMenuBottom {
    +        vertical-align: bottom;
    +        }
    +.BodyTop {
    +        background: URL("images/header/overbodybg.png");
    +        text-align: right;
    +        }
    +.BodyBottom {
    +        vertical-align: bottom;
    +        text-align: right;
    +        font: italic 8pt Georgia, serif;
    +        color: #C0C0C0;
    +        padding-right: 10px;
    +        }
    +
    +.Body {
    +    padding: 15px 20px 0 25px;
    +    }
    +
    +
    +
    +
    +pre, code, .Example {
    +    font: 10pt Courier New, Courier, monospace;
    +    color: #606060;
    +    }
    +a code {
    +    color: #C06060;
    +    }
    +.Example {
    +    overflow: auto;
    +    }
    +
    +.PageTitle {
    +    font: bold italic 21pt Trebuchet MS, sans-serif;  letter-spacing: .5ex; text-transform: uppercase;
    +    margin-bottom: .5em }
    +.IE .PageTitle {
    +    letter-spacing: 1.25ex;
    +    }
    +
    +
    +.Topic {
    +    margin-bottom: 2em }
    +
    +
    +.TopicTitle {
    +    font: 18pt Georgia, serif;
    +    border-width: 0 0 1px 0; border-style: solid; border-color: #C0C0C0;
    +    margin-bottom: .5em
    +    }
    +#SubscribeTopicTitle {
    +    margin-bottom: 0;
    +    }
    +.Subscribe {
    +    font-size: 8pt;
    +    margin-bottom: 2em;
    +    color: #909090;
    +    }
    +
    +.Subscribe a:link,
    +.Subscribe a:hover,
    +.Subscribe a:visited {
    +    color: #909090 }
    +
    +
    +.SubTopic {
    +        font-weight: bold; font-size: 10pt;
    +        padding-top: 1.5em; padding-bottom: .5em;
    +        }
    +
    +.MiniTopic {
    +        font-weight: bold;
    +        padding-top: 1em; padding-bottom: 0em;
    +        }
    +
    +
    +.TOC {
    +        text-align: center;
    +        font: 8pt Verdana, sans-serif;
    +        text-transform: uppercase;
    +        background-color: #F8F8F8;
    +        border-width: 1px; border-style: solid; border-color: #C0C0C0;
    +        margin-bottom: 1.5em;
    +        padding: 2px 0;
    +        -moz-border-radius: 14px;
    +        }
    +
    +    .TOC a {
    +        margin: 0 0.75ex; }
    +
    +    .TOC a:link,
    +    .TOC a:hover,
    +    .TOC a:visited {
    +        color: #404040 }
    +
    +
    +.Example {
    +    background-color: #FDFDFD;
    +    padding: 15px;
    +    border: 1px solid #C0C0C0;
    +    border-width: 1px 1px 1px 6px;
    +    border-style: dashed dashed dashed solid;
    +    color: #707070;
    +    margin: 15px 5ex;
    +    }
    +
    +
    +.LastUpdated {
    +    font: italic 10pt Georgia, serif;
    +    color: #A0A0A0;
    +    margin: 1em 0;
    +    }
    +
    +
    +
    +.FAQSummary {
    +    margin-bottom: 3em;
    +    }
    +.FAQSummaryGroup {
    +    font: bold 12pt Georgia, serif;
    +    margin: 1em 0 .25em 0;
    +    }
    +.FAQGroup {
    +    font: 18pt Georgia, serif;
    +    border-bottom: 1px solid #C0C0C0;
    +    margin-bottom: .5em;
    +    margin-top: 1.5em;
    +    }
    +.FAQSummaryEntry:link,
    +.FAQSummaryEntry:visited,
    +.FAQSummaryEntry:hover,
    +.FAQSummaryEntry:active {
    +    }
    +
    +.FAQEntry {
    +    margin-bottom: 3em;
    +    }
    +.FAQEntryTitle {
    +    font: bold 12pt Georgia, serif;
    +    margin-bottom: .5em;
    +    }
    +.FAQEntry .SubTopic {
    +    font: italic 9pt Verdana, sans-serif;
    +    }
    +
    +
    +
    +.SideMenu {
    +    width: 175px;  /* 195 minus padding */
    +    text-align: center;
    +    padding-top: 15px;
    +    background-color: #F0F0F0;
    +    }
    +.SideMenuBottom {
    +    background-color: #F0F0F0;
    +    }
    +.SideMenuBottomRight {
    +    text-align: right;
    +    }
    +
    +.SideMenuSection {
    +    margin-bottom: 3em;
    +    }
    +
    +.SideMenuTitle {
    +    padding-bottom: 3px;
    +    border-bottom: 1px solid #D0D0D0;
    +    }
    +
    +.SideMenuBody {
    +    padding-top: 1em;
    +    background: URL("images/menu/background.png") repeat-x;
    +    }
    +
    +.SideMenuEntry {
    +    font: 8pt Verdana, sans-serif;
    +    margin: 0 10px 1em 10px;
    +    display: block;
    +    }
    +
    +a.SideMenuEntry:link,
    +a.SideMenuEntry:visited {
    +    color: #000000;
    +    padding: 1px 10px 2px 9px;
    +    }
    +a.SideMenuEntry:hover,
    +a.SideMenuEntry:active,
    +#SelectedSideMenuEntry {
    +    border-style: solid;
    +    border-width: 1px 2px 2px 1px;
    +    padding: 0 8px;
    +    text-decoration: none;
    +    -moz-border-radius: 10px;
    +    }
    +a.SideMenuEntry:hover,
    +a.SideMenuEntry:active {
    +	color: #000000;
    +    border-color: #C8C8C8;
    +    background-color: #F8F8F8;
    +    }
    +#SelectedSideMenuEntry {
    +	color: #000000;
    +    border-color: #606060;
    +    background-color: #FFFFFF;
    +    }
    +
    +.SideMenuSourceForge {
    +    padding-top: 2.5em;
    +    }
    +
    +
    +
    +/* Needed by the release notes for 1.3 */
    +
    +.ExPrototype {
    +    font: 10pt Courier New, Courier, monospace;
    +    padding: 5px 3ex;
    +    background-color: #F4F4F4;
    +    border: 1px solid #D0D0D0;
    +    margin: 1em 0;
    +    }
    +.ExPrototype td {
    +    font: 10pt Courier New, Courier, monospace;
    +    }
    +.ExPrototype .Fade {
    +    color: #8F8F8F;
    +    }
    +
    diff --git a/vendor/naturaldocs/Help/styles.html b/vendor/naturaldocs/Help/styles.html
    new file mode 100644
    index 000000000..808341d1c
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/styles.html
    @@ -0,0 +1,52 @@
    +
    +
    +<html><head><title>CSS Styles - Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +        .StyleTable {
    +            margin: 1em 5ex 0 5ex }
    +
    +        .StyleTable td {
    +            padding-bottom: .5em}
    +
    +        .StyleName {
    +            font: bold 12pt Georgia, serif;
    +            }
    +
    +        .StyleView,
    +        .StyleDownload
    +            {
    +            padding-left: 3ex;
    +            }
    +
    +        .StyleDescription {
    +            width: 100%;
    +            padding-left: 3ex }
    +
    +        .StyleView,
    +        .StyleDownload,
    +        .StyleDescription {
    +            padding-top: 1px;
    +            }
    +        .IE .StyleView,
    +        .IE .StyleDownload,
    +        .IE .StyleDescription {
    +            padding-top: 0;
    +            }
    +
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><a href="troubleshooting.html" class=SideMenuEntry>Troubleshooting</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><span class=SideMenuEntry id=SelectedSideMenuEntry>CSS Styles</span><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>CSS Styles</div><div class=TOC><a href="#DefaultStyles">Default Styles</a> &middot; <a href="#Customizing">Customizing</a> &middot; <a href="#CommonCustomizations">Common Customizations</a></div><div class=Topic><a name="DefaultStyles"></a><div class=TopicTitle>Default Styles</div><p>These are the styles that come with Natural Docs.&nbsp; They all follow the same color scheme and general layout; the choices are more so that you can choose the style of text you want.</p><p>You choose which style you want for your project by adding &ldquo;<code>-s <i>[style name]</i></code>&rdquo; to the command line.</p><table width=100% border=0 cellspacing=0 cellpadding=0 class=StyleTable><tr><td class=StyleName>Default</td><td class=StyleDescription>This is the default style that Natural Docs uses.&nbsp; Most of the text is 10pt Verdana.</td></tr><tr><td class=StyleName>Small</td><td class=StyleDescription>Smaller fonts than Default with most of the text using 8pt Verdana.&nbsp; Some people like the small fonts because you can fit more on the screen at once.&nbsp; However, some people hate them and find them hard to read.</td></tr><tr><td class=StyleName>Roman</td><td class=StyleDescription>Serif fonts with most of the text using 12pt Roman.&nbsp; Some people prefer Roman fonts, usually those that have decent anti-aliasing displays like Mac OS X or Windows XP with ClearType.</td></tr></table></div><div class=Topic><a name="Customizing"></a><div class=TopicTitle>Customizing</div><p>There are two ways to customize the CSS files.&nbsp; One is to build your own file from scratch, and the other is to make a touch-up file that gets applied after one of the default styles.&nbsp; Either way you want to create your own CSS file in your project directory (the one you use with <code>-p</code>) or if you plan on sharing it between many projects, in Natural Docs&rsquo; Styles directory.</p><p>To use a custom file, no matter where you put it, you just use it with <code>-s</code> without the CSS extension.&nbsp; So if you made Red.css, you use &ldquo;<code>-s Red</code>&rdquo;.&nbsp; If you made a touch-up file instead, you use it after one of the default styles, such as with &ldquo;<code>-s Default Red</code>&rdquo;.&nbsp; If you&rsquo;re so inclined, you can string as many touch-up files together as you want or use one of your own as a base.</p><p>The <a href="http://www.naturaldocs.org/documentation/html/files/Info/CSSGuide-txt.html">CSS Guide</a> documents the page structure and CSS styles of Natural Docs&rsquo; output.&nbsp; Always remember to check its <a href="http://www.naturaldocs.org/documentation/html/files/Info/CSSGuide-txt.html#Revisions">revisions section</a> every time you upgrade Natural Docs because it may change between releases.</p></div><div class=Topic><a name="CommonCustomizations"></a><div class=TopicTitle>Common Customizations</div><a name="WebStyleParagraphs"></a><div class="SubTopic First">Web-Style Paragraphs</div><p>Natural Docs defaults to print-style paragraphs like the one you are reading.&nbsp; Each one is indented and there are no blank lines between them.&nbsp; To switch to web-style paragraphs, which have blank lines and no indents, add this to your custom CSS file:</p><pre class=Example>p {
    +   text-indent: 0;
    +   margin-bottom: 1em;
    +   }
    +</pre><a name="PrototypeColors"></a><div class="SubTopic">Prototype Colors</div><p>If you&rsquo;ve <a href="customizingtopics.html#AddingTopicTypes">added a custom topic type</a> and have it <a href="customizinglanguages.html#Prototypes">finding prototypes for you</a>, you may want to have them appear in a different color than the default black and white.&nbsp; Add this to your custom CSS file:</p><pre class=Example>.C<i>[type]</i> .Prototype {
    +   background-color: <i>[color]</i>;
    +   border-color: <i>[color]</i>;
    +   }
    +</pre><p>Replace <code><i>[type]</i></code> with the name of your topic type, minus any symbols and spaces.&nbsp; So if you added a type &ldquo;Sound Effect&rdquo;, you would apply the style to &ldquo;<code>.CSoundEffect .Prototype</code>&rdquo;.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Help/troubleshooting.html b/vendor/naturaldocs/Help/troubleshooting.html
    new file mode 100644
    index 000000000..3cc089e80
    --- /dev/null
    +++ b/vendor/naturaldocs/Help/troubleshooting.html
    @@ -0,0 +1,18 @@
    +
    +
    +<html><head><title>Troubleshooting - Natural Docs</title><link rel=stylesheet type="text/css" href="styles.css"><style type="text/css"><!--
    +
    +
    +            .FAQSummary a:link,
    +            .FAQSummary a:visited,
    +            .FAQSummary a:hover,
    +            .FAQSummary a:active {
    +                color: #000000;
    +    
    +--></style><script language=JavaScript src="javascript/PNGHandling.js"></script><script language=JavaScript src="javascript/BrowserStyles.js"></script></head><body marginwidth=0 marginheight=0 leftmargin=0 topmargin=0><script language=JavaScript><!--
    +OpeningBrowserTags();// --></script>
    +
    +<!-- saved from url=(0026)http://www.naturaldocs.org -->
    +
    +<table width=100% border=0 cellspacing=0 cellpadding=0 class=PageTable float=center><tr><td colspan=3 class=Header><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td><img src="images/header/leftside.png" width=30 height=75><a href="index.html"><img src="images/header/logo.png" width=524 height=75 alt="Natural Docs"></a></td><td align=right><img src="images/header/rightside.png" width=30 height=75></td></tr></table></td></tr><tr><td><img src="images/header/overleftmargin.png" width=10 height=6></td><td class=SideMenuTop><img src="images/header/overmenu.png" width=14 height=6></td><td class=BodyTop><img src="images/header/overbody.png" width=24 height=6></td></tr><tr><td></td><td class=SideMenu nowrap><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/about.png" width=52 height=13 alt="About"></div><div class=SideMenuBody><a href="languages.html" class=SideMenuEntry>Language Support</a><a href="output.html" class=SideMenuEntry>Output Formats</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/using.png" width=45 height=13 alt="Using"></div><div class=SideMenuBody><a href="documenting.html" class=SideMenuEntry>Documenting<br>Your Code</a><a href="keywords.html" class=SideMenuEntry>Keywords</a><a href="running.html" class=SideMenuEntry>Running</a><span class=SideMenuEntry id=SelectedSideMenuEntry>Troubleshooting</span></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/customizing.png" width=96 height=13 alt="Customizing"></div><div class=SideMenuBody><a href="menu.html" class=SideMenuEntry>Organizing the Menu</a><a href="styles.html" class=SideMenuEntry>CSS Styles</a><a href="customizingtopics.html" class=SideMenuEntry>Topics and Keywords</a><a href="customizinglanguages.html" class=SideMenuEntry>Languages, Indexes,<br>and Prototypes</a></div></div><div class=SideMenuSection><div class=SideMenuTitle><img src="images/menu/community.png" width=86 height=13 alt="Community"></div><div class=SideMenuBody><a href="http://www.naturaldocs.org/" class=SideMenuEntry>Web Site</a><a href="http://www.naturaldocs.org/mailinglist.html" class=SideMenuEntry>Mailing Lists</a><a href="http://www.naturaldocs.org/messageboards.html" class=SideMenuEntry>Message Boards</a><a href="http://www.naturaldocs.org/bugs.html" class=SideMenuEntry>Bugs and<br>Feature Requests</a></div></div></td><td class=Body width=100%><div class=PageTitle>Troubleshooting</div><div class=FAQSummary><div class=FAQSummaryGroup>Natural Docs Issues</div><ul><li><a href="#NoDocs" class=FAQSummaryEntry>I don&rsquo;t get any documentation.</a></li><li><a href="#MissingTopics" class=FAQSummaryEntry>Some of my topics don&rsquo;t show up.</a></li><li><a href="#BadFormatting" class=FAQSummaryEntry>Some of my topics aren&rsquo;t formatting correctly.</a></li><li><a href="#NoPrototypes" class=FAQSummaryEntry>I&rsquo;m not getting prototypes.</a></li><li><a href="#LinksDontResolve" class=FAQSummaryEntry>My links aren&rsquo;t working.</a></li></ul><div class=FAQSummaryGroup>Windows Issues</div><ul><li><a href="#CantFindPerl" class=FAQSummaryEntry>I get the message &ldquo;Bad command or file name&rdquo; or &ldquo;perl is not recognized&rdquo;.</a></li><li><a href="#CantFindND" class=FAQSummaryEntry>I get the message &ldquo;Can&rsquo;t open perl script NaturalDocs&rdquo;.</a></li></ul></div><div class=FAQGroup>Natural Docs Issues</div><div class=FAQEntry><div class=FAQEntryTitle><a name=NoDocs></a>I don&rsquo;t get any documentation</div><div class="First SubTopic">Is it recognizing your source files?</div><p>If Natural Docs has never said &ldquo;Parsing <i>n</i> files...&rdquo; when you run it, or <i>n</i> was way too low a number, it is not finding your source files.</p><p>If it has, try this test.&nbsp; Run Natural Docs once.&nbsp; Edit one of your source files and save it.&nbsp; Run Natural Docs again.&nbsp; If it doesn&rsquo;t say &ldquo;Parsing 1 file...&rdquo; it is not recognizing your file.</p><div class=SubTopic>No, it&rsquo;s not recognizing them</div><p>The most likely scenario is that Natural Docs doesn&rsquo;t associate the file extension you&rsquo;re using with your programming language.&nbsp; Open <code>Languages.txt</code> in Natural Docs&rsquo; Config directory and find your language.&nbsp; Underneath it you should see a line that says something like &ldquo;<code>Extensions: c cpp cxx h hpp hxx</code>&rdquo;.&nbsp; Add the file extensions you use and try again.</p><p>If you use extensionless or .cgi files, do the same thing but instead look for a line that says something like &ldquo;<code>Shebang Strings: tclsh wish expect</code>&rdquo;.&nbsp; If it is not there, you may need to add it yourself.&nbsp; Edit it to include whatever appears in your shebang (<code>#!</code>) line that would say this file belongs to your language.</p><p>Otherwise just make sure you included the directory or one of its parents with <a href="running.html#CommandLine"><code>-i</code> on the command line.</a></p><div class=SubTopic>Yes, it&rsquo;s recognizing them</div><p>First note that unless you have <a href="languages.html">full language support</a>, Natural Docs will only include <a href="documenting.html">what you write for it.</a>&nbsp; It will not be able to scan your code and pick out all the classes and functions on its own.</p><p>If the problem is with text files, the most likely scenario is that you&rsquo;re not <a href="documenting/reference.html#TextFiles">including topic lines.</a>&nbsp; Like in comments, only things that appear under &ldquo;<code>keyword: name</code>&rdquo; lines count as Natural Docs content.</p><p>If it&rsquo;s still not working, note that Natural Docs can only read ASCII or UTF-8 encoded files that use Windows (CR+LF) or Unix (LF) line endings.&nbsp; If you use UTF-16 encoded files and/or classic Mac (CR) line endings you need to convert the files.&nbsp; These are due to limitations in Perl.&nbsp; Natural Docs 2.0 won&rsquo;t have these problems.</p></div><div class=FAQEntry><div class=FAQEntryTitle><a name=MissingTopics></a>Some of my topics don&rsquo;t show up</div><ul><li><a href="keywords.html">Check the list of keywords</a> to see if the one you&rsquo;re using is there and you spelled it correctly.&nbsp; Note that the web page only has the default set of keywords.&nbsp; You may need to check <code>Topics.txt</code> in Natural Docs&rsquo; Config directory and your project directory if you&rsquo;ve edited them</li><li>If the topics appear in code, make sure that the comments are alone on a line.&nbsp; You cannot put Natural Docs content on the same line as code.&nbsp; This includes having anything appear after a closing block comment symbol.</li><li>Make sure that if you have more than one topic in a comment, there is a blank line above the topic line.</li><li>If you have text boxes or lines, make sure they are completely unbroken.&nbsp; You can also try removing them completely.</li><li>If the topics appear in a text file, make sure you included topic lines.&nbsp; Like in comments, only things that appear after &ldquo;<code>keyword: name</code>&rdquo; lines count as Natural Docs content.&nbsp; You could just add a <code>Title:</code> line to the top of the file to fix this.</li></ul></div><div class=FAQEntry><div class=FAQEntryTitle><a name=BadFormatting></a>Some of my topics aren&rsquo;t formatting correctly</div><ul><li><a href="documenting/reference.html#Headings">Headings</a> must have a blank line above them.</li><li>Lines directly after <a href="documenting/reference.html#BulletLists">bullet</a> or <a href="documenting/reference.html#DefinitionLists">definition</a> lines are part of the previous bullet or definition, even if it&rsquo;s not indented.&nbsp; Skip a line first to do something else</li><li>If you&rsquo;re getting symbols scattered throughout your text, make sure any text boxes or lines are completely unbroken.&nbsp; You can also try removing them altogether.</li><li>If your example source code is getting mangled, remember to use the <a href="documenting/reference.html#CodeAndTextDiagrams">example code syntax</a>.</li><li>If a line&rsquo;s becoming a <a href="documenting/reference.html#Headings">heading</a> but shouldn&rsquo;t, either get rid of the colon at the end or break it into two lines so the colon appears on the second line</li><li>If a line&rsquo;s becoming a <a href="documenting/reference.html#DefinitionLists">definition</a> but shouldn&rsquo;t, either get rid of the space-dash-space (use two dashes or remove one of the spaces) or break it into two lines so that the space-dash-space is on the second line.</li></ul><p>I realize the last two aren&rsquo;t great.&nbsp; If you have any ideas as to how to reliably detect these kinds of false positives, <a href="#" onClick="location.href='mai' + 'lto:' + 'gregv' + 'alure' + '@' + 'natural' + 'docs.org'; return false;">e-mail me</a>.</p></div><div class=FAQEntry><div class=FAQEntryTitle><a name=NoPrototypes></a>I&rsquo;m not getting prototypes</div><ul><li>The topic must appear directly above the thing it&rsquo;s documenting.</li><li>Topics <a href="documenting/reference.html#DefinitionLists">documented in lists</a> will not get prototypes, even if the list break apart in the output.</li><li>The topic name must be present in the prototype somewhere.&nbsp; Make sure the topic title has the same case as in the prototype and that it&rsquo;s not misspelled.&nbsp; This applies even if your language isn&rsquo;t case sensitive.</li><li>If you&rsquo;re documenting something with a new topic type you <a href="customizingtopics.html">added to <code>Topics.txt</code></a>, you must also <a href="customizinglanguages.html#Prototypes">edit <code>Languages.txt</code></a> to tell it how to detect prototypes for that type.</li></ul></div><div class=FAQEntry><div class=FAQEntryTitle><a name=LinksDontResolve></a>My links aren&rsquo;t working</div><p>If your links appear in the output as &ldquo;<code>&lt;text&gt;</code>&rdquo; instead of being converted to links, do the following:</p><ul><li>Make sure the target appears in the output.&nbsp; The easiest way is to see if it appears in the Everything index.</li><li>Make sure the link is spelled correctly and has the same case as what you&rsquo;re linking to.&nbsp; This applies even if your language isn&rsquo;t case sensitive.</li><li>If the topic your link appears in and the link target are not in the same class (or are not both global) make sure you include the class in the link with <code>class.target</code>, <code>class::target</code>, or <code>class-&gt;target</code>.&nbsp; You can check which classes topics appear in with the Everything index.&nbsp; If your topics are appearing in the wrong classes, fix the documentation remembering the <a href="documenting/reference.html#KeywordsTopicsAndScope">topic scoping rules</a>.</li></ul></div><div class=FAQGroup>Windows Issues</div><div class=FAQEntry><div class=FAQEntryTitle><a name=CantFindPerl></a>I get the message &ldquo;Bad command or file name&rdquo; or &ldquo;perl is not recognized&rdquo;</div><p>What&rsquo;s happening is that NaturalDocs.bat can&rsquo;t find Perl.&nbsp; You need Perl installed to run Natural Docs, so if you haven&rsquo;t done so already, you can download and install <a href="http://www.activestate.com/Products/activeperl/">ActiveState&rsquo;s ActivePerl</a> for free.</p><p>If you already have Perl, it&rsquo;s bin directory is either not in your path or the path isn&rsquo;t being used by whatever you&rsquo;re running it from, which happens on some IDEs.&nbsp; Edit NaturalDocs.bat and on the line that says &ldquo;<code>perl NaturalDocs %NaturalDocsParams%</code>&rdquo;, change <code>perl</code> to be the full path to perl.exe, such as <code>&ldquo;C:\perl\bin\perl.exe&rdquo;</code>.&nbsp; You need to include the quotes if there are spaces in the path.</p></div><div class=FAQEntry><div class=FAQEntryTitle><a name=CantFindND></a>I get the message &ldquo;Can&rsquo;t open perl script NaturalDocs&rdquo;</div><p>What&rsquo;s happening is that Perl can&rsquo;t find the Natural Docs script file.&nbsp; This happens when the working directory or &ldquo;start in&rdquo; folder isn&rsquo;t the directory Natural Docs was installed to.&nbsp; If changing that doesn&rsquo;t work, or if you don&rsquo;t have the option to set that, edit NaturalDocs.bat and find the line that says &ldquo;<code>perl NaturalDocs %NaturalDocsParams%</code>&rdquo;.&nbsp; Change <code>NaturalDocs</code> to include the full path Natural Docs was installed to, such as <code>&ldquo;C:\Program Files\Natural Docs\NaturalDocs&rdquo;</code>.&nbsp; You need to include the quotes if there are spaces in the path.</p></div></td></tr><tr><td></td><td class=SideMenuBottom><table width=100% border=0 cellspacing=0 cellpadding=0><tr><td class=SideMenuBottomLeft><img src="images/menu/bottomleft.png" width=18 height=19></td><td class=SideMenuBottomRight><img src="images/menu/bottomright.png" width=18 height=19></td></tr></table></td><td class=BodyBottom>Copyright &copy; 2003-2010 Greg Valure</td></tr></table><script language=JavaScript><!--
    +ClosingBrowserTags();// --></script></body></html>
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Info/CSSGuide.txt b/vendor/naturaldocs/Info/CSSGuide.txt
    new file mode 100644
    index 000000000..2496b0bfc
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/CSSGuide.txt
    @@ -0,0 +1,947 @@
    +
    +   Architecture: CSS Structure
    +_______________________________________________________________________________
    +
    +It's important to understand the internal HTML file structure and styles in order to design your own CSS style for Natural Docs.  If
    +you're content with the default styles, there's no need to read this document.
    +
    +Topic: Diagram Conventions
    +
    +    The diagrams are designed for clarity.  In the actual HTML, you'd obviously see "<table class=CDescriptionList></table>"
    +    instead of "<table CDescriptionList></table CDescriptionList>".
    +
    +    - A tag with just a style, for example "CTitle", means an unspecified element with that class.  Style with .CTitle.
    +    - A tag that includes a #, for example "#Menu", means an unspecified element with that ID.  Style with #Menu.
    +    - A tag that includes a HTML element as well, for example "table CDescriptionList", means it will always be that element.  You
    +      can style with either .CDescriptionList or table.CDescriptionList.
    +    - A tag that has multiple classes or has an "and" in it, for example "CType and CTopic", means that both styles will apply to the
    +      same element.  You can style it with .CType.CTopic, noting that the space between them must be omitted.
    +    - A tag that has an "or" in it, for example "#Content or #Index", is just shorthand for either of those elements.  The diagram
    +      applies to both of them but only one will actually appear at a time in the output.
    +    - A tag or style with a question mark means that tag or style will only be there in certain situations.
    +
    +
    +Topic: Page Structure
    +_______________________________________________________________________________
    +
    +    The body tag is used to distinguish between the types of pages.
    +
    +    Unframed Content/Index Page:
    +
    +        (start diagram)
    +
    +        <body ContentPage or IndexPage)>
    +            [browser styles]
    +
    +            <#Content or #Index>
    +                Content or Index
    +            </#Content or #Index>
    +
    +            <#Menu>
    +                Menu
    +            </#Menu>
    +
    +            <#Footer>
    +                Footer
    +            </#Footer>
    +
    +            [/browser styles]
    +        </body ContentPage or IndexPage)>
    +
    +        (end diagram)
    +
    +
    +    Unframed Search Results Popup Page:
    +
    +        (start diagram)
    +
    +        <body PopupSearchResultsPage>
    +            [browser styles]
    +
    +            <#Index>
    +                Index
    +            </#Index>
    +
    +            [browser styles]
    +        </body PopupSearchResultsPage>
    +
    +        (end diagram)
    +
    +
    +    Framed Menu Page:
    +
    +        (start diagram)
    +
    +        <body FramedMenuPage>
    +            [browser styles]
    +
    +            <#Menu>
    +                Menu
    +            </#Menu>
    +
    +            <#Footer>
    +                Footer
    +            </#Footer>
    +
    +            [browser styles]
    +        </body FramedMenuPage>
    +
    +        (end diagram)
    +
    +
    +    Framed Content/Index/SearchResults Page:
    +
    +        (start diagram)
    +
    +        <body FramedContentPage or FramedIndexPage or FramedSearchResultsPage>
    +            [browser styles]
    +
    +            <#Content or #Index>
    +                Content or Index
    +            </#Content or #Index>
    +
    +            [browser styles]
    +        </body FramedContentPage or FramedIndexPage or FramedSearchResultsPage>
    +
    +        (end diagram)
    +
    +
    +Styles: Page Styles
    +
    +    ContentPage - An unframed content page.
    +    IndexPage - An unframed index page.
    +    PopupSearchResultsPage - A search results page for use in a popup iframe.
    +
    +    FramedContentPage - A framed content page.
    +    FramedIndexPage - A framed index page.
    +    FramedSearchResultsPage - A framed search results page.
    +
    +    #Footer - The page footer.  Will be in a framed menu page or on its own in a non-framed page.
    +
    +    See Also:
    +
    +        - <#Content>
    +        - <#Menu>
    +        - <#Index>
    +        - <#Footer>
    +
    +
    +
    +
    +Styles: Browser Styles
    +_______________________________________________________________________________
    +
    +
    +    Natural Docs pages include JavaScript to detect which browser the user is running and apply styles so that you can work
    +    around browser quirks right in the CSS file.
    +
    +    The browser type and version styles will be applied immediately after the body tag.  However, neither are guaranteed to be
    +    there; the user may have JavaScript turned off or be using a browser that isn't detected.  These styles should only be used to
    +    correct minor flaws and should not be heavily relied on.
    +
    +    >   <body>
    +    >       <browser type>?
    +    >           <browser version>?
    +    >
    +    >           Page Content
    +    >
    +    >           <browser version>?
    +    >       <browser type>?
    +    >   </body>
    +
    +    For example, if a <CTopic>'s style is giving you problems in Internet Explorer 6, override it with .IE6 .CTopic.  If a <MTitle>'s
    +    style gives you a problem in Opera 7 but only in frames, override it with .Framed.Opera7 .MTitle.
    +
    +    Browser Types:
    +
    +        If the browser is not one of the types below, neither this nor the browser version will be present.  There's the possibility that
    +        some obscure browser will appear as one of the others by spoofing, but the most prominent of these, Opera, Konqueror, and
    +        Safari, are taken care of.
    +
    +        IE - Internet Explorer
    +        Firefox - Firefox and anything else based on the Gecko rendering engine.
    +        Opera - Opera
    +        Safari - Safari
    +        Konqueror - Konqueror and anything else based on the KHTML rendering engine except Safari.
    +
    +    Browser Versions:
    +
    +        If the browser is not one of the versions below, this style will not be present.  The browser type still may be.
    +
    +        IE6 - Internet Explorer 6.x.
    +        IE7 - Internet Explorer 7.x.
    +
    +        Firefox1 - Firefox 1.0.x and anything else based on Gecko 1.7.x.
    +        Firefox15 - Firefox 1.5.x and anything else based on Gecko 1.8.0.x.
    +        Firefox2 - Firefox 2.0.x and anything else based on Gecko 1.8.1.x.
    +
    +        Opera7 - Opera 7.x.
    +        Opera8 - Opera 8.x.
    +        Opera9 - Opera 9.x.
    +
    +        Safari2 - Safari 2.x.
    +        Safari3 - Safari 3.x.
    +
    +    Notes:
    +
    +        Why not apply them to the body tag itself?  The JavaScript is easy enough and everything supports multiple classes, right?
    +        Because IE 6 doesn't support multiple selectors so I wouldn't be able to combine browser and page styles.
    +        .Opera.ContentPage will apply to all ContentPages in IE because it treats it as if only the last class is there.
    +
    +
    +
    +
    +Topic: Content Structure
    +_______________________________________________________________________________
    +
    +
    +    All the topics of a given file is contained in a <#Content>.  All other content styles are prefixed with a C.
    +
    +    Surrounding each piece of content is a <CTopic> and its type; for example, CFunction for a function.  Inside that are the
    +    <CTitle> and if necessary, <CBody>.  Inside <CBody> are analogues to all the top-level <NDMarkup> tags: <h1>, <p>, etc.
    +
    +    In addition to the top-level <NDMarkup> tags, you also have prototypes, class hierarchies, and summaries which are
    +    described in their own sections.
    +
    +    (start diagram)
    +
    +    <#Content>
    +
    +        <CType (CFunction, CVariable, etc.)>
    +            <CTopic and #MainTopic?>
    +
    +                <CTitle>
    +                    Topic title
    +                </CTitle>
    +
    +                <CBody>
    +
    +                    [Class Hierarchy]
    +
    +                    [Prototype]
    +
    +                    <CHeading>
    +                        Heading
    +                    <CHeading>
    +
    +                    <p>
    +                        Paragraph
    +                    </p>
    +
    +                    <pre>
    +                        Code or text diagram
    +                    </pre>
    +
    +                    <ul>
    +                        <li>
    +                            Bullet item
    +                        </li>
    +                    </ul>
    +
    +                    <CImageCaption>?
    +                        Caption
    +                    </CImageCaption>?
    +                    <img>
    +
    +                    <a CImageLink>
    +                        text
    +                    </a CImageLink>
    +
    +                    <table CDescriptionList>
    +                        <tr>
    +                            <td CDLEntry>
    +                                Entry
    +                            </td CDLEntry>
    +                            <td CDLDescription>
    +                                Description
    +                            </td CDLDescription>
    +                        </tr>
    +                    </table CDescriptionList>
    +
    +                    [Summary]
    +
    +               </CBody>
    +
    +           </CTopic and #MainTopic?>
    +       </CType (CFunction, CVariable, etc.)>
    +
    +    </#Content>
    +
    +    (end diagram)
    +
    +    Take advantange of the CSS inheritance model.  For example, you can style all titles via .CTitle, and you can style
    +    specific titles with .CType .CTitle.
    +
    +
    +Styles: Content Styles
    +
    +    #Content - Parent element containing all topics.
    +
    +    CTopic - An individual topic.
    +
    +    CTitle - The title of a topic.
    +    CBody - The body of a topic.  May not exist.
    +    CHeading - Surrounds a heading.
    +    CImageCaption - Surrounds an image caption.
    +    CImageLink - Surrounds a link to an image.
    +
    +    CDescriptionList - A description list, which is the type of list you're reading right now.  Is implemented with a table.
    +    CDLEntry - A description list entry, which is the left side.
    +    CDLDescription - A description list description, which is the right side.
    +
    +    #MainTopic - The ID given to the main topic, which is the first in the file.  It is applied to the <CTopic>.
    +
    +    CType - A placeholder for all type-specific styles.  The actual styles will be C followed by the alphanumeric-only topic type name.
    +                So the CType of a "PL/SQL Function" topic will actually be CPLSQLFunction.
    +
    +
    +
    +
    +Topic: Menu Structure
    +_______________________________________________________________________________
    +
    +
    +    Everything is enclosed in a <#Menu>.  All other menu styles are prefixed with an M.
    +
    +    The title is an <MTitle> and will always be at the beginning of the menu if it exists.  If a subtitle exists as well, it will appear
    +    as an <MSubTitle> inside <MTitle>.  Subtitles aren't allowed without titles.  Most other entries in the menu are contained in
    +    <MEntries>.  Here's the diagram:
    +
    +    (start diagram)
    +
    +    <#Menu>
    +
    +        <MTitle>
    +            Menu title
    +
    +            <MSubTitle>
    +                Menu sub title
    +            </MSubTitle>
    +
    +        </MTitle>
    +
    +        <MEntry>
    +            <MFile (and #MSelected?)>
    +                <a href>File</a href>
    +            </MFile>
    +        </MEntry>
    +
    +        <MEntry>
    +            <MIndex (and #MSelected?)>
    +                <a href>File</a href>
    +            </MIndex>
    +        </MEntry>
    +
    +        <MEntry>
    +            <MText>
    +                Text
    +            </MText>
    +        </MEntry>
    +
    +        <MEntry>
    +            <MLink>
    +                <a href>Link</a href>
    +            </MLink>
    +        </MEntry>
    +
    +        <MEntry>
    +            <MGroup>
    +                <a href>Group</a href>
    +                <MGroupContent>
    +
    +                    (MEntries)
    +
    +                </MGroupContent>
    +           </MGroup>
    +        </MEntry>
    +
    +        <#MSearchPanel and MSearchPanelActive/Inactive>
    +            <input #MSeachField>
    +            <select #MSearchType>
    +                <option #MSearchEverything>
    +                <option>
    +                <option>
    +            </select #MSearchType>
    +        </#MSearchPanel and MSearchPanelActive/Inactive>
    +
    +    </#Menu>
    +
    +    (if in unframed HTML)
    +    <#MSearchResultsWindow>
    +
    +        <iframe #MSearchResults>
    +        </iframe #MSearchResults>
    +
    +        <a #MSearchResultsWindowClose>
    +
    +    </#MSearchResultsWindow>
    +
    +    (end)
    +
    +    The <MFile> or <MIndex> entry that's currently selected will have the <#MSelected> ID, so you can reference it in CSS via
    +    .MFile#MSelected.
    +
    +    The search panel is has its own ID, <#MSearchPanel>, but also has one of the classes <MSearchPanelActive> or
    +    <MSearchPanelInactive> depending on whether any of the controls are selected or the results window is open.
    +    <#MSearchResultsWindow> is separate because it may be floating.
    +
    +
    +Styles: Menu Styles
    +
    +    #Menu - Parent element containing the entire menu.
    +
    +    MTitle - The title of the menu.
    +    MSubTitle - The subtitle of the menu.  Will appear within <MTitle>.
    +
    +    MFile - A file entry.
    +    MGroup - A group entry.
    +    MGroupContent - A container for a <MGroup's> content.
    +    MText - A plain text entry.
    +    MLink - An external link entry.
    +    MIndex - An index entry.
    +
    +    #MSelected - The ID of the currently selected <MFile> or <MIndex>.
    +
    +    MType - <MFile>, <MGroup>, <MText>, <MLink>, or <MIndex>.
    +
    +    #MSearchPanel - Contains all the search controls.
    +    MSearchPanelActive - Applied to <#MSearchPanel> when any of the controls are selected or the results window is open.
    +    MSearchPanelInactive - Applied to <#MSearchPanel> when not in use.
    +
    +    #MSearchField - The text input field of the search panel.
    +    #MSearchType - The drop down type selector of the search panel.
    +    #MSearchEverything - The <#MSearchType> option for the Everything index.
    +
    +    #MSearchResultsWindow - Contains all the search results elements.
    +    #MSearchResults - Contains the iframe that will hold the results.
    +    #MSearchRseultsWindowClose - The link to manually close the search results window.
    +
    +
    +
    +
    +Topic: Class Hierarchy Structure
    +_______________________________________________________________________________
    +
    +
    +    Everything is contained in a single <ClassHierarchy>.  Each entry is surrounded by its type, such as <CHParent>, and the
    +    generic <CHEntry>.  Depending on the context, entries may be surrounded by one or more <CHIndents>.
    +
    +    (start diagram)
    +
    +    <ClassHierarchy>
    +
    +        <CHIndent>?
    +
    +            <CHType>
    +                <CHEntry>
    +
    +                    <a href>?
    +                        Entry
    +                    </a href>
    +
    +                </CHEntry>
    +            </CHType>
    +
    +        </CHIndent>?
    +
    +    </ClassHierarchy>
    +
    +    (end diagram)
    +
    +
    +Styles: Class Hierarchy Styles
    +
    +    ClassHierarchy - The topmost style containing everything.
    +
    +    CHEntry - A generic class entry.
    +
    +    CHParent - The style for a parent class.
    +    CHCurrent - The style for the current class, which is the one the hierarchy is generated for.
    +    CHChild - The style for a child class.
    +    CHChildNote - The style for when a child is added that just shows how many other children were omitted.
    +
    +    CHIndent - A style used to indent a level.
    +
    +    CHType - <CHParent>, <CHCurrent>, <CHChild>, or <CHChildNote>.
    +
    +
    +
    +
    +Topic: Summary Structure
    +_______________________________________________________________________________
    +
    +
    +    Everything is enclosed in a single <Summary>.  All the other summary styles are prefixed with an S.
    +
    +    <STitle> holds the actual word "Summary" and <SBorder> and <STable> hold the content.  <SBorder> exists because different
    +    browsers apply table padding attributes in different ways.  <STable> exists as a class to separate the main table from any other
    +    tables that may be necessary.  Here's a diagram:
    +
    +    >   <Summary>
    +    >
    +    >       <STitle>
    +    >           Title
    +    >       </STitle>
    +    >
    +    >       <SBorder>
    +    >           <table STable>
    +    >               ...
    +    >           </table STable>
    +    >       </SBorder>
    +    >
    +    >   </Summary>
    +
    +    On to the table content.
    +
    +    >   <tr SType and SEntry (and SIndent#?) (and SMarked?)>
    +    >       <td SEntry>
    +    >
    +    >           <a href>Entry</a href>
    +    >
    +    >       </td SEntry>
    +    >       <td SDescription>
    +    >
    +    >           Description
    +    >
    +    >       </td SDescription>
    +    >   </tr SType and SEntry (and SIndent#?) (and SMarked?)>
    +
    +    <SIndent#> exist to allow indenting.  They're necessary because implementing it as nested tables, while structurally cleaner,
    +    won't allow the desciptions to line up on the right throughout the entire summary.  <SMarked> will be applied on almost every
    +    other row to allow for tinting to improve readability.
    +
    +    Use the power of CSS's inheritance rules to specify styles.  For example, to set the style of a group entry, apply it to
    +    .SGroup .SEntry.  However, you could also apply a style to both the group's entry and description by applying the
    +    style to .SGroup td.  Or, you could apply a style to all the entries by applying it to .SEntry.  And so on.
    +
    +
    +Styles: Summary Styles
    +
    +    Summary - The topmost style containing the entire summary.
    +
    +    STitle - Contains the summary title, which is the part that actually says "Summary".
    +
    +    SBorder - Surrounds <STable>, since some browsers can't do table padding right.  A hack, I know.
    +    STable - The actual summary table.  This class separates it from other layout tables that may appear.
    +
    +    SMarked - A class applied to rows that should have a slightly different color than the rest of the rows to make them easier to
    +                    read.
    +
    +    SEntry - The entry (left) side of the table.
    +    SDescription - The description (right) side of the table.
    +
    +    SIndent# - Surrounding entries and descriptions that are part of a group and need to be indented.  Actual styles will be
    +                     SIndent1, SIndent2, etc.
    +
    +    SType - A placeholder for all topic-specific styles.  The actual styles will be S followed by the alphanumeric-only topic type name.
    +                So the SType of a "PL/SQL Function" topic will actually be SPLSQLFunction.
    +
    +
    +
    +
    +Topic: Prototype Structure
    +_______________________________________________________________________________
    +
    +
    +    Everything is enclosed in a <Prototype>.  All other styles are prefixed with a P.
    +
    +    Parameter Type First Style:
    +
    +        For prototypes such as
    +        > void Function (unsigned int* a, int b = 0)
    +        where the types come first.
    +
    +        (start diagram)
    +
    +        <table Prototype>
    +
    +            <td PBeforeParameters>
    +                "void Function ("
    +            </td PBeforeParameters>
    +
    +            <td PTypePrefix>
    +                "unsigned"
    +            </td PTypePrefix>
    +
    +            <td PType>
    +                "int"
    +            </td PType>
    +
    +            <td PParameterPrefix>
    +                "*"
    +            </td PParameterPrefix>
    +
    +            <td PParameter>
    +                "a", "b"
    +            </td PParameter>
    +
    +            <td PDefaultValuePrefix>
    +                "="
    +            </td PDefaultValuePrefix>
    +
    +            <td PDefaultValue>
    +                "0"
    +            </td PDefaultValue>
    +
    +            (repeated as necessary)
    +
    +            <td PAfterParameters>
    +                ")"
    +            </td PAfterParameters>
    +
    +        </table Prototype>
    +
    +        (end diagram)
    +
    +
    +    Parameter Name First Style:
    +
    +        For prototypes such as
    +        > function Function (a, b: int; c: int := 0)
    +        where the parameters come first.
    +
    +        (start diagram)
    +
    +        <table Prototype>
    +
    +            <td PBeforeParameters>
    +                "function Function ("
    +            </td PBeforeParameters>
    +
    +            <td PParameter>
    +                "a,", "b:", "c:"
    +            </td PParameter>
    +
    +            <td PType>
    +                "int"
    +            </td PType>
    +
    +            <td PDefaultValuePrefix>
    +                ":="
    +            </td PDefaultValuePrefix>
    +
    +            <td PDefaultValue>
    +                "0"
    +            </td PDefaultValue>
    +
    +            (repeated as necessary)
    +
    +            <td PAfterParameters>
    +                ")"
    +            </td PAfterParameters>
    +
    +        </table Prototype>
    +
    +        (end diagram)
    +
    +
    +    Note that any section may not exist.  For example, there will be no <PTypePrefix> cells generated if none of the parameters
    +    have it.
    +
    +
    +Styles: Prototype Styles
    +
    +    Prototype - The style encompassing the entire prototype.
    +
    +    PBeforeParameters - The part of the prototype that comes before the parameters.
    +    PAfterParameters - The part of the prototype that comes after the parameters.
    +
    +    PType - The parameter type.
    +    PTypePrefix - The prefix of a parameter type.
    +    PParameter - The parameter name.
    +    PParameterPrefix - The prefix of a parameter name.
    +    PDefaultValue - The default value expression for a parameter.
    +    PDefaultValuePrefix - The prefix of the default value expression.
    +
    +
    +
    +
    +Topic: Link Structure
    +_______________________________________________________________________________
    +
    +
    +    All links to symbols have a type style prefixed with L.  The only exceptions are summary entries; summary descriptions use
    +    them as well.
    +
    +    >   <a LType>
    +    >       Link
    +    >   </a LType>
    +
    +    You can use this to make links to different symbols appear in different styles.  For example, making .LClass bold will make all
    +    links to classes bold, except when appearing in summary entries.  You can combine this with other styles to be even more
    +    specific.  For example, you can apply a style to function links appearing in summary descriptions with .SDescription .LFunction.
    +
    +Styles: Link Styles
    +
    +    LType - A placeholder for all topic-specific styles.  The actual styles will be L followed by the alphanumeric-only topic type name.
    +                So the LType of a "PL/SQL Function" topic will actually be LPLSQLFunction.
    +
    +
    +
    +Topic: Index Structure
    +_______________________________________________________________________________
    +
    +
    +    Everything is enclosed in an <#Index>.  Combine with <Framed> and <Unframed> to distinguish between output formats.  All
    +    other index styles are prefixed with an I.
    +
    +    (start diagram)
    +
    +    <#Index>
    +
    +        <IPageTitle>
    +            Page Title
    +        </IPageTitle>
    +
    +        <INavigationBar>
    +            A - <a href>B</a href> - C ...
    +        </INavigationBar>
    +
    +        <table>
    +
    +            <IHeading>
    +                Heading (A, B, etc.)
    +            </IHeading>
    +
    +            <td ISymbolPrefix>
    +                Prefix, if any
    +            </td ISymbolPrefix>
    +
    +            <td IEntry>
    +                Entry
    +            </td IEntry>
    +
    +            ...
    +
    +        </table>
    +
    +    </#Index>
    +
    +    (end diagram)
    +
    +    Every index entry, including headings, are rows in a table.  The first column of a non-heading are <ISymbolPrefixes> so that
    +    the non-prefix portions align correctly.  The other column are <IEntries>, of which there are multiple formats, described below.
    +
    +    (start diagram)
    +
    +    <a href ISymbol>
    +        Symbol
    +    </a href ISymbol>,
    +    <IParent>
    +        Class
    +    </IParent>
    +
    +    <ISymbol>
    +        Symbol
    +    </ISymbol>
    +    <ISubIndex>
    +        <a href IParent>
    +            Class
    +        </a href IParent>
    +        ...
    +    </ISubIndex>
    +
    +    <ISymbol>
    +        Symbol
    +    </ISymbol>
    +    <ISubIndex>
    +        <IParent>
    +            Class
    +        </IParent>
    +        <ISubIndex>
    +            <a href IFile>
    +                File
    +            </a href IFile>
    +            ...
    +        </ISubIndex>
    +        ...
    +    </ISubIndex>
    +
    +    (end diagram)
    +
    +    Each part of the entry is surrounded by its type, which may or may not be a link.  If an entry has more than one defining class
    +    or file,  they're broken out into <ISubIndexes>.
    +
    +    It's called <IParent> instead of <IClass> because class entries are <ISymbols>.  <IParents> are only used when the symbol
    +    has a class.  If the symbol _is_ a class, the symbol is global.
    +
    +
    +Styles: Index Styles
    +
    +    #Index - Parent element for the entire index.
    +
    +    IPageTitle - The page title.
    +    INavigationBar - The navigation bar.
    +
    +    IHeading - An index heading, such as the letter for the group.
    +
    +    IEntry - An entry in the index.
    +    ISymbolPrefix - The stripped prefix of the entry.
    +    ISymbol - The entry symbol.
    +    IParent - The entry parent class.  If the entry _is_ a class, this isn't defined because classes are global and don't have parent
    +                  classes.  This is why it's called IParent instead of IClass; hopefully it's less confusing.
    +    IFile - The file the entry is defined in.
    +
    +    ISubIndex - The surrounding block if an entry needs to be broken out into a sub-index.
    +
    +    #IFirstHeading - The ID of the first <IHeading> to appear in the file.
    +
    +    #IFirstSymbolPrefix - The ID for the first <ISymbolPrefix> to appear under an <IHeading>.
    +    #ILastSymbolPrefix - The ID for the last <ISymbolPrefix> to appear under an <IHeading>.
    +    #IOnlySymbolPrefix - The ID if there is only one <ISymbolPrefix> for an <IHeading>.
    +
    +
    +
    +Topic: Search Results Structure
    +_______________________________________________________________________________
    +
    +
    +    The search results use virtually the same structure and styles as the indexes, except that <#SearchResults> replaces
    +    <#Index>, there's a new <SRResult> style, and there are a few additional <SRStatus> blocks.
    +
    +    Visibility:
    +
    +        Visibility is *very* important to making the search work correctly.  JavaScript will handle most of it, but your CSS needs to
    +        abide by these rules.
    +
    +        - <SRStatus> sections are visible by default.
    +        - <SRResult> sections are *not* visible by default.  They must use display: none.
    +        - <ISubIndex> should be display: none when under <#SearchResults>.
    +
    +
    +Styles: Search Results Styles
    +
    +    #SearchResults - Parent element for the entire page.
    +    SRStatus - Status message.  Must be visible by default.
    +    SRResult - A result.  All you need to do for this class is set it to display: none.  Nothing else should be set on it.
    +
    +
    +
    +
    +Topic: Tool Tip Structure
    +_______________________________________________________________________________
    +
    +
    +    Tool tips may appear anywhere in the page, mainly because it's assumed that they will use position: absolute and
    +    visibility: hidden.
    +
    +    The entire tool tip is found in a <CToolTip> style, with a CType style inside it.  CTypes are normally outside their elements, but
    +    that would cause it to be partially visible in this case.  We need <CToolTip> to be the outermost style so its visibility and
    +    position can be manipulated in JavaScript.
    +
    +    Inside there's a <CPrototype> and/or the description text.  The description text has no special surrounding tags.
    +
    +    >   <CToolTip>
    +    >
    +    >       <CPrototype>
    +    >           Prototype
    +    >       </CPrototype>
    +    >
    +    >       Summary text
    +    >
    +    >   </CToolTip>
    +
    +Styles: Tool Tip Styles
    +
    +    CToolTip - Surrounds the entire tool tip.  This *must* have position: absolute and visibility: hidden for the tool tip mechanism
    +                    to work.
    +
    +    See also <CPrototype>.
    +
    +
    +Styles: Miscellaneous Styles
    +
    +    blockquote - This HTML element should surround anything that needs to be scrolled if it's too wide, like prototypes and text
    +                       diagrams.  It's not a style because this makes it much easier to do the JavaScript necessary to get this working
    +                       in IE.
    +
    +
    +Group: History
    +
    +Topic: Revisions
    +_______________________________________________________________________________
    +
    +
    +    How the page structure has changed throughout the various releases.
    +
    +    1.4:
    +
    +        - Replaced UnframedPage with <ContentPage> and <IndexPage>.
    +        - Added <#Menu>, <#Content>, <#Footer>, and <#Index>.  They were previously shown in the diagrams as classes but did
    +          not actually appear in the generated output.
    +        - Removed MenuSection, ContentSection, and IndexSection.  Use things like ".ContentPage #Menu" instead.
    +        - Removed tables from the unframed <Page Structure>.  Use CSS to position the elements instead.
    +        - <#MainTopic> is applied to <CTopic> instead of <CType>.
    +        - IE4, IE5, Opera5, Opera6, Netscape, and Netscape4 browser styles have been removed.  <IE7>, <Opera8>,
    +          and <Opera9> have been added.  Gecko has been replaced by <Firefox>, <Firefox1>, <Firefox15>, and <Firefox2>.
    +          KHTML has been replaced by <Safari>, <Safari2>, <Safari3>, and <Konqueror>.
    +        - Removed redundant CParagraph, CCode, and CBulletList classes.  Use <CBody> with p, pre, and ul instead.
    +        - Added <CImageCaption> and <CImageLink>.
    +        - Added <#MSearchPanel>, <#MSearchResultsWindow>, and all related styles.
    +        - Added <Search Results Structure>, <Search Results Styles>, and <FramedSearchResultsPage>.
    +        - Removed SEntrySize.  Apply the width to <SEntry> and <SDescription> instead.
    +        - <SType>, <SEntry>, and <SIndent#> were moved from the td and divs into the tr.
    +        - Removed HB style.  Now using wbr tag.
    +
    +    1.33:
    +
    +        - Added <PDefaultValuePrefix>.
    +
    +    1.32:
    +
    +        - <blockquotes> now surround elements that should scroll if they're too wide for the page.
    +
    +    1.3:
    +
    +        - Removed CPrototype.  See the replacement <Prototype Structure> and <Prototype Styles>.
    +        - Removed SInGroup, SInClass, and SInSection in favor of more general <SIndent#>.
    +        - <CTypes>, <STypes>, and <LTypes> are now completely determined by <Topics.txt> configuration files.
    +        - <CTypes>, <STypes>, and <LTypes> no longer have separate list types.  A CFunctionList is now just a CFunction.
    +        - Indexes are now done with tables.
    +        - ISection was removed.
    +        - <IEntries> are only used for the entry cell, not for each entry in an <ISubIndex>.
    +        - Added <ISymbolPrefix>, related IDs, and <#IFirstHeading>.
    +        - Merged <CType> and <CTopic> into the same element.  Must now be styled with .CType.CTopic (no space) while all
    +          sub-elements will still be .CType .CElement (with space.)
    +
    +    1.21:
    +
    +        - Added <TOPIC_PROPERTY> and TOPIC_PROPERTY_LIST styles, so they get corresponding <CTypes>, <STypes>, and
    +          <LTypes>.
    +
    +    1.2:
    +
    +        - Added <Class Hierarchy Styles> since 1.2 added class hierarchies.
    +
    +    1.16:
    +
    +        - Changed the first topic from having a CMain type to having a normal type with a <#MainTopic> ID.
    +
    +    1.1:
    +
    +        - Added <Tool Tip Styles>.
    +        - Renamed HiddenBreak to <HB>.
    +        - Added <TOPIC_CONSTANT>, TOPIC_CONSTANT_LIST, <TOPIC_TYPE>, and TOPIC_TYPE_LIST types, so they get
    +          corresponding <CTypes>, <STypes>, and <LTypes>.
    +
    +    1.0:
    +
    +        - The <CType> tags now appear arround the <CTopic> tags instead of vice versa.
    +        - Added a <CBody> tag to surround non-<CTitle> elements.
    +        - <SMarked> now appears in tr's instead of td's, where it belonged in the first place.
    +
    +    0.95:
    +
    +        - Added <Browser Styles>.
    +        - Redid <Page Structure>, replacing generic styles like Menu with page type styles like UnframedPage/MenuSection and
    +          FramedMenuPage.
    +
    +    0.91:
    +
    +        - Added <LURL> and <LEMail> link styles, since 0.91 added URL and e-mail links.
    +        - Added <ISection> style, which is better than <IHeading> floating on its own.
    +
    +    0.9:
    +
    +        - Added <Index Styles>, since 0.9 added indexes.
    +
    diff --git a/vendor/naturaldocs/Info/File Parsing.txt b/vendor/naturaldocs/Info/File Parsing.txt
    new file mode 100644
    index 000000000..10bd0e91b
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/File Parsing.txt	
    @@ -0,0 +1,83 @@
    +
    +    Architecture: File Parsing
    +
    +####################################################################################
    +
    +    This is the architecture and code path for general file parsing.  We pick it up at <NaturalDocs::Parser->Parse()> because we're not interested in how the files are gathered and their languages determined for the purposes of this document.  We are just interested in the process each individual file goes through when it's decided that it should be parsed.
    +
    +
    +
    +    Stage: Preparation and Differentiation
    +    _______________________________________________________________________________________________________
    +
    +    <NaturalDocs::Parser->Parse()> can be called from one of two places, <NaturalDocs::Parser->ParseForInformation()> and <NaturalDocs::Parser->ParseForBuild()>, which correspond to the parsing and building phases of Natural Docs.  There is no noteworthy work done in either of them before they call Parse().
    +
    +
    +    Stage: Basic File Processing
    +    _______________________________________________________________________________________________________
    +
    +    The nitty-gritty file handling is no longer done in <NaturalDocs::Parser> itself due to the introduction of full language support in 1.3, as it required two completely different code paths for full and basic language support.  Instead it's handled in NaturalDocs::Languages::Base->ParseFile(), which is really a virtual function that leads to <NaturalDocs::Languages::Simple->ParseFile()> for basic language support or a version appearing in a package derived from <NaturalDocs::Languages::Advanced> for full language support.
    +
    +    The mechinations of how these functions work is for another document, but their responsibility is to feed all comments Natural Docs should be interested in back to the parser via <NaturalDocs::Parser->OnComment()>.
    +
    +
    +    Stage: Comment Processing
    +    _______________________________________________________________________________________________________
    +
    +    <NaturalDocs::Parser->OnComment()> receives the comment sans comment symbols, since that's language specific.  All comment symbols are replaced by spaces in the text instead of removed so any indentation is properly preserved.  Also passed is whether it's a JavaDoc styled comment, as that varies by language as well.
    +
    +    OnComment() runs what it receives through <NaturalDocs::Parser->CleanComment()> which normalizes the text by removing comment boxes and horizontal lines, expanding tabs, etc.
    +
    +
    +    Stage: Comment Type Determination
    +    _______________________________________________________________________________________________________
    +
    +    OnComment() sends the comment to <NaturalDocs::Parser::Native->IsMine()> to test if it's definitely Natural Docs content, such as by starting with a recognized header line.  If so, it sends it to <NaturalDocs::Parser::Native->ParseComment()>.
    +
    +    If not, OnComment() sends the comment to <NaturalDocs::Parser::JavaDoc->IsMine()> to test if it's definitely JavaDoc content, such as by having JavaDoc tags.  If so, it sends it to <NaturalDocs::Parser::JavaDoc->ParseComment()>.
    +
    +    If not, the content is ambiguous.  If it's a JavaDoc-styled comment it goes to <NaturalDocs::Parser::Native->ParseComment()>  to be treated as a headerless Natural Docs comment.  It is ignored otherwise, which lets normal comments slip through.  Note that it's only ambiguous if neither parser claims it; there's no test to see if they both do.  Instead Natural Docs always wins.
    +
    +    We will not go into the JavaDoc code path for the purposes of this document.  It simply converts the JavaDoc comment into <NDMarkup> as best it can, which will never be perfectly, and adds a <NaturalDocs::Parser::ParsedTopic> to the list for that file.  Each of those ParsedTopics will be headerless as indicated by having an undefined <NaturalDocs::Parser::ParsedTopic->Title()>.
    +
    +
    +    Stage: Native Comment Parsing
    +    _______________________________________________________________________________________________________
    +
    +    At this point, parsing is handed off to <NaturalDocs::Parser::Native->ParseComment()>.  It searches for header lines within the comment and divides the content into individual topics.  It also detects (start code) and (end) sections so that anything that would normally be interpreted as a header line can appear there without breaking the topic.
    +
    +    The content between the header lines is sent to <NaturalDocs::Parser::Native->FormatBody()> which handles all the block level formatting such as paragraphs, bullet lists, and code sections.  That function in turn calls <NaturalDocs::Parser::Native->RichFormatTextBlock()> on certain snippets of the text to handle all inline formatting, such as bold, underline, and links, both explicit and automatic.
    +
    +    <NaturalDocs::Parser::Native->ParseComment()> then has the body in <NDMarkup> so it makes a <NaturalDocs::Parser::ParsedTopic> to add to the list.  It keeps track of the scoping via topic scoping, regardless of whether we're using full or basic language support.  Headerless topics are given normal scope regardless of whether they might be classes or other scoped types.
    +
    +
    +    Group: Post Processing
    +    _______________________________________________________________________________________________________
    +
    +    After all the comments have been parsed into ParsedTopics and execution has been returned to <NaturalDocs::Parser->Parse()>, it's time for some after the fact cleanup.  Some things are done like breaking topic lists, determining the menu title, and adding automatic group headings that we won't get into here.  There are two processes that are very relevant though.
    +
    +
    +    Stage: Repairing Packages
    +    _______________________________________________________________________________________________________
    +
    +    If the file we parsed had full language support, the <NaturalDocs::Languages::Advanced> parser would have done more than just generate various OnComment() calls.  It would also return a scope record, as represented by <NaturalDocs::Languages::Advanced::ScopeChange> objects, and a second set of ParsedTopics it extracted purely from the code, which we'll refer to as autotopics.  The scope record shows, purely from the source, what scope each line of code appears in.  This is then combined with the topic scoping to update ParsedTopics that come from the comments in the function <NaturalDocs::Parser->RepairPackages()>.
    +
    +    If a comment topic changes the scope, that's honored until the next autotopic or scope change from the code.  This allows someone to document a class that doesn't appear in the code purely with topic scoping without throwing off anything else.  Any other comment topics have their scope changed to the current scope no matter how it's arrived at.  This allows someone to manually document a function without manually documenting the class and still have it appear under that class.  The scope record will change the scope to part of that class even if topic scoping did not.  Essentially the previous topic scoping is thrown out, which I guess is something that can be improved.
    +
    +    None of this affects the autotopics, as they are known to have the correct scoping since they are gleaned from the code with a dedicated parser.  Wouldn't there be duplication of manually documented code elements, which would appear both in the autotopics and in the comment topics?  Yes.  That brings us to our next stage, which is...
    +
    +
    +    Stage: Merging Auto Topics
    +    _______________________________________________________________________________________________________
    +
    +    As mentioned above, ParseFile() also returns a set of ParsedTopics gleaned from the code called autotopics.  The function <NaturalDocs::Parser->MergeAutoTopics()> merges this list with the comment topics.
    +
    +    The list is basically merged by line number.  Since named topics should appear directly above the thing that they're documenting, topics are tested that way and combined into one if they match.  The description and title of the comment topic is merged with the prototype of the autotopic.  JavaDoc styled comments are also merged in this function, as they should appear directly above the code element they're documenting.  Any headerless topics that don't, either by appearing past the last autotopic or above another comment topic, are discarded.
    +
    +
    +    Stage: Conclusion
    +    _______________________________________________________________________________________________________
    +
    +    Thus ends all processing by <NaturalDocs::Parser->Parse()>.  The file is now a single list of <NaturalDocs::Parser::ParsedTopics> with all the body content in <NDMarkup>.  If we were using <NaturalDocs::Parser->ParseForBuild()>, that's pretty much it and it's ready to be converted into the output format.  If we were using <NaturalDocs::Parser->ParseForInformation()> though, the resulting file is scanned for all relevant information to feed into other packages such as <NaturalDocs::SymbolTable>.
    +
    +    Note that no prototype processing was done in this process, only the possible tranferring of prototypes from one ParsedTopic to another when merging autotopics with comment topics.  Obtaining prototypes and formatting them is handled by <NaturalDocs::Languages::Simple> and <NaturalDocs::Languages::Advanced> derived packages.
    diff --git a/vendor/naturaldocs/Info/HTMLTestCases.pm b/vendor/naturaldocs/Info/HTMLTestCases.pm
    new file mode 100644
    index 000000000..ea434a4a6
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/HTMLTestCases.pm
    @@ -0,0 +1,270 @@
    +###############################################################################
    +#
    +#   File: Browser Testing
    +#
    +###############################################################################
    +#
    +#   This file tests Natural Docs' generated output.  Particularly useful when testing various browsers.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +#
    +#   About: Browsers
    +#
    +#   The specific browser versions tested are below.  Everything is tested on Windows Vista unless otherwise noted.
    +#
    +#   Firefox 2.0.0.10 - 2.0 released October 2006.
    +#   Firefox 1.5.0.8 - 1.5 released Novemer 2005.
    +#   Firefox 1.0.8 - 1.0 released November 2004.  Not critical to support.
    +#
    +#   IE 7.0 - 7.0 released October 2006.
    +#   IE 6.0 - 6.0 released August 2001.  Tested on Windows XP SP2 via Virtual PC.
    +#
    +#	Safari 3.0.4 - 3.0 released June 2007.  Tested Windows version.
    +#	Safari 2.0.4 - 2.0 released April 2005.  Tested on Mac OS X 10.4 Tiger.
    +#
    +#   Opera 9.02 - 9.0 released June 2006.
    +#   Opera 8.54 - 8.5 released September 2005.
    +#   Opera 8.02 - 8.0 released April 2005.
    +#   Opera 7.51 - 7.5 released around August 2004 I think.  Not critical to support.
    +#   Opera 7.02 - 7.0 released January 2003.  Not critical to support.
    +#
    +#   Konqueror 3.5.5 - Tested on openSUSE 10.2 via VMware Player.
    +#
    +
    +
    +###############################################################################
    +# Group: Search
    +
    +#
    +#   Topic: Unframed HTML Search
    +#
    +#   Tests:
    +#
    +#       - Make sure the search box appears and disappears correctly on hover.
    +#       - Type to bring up results.  Type further to narrow them.  Narrow until there's no results.
    +#       - Backspace to bring the results back.  Backspacing to empty closes the results.
    +#       - Type to bring up results with a different first letter.  (Tests iframe content switch.)
    +#       - Type *Z* to bring up empty page when there's nothing with that first letter.  (Tests generic no results page.)
    +#       - Type *Name* in Everything search to test expanding and collapsing, especially between two that differ only by case.
    +#       - Change filter to *Functions* to test changing filter while results are open.  Change to *Types* to switch to one with
    +#         no results.
    +#       - Test Close button on results.  Should deactivate panel as well.
    +#       - Clicking away should deactivate panel if the box is empty, not have an effect if there are results open.
    +#       - Text should always change back to "Search" when deactivating.
    +#
    +#   Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - Functionally OK.  Search panel doesn't activate on hover.  Works fine when clicked.
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - *Broken.*  Results panel doesn't show up.  Border around deactivated search box.
    +#
    +#       Opera 9.0  - OK
    +#       Opera 8.5  - OK
    +#       Opera 8.0  - OK
    +#       Opera 7.5  - Functionally OK.  Search panel has sunken border when deactivated, minor pixel shifting.
    +#       Opera 7.0  - *Broken.*  Completely.
    +#
    +#       Konqueror 3.5  - *Broken.*  Results panel doesn't show up.  Seems to fail on "resultsFrame = window.frames.MSearchResults;"
    +#
    +
    +#
    +#   Topic: Framed HTML Search
    +#
    +#   Tests:
    +#
    +#       - Make sure the search box appears and disappears correctly on hover.
    +#       - Type to bring up results on right.  Type further to narrow them.  Narrow until there's no results.
    +#       - Backspace to bring the results back.
    +#       - Type to bring up results with a different first letter.  (Tests frame content switch.)
    +#       - Type *Z* to bring up empty page when there's nothing with that first letter.  (Tests generic no results page.)
    +#       - Type *Name* in Everything search to see that there's no collapsing in this mode.
    +#       - Change filter to *Functions* to test changing filter while results are open.  Change to *Types* to switch to one with
    +#         no results.
    +#       - Clicking away should deactivate panel.
    +#       - Clicking a result should deactivate panel and show up in correct frame.
    +#       - Text should always change back to "Search" when deactivating.
    +#
    +#   Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - Functionally OK.  Search panel doesn't activate on hover, is a little wide.  Works fine when clicked.
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - Functionally OK.  Has a sunken border around the deactivated seach field.
    +#
    +#       Opera 9.0  - OK
    +#       Opera 8.5  - OK
    +#       Opera 8.0  - OK
    +#       Opera 7.5  - Functionally OK.  Search panel has sunken border when deactivated, minor pixel shifting.
    +#       Opera 7.0  - *Broken.*
    +#
    +#       Konqueror 3.5  - Functionally OK.  Panel doesn't reset and deactivate when clicking a result link.
    +#
    +
    +
    +###############################################################################
    +# Group: Other
    +
    +#
    +#   Topic: Images
    +#
    +#	Tests:
    +#
    +#   - Here is an embedded image on its own line.
    +#
    +#   (see images/logo.png)
    +#
    +#   - Here is a reference in the middle of a sentence, in the middle of a bullet list: (see images/logo.png)  It should have been
    +#     converted to a link with the image appearing below the bullet list and the file name used as a caption.  Make sure the
    +#     caption positions correctly.
    +#   - Here's a link to a non-existent image, which should appear literally: (see images/doesntexist.jpg)
    +#   - Here is an embedded image that doesn't exist on it's own line.
    +#
    +#   (see images/doesntexist.png)
    +#
    +#   - Here is a link using the "(see)" syntax which shouldn't be interpreted as an image link because it doesn't end with an
    +#     acceptable extension.  Also, links should still resolve because of that.  (see <Framed HTML Search>)
    +#
    +#	Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - OK
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - OK
    +#
    +#       Opera 9.0  - OK
    +#       Opera 8.5  - OK
    +#       Opera 8.0  - OK
    +#       Opera 7.5  - OK
    +#       Opera 7.0  - OK
    +#
    +#       Konqueror 3.5  - OK
    +
    +
    +#
    +#	Topic: Prototypes and Tooltips
    +#
    +#	Hover over <NaturalDocs::Parser::JavaDoc->ParseComment()> and <NaturalDocs::Parser::JavaDoc->IsMine()>
    +#
    +#	Tests:
    +#
    +#		- A tooltip should appear about a second after you hover over the link above.
    +#		- It should go away when you move the cursor away.
    +#		- It shoud be positioned directly underneath with a slight gap.
    +#		- The prototype should be formatted cleanly with each parameter on its own line and aligned in columns.
    +#		- The asterisk should be in a separate column.
    +#		- Test it with the link too close to the edge of the window so the pop-up has to shift left to fit.
    +#
    +#	Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - OK
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - OK
    +#
    +#       Opera 9.0  - OK.  Has its own tooltips turned on by default which can cover it up though.
    +#       Opera 8.5  - OK.  Has its own tooltips turned on by default which can cover it up though.
    +#       Opera 8.0  - OK.  Has its own tooltips turned on by default which can cover it up though.
    +#       Opera 7.5  - OK.  Has its own tooltips turned on by default which can cover it up though.
    +#       Opera 7.0  - *Broken.*  Usually works, if the window is too narrow may collapse completely.
    +#
    +#       Konqueror 3.5  - OK
    +#
    +
    +
    +#
    +#	Topic: Long code block scrolling
    +#
    +#	Go to <Prototype Parameter Styles>.
    +#
    +#	Tests:
    +#
    +#		- Shrink the browser window so that a line extends past the end of it.  Only the line should have a scrollbar, not the
    +#		  entire page.
    +#		- Expand the browser window.  The scrollbar should disappear.
    +#
    +#	Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - OK
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - OK
    +#
    +#       Opera 9.0  - OK
    +#       Opera 8.5  - OK
    +#       Opera 8.0  - OK
    +#       Opera 7.5  - OK
    +#       Opera 7.0  - OK
    +#
    +#       Konqueror 3.5  - OK
    +#
    +
    +
    +#
    +#	Topic: Menu and Class Hierarchies
    +#
    +#	Go to <NaturalDocs::Languages::Simple>.
    +#
    +#	Tests:
    +#
    +#		- Class hierarchy should look okay.
    +#		- Make sure the menu hierarchy opens up on its own when the page is loaded.
    +#		- You should be able to click on groups to open and close them.
    +#
    +#	Results:
    +#
    +#       Firefox 2.0  - OK
    +#       Firefox 1.5  - OK
    +#       Firefox 1.0  - OK
    +#
    +#       IE 7.0  - OK
    +#       IE 6.0  - OK
    +#
    +#		Safari 3.0  - OK
    +#		Safari 2.0  - OK
    +#
    +#       Opera 9.0  - OK
    +#       Opera 8.5  - OK
    +#       Opera 8.0  - OK
    +#       Opera 7.5  - OK
    +#       Opera 7.0  - OK
    +#
    +#       Konqueror 3.5  - OK
    +#
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Info/Languages.txt b/vendor/naturaldocs/Info/Languages.txt
    new file mode 100644
    index 000000000..c564eb582
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/Languages.txt
    @@ -0,0 +1,107 @@
    +
    +    Title: Language Notes
    +_______________________________________________________________________________
    +
    +    This is more for my personal reference than anything else.
    +
    +
    +    ___________________________________________________________________________
    +
    +    Topic: Prototype Parameter Styles
    +    ___________________________________________________________________________
    +
    +    Parameters via Commas, Typed via Spaces:
    +
    +        > FunctionName ( type indentifier, type identifier = value, modifier type identifier )
    +        > FunctionName ( indentifier, identifier = value )
    +
    +        The general idea is that parameters are separated by commas.  Identifiers cannot contain spaces.  Types and modifiers,
    +        if available, are separated from the identifiers with spaces.  There may be an equals sign to set the default value.
    +
    +        So parsing means splitting by commas, stripping everything past an equals sign for the default value, stripping everything
    +        after the last space for the identifier, and the rest is the type.  If there are no internal spaces after the default value is
    +        stripped, it's all identifier.
    +
    +        Note that internal parenthesis, brackets, braces, and angle brackets should be parsed out.  They may be present in default
    +        values or types and any commas and equal signs in them should not be included.
    +
    +        Applies to C++, Java, C#, JavaScript, Python, PHP, Ruby.
    +
    +        Applies to Perl as well, even though it doesn't have any real parameter declaration structure.  Just adding it with comments
    +        is fine.
    +
    +    Parameters via Semicolons and Commas, Typed via Colons:
    +
    +        > FunctionName ( identifier: type; identifier, identifier: type; identifier: type := value )
    +
    +        Parameters via semicolons, types via colons.  However, there can be more than one parameter per type via commas.
    +        Default values via colon-equals.
    +
    +        Applies to Pascal, Ada.
    +
    +
    +    SQL:
    +
    +        > FunctionName ( identifier type, identifier modifier type, identifier type := value )
    +
    +        Parameters separated by commas.  Identifiers come before the types and are separated by a space.  Default values are
    +        specified with colon-equals.
    +
    +        > FunctionName @identifier type, @dentifier modifier type, @identifier type = value
    +
    +        Microsoft's SQL uses equals instead of colon-equals, doesn't need parenthesis, and starts its parameter names with an @
    +        symbol.
    +
    +
    +    Visual Basic:
    +
    +        > FunctionName ( modifiers identifier as type, identifier = value )
    +
    +        Parameters separated by commas.  Default values via equals.  However, any number of modifiers may appear before the
    +        identifier.  Those modifiers are ByVal, ByRef, Optional, and ParamArray.
    +
    +
    +    Tcl:
    +
    +        > FunctionName { identifier identifier { whatever } } { code }
    +
    +        Identifiers are specified in the first set of braces and have no commas.  However, they can be broken out into sub-braces.
    +
    +
    +    ___________________________________________________________________________
    +
    +    Topic: Syntax References
    +    ___________________________________________________________________________
    +
    +    C++ - http://www.csci.csusb.edu/dick/c++std/syntax.html
    +
    +    C# - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/CSharpSpecStart.asp.  Open in IE.
    +
    +    Java - http://cui.unige.ch/db-research/Enseignement/analyseinfo/
    +    Ada - http://cui.unige.ch/db-research/Enseignement/analyseinfo/
    +
    +    SQL - http://cui.unige.ch/db-research/Enseignement/analyseinfo/,
    +             <http://www.cs.umb.edu/cs634/ora9idocs/appdev.920/a96624/13_elems.htm>, or
    +             <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_tsqlcon_6lyk.asp?frame=true> (open in IE).
    +
    +    JavaScript - http://academ.hvcc.edu/~kantopet/javascript/index.php
    +
    +    Python - http://www.python.org/doc/2.3.4/ref/ref.html
    +
    +    PHP - http://www.php.net/manual/en/langref.php
    +
    +    Visual Basic - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbls7/html/vbspecstart.asp.  Open in IE.
    +
    +    Pascal - <http://pages.cpsc.ucalgary.ca/~becker/231/SyntaxDiagrams/pascal-syntax_files/frame.htm>.  Open in IE.
    +
    +    Ruby - http://www.rubycentral.com/book/
    +
    +    ActionScript 2 - <http://download.macromedia.com/pub/documentation/en/flash/fl8/fl8_as2lr.pdf>
    +    ActionScript 3 - <http://download.macromedia.com/pub/documentation/en/flex/2/prog_actionscript30.pdf>
    +    E2X - http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-357.pdf
    +
    +    R - Somewhere on http://www.r-project.org.
    +
    +    ColdFusion - <http://livedocs.macromedia.com/coldfusion/6/Developing_ColdFusion_MX_Applications_with_CFML/contents.htm>
    +
    +    Eiffel - http://www.gobosoft.com/eiffel/syntax/
    diff --git a/vendor/naturaldocs/Info/NDMarkup.txt b/vendor/naturaldocs/Info/NDMarkup.txt
    new file mode 100644
    index 000000000..23051722e
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/NDMarkup.txt
    @@ -0,0 +1,92 @@
    +
    +    Architecture: NDMarkup
    +_______________________________________________________________________________
    +
    +A markup format used by the parser, both internally and in <NaturalDocs::Parser::ParsedTopic> objects.  Text formatted in
    +NDMarkup will only have the tags documented below.
    +
    +
    +About: Top-Level Tags
    +
    +   All content will be surrounded by one of the top-level tags.  These tags will not appear within each other.
    +
    +   <p></p>         - Surrounds a paragraph.  Paragraph breaks will replace double line breaks, and single line breaks will
    +                            be removed completely.
    +
    +   <code type=""></code>   - Surrounds code or text diagrams that should appear literally in the output.  The type can
    +   									     be code, text, or anonymous, in which case it's not specified whether it's code or text.
    +
    +   <h></h>         - Surrounds a heading.
    +
    +   <ul></ul>       - Surrounds a bulleted (unordered) list.
    +   <dl></dl>       - Surrounds a description list, which is what you are reading.
    +
    +   <img mode="inline" target="" original=""> - An inline image.  Target contains the image target, and original contains the
    +                                                                    original text in case it doesn't resolve.
    +
    +
    +About: List Item Tags
    +
    +   These tags will only appear within their respective lists.
    +
    +   <li></li>       - Surrounds a bulleted list item.
    +   <de></de>   - Surrounds a description list entry, which is the left side.  It will always be followed by a description list
    +                         description.
    +   <ds></ds>   - Surrounds a description list symbol.  This is the same as a description list entry, except that the content
    +                         is also a referenceable symbol.  This occurs when inside a list topic.  This tag will always
    +                         be followed by a description list description.
    +   <dd></dd>   - Surrounds a description list description, which is the right side.  It will always be preceded by a description
    +                         list entry or symbol.
    +
    +About: Text Tags
    +
    +   These tags will only appear in paragraphs, headings, or description list descriptions.
    +
    +   <b></b>         - Bold
    +   <i></i>           - Italics
    +   <u></u>         - Underline
    +
    +   <link target="" name="" original=""> - Surrounds a potential link to a symbol; potential because the target is not guaranteed to
    +                                                            exist.  This tag merely designates an attempted link.  Target is what is attempting to be
    +                                                            linked to, name is the text that should appear for a successful link, and original is the
    +                                                            original text in case the link doesn't resolve.
    +
    +   <url target="" name="">                   - An external link.  There's no need for an original attribute because it will always be
    +                                                             turned into an actual link.
    +   <email target="" name="">               - A link to an e-mail address.
    +
    +   <img mode="link" target="" original=""> - An image link.  Target contains the image target, and original contains the original
    +                                                                 text in case it doesn't resolve.
    +
    +
    +About: Amp Chars
    +
    +   These are the only amp chars supported, and will appear everywhere.  Every other character will appear as is.
    +
    +   &amp;    - The ampersand &.
    +   &quot;    - The double quote ".
    +   &lt;        - The less than sign <.
    +   &gt;       - The greater than sign >.
    +
    +About: Tabs
    +
    +    NDMarkup will not contain tab characters, only spaces.  Any tab characters appearing in the source files will be
    +    expanded/replaced as necessary.
    +
    +
    +About: General Tag Properties
    +
    +   Since the tags are generated, they will always have the following properties, which will make pattern matching much
    +   easier.
    +
    +   - Tags and amp chars will always be in all lowercase.
    +   - Properties will appear exactly as documented here.  They will be in all lowercase, in the documented order, and will have no
    +     extraneous whitespace.  Anything appearing in the properties will have amp chars.
    +   - All code is valid, meaning tags will always be closed, <li>s will only appear within <ul>s, etc.
    +
    +   So, for example, you can match description list entries with /<de>(.+?)<\/de>/ and $1 will be the text.  No surprises or
    +   gotchas.  No need for sophisticated parsing routines.
    +
    +   Remember that for symbol definitions, the text should appear as is, but internally (such as for the anchor) they need to
    +   be passed through <NaturalDocs::SymbolTable->Defines()> so that the output file is just as tolerant as
    +   <NaturalDocs::SymbolTable>.
    diff --git a/vendor/naturaldocs/Info/Symbol Management.txt b/vendor/naturaldocs/Info/Symbol Management.txt
    new file mode 100644
    index 000000000..fb2bad233
    --- /dev/null
    +++ b/vendor/naturaldocs/Info/Symbol Management.txt	
    @@ -0,0 +1,59 @@
    +
    +    Architecture: Symbol Management
    +
    +####################################################################################
    +
    +    This is the architecture and code path for symbol management.  This is almost exclusively managed by <NaturalDocs::SymbolTable>, but it's complicated enough that I want a plain-English walk through of the code paths anyway.
    +
    +    An important thing to remember is that each section below is simplified initially and then expanded upon in later sections as more facets of the code are introduced.  You will not get the whole story of what a function does by reading just one section.
    +
    +
    +
    +    Topic: Symbol Storage
    +    _______________________________________________________________________________________________________
    +
    +    Symbols are indexed primarily by their <SymbolString>, which is the normalized, pre-parsed series of identifiers that make it up.  A symbol can have any number of definitions, including none, but can only have one definition per file.  If a symbol is defined more than once in a file, only the first definition is counted.  Stored for each definition is the <TopicType>, summary, and prototype.
    +
    +    Each symbol that has a definition has one designated as the global definition.  This is the one linked to by other files, unless that file happens to have its own definition which then takes precedence.  Which definition is chosen is rather arbitrary at this point; probably the first one that got defined.  Similarly, if the global definition is deleted, which one is chosen to replace it is completely arbitrary.
    +
    +    Each symbol also stores a list of references to it.  Note that references can be interpreted as multiple symbols, and each of those symbols will store a link back to the reference.  In other words, every reference a symbol stores is one that _can_ be interpreted as that symbol, but that is not necessarily the interpretation the reference actually uses.  A reference could have a better interpretation it uses instead.
    +
    +    For example, suppose there are two functions, MyFunction() and MyClass.MyFunction().  The reference text "MyFunction()" appearing in MyClass can be interpreted as either MyClass.MyFunction(), or if that doesn't exist, the global MyFunction().  Both the symbols for MyFunction() and MyClass.MyFunction() will store that it's referenced by the link, even though the class scoped one serves as the actual definition.
    +
    +    This is also the reason a symbol can exist that has no definitions: it has references.  We want symbols to be created in the table for each reference interpretation, even if it doesn't exist.  These are called potential symbols.  The reason is so we know whether a new symbol definition fulfills an existing reference, since it may be a better interpretation for the reference than what is currently used.
    +
    +
    +
    +    Topic: Reference Storage
    +    _______________________________________________________________________________________________________
    +
    +    References are indexed primarily by their <ReferenceString>, which is actually an elaborate data structure packed into a string.  It includes a <SymbolString> of the text that appears in the link and a bunch of other data that determines the rules by which the link can be resolved.  For example, it includes the scope it appears in and any "using" statements in effect, which are alternate possible scopes.  It includes the type of link it is (text links, the ones you explicitly put in comments, aren't the only kind) and resolving flags which encode the language-specific rules of non-text links.  But the bottom line is the <ReferenceString> encodes everything that influences how it may be resolved, so if two links come up with the same rules, they're considered two definitions of the same reference.  This is the understanding of the word "reference" that will used in this document.
    +
    +    Like symbols, each reference stores a list of definitions.  However, it only stores the name as all the other relevant information is encoded in the <ReferenceString> itself.  Unlike a symbol, which can be linked to the same no matter what kind of definitions it has, references that are in any way different might be interpreted differently and so need their own distinct entries in the symbol table.
    +
    +    References also store a list of interpretations.  Every possible interpretation of the reference is stored and given a numeric score.  The higher the score, the better it suits the reference.  In the MyFunction() example from before, MyClass.MyFunction() would have a higher score than just MyFunction() because the local scope should win.  Each interpretation has a unique score, there are no duplicates.
    +
    +    So the symbol and reference data structures are complimentary.  Each symbol has a list of every reference that might be interpreted as it, and every reference has a list of each symbol that it could be interpreted as.  Again, objects are created for potential symbols (those with references but no definitions) so that this structure always remains intact.
    +
    +    The interpretation with the highest score which actually exists is deemed the current interpretation of the reference.  Unlike symbols where the next global definition is arbitrary, the succession of reference interpretations is very controlled and predictable.
    +
    +
    +    Topic: Change Detection
    +    _______________________________________________________________________________________________________
    +
    +    Change management is handled a couple of ways.  First, there is a secondary file index in <NaturalDocs::SymbolTable> that stores which symbols and references are stored in each file.  It doesn't have any information other than a list of <SymbolStrings> and <ReferenceStrings> since they can be used in the main structures to look up the details.  If a file is deleted, the symbol table can then prune any definitions that should no longer be in the table.
    +
    +    Another way deals with how the information parsing stage works.  Files parsed for information just have their symbols and references added to the table regardless of whether this was the first time it was ever parsed or if it had been parsed before.  If it had been parsed before, all the information from the previous parse should be in the symbol table and file indexes already.  If a new symbol or reference is defined, that's fine, it's added to the table normally.  However, if a symbol is redefined it's ignored because only the first definition matters.  Also, this won't detect things that disappear.
    +
    +    Enter watched files.  <NaturalDocs::Parser> tells <NaturalDocs::SymbolTable> to designate a file as watched before it starts parsing it, and then says to analyze the changes when it's done.  The watched file is a second index of all the symbols and references that were defined since the watch started, including the specific details on the symbol definitions.  When the analysis is done, it compares the list of symbols and references to the one in the main file index.  Any that appear in the main file index but not the watched one are deleted because they didn't show up the second time around.  Any symbol definitions that are different in the watched file than the main file are changed to the former, since the first definition that appeared the second time around was different than the original.
    +
    +
    +    Topic: Change Management
    +    _______________________________________________________________________________________________________
    +
    +    When a symbol's global definition changes, either because it switches to another file or because the details of the current file's definition changed (prototype, summary, etc.) it goes through all the references that can be interpreted as that symbol, finds the ones that use it as their current definition, and marks all the files that define them for rebuilding.  The links in their output files have to be changed to the new definition or at least have their tooltips updated.
    +
    +    When a symbol's last definition is deleted, it goes through all the references that can be interpreted as that symbol, finds the ones that use it as their current definition, and has them reinterpreted to the definition with the next highest score.  The files that define them are also marked for rebuilding.
    +
    +    When a potential symbol's first definition is found, it goes through all the references that can be interpreted as it and sees if it can serve as a higher scored interpretation than the current one.  If so, the interpretations are changed and all the files that define them are marked for rebuilding.
    +
    diff --git a/vendor/naturaldocs/Info/images/Logo.png b/vendor/naturaldocs/Info/images/Logo.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..87395ec5e03d456ac72df118fce3f27946cdd768
    GIT binary patch
    literal 12405
    zcmV-*FpAHKP)<h;3K|Lk000e1NJLTq009gD002D*1^@s6Slw4I00004XF*Lt006JZ
    zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBVN2T4RhRCwC#
    zT?v>J)wMp=d-tsDtAGN-uCgm2f@lzph;fTi#FrS2zL-Uyd5MOYxV@Jc6D5hhBpP><
    z7*~wqg0d(o%OJRbY@#3xJG1soPj^?nd(K_zR&{kx&oD89U3~S?(_MAz*16~Y_w3hz
    z)*Z%ouX(t5;K1?IL!sC#pU*d(eHZongB6D1eS>8|;V2x1<8wN4&BJs$xsiQrG)?n^
    z=H~dbn>H<f`S-uOv>t!XyZt<`^WFH<S9|y9G4Q5PDEbY<Fd~JaDIA64@IEZdYHDt7
    ze7ve^(=Tqj^|a5}SLUGthHv!fv%;6m{`HN~Xz33P!xuA*mO4=wh{91gzUUH%K3^@%
    zj5jvc&U^Hc|GK%d^7%wA;K<dS|J-wb)@kzOIe(8til(#?!3rZ*I1cAESWc)`@wB1M
    zQntuC{{DPGqHWJ>YHC=#bm>DEKK0bi``MrA)&Pe8vdiZ689jRDD}g|00AA+R2Pf?c
    z;7~XY2Ql0P(@fLEPp4Dj@09kL|BeU0&i)&D+%&BgdEMvpiK%7KXP?inP6QCYKR~Bp
    z__fcze>IHclsfoawjh;i-dtHZ|0@qZ_`Pjjpb_u_ixDGE>o8)(^vC`F-~f)OR$i(`
    zL7*rc|Ly}Px18t*0&D`%nNBCgZxS4kr_<v9&!2I*6krgD1Oh>JhS?c{U@!uqP=tX<
    zsNEzJ-i(#kJjFSWbDv?rS?|C9>OnVXxO;>!7ro`yRrAWryL~g44&;7l0W1o~=LZV7
    z>L+R9BM{i2kW%2Fs}Y`30IujlU7$(p53)an1c3khaP{}ANzEkyOQ;yWoDvLwwxNJa
    zN-5Yk{2%*?z$L-Wt8YOD8o>Yqk8lwKkSKx5p-3U_dEC;xuCCtwm$^S3^F8*lQMYLr
    zF7OClI_GzjM~#~K5+kIq;3_Q~|88l)mJSdMlIrhNG68A!T{bZAF)`og3ka1UK!D(9
    zlg`J~03WFVelhv|3N$`nwa8Y5QmF*JPb#fTL#C$U1W4+LL`on`01|=x9h~`th(5oW
    z)Mc(+^WvEgJaE+__EobKQ35XMqPSw<#Gm7564R_~rl10N6pr?sK$ZZ91SAm%#KcJ^
    zo4ERbE47D6??*sjlOIKSCQ1uX;Gd;k0I!qce{OL4JQ>*#_6t123;>E8bTAoUB$^x9
    z`!_-&5hpqzSjOXZ1b~rnF~o|>8F&=88t5D#(Sj+afB*4w+5fB9sZ<j~lP)6Xo_q7D
    z6DQ7|-x^h<02+m(Ej9)T4*dTnT2&Do+*~Y;gk0ATlX#B$JqLxLP#26Nm|ms;js|FI
    zs)Od{MnxmEcf?}lO!6pYpTljEC|a85%a%Pd^QotPwvc^n!mk6GbWzl+*YK}O15hpy
    z$o5;ojaoR|qPMg!NQnUfLfRhO2TBTfa&X`VKn@OEz3>8tf0xnn^E`sVD4nrbM+O)O
    z9QEw1WgyZ>r{BL9qR}!aD(XNq0}pA;Yu@75Es+i}f&KTkP)pJhFi@^3?a;CJX=&5U
    zlNqdnl<_509V8Ige_P5Rh^wIu3{6VC$TaL3LX8MPh%tD8t&XHp2(a)n?wMS%{?d;@
    zkf|?t#)>*J6{Q;L>-R$vX<oM%>KhJ{#8F(_iRhV2k{}StgPe#7?7v^KvsMR;a9LUR
    zE|G9qUn`3)R{)4FH&EEtey3*wB?hTxaFxOYr_bL<z#t_CR4cgT5cJl+t%9VQ&*3uy
    zP+Zg*!l7aUjmE}WNT-reSGyPD4K+|w(wV?Rf^Vh`$numz+y`O;Vna>MuDvK_1jOiu
    zhYX!o!Pvr=Hq)(EMe<pZq>IU3p$uh^lCG*;QE_%Wh^4u!^dK5%TQYd&NY-yZKRr6d
    zMByeT{?;?btYOTWfC&@&Fo5vGMHhVqo_Jz8y!^^XB8DJF`m_WFUCq#egaW6?wP-3Z
    za*k(<8o3Am7+<<EM<Z`pc@Kz0OBrzNg9KAok_^NTR_}lYCV`ZecBL`#0%nKwk)7<m
    z;Ui8TyzI>fx3JIk5->uMaA|il-M+{!S0?TfYbNoO8@Kp`m(g!BXa!fIY(^~E7Qa8~
    zXiFlbG=yP$d<Ema_QKIxfJhU4o5@n2AiWtsmzDWo-reVV+A1R95+(|FX1a7K(ce9W
    ze66q?_ej?ww`(NH6TpM1%$$o8Z`u$GgJ0P=q<4hdJA64;&Liyz1!K^myf;Z32pkrp
    zErQ2>Hf}{lo!Hqit4F1!Ssx5UyW%5|oCp|5*$^AJsA-w)4j7#Il5pvqfiQ4Dx#xBB
    z=dXh|-rP+-=0G6g2^unT4;lH^TgK&l?yv8B7wYO5^8qqX`|I#h(y<xRIBc1dk5N+C
    z9uj1cM{q#bo&}b&O(6aE>}iHeX5S0tWq$bI|DDYMA}Ze7Pb(d|I5FVKf|d=DQjIAs
    z1-S0o@onz^r=K2x)vI?vQz8v3R_rFJMoKmkIKE60M-kdD*t;_og4>VMCe+mKB9*12
    zv<tan+aO^^1z`B3gyCnCF<_bP4H#?%!AZ_mj(h&FoOQYyrcUh#7hdROkeM@w!UrFG
    z2K)EdKqO+(fV&5pRwE`&q>|0>gCD#|qVcq8$G`;_@U|Kn67b}cm9TQ<9@5>=w`~M`
    zfF_ytVi;RuvQ;8IM*t23#+1^7?zP~j(`$h%b&w>r5UCjWes~X#;lYDDVe{slaQyN8
    z=~{A)BFIEiG)6`XF%OE4j5qe|j>9$A%x5cL8m_ov0vvNpw^qGR-@YB`#N(o`1HAn5
    zdRV-8J2W<j$g_ab&XMkVbsu5I`yI-AvT@o$V}=L1Wt;cK$sk%>+@)3YKx_c+7%`Mv
    zl?|ZJ%mx^3CAI}1lW?#Nv~)l7%-eAC$pfK#_l}NBFTZ>|+<o_(Y^7)*H6kEm>7`2u
    zEVY{9X|Xn+eQqW6>Rk#GCk&x>UweHMlrUmg;+eF_8SQ68LqJUlRcO|%qoGsB2;Bdd
    z^&)79_1EY7LIQ-0b!cvmEAij4IEY9`Zhw$=hlmP(B5*XNjlsv|4vnS+LL@QpYs@_8
    zZN~)~^#8712jHovmcjMce@$IeR~LsHZ@eG2ZL5X=>la3FO6g_zu-*(@y27!?_EW%x
    z$Moq#VA7;x;J*8oGwGw2R#fuB;M(}5vTa15w5%soSARweHoiNKV{PqTNHjMw$*voX
    zb3R5&#0H^>fRr!{_N9?E{@$_;O|3-?99+^haiXh<oY}FiUCS6~jD=tRatXhVsV6uQ
    zZ5NxR>8$}C;>*s4`bPTo<BxY^)IVSG4Zm$%Z8xFGqKTFi5qRZg0aB4rPsE8v;mDr3
    ze4m$UVdpH!fG9mEO4n2+4Y`rBNEj)(2=5+u&J+Azm33)2V<JdmFl|ptK9VY4XNZVL
    zK3)a^gnn$?_$eFLJ*0n!1wU{VDg~Q19e{P~s@OvD4)p2M0p`p(6-JG!;9_}E7{34g
    z$?)*Q%bB`z5F!@SdxGH%!|RcLpoCab+?9dGPKuUAX$$$IiAm?k#|Ro2Zr+j=1F=Ek
    zgyImBR)&0Lm(i+8#vkJ&-ImAp+lH5U^B#upeeY8B_t8gpg)`3_01Fm;LWszt`hwz_
    zw6@SH?vqh|8lX_JPyknXu16E1g4JRYQfuBNMIpO!_UvXj>Zmfdg6*eOn?F1FZ#A%T
    z*IIQ(LX$KG6F{JG+2Ofx0z`}BPvgkLu`?D1hRk?BwUHOixy*>;Nxb0?c=8&8F?s-^
    zky0985PXX240;2Q{yTTp!Q7uc0#{u%1HO9Z1a<eNmyU-|K7Em?y0xq+X^j1H8No4m
    zEmqWlf$?7QO2uLw$lHZ>s)GkVgW{4-WE9OQrD_X&K%d)aAZginXSEuoapm{>g2Epk
    zQG@o^zb<4bR|=P1HcMT5)>$JV9#6v3rMuXpkYNmMWxzFwO-^5kV(^317Jr2R`#az_
    zNj1O~5bu>a{_O!4v~o8mm_*S4<HvVrL=Mq+=xz)IqJSPIpYiXDAHerdr+5Y=(hFA(
    zcg%x(vRJLmpP=hKJfEaqpoC$H|6Ai41wZv2$JDLSVSmHn5(XCKWS7GCkt&dkB1LJh
    zDaa%|#$Wf|vjB>VA~0pjDAmT5S4@Ptb6+C<BR*6M(3j_kMLQ7Rq8$TyxU94%Q%!bL
    zv}jHBZuVU>l$3PI@S-S9KpW10V~vn2LyLyO))U;6x-A(8`FByq1oz$l0-SKdaWHuB
    z(dya@FB}ISuA47b216LGeCoNlnsNNF3;Rf)yybou@wT?V-_pcv-Iwq8NK~>^K@}A3
    z(a;(zO2Qdu43ownitqQMaga$E*e}_RQ&q6k1n_!>?4Q&7lKomckjnPd;OZM@AW>hx
    zm%V$MRu+^Zus^o6l|BSofRZ4*R7xqK<}YD5`OflKh~#^r4I&_dzI1x2?@`@A35DJM
    z``<qgqel;76M!dlb?a6J<4@=Tm6ZocA_dR%bjuZ-=IlT6o;%hfA649|y_RTf-|Mc|
    zVXNeJ?1_<Qe}E!G(LNY&JV<;}R^AhQbxfsiH~<at15A2Ou*JIv*{#53qtX|EfWAz)
    z4PosG7-TdvO(iD~V6c@V6pX?xw>$!O+;J83?Ab+K|DRV+hCA<kolVkufG`N=7d8-T
    z%h_K`MhMFHF!TwZa?~Ku)f7p5-H$H^L+Z<D6F_txF}xhkJ8vXo!gA?uq8Noxa}1Km
    z%$A{mP2OvY6l|VC9Z>3lY#*5dV@WcvwUeu`O*;-HrP94^hUV>|%7L%el&FDy=`B!R
    z-b>_Sgham}qaH)Pw6N)JX2eNwx|{aztuP$X^Mws0n{`x<wLad`{t*oLI6p;!)RgM#
    zCMLPQ23K4$Q(Ze`a94P1*=NLSl;NsWF-wh;90ME=38+q*_yt{fr7V2`b4sFS#+bUn
    zJr}>pF_y)CoI!1%Qt|oyA;ljp%}JETG0PPfy#}VfA}ugd6eX{^x;@a;SVt0SSy>O7
    zYnF3>JtPcQ5)U_4wx8(cRk@2BT#8wT6L9OTkH9_mT&sS=N%@`cOoZERTPRipL$lh;
    z{sOq)i!*>pp|3Iq1jW{}GCzzQ*#jm|J_fpUar&Z{F5LnfHtd77Yxk+}WgG}z#x2cM
    zkZ$nxuMf|7r;#HEz~BG=1NHZY4g2BVdsmD5k_-T&P*Ks1J+Fv7Lytec7FMj-BiauL
    zv6>qu2Mj2I-n~0PzkVHI%$PnfZ{FLmb!!8uaFTxfU7A=QQ!r^#Zx}M9D-0dl10H>}
    z5|%C7M%RoR*ALD;_c-X@y#wsoQ_EJ|6;M+X7Y0NAytdGS)7{FN?4|$K8n2YTf?Af*
    zrgrh&(1nUwo6%?kx#;y{!i2%_&_gQ-_;r<7@`ns@KlRk3p>yXLoOIGLj{8@w+D@LD
    zg$uWmff&_cENJ62qRJF~3?@uao7mo9239yu`mtcadf2|b7T$YrpD;K_ls8HSI1U(4
    z3WEmqg6Y#M9QR_}NoD0{OtQ5EFGXk?=VP0}1T~DuK&coPK(rAe%@6=<Yj=@7=-|Qa
    zBx&eLY*seFaEoHu0iz8niD^2Jn;stE`gJIjh8;U<m_+ecxbC`()bHq%pE+|7JpcS=
    z0s}OFA!s<;vRqyQQ;GLDxsWi^ruBjI&*ysW`|odOAT%HL?yaZm&pPWkxagu2+2rXD
    zix+Q%zx{0;)0<7DFC*G7S+W^kcwsr|eV1Q;GP`G@V5IeM(@lS4;%`?nCdvL)RCI*z
    ze}6&-4S0%j0~;g+KhCh@#&u=SAK`Ir9lN%XUCVPZBs0yMH$CH8WCC=Ted}8%!6~N<
    z(L7J3Fm`Mgq8~CizRRhn_G>jQka=)&=&Fn}YLnNxv}xZu3Bw*wBhxmbVqY><1$|}b
    z^(8R&qKHkZZLo9aUWdfmp+k_VxCpi}?v~eVApQFn!|d57GVm;cMT<USJop5oNdsLo
    zc5Gjw>DRtCf=%8*@W($cgUy@kL=~*C()(plAsQ1A{I9%nD%rXI`Omd*_ua2B(Vy4$
    z_~th!!HgMv@!>T7khNvf0>Y))Mz%N&V++gx1_)cJFMIaX30~>T_-_j9%dzmuC;J(=
    zf6T^*pdhaRMU+}`&NVml5P!+z2(}0kH8oWPs%Q)?E$yyC%5}i-<wEoJKb;Z*uOt*>
    zlY(d1En2hz9((K+$4j1l_82x`eF+t1K^5umcS=Nd|0U8RVvZl*RZWok`X;9OJPW&b
    z*C;XY>8F>&%P+r6zfYM`!9XfTgD&en%3>$d!=q!U^gzf0E>bG_@WY*q0q=+FuYcI_
    zF3rLvi+z(K=i<ei;F3%3WBq!@1sHLa3a*!U`;Y&4l)cYg@b0@?G~i9b>8Dpf-@YB-
    z+H3EJ)vGsY&y1^-=(f^=_t(_M(8?h}9@^<GVL*#~#Y^c0MUq<-Hg0rE85rCpYi{6)
    z69ajwU%xP;*%Shc#~xb-fBy4g0t;T8H^?UKCro928eV<%JpzDhubm3RhLsXMc{XlJ
    zl?KG`#*OPj?cue*{q2iPJ$i?X_=a%x-*e9bM*BuZV_Y!E7@3Td_hRbQ-UJpnE_dGf
    z0{g#KsUUB^y_tRYyoyngg>__6<Cba5y-xTM`OB_tY7uyFbtFvhg05Ixh}>i1bF31U
    z`7b*y?XzwtL6=&Py+o9)&_%_a;gLrc!^)NG9G(2eHyHYN^+97(Ei@x&pl*n|rp9E_
    zcqUH_u)={DJh-!Jr>bf{5t^zrk)bR^_ify`U0s9H0S+9x?5MAzk237piLnMR1t(m*
    z<mROSAs%3~fwhazlm{A?Fcn$$od+#(gXEH>?>XL?R239Y5m?A5s2d5>w1V31=q_;D
    zX~)6bpFIYfHt&R&URtGoTePT>(JT&4?41S=?$Rb&P_UZl2R|)W)itMEwQZ+Y;+f?z
    zrsm`?83smU6@*>84myo~u>iTn(`J&!7Tx2;#U@;R^-1*mnzdWu>1W<i?)d;liPFi{
    zN1W)g%{iBiXZNO98x1_QoVADaEn?qYcG(GZ4Fd17w>D6F+%OwahS{pB1FBzr`jk;S
    z*q=rQU}MJgrtddx+QHgwq<6#j;bRnp_dnP{U{6vFjY+yyboL9WBAIGpe9`Fm4cjka
    zk_Pq_#~dY&IFm%ONEo(Z>0ffnfF2z%&`yHkPN<eJQ4x)xMMa(A_S^r;RFu7r0sr1L
    zr$BKL50pwV(7+I6htX3rgyL_eK>b)Oq{S*Ua@Q`oWTRvx^cj8!LC}VPt8$v%%Z;=K
    z4+t{?cJ!$(VW4r6P999=7lC6uP|Xz)ndar!@P8SGiUD6_v~!8$WZ0DD>7+YT^u?V5
    zK4r>Cc;bmA5N}A5F>A$&PnjxrD^owNXN>n5qgkBV!d0&=w7`0kyf4}qI#wQOm~1-_
    zLAHc5E8jLOMIU?*IjQt$RHQvdJ>vT`vk5+FQXfVWuB*SX^aH_*A>u_Uz{_Y!pGhSP
    zFd8qVYteO!>IoVOn=#~?t=0)P+2>q3PP0?2BAy8NBZ9tBY7^<LgBgN0@)`zGVOugZ
    ze$u3&(C4T!9;JyX2xQ>pp#<~iuZHEzxAH>97PN}aJ`pL6JeX{5^7!2cCB@z7IVb_w
    zGSINBw9iAr_}8l>_Qo(y<14S35T-C*jOw$jBDneHha3ZrM!fHScM1h3ag`zIX{~pQ
    zV9z|WT>bv^)9;A<f{2JB^$U-N=`gn1s(_f(!aANA%OEpkL#k{XipAV(5l}HA-Yzpe
    zay6I{M5Vh<Z40?njhvB|J9qA5QpLLzwHb|egp!hOOuXy~9XlRHaSS-{_?)MoUU5jY
    z$fi297w@<wjJC9^yQJN1p<2b&ktR|fPCeB&WG`E`j_83j<RS9>426nCG;9>!d~?0J
    z{<PCZkSd6Piju6~pMYbJ?IS>-ks?%)w!A&wCbos+foji#4=#j<9(s*b7HP=GzDacj
    zkGt-=f-MFESbv-8-KgLYYwM+#HnF~J6M<-c<(13l<Z~^kq*$+Okk3m>y3;!g^*)g4
    zRt1}hJz~P!!As&*PEFT8{i=hpSdvK?RdCZy_rWiIak;v>fB)|AwXY3_C!c(eUfeF&
    z;%<*)Arg4~`h9Tr+5bcB;z|*Tl&SD(88+>#2xVz*t+eoF=s=f?slfp-d?Fei`Z#Dl
    zb$gQ=Z{1yUB+9UO7hqIYZct&~XbI=wW03`%3BaPrjR_xpbZE5D`_;Z)63Ag2fxO%G
    z<}q8MvR&P2=-s=RsTC2m%E#kr2D01(!oi+b5u=LZ0bU}nrly)ywu*|Q=)OUNI>DMX
    zyXm<ot;l2b>W#v-6%npa?h(V!xG18BW5I$AA~3B~g>2*BOP9V6XPjY6Dpy@K9nLwY
    z8lHcC4J=vmv09wC-OLVmZw<}45#3VN$-Tf8`&?4egVAP}gXaV86)isxNE_lX9DzdM
    z@N7tL2NDd4hwfH%O`*Z*(MK1+C6~-l_ndRiI6`nFplO<pB0YLr=$l5O;v|y9LBjOw
    z*8z?@t}g+~)Tsl>laT4rNo#c(+^1V&FdacU+1}L=cL1_2!x5~NDUFyO?Pm5vt{S|q
    z2+uH;M^RBTn=E^Xo=8{ADrqw<oP6Sr{xviDn?GMTJzg!*H+x%h%+e&0>c_5K)#`l^
    zK>POX>9FTW!=fRA0qr;pXRQ^qKS;np7c5E_*iUqw>)^ztMj0bvQagN6<RFD!7Ac|P
    z_ozN2zdro%B3c*+4eFyw*X8i-Z%<)r=JD|O<L@v)+9iNOFPWJCoRk2@rF2|${TYBE
    zRc_2|WuMF(ZdD_X*sVixt6EyLp6)WU)XQ+6dKp=OA9`pJ^ytwUrcE2G<p`WiE=?5s
    z>}~C&YcK5p5*fibYt|T;J)0Z2FctpUXDi{Ur`}=u;2UhR42B>5=weNDMgP3*%F|Ex
    z4pLrjKmA=ZRVMvfL%YuOSS{^qaV(tuaD`YKen;gpJJzL)YClqXGt*fQg%-}p)%JC(
    zW-FSLpIg<IhPcNf2?GcAa{Rhw%PuxKJBYfYNiu@kc7#S1>m$d+8C`2A8soLL%2rBV
    zFYIG17W9a7G8Ap>NKK3x@hQHQk|ny?k?-s48{nF2?xltCs;ka%yaTF;Km6h8@YY)!
    znM$*QjVF3Nd_p4dwQ62spJV|lpU?0F3}x8&`O_XM$-lOiqPJM-t$!jqqdz?wO^Mip
    zXJPQ*zR;_e9RTy=AD;);U;js;mVhH7&aUKyNn#~%!wnZI@$(OVSOBlR##7SKZ*E8J
    zd4=_6_AhhCH?M1Qxd|aJm!h}*$8r?$apzq)B3`xl1e7|^_S3UxIox{d>^9TF*{}8u
    z+&c7tFiW|fG0sIw0X(2?8>t*6B|Z(LEYC&IemD2;nRXFr6Uk?+^)PeG;yf-FWqFNU
    zDDgcI#Q*lU*WtnoPli)Y8SO}&m@uJ&Ng-Wf?%Wp`U>PE|q&17U0dk*M8RVE9FbL=@
    zPq(VK2;Tl@?q^EXgsT%65S30x;O9So0BUO+)V)1>c4h>-n5sqT2}x4aLgX(kH5gc&
    zp(Y3-_bacgb&2npThQgAMRv8p`(4~Cadx^`o(594sPC;BLDoJXeNbxa)F}+N+;Z_D
    z(!yIcI8@%@@9166GdF!rrGm&E#LyNqZQ3wL8%vg~6*_-Ft@?T@eMw2Qg=bivV}z=V
    zmpWBZ(M#}SN`;C$4cL}TjkAr~sIs6P3d<d$@%ou(R>GB6{swNm@m|=mV~<1HIQ{g2
    z-0dw2F}G&^NxJ2CpO;(Jk}k2da<O3cYP-8u^<XQ_73y}ep@R%vTv$B_v+cHTuY)`8
    zc+@dqg9rCxdfiE)t+aBdqMHg`U86_$P!nYF;x**k#;i!%=Rvr*XwzHpc{6E7zq~B@
    zR*rYg0x;QJi}|#xT!>z7P3Lv8LK)I#;>0100b}h$3lDGuEsv+|{E<b<%6VU!_*A+k
    zL%|fxm@!IIHyYrj1*>^fCK@w=m)CF5yb(QmbRxbng@IR}1CoxG_`XLEyYSPlssltX
    zY_n?Ten)$eut6?SQxq%8>~orOH}IOY+#ZoV|I9Onz<~Zes75NLonr)MDFcq>?|cm3
    z`qu5Re*IQk(l~J-@wwaWov$0({hpa~KCSV+Tf)#XN@XBT_Oqqmy-&9#Jo~jW?7&Zz
    zgF=jWu~;W~`|YhvjDFHn$V$_8di;J2(7~XT5hHp##8Qd`q?(U03d-?QXm2p1QkE1;
    z-F-5>(z+Eb+<|J~IRQuv)UHp9YE`LpDr<&sCT*-NS)a~((o`WVMZs>PMh#Tge)Q3&
    zheV4^$qCxT5AHLY%94u@@f|UEN(MfC^{iuQ!ph_S{%<~ee?NITWMCb><GX7<(Iku*
    z^gFsU5A4E;Y`S^f9;1p1o2Rf18L(HS#a3W@C!9Ed=$^#5k91m_Gba5_`>3+BXRjhr
    z*Q1em_T(u8V9eN~DEJS@CK|<n#;$axQV}Lq|4CQER3IbLAzwQ4YfTX}2rbTW%RAE3
    z^b}8=W#HTE)UDd-tYVOESq>B8c7P|Jd<zyXT<&Qhm$nO8iCSObv0yE)Fl}$ICq;Jm
    zMmsX4U1l#zu^ETRSry7oaC7e>F2i**4SXrBk9KX%^UgcL!<%Ad5#L+#zL=WGnN<FE
    z+j9~;BwFNF&02rjsr{y>T|)PMNHJQ{o5w-aLOgzu>H8z$f(uSkcR%;sQl@sjN4z5w
    z{-kTVu`vzv=Py&&qPK|0oHRS$)qWPnS@c#?+Vju9#hz;^b#B?Rj~u*l{`r#_-=b>V
    zpki6<JzWx_3cCT<T{j6vj_fMlqn?bX!-jPi##GxCi!Sm=q?p3i^`I}Wv1$3#!x5~U
    zbKRZMoS~A)2+H<VO{BWORR*~_MWX@!orf4G#U?7T;^UE^k#HH=G=BS=r(pfMk3G}p
    z@gDgO8#dTs&Qqrz&j{|PC{Yw$6Cy&dZ=Z6w=%P~`D_WX%({AFEKgA<6<>u7&nw1<~
    zUlgH-W%yZpm_<7ME61psnb`N8ny5NpKu=zkn<4`nC~6fW1cwgo;_$sI=|y@)(rK4?
    zoS9jyzqdog6m->dD^8g@n$gnNMp~pY)d$Hto@1E%G}HFPbf>uEGk72I<bC8Pr(0vZ
    z8sZHHm_F}^8*Z4vK-W(Ad+yn#Fz@d95RH^E-YMcyx}-kvVos=rJ@NN9)b(eaF@{}-
    z(T`ljm+AtlbbO5CaPGM$s)_oKC*EXj#3+Ij<1O%f?6Frh8(Sv^24}MV9fYRFYU-~X
    z??h8Al$BcWqaRJ9{E#=_SWoYXJcKHA|Nh-bHNgTmDaJ1{+S4HSx2<k1S^S=g5^hbD
    zt>Pi=9xqiyMN_w=lGx&nd5B`BDa2wff?@o6C=5yu?9?erVpx(yw`LOgGM`CN6_o3M
    zTf||aTH1`l+@Ia=FoMd$IhJys`(gF!E!qHffuH>3TqrH`(FQM=SaarAhr!+RzU>G@
    z#}y<TVE}VV1&kiulSE2W#!Rp_tAsyaUVG`BDb$ufkbtYNo&b?34=9r=6$bIhW9H0>
    zFm2iph%*Mlv$PE0o8OuM|NY;y;HN+RlWS#9k%&y`()3yUo#wd^I_lGgy_9>B5Tb5M
    z%g^v-(4trGZt&w)&|=EeflQ_8PGiNz?1W3T<Z3~R#P5_6d2_3@rg|qHIF^8}-JEeC
    zg9i15VZ(b8ts0mFfrVb`8~AzbaUEg$>HXoh+s}qy|N3IKO4}*v_uTU|dlv>4v5ss!
    z%1GrhWD#I0GZ>`r_8&;Fd;hNN4wuZHL`7yWFGS{d;Ai{}V<1M28bDxi{q=uh_cl`w
    z6ff+<BSio4k7Z2VvsE3u{+GY{IvjU=XV&Na^bUrv2~M9;0rP(M4T{&e_ukhOJvwWp
    z2IgEgl}SvaS%2g7{!NKGn03w=`1#K-Q_;N(URtAy0wJ8_J1@D4WQjQso~nqh;#e;K
    z3P$<#8#e5$Ib+9OdP9Z-#xOKre{170Y!@f4!aiRkTzTcmFnRI_5fQhE5o{rw_&y4o
    zb?(M(c<BE;6t}VQAQk;M;e?~%)?2R>Zq~oBRkWTg%y>U$B4WVOtXZSrJKs6mvv-(p
    z@SEQ}$;8Q3@bb%dW&HlwV{gNP1)Isb&Z}$Hu;=cE2Oju7d1>6omMyzke_o_WJ`@o#
    zMV&nbe*gQcpjWRho_)Z)f}3xC6xOa?$NF)v`u_39F>H7hg~}r{^5C8XtvV$oDY)&n
    zOH?0_iGT8whZz$!)BB?#Q+hjak5f!#=Cl~eXfdlzw0Pu^WlV%$OA=DAUQzhNAFg)T
    zhvv@3iC03wVLSpcS6qOeRh%F?m6VtaIA=NnmRfr}_uLyqw|)B>i4M_dIj<TXh&YNC
    z@ig=}xtfuePCRh{lLpQwfLya?D?Iz`8d$e(H_@<j=Lmtt^y$Yk{@MX|-SsqV-Fkr9
    zM;9onBM9E8PBb)N09XT~#U(Iq+|ZnT#W3?b?)WE-l}v39G70AtworZh+cR?Bi}b$v
    z=EtF?#-t6JQC(?Gz0!DmKdErYSLlh6Zq{s}6)RUheEWwVK99K?pJ;&b)j4Cv%)Y_X
    zYnv4`^4Wia`^N9Q^K1%q$#&r6Sg>HD2z}08%?JsLgM%-OgMI$_6B!7d1vlRKC?jMw
    z>8m)ocuBp3G>FI$GiOeKv15m-K%FO^cmrO1v66vR9i<k}m@%3$>})Y0+bLk|qmK>>
    zrr`A`5cII5S!t;WmtQ`e4el7#yZi5dg)!_JVo({)hkd|)mX%@I!IR+ZvnQ+Tk@?<y
    za~-WpJij3hix&MRgDwjfu7d|3Tt&<<WJo6xy>lJ!zrO>1^{d4yUx0&kT+w3OxT6>?
    zCQ(_$d};B(1FysS^?RYBq9<eWZ)ANoret4t-E%6(A$x3qJ(hFkjAByP;5GpS0;N;~
    z5GZ(RdyK{aPn;08?Lv<~Fa>Bd(87ZCArQDOyl@Hwtln_aNlv#c=INkH_u`AI9M8c2
    z?ZQM&1m>JCt6|cl!K6Ou0f(GVUS^8}Rzu&Xc4fylc!)j+F;s2OIAbikf20FI(YsYy
    zxslD2N?KqMv@o=s*KG~t9k^9rznkU|0!=K|IVV`)v1-*L3^4xb1dO3W&zv)Q)WtXG
    z;NbkP^`IeR_mP++;?WlM^|VS+bX$PCO>vSV0i2uk8s3a%aTgl4gDVrZb^ZktVfi~-
    z;o!lvSd9Y;Seg>G{5srdhk=(*1Ts+uqL@@^QAh=u1`ixWta}(?gMo}8L#^d%h=Yp%
    z^Xm;tJr0L-CXLW!L0bm(fk`Iz^`P<$R3A`Gq_*pck+IESC`$S_(nd-Q_*<g6p6E#R
    zK~g*;libf?zPj^dshBF?CRbER3)K5-YIYEPc=`U)oU}lZJ50$}F}|5mns1$mx;Uyo
    z$QKlFgez{iR1_bMWaPVeM`b1{g_~>=M5kIx#ivYRu!LbYB+BafJ@TBK<?8sZXHo9?
    zQQKweV38T*h0jk?&5tInM552$n931!6%f1=RU!DYd?GJ?(<DHsmd1(4N2GWLt;j8+
    z&7lG!Lpa1;+O%7oX)69g0F${rDs5l9t6$Xn2#ayHlLzyw?MZu+)K!tq1QQhzbflNf
    z84qZKNagCsZeO?V1p*9J+>i}e6EEQEk}NF46(-KV%Lg=godjO^mxzVCk%{^jasY@S
    z0(=u7X93^rFE48h;3Y`>`Sagbo9}Qv75HXI$jAmywn>E&iXr&GhqxON^}mn_A+bRb
    z-}9C4*+FIPwW7s1=-77)_;clcACi#*V&WM>t)e|B!$rK1G1eFmy1$+K5mFN?kmmIl
    zxW(HNW#=MFN0oIsNCajR6Ky&+gY)mnHR=zDqQ8O(JZO<7)vkek$H1Dz+1#{{1%eE*
    zT?DUg6;?%sc`{U3(Myh@kxL~JOl3&dIn@>kLO5Nbg_k=E-eyX^b~2t!#*#Eu<s2bP
    z<fg%&7c}gx9_2gRQnkGWri?)Lt1T{gZ3|V$1IH96v*ORF0t}faW!LevlgA7pQ6shR
    z{ty(4`WIruDJ!kLt6$NYBqzU|H&&h%@-2IxV>HTR6*VjEitfyeT19M-jR&N?aR2@-
    zd%f}*B*82PNVxyb1{{_kmLbBu4JE?KnxS^~rB4%!T1uud1d~LlUCG$`DkLYR{p_e?
    z1#xD5A|WHng`^Lzt{3toBwWo68gQ@c?F1%S5LjLxR+*BL0cBb_0Y-pt5$W9rLluAJ
    z*_3{zCh5#1)89D_Oh;C%P75q4i2Ime8z`CR{r0AXUsa5feZs!Vq$l2fK!A>+MKNXv
    z41IGXS?W%{Q%k$J+Z!$0<ES=S(uy3l+>Z14vJ|4TFtSfo`IhAkWd4V<eM^lCj=Ns~
    zx-8`DrjwoQ=2Ic#hS(qquaW%XHCKGDJ$_o=_1^wXy8U^3h8E_KJ@g*2K@OS(jC6JN
    zXVs03H4RLxE^^y0^nvFDw6yPa3B)VB*{*bK=i*x$!06@e4J}1IH(E;K>l0e$LD&NX
    zYEUdI=;)8LM7yPc$Jv%o#rnH5L-_=9Cb;P<p`IIKUmtRSFuz{+jQCw8<s~@!lv7Ji
    z?#vBBeA=$&kdeix(*i|N6nVLyKOpw9%}xvbH_Jk1wOr44jw63ZGpEs0pRjT=<e#)=
    zc+kh^uv6r}CygN{Yfxozx+jhe{yvSi-jh^<GOvLebn%W}tHY<}jI(T|Ur96WzGQAM
    zkPlcm7k^Z}Fb>1-=f#8VTo}{CmSLd%s{yeAF+v9nl&zcg?c21qXV2q?J0Id!Nlfi~
    zojG%>DDKITD-o#`DQy|`Ch)zyRyKQ8aud<YxW@j?%(mU6r$stf|Gl2?rdu`|Ics7V
    zdC(DZrh>~V+er~$fjWOKS~#;}b3eZ&x_P%LYo+?znAq6bXXajrvX4RQo^hycWxqe)
    zd6Ibzj=3r9CH3`HTFqF~V-Yg2An*>v27wWlgmtvEHEi3q;{6`ohYxo$v6`Cd4?|-f
    zE9WXuIKHR{=2g(nZHWd5he`?rIpPk>Ga4EVyl7xe3!|^R)DRnkIdLcrRaI-&A~pz&
    zFr|cn2vW0s`>OYb4V!sZEY_(Q^mJ~ims(Qb(?!o*QbhqYzNiP<LZo*FSDlQVybH(S
    z3l&)kGVxN426^&S?G&HiR+B8JfhQi{U%z9=nl*?G0wXNxB|-G4VGUMo_~_-8#~=Uo
    zscu(`p|&Z6tYG0)>Z@>kK_v{a{U6V5Xv`=PSU8SE05QmZ<pnItqXza|-@NJdl?)Sh
    z2*4n5L;?otCurE%x#i;}Z};tc!pQRSqssM0Er$`*DN#7TFC2#tX!v=#`R2IVVXbg{
    zStJk3^LuG#sIJ~#jaYz~Ah02+1BNN|lqz;^eP{WfUpaa5f1MK!7YFsmoj_q5Lv^EE
    z;W(TCBS`wJ3=)#A)53A&+A}Q6{hiauiQth))F%-G5DNq*Gzg4<fOZ##_MyM^<9PhQ
    z2P;?prTd8|UU?E@D#PnRPVb13)0R;<zL*EP5^?-eB5};`FO1!h3>MzN0or_~d@YO3
    z{kIVV5DNq*B;?J0S8IqO=x_DM8xQ#Q@81&W(sgiGzdz)c^&s@TP_KWy{9oZb98UC{
    zVrVo*e)<DJk&jTI%aL?iVpll+&;Fco9a=RLwaFDL?yF?;9#s+CGjjtw_e=YQmpFg`
    zf`MCDdmgis#>8SBM~xbF$>{R(KCw*1Do7QF`@uJcs;5{C%zP{yUlwV@Id`k8x5Zbl
    zezcNF7OUBxxYs7SQ$H2zNIKU{N4o_&<}pnUgSd`o=b&CaM-Hv1IIVXq);W~@;RO&m
    z+z!l;WK2T&3&llU@=lR19ET$~WX{icd|&g%jjw*TW9Pf;ghYXER8&W{u@mbvBwL!*
    zhOliQXyE1!73{>k@B!@X-L2cOzFj(3bnMz~Xh|eo8Ym3R;eB9@k@|*xB)TGC=$jxF
    zjwAD6sz++i?sav0_ia3|d-sPZMPNC$&Fn<bz=}^h1X!fp$YbPy9|Qy3o~esaQLv;D
    zZjOLeP_fD?7Pp9$l=LVL1j7D;NO2giMHEX<VfI#G7?0dxPNfp*y1J@*;6Yfm!ajju
    zf!>ghg$ja3&PFe=$VYELAP5U-1A(IlJ8_S>ZaQcHPY*1}vW25?e1U~Yaq3{P8+e>W
    zl>iEqDiXTQqAi97DQzHdph2`;00o{g0T=;jYvxelC>(|3^ElE%YCyn<3(&w5gR&X~
    zP^4NiezbOfHIyRIC>jy~B0t+3Qx=ZGQ8>PE>Im3j6X+sM3V@KGO~|hnz#&g?p9Um_
    nqi__CFTPqLful{w{|hhxFW3;f{4<(r00000NkvXXu0mjfEp_Uy
    
    literal 0
    HcmV?d00001
    
    diff --git a/vendor/naturaldocs/JavaScript/GooglePrettify.js b/vendor/naturaldocs/JavaScript/GooglePrettify.js
    new file mode 100644
    index 000000000..fda4bf1ed
    --- /dev/null
    +++ b/vendor/naturaldocs/JavaScript/GooglePrettify.js
    @@ -0,0 +1,1526 @@
    +
    +// This code comes from the December 2009 release of Google Prettify, which is Copyright © 2006 Google Inc.
    +// Minor modifications are marked with "ND Change" comments.
    +// As part of Natural Docs, this code is licensed under version 3 of the GNU Affero General Public License (AGPL.)
    +// However, it may also be obtained separately under version 2.0 of the Apache License.
    +// Refer to License.txt for the complete details
    +
    +
    +// Main code
    +// ____________________________________________________________________________
    +
    +// Copyright (C) 2006 Google Inc.
    +//
    +// Licensed under the Apache License, Version 2.0 (the "License");
    +// you may not use this file except in compliance with the License.
    +// You may obtain a copy of the License at
    +//
    +//      http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS IS" BASIS,
    +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +// See the License for the specific language governing permissions and
    +// limitations under the License.
    +
    +
    +/**
    + * @fileoverview
    + * some functions for browser-side pretty printing of code contained in html.
    + * <p>
    + *
    + * For a fairly comprehensive set of languages see the
    + * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
    + * file that came with this source.  At a minimum, the lexer should work on a
    + * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
    + * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
    + * and a subset of Perl, but, because of commenting conventions, doesn't work on
    + * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
    + * <p>
    + * Usage: <ol>
    + * <li> include this source file in an html page via
    + *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
    + * <li> define style rules.  See the example page for examples.
    + * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
    + *    {@code class=prettyprint.}
    + *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
    + *    printer needs to do more substantial DOM manipulations to support that, so
    + *    some css styles may not be preserved.
    + * </ol>
    + * That's it.  I wanted to keep the API as simple as possible, so there's no
    + * need to specify which language the code is in, but if you wish, you can add
    + * another class to the {@code <pre>} or {@code <code>} element to specify the
    + * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    + * starts with "lang-" followed by a file extension, specifies the file type.
    + * See the "lang-*.js" files in this directory for code that implements
    + * per-language file handlers.
    + * <p>
    + * Change log:<br>
    + * cbeust, 2006/08/22
    + * <blockquote>
    + *   Java annotations (start with "@") are now captured as literals ("lit")
    + * </blockquote>
    + * @requires console
    + * @overrides window
    + */
    +
    +// JSLint declarations
    +/*global console, document, navigator, setTimeout, window */
    +
    +/**
    + * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    + * UI events.
    + * If set to {@code false}, {@code prettyPrint()} is synchronous.
    + */
    +window['PR_SHOULD_USE_CONTINUATION'] = true;
    +
    +/** the number of characters between tab columns */
    +window['PR_TAB_WIDTH'] = 8;
    +
    +/** Walks the DOM returning a properly escaped version of innerHTML.
    +  * @param {Node} node
    +  * @param {Array.<string>} out output buffer that receives chunks of HTML.
    +  */
    +window['PR_normalizedHtml']
    +
    +/** Contains functions for creating and registering new language handlers.
    +  * @type {Object}
    +  */
    +  = window['PR']
    +
    +/** Pretty print a chunk of code.
    +  *
    +  * @param {string} sourceCodeHtml code as html
    +  * @return {string} code as html, but prettier
    +  */
    +  = window['prettyPrintOne']
    +/** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +  * {@code class=prettyprint} and prettify them.
    +  * @param {Function?} opt_whenDone if specified, called when the last entry
    +  *     has been finished.
    +  */
    +  = window['prettyPrint'] = void 0;
    +
    +/** browser detection. @extern @returns false if not IE, otherwise the major version. */
    +window['_pr_isIE6'] = function () {
    +  var ieVersion = navigator && navigator.userAgent &&
    +      navigator.userAgent.match(/\bMSIE ([678])\./);
    +  ieVersion = ieVersion ? +ieVersion[1] : false;
    +  window['_pr_isIE6'] = function () { return ieVersion; };
    +  return ieVersion;
    +};
    +
    +
    +(function () {
    +  // Keyword lists for various languages.
    +  var FLOW_CONTROL_KEYWORDS =
    +      "break continue do else for if return while ";
    +  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
    +      "double enum extern float goto int long register short signed sizeof " +
    +      "static struct switch typedef union unsigned void volatile ";
    +  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
    +      "new operator private protected public this throw true try typeof ";
    +  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
    +      "concept concept_map const_cast constexpr decltype " +
    +      "dynamic_cast explicit export friend inline late_check " +
    +      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
    +      "template typeid typename using virtual wchar_t where ";
    +  var JAVA_KEYWORDS = COMMON_KEYWORDS +
    +      "abstract boolean byte extends final finally implements import " +
    +      "instanceof null native package strictfp super synchronized throws " +
    +      "transient ";
    +  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
    +      "as base by checked decimal delegate descending event " +
    +      "fixed foreach from group implicit in interface internal into is lock " +
    +      "object out override orderby params partial readonly ref sbyte sealed " +
    +      "stackalloc string select uint ulong unchecked unsafe ushort var ";
    +  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
    +      "debugger eval export function get null set undefined var with " +
    +      "Infinity NaN ";
    +  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
    +      "goto if import last local my next no our print package redo require " +
    +      "sub undef unless until use wantarray while BEGIN END ";
    +  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
    +      "elif except exec finally from global import in is lambda " +
    +      "nonlocal not or pass print raise try with yield " +
    +      "False True None ";
    +  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
    +      " defined elsif end ensure false in module next nil not or redo rescue " +
    +      "retry self super then true undef unless until when yield BEGIN END ";
    +  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
    +      "function in local set then until ";
    +  var ALL_KEYWORDS = (
    +      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
    +      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
    +
    +  // token style names.  correspond to css classes
    +  /** token style for a string literal */
    +  var PR_STRING = 'str';
    +  /** token style for a keyword */
    +  var PR_KEYWORD = 'kwd';
    +  /** token style for a comment */
    +  var PR_COMMENT = 'com';
    +  /** token style for a type */
    +  var PR_TYPE = 'typ';
    +  /** token style for a literal value.  e.g. 1, null, true. */
    +  var PR_LITERAL = 'lit';
    +  /** token style for a punctuation string. */
    +  var PR_PUNCTUATION = 'pun';
    +  /** token style for a punctuation string. */
    +  var PR_PLAIN = 'pln';
    +
    +  /** token style for an sgml tag. */
    +  var PR_TAG = 'tag';
    +  /** token style for a markup declaration such as a DOCTYPE. */
    +  var PR_DECLARATION = 'dec';
    +  /** token style for embedded source. */
    +  var PR_SOURCE = 'src';
    +  /** token style for an sgml attribute name. */
    +  var PR_ATTRIB_NAME = 'atn';
    +  /** token style for an sgml attribute value. */
    +  var PR_ATTRIB_VALUE = 'atv';
    +
    +  /**
    +   * A class that indicates a section of markup that is not code, e.g. to allow
    +   * embedding of line numbers within code listings.
    +   */
    +  var PR_NOCODE = 'nocode';
    +
    +  /** A set of tokens that can precede a regular expression literal in
    +    * javascript.
    +    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
    +    * list, but I've removed ones that might be problematic when seen in
    +    * languages that don't support regular expression literals.
    +    *
    +    * <p>Specifically, I've removed any keywords that can't precede a regexp
    +    * literal in a syntactically legal javascript program, and I've removed the
    +    * "in" keyword since it's not a keyword in many languages, and might be used
    +    * as a count of inches.
    +    *
    +    * <p>The link a above does not accurately describe EcmaScript rules since
    +    * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    +    * very well in practice.
    +    *
    +    * @private
    +    */
    +  var REGEXP_PRECEDER_PATTERN = function () {
    +      var preceders = [
    +          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
    +          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
    +          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
    +          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
    +          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
    +          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
    +          "||=", "~" /* handles =~ and !~ */,
    +          "break", "case", "continue", "delete",
    +          "do", "else", "finally", "instanceof",
    +          "return", "throw", "try", "typeof"
    +          ];
    +      var pattern = '(?:^^|[+-]';
    +      for (var i = 0; i < preceders.length; ++i) {
    +        pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
    +      }
    +      pattern += ')\\s*';  // matches at end, and matches empty string
    +      return pattern;
    +      // CAVEAT: this does not properly handle the case where a regular
    +      // expression immediately follows another since a regular expression may
    +      // have flags for case-sensitivity and the like.  Having regexp tokens
    +      // adjacent is not valid in any language I'm aware of, so I'm punting.
    +      // TODO: maybe style special characters inside a regexp as punctuation.
    +    }();
    +
    +  // Define regexps here so that the interpreter doesn't have to create an
    +  // object each time the function containing them is called.
    +  // The language spec requires a new object created even if you don't access
    +  // the $1 members.
    +  var pr_amp = /&/g;
    +  var pr_lt = /</g;
    +  var pr_gt = />/g;
    +  var pr_quot = /\"/g;
    +  /** like textToHtml but escapes double quotes to be attribute safe. */
    +  function attribToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;')
    +        .replace(pr_quot, '&quot;');
    +  }
    +
    +  /** escapest html special characters to html. */
    +  function textToHtml(str) {
    +    return str.replace(pr_amp, '&amp;')
    +        .replace(pr_lt, '&lt;')
    +        .replace(pr_gt, '&gt;');
    +  }
    +
    +
    +  var pr_ltEnt = /&lt;/g;
    +  var pr_gtEnt = /&gt;/g;
    +  var pr_aposEnt = /&apos;/g;
    +  var pr_quotEnt = /&quot;/g;
    +  var pr_ampEnt = /&amp;/g;
    +  var pr_nbspEnt = /&nbsp;/g;
    +  /** unescapes html to plain text. */
    +  function htmlToText(html) {
    +    var pos = html.indexOf('&');
    +    if (pos < 0) { return html; }
    +    // Handle numeric entities specially.  We can't use functional substitution
    +    // since that doesn't work in older versions of Safari.
    +    // These should be rare since most browsers convert them to normal chars.
    +    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
    +      var end = html.indexOf(';', pos);
    +      if (end >= 0) {
    +        var num = html.substring(pos + 3, end);
    +        var radix = 10;
    +        if (num && num.charAt(0) === 'x') {
    +          num = num.substring(1);
    +          radix = 16;
    +        }
    +        var codePoint = parseInt(num, radix);
    +        if (!isNaN(codePoint)) {
    +          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
    +                  html.substring(end + 1));
    +        }
    +      }
    +    }
    +
    +    return html.replace(pr_ltEnt, '<')
    +        .replace(pr_gtEnt, '>')
    +        .replace(pr_aposEnt, "'")
    +        .replace(pr_quotEnt, '"')
    +        .replace(pr_nbspEnt, ' ')
    +        .replace(pr_ampEnt, '&');
    +  }
    +
    +  /** is the given node's innerHTML normally unescaped? */
    +  function isRawContent(node) {
    +    return 'XMP' === node.tagName;
    +  }
    +
    +  var newlineRe = /[\r\n]/g;
    +  /**
    +   * Are newlines and adjacent spaces significant in the given node's innerHTML?
    +   */
    +  function isPreformatted(node, content) {
    +    // PRE means preformatted, and is a very common case, so don't create
    +    // unnecessary computed style objects.
    +    if ('PRE' === node.tagName) { return true; }
    +    if (!newlineRe.test(content)) { return true; }  // Don't care
    +    var whitespace = '';
    +    // For disconnected nodes, IE has no currentStyle.
    +    if (node.currentStyle) {
    +      whitespace = node.currentStyle.whiteSpace;
    +    } else if (window.getComputedStyle) {
    +      // Firefox makes a best guess if node is disconnected whereas Safari
    +      // returns the empty string.
    +      whitespace = window.getComputedStyle(node, null).whiteSpace;
    +    }
    +    return !whitespace || whitespace === 'pre';
    +  }
    +
    +  function normalizedHtml(node, out) {
    +    switch (node.nodeType) {
    +      case 1:  // an element
    +        var name = node.tagName.toLowerCase();
    +        out.push('<', name);
    +        for (var i = 0; i < node.attributes.length; ++i) {
    +          var attr = node.attributes[i];
    +          if (!attr.specified) { continue; }
    +          out.push(' ');
    +          normalizedHtml(attr, out);
    +        }
    +        out.push('>');
    +        for (var child = node.firstChild; child; child = child.nextSibling) {
    +          normalizedHtml(child, out);
    +        }
    +        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
    +          out.push('<\/', name, '>');
    +        }
    +        break;
    +      case 2: // an attribute
    +        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
    +        break;
    +      case 3: case 4: // text
    +        out.push(textToHtml(node.nodeValue));
    +        break;
    +    }
    +  }
    +
    +  /**
    +   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    +   * matches the union o the sets o strings matched d by the input RegExp.
    +   * Since it matches globally, if the input strings have a start-of-input
    +   * anchor (/^.../), it is ignored for the purposes of unioning.
    +   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    +   * @return {RegExp} a global regex.
    +   */
    +  function combinePrefixPatterns(regexs) {
    +    var capturedGroupIndex = 0;
    +
    +    var needToFoldCase = false;
    +    var ignoreCase = false;
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.ignoreCase) {
    +        ignoreCase = true;
    +      } else if (/[a-z]/i.test(regex.source.replace(
    +                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    +        needToFoldCase = true;
    +        ignoreCase = false;
    +        break;
    +      }
    +    }
    +
    +    function decodeEscape(charsetPart) {
    +      if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
    +      switch (charsetPart.charAt(1)) {
    +        case 'b': return 8;
    +        case 't': return 9;
    +        case 'n': return 0xa;
    +        case 'v': return 0xb;
    +        case 'f': return 0xc;
    +        case 'r': return 0xd;
    +        case 'u': case 'x':
    +          return parseInt(charsetPart.substring(2), 16)
    +              || charsetPart.charCodeAt(1);
    +        case '0': case '1': case '2': case '3': case '4':
    +        case '5': case '6': case '7':
    +          return parseInt(charsetPart.substring(1), 8);
    +        default: return charsetPart.charCodeAt(1);
    +      }
    +    }
    +
    +    function encodeEscape(charCode) {
    +      if (charCode < 0x20) {
    +        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    +      }
    +      var ch = String.fromCharCode(charCode);
    +      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
    +        ch = '\\' + ch;
    +      }
    +      return ch;
    +    }
    +
    +    function caseFoldCharset(charSet) {
    +      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    +          new RegExp(
    +              '\\\\u[0-9A-Fa-f]{4}'
    +              + '|\\\\x[0-9A-Fa-f]{2}'
    +              + '|\\\\[0-3][0-7]{0,2}'
    +              + '|\\\\[0-7]{1,2}'
    +              + '|\\\\[\\s\\S]'
    +              + '|-'
    +              + '|[^-\\\\]',
    +              'g'));
    +      var groups = [];
    +      var ranges = [];
    +      var inverse = charsetParts[0] === '^';
    +      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    +        var p = charsetParts[i];
    +        switch (p) {
    +          case '\\B': case '\\b':
    +          case '\\D': case '\\d':
    +          case '\\S': case '\\s':
    +          case '\\W': case '\\w':
    +            groups.push(p);
    +            continue;
    +        }
    +        var start = decodeEscape(p);
    +        var end;
    +        if (i + 2 < n && '-' === charsetParts[i + 1]) {
    +          end = decodeEscape(charsetParts[i + 2]);
    +          i += 2;
    +        } else {
    +          end = start;
    +        }
    +        ranges.push([start, end]);
    +        // If the range might intersect letters, then expand it.
    +        if (!(end < 65 || start > 122)) {
    +          if (!(end < 65 || start > 90)) {
    +            ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    +          }
    +          if (!(end < 97 || start > 122)) {
    +            ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    +          }
    +        }
    +      }
    +
    +      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    +      // -> [[1, 12], [14, 14], [16, 17]]
    +      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    +      var consolidatedRanges = [];
    +      var lastRange = [NaN, NaN];
    +      for (var i = 0; i < ranges.length; ++i) {
    +        var range = ranges[i];
    +        if (range[0] <= lastRange[1] + 1) {
    +          lastRange[1] = Math.max(lastRange[1], range[1]);
    +        } else {
    +          consolidatedRanges.push(lastRange = range);
    +        }
    +      }
    +
    +      var out = ['['];
    +      if (inverse) { out.push('^'); }
    +      out.push.apply(out, groups);
    +      for (var i = 0; i < consolidatedRanges.length; ++i) {
    +        var range = consolidatedRanges[i];
    +        out.push(encodeEscape(range[0]));
    +        if (range[1] > range[0]) {
    +          if (range[1] + 1 > range[0]) { out.push('-'); }
    +          out.push(encodeEscape(range[1]));
    +        }
    +      }
    +      out.push(']');
    +      return out.join('');
    +    }
    +
    +    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    +      // Split into character sets, escape sequences, punctuation strings
    +      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    +      // include any of the above.
    +      var parts = regex.source.match(
    +          new RegExp(
    +              '(?:'
    +              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    +              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    +              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    +              + '|\\\\[0-9]+'  // a back-reference or octal escape
    +              + '|\\\\[^ux0-9]'  // other escape sequence
    +              + '|\\(\\?[:!=]'  // start of a non-capturing group
    +              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
    +              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    +              + ')',
    +              'g'));
    +      var n = parts.length;
    +
    +      // Maps captured group numbers to the number they will occupy in
    +      // the output or to -1 if that has not been determined, or to
    +      // undefined if they need not be capturing in the output.
    +      var capturedGroups = [];
    +
    +      // Walk over and identify back references to build the capturedGroups
    +      // mapping.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          // groups are 1-indexed, so max group index is count of '('
    +          ++groupIndex;
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            capturedGroups[decimalValue] = -1;
    +          }
    +        }
    +      }
    +
    +      // Renumber groups and reduce capturing groups to non-capturing groups
    +      // where possible.
    +      for (var i = 1; i < capturedGroups.length; ++i) {
    +        if (-1 === capturedGroups[i]) {
    +          capturedGroups[i] = ++capturedGroupIndex;
    +        }
    +      }
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        var p = parts[i];
    +        if (p === '(') {
    +          ++groupIndex;
    +          if (capturedGroups[groupIndex] === undefined) {
    +            parts[i] = '(?:';
    +          }
    +        } else if ('\\' === p.charAt(0)) {
    +          var decimalValue = +p.substring(1);
    +          if (decimalValue && decimalValue <= groupIndex) {
    +            parts[i] = '\\' + capturedGroups[groupIndex];
    +          }
    +        }
    +      }
    +
    +      // Remove any prefix anchors so that the output will match anywhere.
    +      // ^^ really does mean an anchored match though.
    +      for (var i = 0, groupIndex = 0; i < n; ++i) {
    +        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    +      }
    +
    +      // Expand letters to groupts to handle mixing of case-sensitive and
    +      // case-insensitive patterns if necessary.
    +      if (regex.ignoreCase && needToFoldCase) {
    +        for (var i = 0; i < n; ++i) {
    +          var p = parts[i];
    +          var ch0 = p.charAt(0);
    +          if (p.length >= 2 && ch0 === '[') {
    +            parts[i] = caseFoldCharset(p);
    +          } else if (ch0 !== '\\') {
    +            // TODO: handle letters in numeric escapes.
    +            parts[i] = p.replace(
    +                /[a-zA-Z]/g,
    +                function (ch) {
    +                  var cc = ch.charCodeAt(0);
    +                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    +                });
    +          }
    +        }
    +      }
    +
    +      return parts.join('');
    +    }
    +
    +    var rewritten = [];
    +    for (var i = 0, n = regexs.length; i < n; ++i) {
    +      var regex = regexs[i];
    +      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    +      rewritten.push(
    +          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    +    }
    +
    +    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    +  }
    +
    +  var PR_innerHtmlWorks = null;
    +  function getInnerHtml(node) {
    +    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    +    // an html description of well formed XML and the containing tag is a PRE
    +    // tag, so we detect that case and emulate innerHTML.
    +    if (null === PR_innerHtmlWorks) {
    +      var testNode = document.createElement('PRE');
    +      testNode.appendChild(
    +          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
    +      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    +    }
    +
    +    if (PR_innerHtmlWorks) {
    +      var content = node.innerHTML;
    +      // XMP tags contain unescaped entities so require special handling.
    +      if (isRawContent(node)) {
    +        content = textToHtml(content);
    +      } else if (!isPreformatted(node, content)) {
    +        content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1')
    +            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
    +      }
    +      return content;
    +    }
    +
    +    var out = [];
    +    for (var child = node.firstChild; child; child = child.nextSibling) {
    +      normalizedHtml(child, out);
    +    }
    +    return out.join('');
    +  }
    +
    +  /** returns a function that expand tabs to spaces.  This function can be fed
    +    * successive chunks of text, and will maintain its own internal state to
    +    * keep track of how tabs are expanded.
    +    * @return {function (string) : string} a function that takes
    +    *   plain text and return the text with tabs expanded.
    +    * @private
    +    */
    +  function makeTabExpander(tabWidth) {
    +    var SPACES = '                ';
    +    var charInLine = 0;
    +
    +    return function (plainText) {
    +      // walk over each character looking for tabs and newlines.
    +      // On tabs, expand them.  On newlines, reset charInLine.
    +      // Otherwise increment charInLine
    +      var out = null;
    +      var pos = 0;
    +      for (var i = 0, n = plainText.length; i < n; ++i) {
    +        var ch = plainText.charAt(i);
    +
    +        switch (ch) {
    +          case '\t':
    +            if (!out) { out = []; }
    +            out.push(plainText.substring(pos, i));
    +            // calculate how much space we need in front of this part
    +            // nSpaces is the amount of padding -- the number of spaces needed
    +            // to move us to the next column, where columns occur at factors of
    +            // tabWidth.
    +            var nSpaces = tabWidth - (charInLine % tabWidth);
    +            charInLine += nSpaces;
    +            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
    +              out.push(SPACES.substring(0, nSpaces));
    +            }
    +            pos = i + 1;
    +            break;
    +          case '\n':
    +            charInLine = 0;
    +            break;
    +          default:
    +            ++charInLine;
    +        }
    +      }
    +      if (!out) { return plainText; }
    +      out.push(plainText.substring(pos));
    +      return out.join('');
    +    };
    +  }
    +
    +  var pr_chunkPattern = new RegExp(
    +      '[^<]+'  // A run of characters other than '<'
    +      + '|<\!--[\\s\\S]*?--\>'  // an HTML comment
    +      + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // a CDATA section
    +      // a probable tag that should not be highlighted
    +      + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
    +      + '|<',  // A '<' that does not begin a larger chunk
    +      'g');
    +  var pr_commentPrefix = /^<\!--/;
    +  var pr_cdataPrefix = /^<!\[CDATA\[/;
    +  var pr_brPrefix = /^<br\b/i;
    +  var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
    +
    +  /** split markup into chunks of html tags (style null) and
    +    * plain text (style {@link #PR_PLAIN}), converting tags which are
    +    * significant for tokenization (<br>) into their textual equivalent.
    +    *
    +    * @param {string} s html where whitespace is considered significant.
    +    * @return {Object} source code and extracted tags.
    +    * @private
    +    */
    +  function extractTags(s) {
    +    // since the pattern has the 'g' modifier and defines no capturing groups,
    +    // this will return a list of all chunks which we then classify and wrap as
    +    // PR_Tokens
    +    var matches = s.match(pr_chunkPattern);
    +    var sourceBuf = [];
    +    var sourceBufLen = 0;
    +    var extractedTags = [];
    +    if (matches) {
    +      for (var i = 0, n = matches.length; i < n; ++i) {
    +        var match = matches[i];
    +        if (match.length > 1 && match.charAt(0) === '<') {
    +          if (pr_commentPrefix.test(match)) { continue; }
    +          if (pr_cdataPrefix.test(match)) {
    +            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
    +            sourceBuf.push(match.substring(9, match.length - 3));
    +            sourceBufLen += match.length - 12;
    +          } else if (pr_brPrefix.test(match)) {
    +            // <br> tags are lexically significant so convert them to text.
    +            // This is undone later.
    +            sourceBuf.push('\n');
    +            ++sourceBufLen;
    +          } else {
    +            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
    +              // A <span class="nocode"> will start a section that should be
    +              // ignored.  Continue walking the list until we see a matching end
    +              // tag.
    +              var name = match.match(pr_tagNameRe)[2];
    +              var depth = 1;
    +              var j;
    +              end_tag_loop:
    +              for (j = i + 1; j < n; ++j) {
    +                var name2 = matches[j].match(pr_tagNameRe);
    +                if (name2 && name2[2] === name) {
    +                  if (name2[1] === '/') {
    +                    if (--depth === 0) { break end_tag_loop; }
    +                  } else {
    +                    ++depth;
    +                  }
    +                }
    +              }
    +              if (j < n) {
    +                extractedTags.push(
    +                    sourceBufLen, matches.slice(i, j + 1).join(''));
    +                i = j;
    +              } else {  // Ignore unclosed sections.
    +                extractedTags.push(sourceBufLen, match);
    +              }
    +            } else {
    +              extractedTags.push(sourceBufLen, match);
    +            }
    +          }
    +        } else {
    +          var literalText = htmlToText(match);
    +          sourceBuf.push(literalText);
    +          sourceBufLen += literalText.length;
    +        }
    +      }
    +    }
    +    return { source: sourceBuf.join(''), tags: extractedTags };
    +  }
    +
    +  /** True if the given tag contains a class attribute with the nocode class. */
    +  function isNoCodeTag(tag) {
    +    return !!tag
    +        // First canonicalize the representation of attributes
    +        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
    +                 ' $1="$2$3$4"')
    +        // Then look for the attribute we want.
    +        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
    +  }
    +
    +  /**
    +   * Apply the given language handler to sourceCode and add the resulting
    +   * decorations to out.
    +   * @param {number} basePos the index of sourceCode within the chunk of source
    +   *    whose decorations are already present on out.
    +   */
    +  function appendDecorations(basePos, sourceCode, langHandler, out) {
    +    if (!sourceCode) { return; }
    +    var job = {
    +      source: sourceCode,
    +      basePos: basePos
    +    };
    +    langHandler(job);
    +    out.push.apply(out, job.decorations);
    +  }
    +
    +  /** Given triples of [style, pattern, context] returns a lexing function,
    +    * The lexing function interprets the patterns to find token boundaries and
    +    * returns a decoration list of the form
    +    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    +    * where index_n is an index into the sourceCode, and style_n is a style
    +    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    +    * all characters in sourceCode[index_n-1:index_n].
    +    *
    +    * The stylePatterns is a list whose elements have the form
    +    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    +    *
    +    * Style is a style constant like PR_PLAIN, or can be a string of the
    +    * form 'lang-FOO', where FOO is a language extension describing the
    +    * language of the portion of the token in $1 after pattern executes.
    +    * E.g., if style is 'lang-lisp', and group 1 contains the text
    +    * '(hello (world))', then that portion of the token will be passed to the
    +    * registered lisp handler for formatting.
    +    * The text before and after group 1 will be restyled using this decorator
    +    * so decorators should take care that this doesn't result in infinite
    +    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    +    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    +    * '<script>foo()<\/script>', which would cause the current decorator to
    +    * be called with '<script>' which would not match the same rule since
    +    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    +    * the generic tag rule.  The handler registered for the 'js' extension would
    +    * then be called with 'foo()', and finally, the current decorator would
    +    * be called with '<\/script>' which would not match the original rule and
    +    * so the generic tag rule would identify it as a tag.
    +    *
    +    * Pattern must only match prefixes, and if it matches a prefix, then that
    +    * match is considered a token with the same style.
    +    *
    +    * Context is applied to the last non-whitespace, non-comment token
    +    * recognized.
    +    *
    +    * Shortcut is an optional string of characters, any of which, if the first
    +    * character, gurantee that this pattern and only this pattern matches.
    +    *
    +    * @param {Array} shortcutStylePatterns patterns that always start with
    +    *   a known character.  Must have a shortcut string.
    +    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    +    *   order if the shortcut ones fail.  May have shortcuts.
    +    *
    +    * @return {function (Object)} a
    +    *   function that takes source code and returns a list of decorations.
    +    */
    +  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    +    var shortcuts = {};
    +    var tokenizer;
    +    (function () {
    +      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    +      var allRegexs = [];
    +      var regexKeys = {};
    +      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    +        var patternParts = allPatterns[i];
    +        var shortcutChars = patternParts[3];
    +        if (shortcutChars) {
    +          for (var c = shortcutChars.length; --c >= 0;) {
    +            shortcuts[shortcutChars.charAt(c)] = patternParts;
    +          }
    +        }
    +        var regex = patternParts[1];
    +        var k = '' + regex;
    +        if (!regexKeys.hasOwnProperty(k)) {
    +          allRegexs.push(regex);
    +          regexKeys[k] = null;
    +        }
    +      }
    +      allRegexs.push(/[\0-\uffff]/);
    +      tokenizer = combinePrefixPatterns(allRegexs);
    +    })();
    +
    +    var nPatterns = fallthroughStylePatterns.length;
    +    var notWs = /\S/;
    +
    +    /**
    +     * Lexes job.source and produces an output array job.decorations of style
    +     * classes preceded by the position at which they start in job.source in
    +     * order.
    +     *
    +     * @param {Object} job an object like {@code
    +     *    source: {string} sourceText plain text,
    +     *    basePos: {int} position of job.source in the larger chunk of
    +     *        sourceCode.
    +     * }
    +     */
    +    var decorate = function (job) {
    +      var sourceCode = job.source, basePos = job.basePos;
    +      /** Even entries are positions in source in ascending order.  Odd enties
    +        * are style markers (e.g., PR_COMMENT) that run from that position until
    +        * the end.
    +        * @type {Array.<number|string>}
    +        */
    +      var decorations = [basePos, PR_PLAIN];
    +      var pos = 0;  // index into sourceCode
    +      var tokens = sourceCode.match(tokenizer) || [];
    +      var styleCache = {};
    +
    +      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    +        var token = tokens[ti];
    +        var style = styleCache[token];
    +        var match = void 0;
    +
    +        var isEmbedded;
    +        if (typeof style === 'string') {
    +          isEmbedded = false;
    +        } else {
    +          var patternParts = shortcuts[token.charAt(0)];
    +          if (patternParts) {
    +            match = token.match(patternParts[1]);
    +            style = patternParts[0];
    +          } else {
    +            for (var i = 0; i < nPatterns; ++i) {
    +              patternParts = fallthroughStylePatterns[i];
    +              match = token.match(patternParts[1]);
    +              if (match) {
    +                style = patternParts[0];
    +                break;
    +              }
    +            }
    +
    +            if (!match) {  // make sure that we make progress
    +              style = PR_PLAIN;
    +            }
    +          }
    +
    +          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    +          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    +            isEmbedded = false;
    +            style = PR_SOURCE;
    +          }
    +
    +          if (!isEmbedded) { styleCache[token] = style; }
    +        }
    +
    +        var tokenStart = pos;
    +        pos += token.length;
    +
    +        if (!isEmbedded) {
    +          decorations.push(basePos + tokenStart, style);
    +        } else {  // Treat group 1 as an embedded block of source code.
    +          var embeddedSource = match[1];
    +          var embeddedSourceStart = token.indexOf(embeddedSource);
    +          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    +          if (match[2]) {
    +            // If embeddedSource can be blank, then it would match at the
    +            // beginning which would cause us to infinitely recurse on the
    +            // entire token, so we catch the right context in match[2].
    +            embeddedSourceEnd = token.length - match[2].length;
    +            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    +          }
    +          var lang = style.substring(5);
    +          // Decorate the left of the embedded source
    +          appendDecorations(
    +              basePos + tokenStart,
    +              token.substring(0, embeddedSourceStart),
    +              decorate, decorations);
    +          // Decorate the embedded source
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceStart,
    +              embeddedSource,
    +              langHandlerForExtension(lang, embeddedSource),
    +              decorations);
    +          // Decorate the right of the embedded section
    +          appendDecorations(
    +              basePos + tokenStart + embeddedSourceEnd,
    +              token.substring(embeddedSourceEnd),
    +              decorate, decorations);
    +        }
    +      }
    +      job.decorations = decorations;
    +    };
    +    return decorate;
    +  }
    +
    +  /** returns a function that produces a list of decorations from source text.
    +    *
    +    * This code treats ", ', and ` as string delimiters, and \ as a string
    +    * escape.  It does not recognize perl's qq() style strings.
    +    * It has no special handling for double delimiter escapes as in basic, or
    +    * the tripled delimiters used in python, but should work on those regardless
    +    * although in those cases a single string literal may be broken up into
    +    * multiple adjacent string literals.
    +    *
    +    * It recognizes C, C++, and shell style comments.
    +    *
    +    * @param {Object} options a set of optional parameters.
    +    * @return {function (Object)} a function that examines the source code
    +    *     in the input job and builds the decoration list.
    +    */
    +  function sourceDecorator(options) {
    +    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    +    if (options['tripleQuotedStrings']) {
    +      // '''multi-line-string''', 'single-line-string', and double-quoted
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    +           null, '\'"']);
    +    } else if (options['multiLineStrings']) {
    +      // 'multi-line-string', "multi-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    +           null, '\'"`']);
    +    } else {
    +      // 'single-line-string', "single-line-string"
    +      shortcutStylePatterns.push(
    +          [PR_STRING,
    +           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    +           null, '"\'']);
    +    }
    +    if (options['verbatimStrings']) {
    +      // verbatim-string-literal production from the C# grammar.  See issue 93.
    +      fallthroughStylePatterns.push(
    +          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    +    }
    +    if (options['hashComments']) {
    +      if (options['cStyleComments']) {
    +        // Stop C preprocessor declarations at an unclosed open comment
    +        shortcutStylePatterns.push(
    +            [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
    +             null, '#']);
    +        fallthroughStylePatterns.push(
    +            [PR_STRING,
    +             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
    +             null]);
    +      } else {
    +        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +      }
    +    }
    +    if (options['cStyleComments']) {
    +      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    +      fallthroughStylePatterns.push(
    +          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    +    }
    +    if (options['regexLiterals']) {
    +      var REGEX_LITERAL = (
    +          // A regular expression literal starts with a slash that is
    +          // not followed by * or / so that it is not confused with
    +          // comments.
    +          '/(?=[^/*])'
    +          // and then contains any number of raw characters,
    +          + '(?:[^/\\x5B\\x5C]'
    +          // escape sequences (\x5C),
    +          +    '|\\x5C[\\s\\S]'
    +          // or non-nesting character sets (\x5B\x5D);
    +          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
    +          // finally closed by a /.
    +          + '/');
    +      fallthroughStylePatterns.push(
    +          ['lang-regex',
    +           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    +           ]);
    +    }
    +
    +    var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
    +    if (keywords.length) {
    +      fallthroughStylePatterns.push(
    +          [PR_KEYWORD,
    +           new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
    +    }
    +
    +    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    +    fallthroughStylePatterns.push(
    +        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    +        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
    +        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    +        [PR_LITERAL,
    +         new RegExp(
    +             '^(?:'
    +             // A hex number
    +             + '0x[a-f0-9]+'
    +             // or an octal or decimal number,
    +             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    +             // possibly in scientific notation
    +             + '(?:e[+\\-]?\\d+)?'
    +             + ')'
    +             // with an optional modifier like UL for unsigned long
    +             + '[a-z]*', 'i'),
    +         null, '0123456789'],
    +        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
    +
    +    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    +  }
    +
    +  var decorateSource = sourceDecorator({
    +        'keywords': ALL_KEYWORDS,
    +        'hashComments': true,
    +        'cStyleComments': true,
    +        'multiLineStrings': true,
    +        'regexLiterals': true
    +      });
    +
    +  /** Breaks {@code job.source} around style boundaries in
    +    * {@code job.decorations} while re-interleaving {@code job.extractedTags},
    +    * and leaves the result in {@code job.prettyPrintedHtml}.
    +    * @param {Object} job like {
    +    *    source: {string} source as plain text,
    +    *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
    +    *                   html preceded by their position in {@code job.source}
    +    *                   in order
    +    *    decorations: {Array.<number|string} an array of style classes preceded
    +    *                 by the position at which they start in job.source in order
    +    * }
    +    * @private
    +    */
    +  function recombineTagsAndDecorations(job) {
    +    var sourceText = job.source;
    +    var extractedTags = job.extractedTags;
    +    var decorations = job.decorations;
    +
    +    var html = [];
    +    // index past the last char in sourceText written to html
    +    var outputIdx = 0;
    +
    +    var openDecoration = null;
    +    var currentDecoration = null;
    +    var tagPos = 0;  // index into extractedTags
    +    var decPos = 0;  // index into decorations
    +    var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
    +
    +    var adjacentSpaceRe = /([\r\n ]) /g;
    +    var startOrSpaceRe = /(^| ) /gm;
    +    var newlineRe = /\r\n?|\n/g;
    +    var trailingSpaceRe = /[ \r\n]$/;
    +    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
    +
    +    // A helper function that is responsible for opening sections of decoration
    +    // and outputing properly escaped chunks of source
    +    function emitTextUpTo(sourceIdx) {
    +      if (sourceIdx > outputIdx) {
    +        if (openDecoration && openDecoration !== currentDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        if (!openDecoration && currentDecoration) {
    +          openDecoration = currentDecoration;
    +          html.push('<span class="', openDecoration, '">');
    +        }
    +        // This interacts badly with some wikis which introduces paragraph tags
    +        // into pre blocks for some strange reason.
    +        // It's necessary for IE though which seems to lose the preformattedness
    +        // of <pre> tags when their innerHTML is assigned.
    +        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
    +        // and it serves to undo the conversion of <br>s to newlines done in
    +        // chunkify.
    +        var htmlChunk = textToHtml(
    +            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
    +            .replace(lastWasSpace
    +                     ? startOrSpaceRe
    +                     : adjacentSpaceRe, '$1&nbsp;');
    +        // Keep track of whether we need to escape space at the beginning of the
    +        // next chunk.
    +        lastWasSpace = trailingSpaceRe.test(htmlChunk);
    +        // IE collapses multiple adjacient <br>s into 1 line break.
    +        // Prefix every <br> with '&nbsp;' can prevent such IE's behavior.
    +        var lineBreakHtml = window['_pr_isIE6']() ? '&nbsp;<br />' : '<br />';
    +        html.push(htmlChunk.replace(newlineRe, lineBreakHtml));
    +        outputIdx = sourceIdx;
    +      }
    +    }
    +
    +    while (true) {
    +      // Determine if we're going to consume a tag this time around.  Otherwise
    +      // we consume a decoration or exit.
    +      var outputTag;
    +      if (tagPos < extractedTags.length) {
    +        if (decPos < decorations.length) {
    +          // Pick one giving preference to extractedTags since we shouldn't open
    +          // a new style that we're going to have to immediately close in order
    +          // to output a tag.
    +          outputTag = extractedTags[tagPos] <= decorations[decPos];
    +        } else {
    +          outputTag = true;
    +        }
    +      } else {
    +        outputTag = false;
    +      }
    +      // Consume either a decoration or a tag or exit.
    +      if (outputTag) {
    +        emitTextUpTo(extractedTags[tagPos]);
    +        if (openDecoration) {
    +          // Close the current decoration
    +          html.push('</span>');
    +          openDecoration = null;
    +        }
    +        html.push(extractedTags[tagPos + 1]);
    +        tagPos += 2;
    +      } else if (decPos < decorations.length) {
    +        emitTextUpTo(decorations[decPos]);
    +        currentDecoration = decorations[decPos + 1];
    +        decPos += 2;
    +      } else {
    +        break;
    +      }
    +    }
    +    emitTextUpTo(sourceText.length);
    +    if (openDecoration) {
    +      html.push('</span>');
    +    }
    +    job.prettyPrintedHtml = html.join('');
    +  }
    +
    +  /** Maps language-specific file extensions to handlers. */
    +  var langHandlerRegistry = {};
    +  /** Register a language handler for the given file extensions.
    +    * @param {function (Object)} handler a function from source code to a list
    +    *      of decorations.  Takes a single argument job which describes the
    +    *      state of the computation.   The single parameter has the form
    +    *      {@code {
    +    *        source: {string} as plain text.
    +    *        decorations: {Array.<number|string>} an array of style classes
    +    *                     preceded by the position at which they start in
    +    *                     job.source in order.
    +    *                     The language handler should assigned this field.
    +    *        basePos: {int} the position of source in the larger source chunk.
    +    *                 All positions in the output decorations array are relative
    +    *                 to the larger source chunk.
    +    *      } }
    +    * @param {Array.<string>} fileExtensions
    +    */
    +  function registerLangHandler(handler, fileExtensions) {
    +    for (var i = fileExtensions.length; --i >= 0;) {
    +      var ext = fileExtensions[i];
    +      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    +        langHandlerRegistry[ext] = handler;
    +      } else if ('console' in window) {
    +        console.warn('cannot override language handler %s', ext);
    +      }
    +    }
    +  }
    +  function langHandlerForExtension(extension, source) {
    +    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    +      // Treat it as markup if the first non whitespace character is a < and
    +      // the last non-whitespace character is a >.
    +      extension = /^\s*</.test(source)
    +          ? 'default-markup'
    +          : 'default-code';
    +    }
    +    return langHandlerRegistry[extension];
    +  }
    +  registerLangHandler(decorateSource, ['default-code']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [],
    +          [
    +           [PR_PLAIN,       /^[^<?]+/],
    +           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    +           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    +           // Unescaped content in an unknown language
    +           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    +           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    +           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    +           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    +           // Unescaped content in javascript.  (Or possibly vbscript).
    +           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    +           // Contains unescaped stylesheet content
    +           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    +           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    +          ]),
    +      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    +  registerLangHandler(
    +      createSimpleLexer(
    +          [
    +           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    +           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    +           ],
    +          [
    +           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    +           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    +           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    +           [PR_PUNCTUATION,  /^[=<>\/]+/],
    +           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    +           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    +           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    +           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    +           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    +           ]),
    +      ['in.tag']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CPP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true
    +        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': 'null true false'
    +        }), ['json']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': CSHARP_KEYWORDS,
    +          'hashComments': true,
    +          'cStyleComments': true,
    +          'verbatimStrings': true
    +        }), ['cs']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JAVA_KEYWORDS,
    +          'cStyleComments': true
    +        }), ['java']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': SH_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true
    +        }), ['bsh', 'csh', 'sh']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PYTHON_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'tripleQuotedStrings': true
    +        }), ['cv', 'py']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': PERL_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['perl', 'pl', 'pm']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': RUBY_KEYWORDS,
    +          'hashComments': true,
    +          'multiLineStrings': true,
    +          'regexLiterals': true
    +        }), ['rb']);
    +  registerLangHandler(sourceDecorator({
    +          'keywords': JSCRIPT_KEYWORDS,
    +          'cStyleComments': true,
    +          'regexLiterals': true
    +        }), ['js']);
    +  registerLangHandler(
    +      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    +
    +  function applyDecorator(job) {
    +    var sourceCodeHtml = job.sourceCodeHtml;
    +    var opt_langExtension = job.langExtension;
    +
    +    // Prepopulate output in case processing fails with an exception.
    +    job.prettyPrintedHtml = sourceCodeHtml;
    +
    +    try {
    +      // Extract tags, and convert the source code to plain text.
    +      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
    +      /** Plain text. @type {string} */
    +      var source = sourceAndExtractedTags.source;
    +      job.source = source;
    +      job.basePos = 0;
    +
    +      /** Even entries are positions in source in ascending order.  Odd entries
    +        * are tags that were extracted at that position.
    +        * @type {Array.<number|string>}
    +        */
    +      job.extractedTags = sourceAndExtractedTags.tags;
    +
    +      // Apply the appropriate language handler
    +      langHandlerForExtension(opt_langExtension, source)(job);
    +      // Integrate the decorations and tags back into the source code to produce
    +      // a decorated html string which is left in job.prettyPrintedHtml.
    +      recombineTagsAndDecorations(job);
    +    } catch (e) {
    +      if ('console' in window) {
    +        console.log(e);
    +        console.trace();
    +      }
    +    }
    +  }
    +
    +  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
    +    var job = {
    +      sourceCodeHtml: sourceCodeHtml,
    +      langExtension: opt_langExtension
    +    };
    +    applyDecorator(job);
    +    return job.prettyPrintedHtml;
    +  }
    +
    +  function prettyPrint(opt_whenDone) {
    +    var isIE678 = window['_pr_isIE6']();
    +    var ieNewline = isIE678 === 6 ? '\r\n' : '\r';
    +    // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
    +
    +    // fetch a list of nodes to rewrite
    +    var codeSegments = [
    +        document.getElementsByTagName('pre'),
    +        document.getElementsByTagName('code'),
    +        document.getElementsByTagName('td'),  /* ND Change: Add tables to support prototypes. */
    +        document.getElementsByTagName('xmp') ];
    +    var elements = [];
    +    for (var i = 0; i < codeSegments.length; ++i) {
    +      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    +        elements.push(codeSegments[i][j]);
    +      }
    +    }
    +    codeSegments = null;
    +
    +    var clock = Date;
    +    if (!clock['now']) {
    +      clock = { 'now': function () { return (new Date).getTime(); } };
    +    }
    +
    +    // The loop is broken into a series of continuations to make sure that we
    +    // don't make the browser unresponsive when rewriting a large page.
    +    var k = 0;
    +    var prettyPrintingJob;
    +
    +    function doWork() {
    +      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    +                     clock.now() + 250 /* ms */ :
    +                     Infinity);
    +      for (; k < elements.length && clock.now() < endTime; k++) {
    +        var cs = elements[k];
    +        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    +          // If the classes includes a language extensions, use it.
    +          // Language extensions can be specified like
    +          //     <pre class="prettyprint lang-cpp">
    +          // the language extension "cpp" is used to find a language handler as
    +          // passed to PR_registerLangHandler.
    +          var langExtension = cs.className.match(/\blang-(\w+)\b/);
    +          if (langExtension) { langExtension = langExtension[1]; }
    +
    +          // make sure this is not nested in an already prettified element
    +          var nested = false;
    +          for (var p = cs.parentNode; p; p = p.parentNode) {
    +            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    +                 p.tagName === 'xmp' || p.tagName === 'td') &&  /* ND Change: Add tables to support prototypes */
    +                p.className && p.className.indexOf('prettyprint') >= 0) {
    +              nested = true;
    +              break;
    +            }
    +          }
    +          if (!nested) {
    +            // fetch the content as a snippet of properly escaped HTML.
    +            // Firefox adds newlines at the end.
    +            var content = getInnerHtml(cs);
    +            content = content.replace(/(?:\r\n?|\n)$/, '');
    +
    +	  		/* ND Change: we need to preserve &nbsp;s so change them to a special character instead of a space. */
    +			content = content.replace(/&nbsp;/g, '\x11');
    +
    +            // do the pretty printing
    +            prettyPrintingJob = {
    +              sourceCodeHtml: content,
    +              langExtension: langExtension,
    +              sourceNode: cs
    +            };
    +            applyDecorator(prettyPrintingJob);
    +            replaceWithPrettyPrintedHtml();
    +          }
    +        }
    +      }
    +      if (k < elements.length) {
    +        // finish up in a continuation
    +        setTimeout(doWork, 250);
    +      } else if (opt_whenDone) {
    +        opt_whenDone();
    +      }
    +    }
    +
    +    function replaceWithPrettyPrintedHtml() {
    +      var newContent = prettyPrintingJob.prettyPrintedHtml;
    +      if (!newContent) { return; }
    +
    +      /* ND Change: Restore the preserved &nbsp;s.  */
    +	  newContent = newContent.replace(/\x11/g, '&nbsp;');
    +
    +      var cs = prettyPrintingJob.sourceNode;
    +
    +      // push the prettified html back into the tag.
    +      if (!isRawContent(cs)) {
    +        // just replace the old html with the new
    +        cs.innerHTML = newContent;
    +      } else {
    +        // we need to change the tag to a <pre> since <xmp>s do not allow
    +        // embedded tags such as the span tags used to attach styles to
    +        // sections of source code.
    +        var pre = document.createElement('PRE');
    +        for (var i = 0; i < cs.attributes.length; ++i) {
    +          var a = cs.attributes[i];
    +          if (a.specified) {
    +            var aname = a.name.toLowerCase();
    +            if (aname === 'class') {
    +              pre.className = a.value;  // For IE 6
    +            } else {
    +              pre.setAttribute(a.name, a.value);
    +            }
    +          }
    +        }
    +        pre.innerHTML = newContent;
    +
    +        // remove the old
    +        cs.parentNode.replaceChild(pre, cs);
    +        cs = pre;
    +      }
    +
    +      // Replace <br>s with line-feeds so that copying and pasting works
    +      // on IE 6.
    +      // Doing this on other browsers breaks lots of stuff since \r\n is
    +      // treated as two newlines on Firefox, and doing this also slows
    +      // down rendering.
    +      if (isIE678 && cs.tagName === 'PRE') {
    +        var lineBreaks = cs.getElementsByTagName('br');
    +        for (var j = lineBreaks.length; --j >= 0;) {
    +          var lineBreak = lineBreaks[j];
    +          lineBreak.parentNode.replaceChild(
    +              document.createTextNode(ieNewline), lineBreak);
    +        }
    +      }
    +    }
    +
    +    doWork();
    +  }
    +
    +  window['PR_normalizedHtml'] = normalizedHtml;
    +  window['prettyPrintOne'] = prettyPrintOne;
    +  window['prettyPrint'] = prettyPrint;
    +  window['PR'] = {
    +        'combinePrefixPatterns': combinePrefixPatterns,
    +        'createSimpleLexer': createSimpleLexer,
    +        'registerLangHandler': registerLangHandler,
    +        'sourceDecorator': sourceDecorator,
    +        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    +        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    +        'PR_COMMENT': PR_COMMENT,
    +        'PR_DECLARATION': PR_DECLARATION,
    +        'PR_KEYWORD': PR_KEYWORD,
    +        'PR_LITERAL': PR_LITERAL,
    +        'PR_NOCODE': PR_NOCODE,
    +        'PR_PLAIN': PR_PLAIN,
    +        'PR_PUNCTUATION': PR_PUNCTUATION,
    +        'PR_SOURCE': PR_SOURCE,
    +        'PR_STRING': PR_STRING,
    +        'PR_TAG': PR_TAG,
    +        'PR_TYPE': PR_TYPE
    +      };
    +})();
    +
    +
    +// ____________________________________________________________________________
    +
    +
    +
    +// Lua extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],[PR.PR_STRING,/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],[PR.PR_KEYWORD,/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_]\w*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),['lua'])
    +
    +
    +// Haskell extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\x0B\x0C\r ]+/,null,'	\n\r '],[PR.PR_STRING,/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'\"'],[PR.PR_STRING,/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,'\''],[PR.PR_LITERAL,/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,'0123456789']],[[PR.PR_COMMENT,/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],[PR.PR_KEYWORD,/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,null],[PR.PR_PLAIN,/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],[PR.PR_PUNCTUATION,/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),['hs'])
    +
    +
    +// ML extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_COMMENT,/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,'#'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],[PR.PR_KEYWORD,/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],[PR.PR_LITERAL,/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],[PR.PR_PUNCTUATION,/^[^\t\n\r \xA0\"\'\w]+/]]),['fs','ml'])
    +
    +
    +// SQL extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],[PR.PR_KEYWORD,/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_][\w-]*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),['sql'])
    +
    +
    +// VB extension
    +
    +PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0\u2028\u2029]+/,null,'	\n\r \xa0\u2028\u2029'],[PR.PR_STRING,/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'\"\u201c\u201d'],[PR.PR_COMMENT,/^[\'\u2018\u2019][^\r\n\u2028\u2029]*/,null,'\'\u2018\u2019']],[[PR.PR_KEYWORD,/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,null],[PR.PR_COMMENT,/^REM[^\r\n\u2028\u2029]*/i],[PR.PR_LITERAL,/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],[PR.PR_PLAIN,/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],[PR.PR_PUNCTUATION,/^(?:\[|\])/]]),['vb','vbs'])
    diff --git a/vendor/naturaldocs/JavaScript/NaturalDocs.js b/vendor/naturaldocs/JavaScript/NaturalDocs.js
    new file mode 100644
    index 000000000..3f42acde6
    --- /dev/null
    +++ b/vendor/naturaldocs/JavaScript/NaturalDocs.js
    @@ -0,0 +1,841 @@
    +// This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +// Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +// Refer to License.txt for the complete details
    +
    +// This file may be distributed with documentation files generated by Natural Docs.
    +// Such documentation is not covered by Natural Docs' copyright and licensing,
    +// and may have its own copyright and distribution terms as decided by its author.
    +
    +
    +//
    +//  Browser Styles
    +// ____________________________________________________________________________
    +
    +var agt=navigator.userAgent.toLowerCase();
    +var browserType;
    +var browserVer;
    +
    +if (agt.indexOf("opera") != -1)
    +    {
    +    browserType = "Opera";
    +
    +    if (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1)
    +        {  browserVer = "Opera7";  }
    +    else if (agt.indexOf("opera 8") != -1 || agt.indexOf("opera/8") != -1)
    +        {  browserVer = "Opera8";  }
    +    else if (agt.indexOf("opera 9") != -1 || agt.indexOf("opera/9") != -1)
    +        {  browserVer = "Opera9";  }
    +    }
    +
    +else if (agt.indexOf("applewebkit") != -1)
    +    {
    +    browserType = "Safari";
    +
    +    if (agt.indexOf("version/3") != -1)
    +        {  browserVer = "Safari3";  }
    +    else if (agt.indexOf("safari/4") != -1)
    +        {  browserVer = "Safari2";  }
    +    }
    +
    +else if (agt.indexOf("khtml") != -1)
    +    {
    +    browserType = "Konqueror";
    +    }
    +
    +else if (agt.indexOf("msie") != -1)
    +    {
    +    browserType = "IE";
    +
    +    if (agt.indexOf("msie 6") != -1)
    +        {  browserVer = "IE6";  }
    +    else if (agt.indexOf("msie 7") != -1)
    +        {  browserVer = "IE7";  }
    +    }
    +
    +else if (agt.indexOf("gecko") != -1)
    +    {
    +    browserType = "Firefox";
    +
    +    if (agt.indexOf("rv:1.7") != -1)
    +        {  browserVer = "Firefox1";  }
    +    else if (agt.indexOf("rv:1.8)") != -1 || agt.indexOf("rv:1.8.0") != -1)
    +        {  browserVer = "Firefox15";  }
    +    else if (agt.indexOf("rv:1.8.1") != -1)
    +        {  browserVer = "Firefox2";  }
    +    }
    +
    +
    +//
    +//  Support Functions
    +// ____________________________________________________________________________
    +
    +
    +function GetXPosition(item)
    +    {
    +    var position = 0;
    +
    +    if (item.offsetWidth != null)
    +        {
    +        while (item != document.body && item != null)
    +            {
    +            position += item.offsetLeft;
    +            item = item.offsetParent;
    +            };
    +        };
    +
    +    return position;
    +    };
    +
    +
    +function GetYPosition(item)
    +    {
    +    var position = 0;
    +
    +    if (item.offsetWidth != null)
    +        {
    +        while (item != document.body && item != null)
    +            {
    +            position += item.offsetTop;
    +            item = item.offsetParent;
    +            };
    +        };
    +
    +    return position;
    +    };
    +
    +
    +function MoveToPosition(item, x, y)
    +    {
    +    // Opera 5 chokes on the px extension, so it can use the Microsoft one instead.
    +
    +    if (item.style.left != null)
    +        {
    +        item.style.left = x + "px";
    +        item.style.top = y + "px";
    +        }
    +    else if (item.style.pixelLeft != null)
    +        {
    +        item.style.pixelLeft = x;
    +        item.style.pixelTop = y;
    +        };
    +    };
    +
    +
    +//
    +//  Menu
    +// ____________________________________________________________________________
    +
    +
    +function ToggleMenu(id)
    +    {
    +    if (!window.document.getElementById)
    +        {  return;  };
    +
    +    var display = window.document.getElementById(id).style.display;
    +
    +    if (display == "none")
    +        {  display = "block";  }
    +    else
    +        {  display = "none";  }
    +
    +    window.document.getElementById(id).style.display = display;
    +    }
    +
    +function HideAllBut(ids, max)
    +    {
    +    if (document.getElementById)
    +        {
    +        ids.sort( function(a,b) { return a - b; } );
    +        var number = 1;
    +
    +        while (number < max)
    +            {
    +            if (ids.length > 0 && number == ids[0])
    +                {  ids.shift();  }
    +            else
    +                {
    +                document.getElementById("MGroupContent" + number).style.display = "none";
    +                };
    +
    +            number++;
    +            };
    +        };
    +    }
    +
    +
    +//
    +//  Tooltips
    +// ____________________________________________________________________________
    +
    +
    +var tooltipTimer = 0;
    +
    +function ShowTip(event, tooltipID, linkID)
    +    {
    +    if (tooltipTimer)
    +        {  clearTimeout(tooltipTimer);  };
    +
    +    var docX = event.clientX + window.pageXOffset;
    +    var docY = event.clientY + window.pageYOffset;
    +
    +    var showCommand = "ReallyShowTip('" + tooltipID + "', '" + linkID + "', " + docX + ", " + docY + ")";
    +
    +    tooltipTimer = setTimeout(showCommand, 1000);
    +    }
    +
    +function ReallyShowTip(tooltipID, linkID, docX, docY)
    +    {
    +    tooltipTimer = 0;
    +
    +    var tooltip;
    +    var link;
    +
    +    if (document.getElementById)
    +        {
    +        tooltip = document.getElementById(tooltipID);
    +        link = document.getElementById(linkID);
    +        }
    +/*    else if (document.all)
    +        {
    +        tooltip = eval("document.all['" + tooltipID + "']");
    +        link = eval("document.all['" + linkID + "']");
    +        }
    +*/
    +    if (tooltip)
    +        {
    +        var left = GetXPosition(link);
    +        var top = GetYPosition(link);
    +        top += link.offsetHeight;
    +
    +
    +        // The fallback method is to use the mouse X and Y relative to the document.  We use a separate if and test if its a number
    +        // in case some browser snuck through the above if statement but didn't support everything.
    +
    +        if (!isFinite(top) || top == 0)
    +            {
    +            left = docX;
    +            top = docY;
    +            }
    +
    +        // Some spacing to get it out from under the cursor.
    +
    +        top += 10;
    +
    +        // Make sure the tooltip doesnt get smushed by being too close to the edge, or in some browsers, go off the edge of the
    +        // page.  We do it here because Konqueror does get offsetWidth right even if it doesnt get the positioning right.
    +
    +        if (tooltip.offsetWidth != null)
    +            {
    +            var width = tooltip.offsetWidth;
    +            var docWidth = document.body.clientWidth;
    +
    +            if (left + width > docWidth)
    +                {  left = docWidth - width - 1;  }
    +
    +            // If there's a horizontal scroll bar we could go past zero because it's using the page width, not the window width.
    +            if (left < 0)
    +                {  left = 0;  };
    +            }
    +
    +        MoveToPosition(tooltip, left, top);
    +        tooltip.style.visibility = "visible";
    +        }
    +    }
    +
    +function HideTip(tooltipID)
    +    {
    +    if (tooltipTimer)
    +        {
    +        clearTimeout(tooltipTimer);
    +        tooltipTimer = 0;
    +        }
    +
    +    var tooltip;
    +
    +    if (document.getElementById)
    +        {  tooltip = document.getElementById(tooltipID); }
    +    else if (document.all)
    +        {  tooltip = eval("document.all['" + tooltipID + "']");  }
    +
    +    if (tooltip)
    +        {  tooltip.style.visibility = "hidden";  }
    +    }
    +
    +
    +//
    +//  Blockquote fix for IE
    +// ____________________________________________________________________________
    +
    +
    +function NDOnLoad()
    +    {
    +    if (browserVer == "IE6")
    +        {
    +        var scrollboxes = document.getElementsByTagName('blockquote');
    +
    +        if (scrollboxes.item(0))
    +            {
    +            NDDoResize();
    +            window.onresize=NDOnResize;
    +            };
    +        };
    +    };
    +
    +
    +var resizeTimer = 0;
    +
    +function NDOnResize()
    +    {
    +    if (resizeTimer != 0)
    +        {  clearTimeout(resizeTimer);  };
    +
    +    resizeTimer = setTimeout(NDDoResize, 250);
    +    };
    +
    +
    +function NDDoResize()
    +    {
    +    var scrollboxes = document.getElementsByTagName('blockquote');
    +
    +    var i;
    +    var item;
    +
    +    i = 0;
    +    while (item = scrollboxes.item(i))
    +        {
    +        item.style.width = 100;
    +        i++;
    +        };
    +
    +    i = 0;
    +    while (item = scrollboxes.item(i))
    +        {
    +        item.style.width = item.parentNode.offsetWidth;
    +        i++;
    +        };
    +
    +    clearTimeout(resizeTimer);
    +    resizeTimer = 0;
    +    }
    +
    +
    +
    +/* ________________________________________________________________________________________________________
    +
    +    Class: SearchPanel
    +    ________________________________________________________________________________________________________
    +
    +    A class handling everything associated with the search panel.
    +
    +    Parameters:
    +
    +        name - The name of the global variable that will be storing this instance.  Is needed to be able to set timeouts.
    +        mode - The mode the search is going to work in.  Pass <NaturalDocs::Builder::Base->CommandLineOption()>, so the
    +                   value will be something like "HTML" or "FramedHTML".
    +
    +    ________________________________________________________________________________________________________
    +*/
    +
    +
    +function SearchPanel(name, mode, resultsPath)
    +    {
    +    if (!name || !mode || !resultsPath)
    +        {  alert("Incorrect parameters to SearchPanel.");  };
    +
    +
    +    // Group: Variables
    +    // ________________________________________________________________________
    +
    +    /*
    +        var: name
    +        The name of the global variable that will be storing this instance of the class.
    +    */
    +    this.name = name;
    +
    +    /*
    +        var: mode
    +        The mode the search is going to work in, such as "HTML" or "FramedHTML".
    +    */
    +    this.mode = mode;
    +
    +    /*
    +        var: resultsPath
    +        The relative path from the current HTML page to the results page directory.
    +    */
    +    this.resultsPath = resultsPath;
    +
    +    /*
    +        var: keyTimeout
    +        The timeout used between a keystroke and when a search is performed.
    +    */
    +    this.keyTimeout = 0;
    +
    +    /*
    +        var: keyTimeoutLength
    +        The length of <keyTimeout> in thousandths of a second.
    +    */
    +    this.keyTimeoutLength = 500;
    +
    +    /*
    +        var: lastSearchValue
    +        The last search string executed, or an empty string if none.
    +    */
    +    this.lastSearchValue = "";
    +
    +    /*
    +        var: lastResultsPage
    +        The last results page.  The value is only relevant if <lastSearchValue> is set.
    +    */
    +    this.lastResultsPage = "";
    +
    +    /*
    +        var: deactivateTimeout
    +
    +        The timeout used between when a control is deactivated and when the entire panel is deactivated.  Is necessary
    +        because a control may be deactivated in favor of another control in the same panel, in which case it should stay
    +        active.
    +    */
    +    this.deactivateTimout = 0;
    +
    +    /*
    +        var: deactivateTimeoutLength
    +        The length of <deactivateTimeout> in thousandths of a second.
    +    */
    +    this.deactivateTimeoutLength = 200;
    +
    +
    +
    +
    +    // Group: DOM Elements
    +    // ________________________________________________________________________
    +
    +
    +    // Function: DOMSearchField
    +    this.DOMSearchField = function()
    +        {  return document.getElementById("MSearchField");  };
    +
    +    // Function: DOMSearchType
    +    this.DOMSearchType = function()
    +        {  return document.getElementById("MSearchType");  };
    +
    +    // Function: DOMPopupSearchResults
    +    this.DOMPopupSearchResults = function()
    +        {  return document.getElementById("MSearchResults");  };
    +
    +    // Function: DOMPopupSearchResultsWindow
    +    this.DOMPopupSearchResultsWindow = function()
    +        {  return document.getElementById("MSearchResultsWindow");  };
    +
    +    // Function: DOMSearchPanel
    +    this.DOMSearchPanel = function()
    +        {  return document.getElementById("MSearchPanel");  };
    +
    +
    +
    +
    +    // Group: Event Handlers
    +    // ________________________________________________________________________
    +
    +
    +    /*
    +        Function: OnSearchFieldFocus
    +        Called when focus is added or removed from the search field.
    +    */
    +    this.OnSearchFieldFocus = function(isActive)
    +        {
    +        this.Activate(isActive);
    +        };
    +
    +
    +    /*
    +        Function: OnSearchFieldChange
    +        Called when the content of the search field is changed.
    +    */
    +    this.OnSearchFieldChange = function()
    +        {
    +        if (this.keyTimeout)
    +            {
    +            clearTimeout(this.keyTimeout);
    +            this.keyTimeout = 0;
    +            };
    +
    +        var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
    +
    +        if (searchValue != this.lastSearchValue)
    +            {
    +            if (searchValue != "")
    +                {
    +                this.keyTimeout = setTimeout(this.name + ".Search()", this.keyTimeoutLength);
    +                }
    +            else
    +                {
    +                if (this.mode == "HTML")
    +                    {  this.DOMPopupSearchResultsWindow().style.display = "none";  };
    +                this.lastSearchValue = "";
    +                };
    +            };
    +        };
    +
    +
    +    /*
    +        Function: OnSearchTypeFocus
    +        Called when focus is added or removed from the search type.
    +    */
    +    this.OnSearchTypeFocus = function(isActive)
    +        {
    +        this.Activate(isActive);
    +        };
    +
    +
    +    /*
    +        Function: OnSearchTypeChange
    +        Called when the search type is changed.
    +    */
    +    this.OnSearchTypeChange = function()
    +        {
    +        var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
    +
    +        if (searchValue != "")
    +            {
    +            this.Search();
    +            };
    +        };
    +
    +
    +
    +    // Group: Action Functions
    +    // ________________________________________________________________________
    +
    +
    +    /*
    +        Function: CloseResultsWindow
    +        Closes the results window.
    +    */
    +    this.CloseResultsWindow = function()
    +        {
    +        this.DOMPopupSearchResultsWindow().style.display = "none";
    +        this.Activate(false, true);
    +        };
    +
    +
    +    /*
    +        Function: Search
    +        Performs a search.
    +    */
    +    this.Search = function()
    +        {
    +        this.keyTimeout = 0;
    +
    +        var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
    +        var searchTopic = this.DOMSearchType().value;
    +
    +        var pageExtension = searchValue.substr(0,1);
    +
    +        if (pageExtension.match(/^[a-z]/i))
    +            {  pageExtension = pageExtension.toUpperCase();  }
    +        else if (pageExtension.match(/^[0-9]/))
    +            {  pageExtension = 'Numbers';  }
    +        else
    +            {  pageExtension = "Symbols";  };
    +
    +        var resultsPage;
    +        var resultsPageWithSearch;
    +        var hasResultsPage;
    +
    +        // indexSectionsWithContent is defined in searchdata.js
    +        if (indexSectionsWithContent[searchTopic][pageExtension] == true)
    +            {
    +            resultsPage = this.resultsPath + '/' + searchTopic + pageExtension + '.html';
    +            resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
    +            hasResultsPage = true;
    +            }
    +        else
    +            {
    +            resultsPage = this.resultsPath + '/NoResults.html';
    +            resultsPageWithSearch = resultsPage;
    +            hasResultsPage = false;
    +            };
    +
    +        var resultsFrame;
    +        if (this.mode == "HTML")
    +            {  resultsFrame = window.frames.MSearchResults;  }
    +        else if (this.mode == "FramedHTML")
    +            {  resultsFrame = window.top.frames['Content'];  };
    +
    +
    +        if (resultsPage != this.lastResultsPage ||
    +
    +            // Bug in IE.  If everything becomes hidden in a run, none of them will be able to be reshown in the next for some
    +            // reason.  It counts the right number of results, and you can even read the display as "block" after setting it, but it
    +            // just doesn't work in IE 6 or IE 7.  So if we're on the right page but the previous search had no results, reload the
    +            // page anyway to get around the bug.
    +            (browserType == "IE" && hasResultsPage &&
    +            	(!resultsFrame.searchResults || resultsFrame.searchResults.lastMatchCount == 0)) )
    +
    +            {
    +            resultsFrame.location.href = resultsPageWithSearch;
    +            }
    +
    +        // So if the results page is right and there's no IE bug, reperform the search on the existing page.  We have to check if there
    +        // are results because NoResults.html doesn't have any JavaScript, and it would be useless to do anything on that page even
    +        // if it did.
    +        else if (hasResultsPage)
    +            {
    +            // We need to check if this exists in case the frame is present but didn't finish loading.
    +            if (resultsFrame.searchResults)
    +                {  resultsFrame.searchResults.Search(searchValue);  }
    +
    +            // Otherwise just reload instead of waiting.
    +            else
    +                {  resultsFrame.location.href = resultsPageWithSearch;  };
    +            };
    +
    +
    +        var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
    +
    +        if (this.mode == "HTML" && domPopupSearchResultsWindow.style.display != "block")
    +            {
    +            var domSearchType = this.DOMSearchType();
    +
    +            var left = GetXPosition(domSearchType);
    +            var top = GetYPosition(domSearchType) + domSearchType.offsetHeight;
    +
    +            MoveToPosition(domPopupSearchResultsWindow, left, top);
    +            domPopupSearchResultsWindow.style.display = 'block';
    +            };
    +
    +
    +        this.lastSearchValue = searchValue;
    +        this.lastResultsPage = resultsPage;
    +        };
    +
    +
    +
    +    // Group: Activation Functions
    +    // Functions that handle whether the entire panel is active or not.
    +    // ________________________________________________________________________
    +
    +
    +    /*
    +        Function: Activate
    +
    +        Activates or deactivates the search panel, resetting things to their default values if necessary.  You can call this on every
    +        control's OnBlur() and it will handle not deactivating the entire panel when focus is just switching between them transparently.
    +
    +        Parameters:
    +
    +            isActive - Whether you're activating or deactivating the panel.
    +            ignoreDeactivateDelay - Set if you're positive the action will deactivate the panel and thus want to skip the delay.
    +    */
    +    this.Activate = function(isActive, ignoreDeactivateDelay)
    +        {
    +        // We want to ignore isActive being false while the results window is open.
    +        if (isActive || (this.mode == "HTML" && this.DOMPopupSearchResultsWindow().style.display == "block"))
    +            {
    +            if (this.inactivateTimeout)
    +                {
    +                clearTimeout(this.inactivateTimeout);
    +                this.inactivateTimeout = 0;
    +                };
    +
    +            this.DOMSearchPanel().className = 'MSearchPanelActive';
    +
    +            var searchField = this.DOMSearchField();
    +
    +            if (searchField.value == 'Search')
    +                 {  searchField.value = "";  }
    +            }
    +        else if (!ignoreDeactivateDelay)
    +            {
    +            this.inactivateTimeout = setTimeout(this.name + ".InactivateAfterTimeout()", this.inactivateTimeoutLength);
    +            }
    +        else
    +            {
    +            this.InactivateAfterTimeout();
    +            };
    +        };
    +
    +
    +    /*
    +        Function: InactivateAfterTimeout
    +
    +        Called by <inactivateTimeout>, which is set by <Activate()>.  Inactivation occurs on a timeout because a control may
    +        receive OnBlur() when focus is really transferring to another control in the search panel.  In this case we don't want to
    +        actually deactivate the panel because not only would that cause a visible flicker but it could also reset the search value.
    +        So by doing it on a timeout instead, there's a short period where the second control's OnFocus() can cancel the deactivation.
    +    */
    +    this.InactivateAfterTimeout = function()
    +        {
    +        this.inactivateTimeout = 0;
    +
    +        this.DOMSearchPanel().className = 'MSearchPanelInactive';
    +        this.DOMSearchField().value = "Search";
    +
    +	    this.lastSearchValue = "";
    +	    this.lastResultsPage = "";
    +        };
    +    };
    +
    +
    +
    +
    +/* ________________________________________________________________________________________________________
    +
    +   Class: SearchResults
    +   _________________________________________________________________________________________________________
    +
    +   The class that handles everything on the search results page.
    +   _________________________________________________________________________________________________________
    +*/
    +
    +
    +function SearchResults(name, mode)
    +    {
    +    /*
    +        var: mode
    +        The mode the search is going to work in, such as "HTML" or "FramedHTML".
    +    */
    +    this.mode = mode;
    +
    +    /*
    +        var: lastMatchCount
    +        The number of matches from the last run of <Search()>.
    +    */
    +    this.lastMatchCount = 0;
    +
    +
    +    /*
    +        Function: Toggle
    +        Toggles the visibility of the passed element ID.
    +    */
    +    this.Toggle = function(id)
    +        {
    +        if (this.mode == "FramedHTML")
    +            {  return;  };
    +
    +        var parentElement = document.getElementById(id);
    +
    +        var element = parentElement.firstChild;
    +
    +        while (element && element != parentElement)
    +            {
    +            if (element.nodeName == 'DIV' && element.className == 'ISubIndex')
    +                {
    +                if (element.style.display == 'block')
    +                    {  element.style.display = "none";  }
    +                else
    +                    {  element.style.display = 'block';  }
    +                };
    +
    +            if (element.nodeName == 'DIV' && element.hasChildNodes())
    +                {  element = element.firstChild;  }
    +            else if (element.nextSibling)
    +                {  element = element.nextSibling;  }
    +            else
    +                {
    +                do
    +                    {
    +                    element = element.parentNode;
    +                    }
    +                while (element && element != parentElement && !element.nextSibling);
    +
    +                if (element && element != parentElement)
    +                    {  element = element.nextSibling;  };
    +                };
    +            };
    +        };
    +
    +
    +    /*
    +        Function: Search
    +
    +        Searches for the passed string.  If there is no parameter, it takes it from the URL query.
    +
    +        Always returns true, since other documents may try to call it and that may or may not be possible.
    +    */
    +    this.Search = function(search)
    +        {
    +        if (!search)
    +            {
    +            search = window.location.search;
    +            search = search.substring(1);  // Remove the leading ?
    +            search = unescape(search);
    +            };
    +
    +        search = search.replace(/^ +/, "");
    +        search = search.replace(/ +$/, "");
    +        search = search.toLowerCase();
    +
    +        if (search.match(/[^a-z0-9]/)) // Just a little speedup so it doesn't have to go through the below unnecessarily.
    +            {
    +            search = search.replace(/\_/g, "_und");
    +            search = search.replace(/\ +/gi, "_spc");
    +            search = search.replace(/\~/g, "_til");
    +            search = search.replace(/\!/g, "_exc");
    +            search = search.replace(/\@/g, "_att");
    +            search = search.replace(/\#/g, "_num");
    +            search = search.replace(/\$/g, "_dol");
    +            search = search.replace(/\%/g, "_pct");
    +            search = search.replace(/\^/g, "_car");
    +            search = search.replace(/\&/g, "_amp");
    +            search = search.replace(/\*/g, "_ast");
    +            search = search.replace(/\(/g, "_lpa");
    +            search = search.replace(/\)/g, "_rpa");
    +            search = search.replace(/\-/g, "_min");
    +            search = search.replace(/\+/g, "_plu");
    +            search = search.replace(/\=/g, "_equ");
    +            search = search.replace(/\{/g, "_lbc");
    +            search = search.replace(/\}/g, "_rbc");
    +            search = search.replace(/\[/g, "_lbk");
    +            search = search.replace(/\]/g, "_rbk");
    +            search = search.replace(/\:/g, "_col");
    +            search = search.replace(/\;/g, "_sco");
    +            search = search.replace(/\"/g, "_quo");
    +            search = search.replace(/\'/g, "_apo");
    +            search = search.replace(/\</g, "_lan");
    +            search = search.replace(/\>/g, "_ran");
    +            search = search.replace(/\,/g, "_com");
    +            search = search.replace(/\./g, "_per");
    +            search = search.replace(/\?/g, "_que");
    +            search = search.replace(/\//g, "_sla");
    +            search = search.replace(/[^a-z0-9\_]i/gi, "_zzz");
    +            };
    +
    +        var resultRows = document.getElementsByTagName("div");
    +        var matches = 0;
    +
    +        var i = 0;
    +        while (i < resultRows.length)
    +            {
    +            var row = resultRows.item(i);
    +
    +            if (row.className == "SRResult")
    +                {
    +                var rowMatchName = row.id.toLowerCase();
    +                rowMatchName = rowMatchName.replace(/^sr\d*_/, '');
    +
    +                if (search.length <= rowMatchName.length && rowMatchName.substr(0, search.length) == search)
    +                    {
    +                    row.style.display = "block";
    +                    matches++;
    +                    }
    +                else
    +                    {  row.style.display = "none";  };
    +                };
    +
    +            i++;
    +            };
    +
    +        document.getElementById("Searching").style.display="none";
    +
    +        if (matches == 0)
    +            {  document.getElementById("NoMatches").style.display="block";  }
    +        else
    +            {  document.getElementById("NoMatches").style.display="none";  }
    +
    +        this.lastMatchCount = matches;
    +
    +        return true;
    +        };
    +    };
    +
    diff --git a/vendor/naturaldocs/License.txt b/vendor/naturaldocs/License.txt
    new file mode 100644
    index 000000000..70aabbed8
    --- /dev/null
    +++ b/vendor/naturaldocs/License.txt
    @@ -0,0 +1,275 @@
    +Title: License
    +
    +Natural Docs is Copyright © 2003-2010 Greg Valure.  Natural Docs is licensed under version 3 of the GNU
    +Affero General Public License (AGPL).
    +
    +Natural Docs incorporates code from Google Prettify, which is Copyright © 2006 Google Inc.  Google Prettify
    +may be obtained separately under version 2.0 of the Apache License.  However, this combined product is still
    +licensed under the terms of the AGPLv3.
    +
    +Portions of Natural Docs may be distributed with generated documentation files in order to help them function,
    +such as JavaScript and CSS files.  These individual files retain their copyright and licensing terms, but they do
    +not apply to the remainder of the generated documentation.  All other generated documentation files remain
    +under the copyright and distribution terms decided by its author(s).
    +
    +
    +Topic: GNU Affero General Public License
    +
    +	Version 3, 19 November 2007
    +
    +	Copyright © 2007 Free Software Foundation, Inc.  <http://fsf.org/>
    +
    +	Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
    +
    +
    +	Preamble:
    +
    +	The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.
    +
    +	The licenses for most software and other practical works are designed to take away your freedom to share and change the works.  By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.
    +
    +	When we speak of free software, we are referring to freedom, not price.  Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
    +
    +	Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.
    +
    +	A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate.  Many developers of free software are heartened and encouraged by the resulting cooperation.  However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.
    +
    +	The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community.  It requires the operator of a network server to provide the source code of the modified version running there to the users of that server.  Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.
    +
    +	An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals.  This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.
    +
    +	The precise terms and conditions for copying, distribution and modification follow.
    +
    +
    +	TERMS AND CONDITIONS:
    +
    +	0. Definitions:
    +
    +	"This License" refers to version 3 of the GNU Affero General Public License.
    +
    +	"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
    +
    +	"The Program" refers to any copyrightable work licensed under this License.  Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations
    +
    +	To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy.  The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.
    +
    +	A "covered work" means either the unmodified Program or a work based on the Program.
    +
    +	To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy.  Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
    +
    +	To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
    +
    +	An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this
    +	License, and how to view a copy of this License.  If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
    +
    +
    +	1. Source Code:
    +
    +	The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.
    +
    +	A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
    +
    +	The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
    +
    +	The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
    +
    +	The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
    +
    +	The Corresponding Source for a work in source code form is that same work.
    +
    +
    +	2. Basic Permissions:
    +
    +	All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
    +
    +	You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
    +
    +	Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
    +
    +
    +	3. Protecting Users' Legal Rights From Anti-Circumvention Law:
    +
    +	No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
    +
    +	When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
    +
    +
    +	4. Conveying Verbatim Copies:
    +
    +	You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
    +
    +	You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
    +
    +
    +	5. Conveying Modified Source Versions:
    +
    +	You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code
    +under the terms of section 4, provided that you also meet all of these conditions:
    +
    +	    * a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
    +	    * b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".
    +	    * c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
    +	    * d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
    +
    +	A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
    +
    +
    +	6. Conveying Non-Source Forms:
    +
    +	You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the
    +machine-readable Corresponding Source under the terms of this License, in one of these ways:
    +
    +	    * a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
    +	    * b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
    +	    * c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
    +	    * d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
    +	    * e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
    +
    +	A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
    +
    +	A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
    +
    +	"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
    +
    +	If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
    +
    +	The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
    +
    +	Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
    +
    +
    +	7. Additional Terms:
    +
    +	"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
    +
    +	When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
    +
    +	Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the
    +copyright holders of that material) supplement the terms of this License with terms:
    +
    +	    * a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
    +	    * b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
    +	    * c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
    +	    * d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
    +	    * e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
    +	    * f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
    +
    +	All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
    +
    +	If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
    +
    +	Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
    +
    +
    +	8. Termination:
    +
    +	You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
    +
    +	However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
    +
    +	Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
    +
    +	Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
    +
    +
    +	9. Acceptance Not Required for Having Copies:
    +
    +	You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
    +
    +
    +	10. Automatic Licensing of Downstream Recipients:
    +
    +	Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
    +
    +	An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
    +
    +	You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
    +
    +
    +	11. Patents:
    +
    +	A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".
    +
    +	A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
    +
    +	Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
    +
    +	In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
    +
    +	If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
    +
    +	If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
    +
    +	A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
    +
    +	Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
    +
    +
    +	12. No Surrender of Others' Freedom:
    +
    +	If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
    +
    +
    +	13. Remote Network Interaction; Use with the GNU General Public License:
    +
    +	Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.
    +
    +	Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.
    +
    +
    +	14. Revised Versions of this License:
    +
    +	The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
    +
    +	Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.
    +
    +	If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
    +
    +	Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
    +
    +
    +	15. Disclaimer of Warranty:
    +
    +	THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    +
    +
    +	16. Limitation of Liability:
    +
    +	IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
    +
    +
    +	17. Interpretation of Sections 15 and 16:
    +
    +	If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
    +
    +	END OF TERMS AND CONDITIONS
    +
    +
    +How to Apply These Terms to Your New Programs:
    +
    +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
    +
    +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
    +
    +    <one line to give the program's name and a brief idea of what it does.>
    +    Copyright (C) <year>  <name of author>
    +
    +    This program is free software: you can redistribute it and/or modify
    +    it under the terms of the GNU Affero General Public License as
    +    published by the Free Software Foundation, either version 3 of the
    +    License, or (at your option) any later version.
    +
    +    This program is distributed in the hope that it will be useful,
    +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +    GNU Affero General Public License for more details.
    +
    +    You should have received a copy of the GNU Affero General Public License
    +    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    +
    +Also add information on how to contact you by electronic and paper mail.
    +
    +If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
    +
    +You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
    +
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/BinaryFile.pm b/vendor/naturaldocs/Modules/NaturalDocs/BinaryFile.pm
    new file mode 100644
    index 000000000..ff9a444b1
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/BinaryFile.pm
    @@ -0,0 +1,295 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::BinaryFile
    +#
    +###############################################################################
    +#
    +#   A package to manage Natural Docs' binary data files.
    +#
    +#   Usage:
    +#
    +#       - Only one data file can be managed with this package at a time.  You must close the file before opening another
    +#         one.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::BinaryFile;
    +
    +use vars qw(@EXPORT @ISA);
    +require Exporter;
    +@ISA = qw(Exporter);
    +
    +@EXPORT = ('BINARY_FORMAT');
    +
    +
    +###############################################################################
    +# Group: Format
    +
    +#
    +#   Topic: Standard Header
    +#
    +#   > [UInt8: BINARY_FORMAT]
    +#   > [VersionInt: app version]
    +#
    +#   The first byte is <BINARY_FORMAT>, which distinguishes binary configuration files from text ones, since Natural Docs
    +#   used to use text data files with the same name.
    +#
    +#   The next section is the version of Natural Docs that wrote the file, as defined by <NaturalDocs::Settings->AppVersion>
    +#   and written by <NaturalDocs::Version->ToBinaryFile()>.
    +#
    +
    +#
    +#   Topic: Data Types
    +#
    +#   All the integer data types are written most significant byte first, aka big endian.
    +#
    +#   An AString16 is a UInt16 followed by that many 8-bit ASCII characters.  It doesn't include a null character at the end.  Undef
    +#   strings are represented by a zero for the UInt16 and nothing following it.
    +#
    +
    +#
    +#   Constant: BINARY_FORMAT
    +#
    +#   An 8-bit constant that's used as the first byte of binary data files.  This is used so that you can easily distinguish between
    +#   binary and old-style text data files.  It's not a character that would appear in plain text files.
    +#
    +use constant BINARY_FORMAT => pack('C', 0x06);
    +# Which is ACK or acknowledge in ASCII.  Is the cool spade character in DOS displays.
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +#
    +#   handle: FH_BINARYDATAFILE
    +#
    +#   The file handle used for the data file.
    +#
    +
    +
    +#
    +#   string: currentFile
    +#
    +#   The <FileName> for the current configuration file being parsed.
    +#
    +my $currentFile;
    +
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: OpenForReading
    +#
    +#   Opens a binary file for reading.
    +#
    +#   Parameters:
    +#
    +#       minimumVersion - The minimum version of the file format that is acceptible.  May be undef.
    +#
    +#   Returns:
    +#
    +#       The format <VersionInt> or undef if it failed.  It could fail for any of the following reasons.
    +#
    +#       - The file doesn't exist.
    +#       - The file couldn't be opened.
    +#       - The file didn't have the proper header.
    +#       - Either the application or the file was from a development release, and they're not the exact same development release.
    +#       - The file's format was less than the minimum version, if one was defined.
    +#       - The file was from a later application version than the current.
    +#
    +sub OpenForReading #(FileName file, optional VersionInt minimumVersion) => VersionInt
    +    {
    +    my ($self, $file, $minimumVersion) = @_;
    +
    +    if (defined $currentFile)
    +        {  die "Tried to open binary file " . $file . " for reading when " . $currentFile . " was already open.";  };
    +
    +    $currentFile = $file;
    +
    +    if (open(FH_BINARYDATAFILE, '<' . $currentFile))
    +        {
    +        # See if it's binary.
    +        binmode(FH_BINARYDATAFILE);
    +
    +        my $firstChar;
    +        read(FH_BINARYDATAFILE, $firstChar, 1);
    +
    +        if ($firstChar == ::BINARY_FORMAT())
    +            {
    +            my $version = NaturalDocs::Version->FromBinaryFile(\*FH_BINARYDATAFILE);
    +
    +            if (NaturalDocs::Version->CheckFileFormat($version, $minimumVersion))
    +                {  return $version;  };
    +            };
    +
    +        close(FH_BINARYDATAFILE);
    +        };
    +
    +    $currentFile = undef;
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: OpenForWriting
    +#
    +#   Opens a binary file for writing and writes the standard header.  Dies if the file cannot be opened.
    +#
    +sub OpenForWriting #(FileName file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $currentFile)
    +        {  die "Tried to open binary file " . $file . " for writing when " . $currentFile . " was already open.";  };
    +
    +    $currentFile = $file;
    +
    +    open (FH_BINARYDATAFILE, '>' . $currentFile)
    +        or die "Couldn't save " . $file . ".\n";
    +
    +    binmode(FH_BINARYDATAFILE);
    +
    +    print FH_BINARYDATAFILE '' . ::BINARY_FORMAT();
    +    NaturalDocs::Version->ToBinaryFile(\*FH_BINARYDATAFILE, NaturalDocs::Settings->AppVersion());
    +    };
    +
    +
    +#
    +#   Function: Close
    +#
    +#   Closes the current configuration file.
    +#
    +sub Close
    +    {
    +    my $self = shift;
    +
    +    if (!$currentFile)
    +        {  die "Tried to close a binary file when one wasn't open.";  };
    +
    +    close(FH_BINARYDATAFILE);
    +    $currentFile = undef;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Reading Functions
    +
    +
    +#
    +#   Function: GetUInt8
    +#   Reads and returns a UInt8 from the open file.
    +#
    +sub GetUInt8 # => UInt8
    +    {
    +    my $raw;
    +    read(FH_BINARYDATAFILE, $raw, 1);
    +
    +    return unpack('C', $raw);
    +    };
    +
    +#
    +#   Function: GetUInt16
    +#   Reads and returns a UInt16 from the open file.
    +#
    +sub GetUInt16 # => UInt16
    +    {
    +    my $raw;
    +    read(FH_BINARYDATAFILE, $raw, 2);
    +
    +    return unpack('n', $raw);
    +    };
    +
    +#
    +#   Function: GetUInt32
    +#   Reads and returns a UInt32 from the open file.
    +#
    +sub GetUInt32 # => UInt32
    +    {
    +    my $raw;
    +    read(FH_BINARYDATAFILE, $raw, 4);
    +
    +    return unpack('N', $raw);
    +    };
    +
    +#
    +#   Function: GetAString16
    +#   Reads and returns an AString16 from the open file.  Supports undef strings.
    +#
    +sub GetAString16 # => string
    +    {
    +    my $rawLength;
    +    read(FH_BINARYDATAFILE, $rawLength, 2);
    +    my $length = unpack('n', $rawLength);
    +
    +    if (!$length)
    +        {  return undef;  };
    +
    +    my $string;
    +    read(FH_BINARYDATAFILE, $string, $length);
    +
    +    return $string;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Writing Functions
    +
    +
    +#
    +#   Function: WriteUInt8
    +#   Writes a UInt8 to the open file.
    +#
    +sub WriteUInt8 #(UInt8 value)
    +    {
    +    my ($self, $value) = @_;
    +    print FH_BINARYDATAFILE pack('C', $value);
    +    };
    +
    +#
    +#   Function: WriteUInt16
    +#   Writes a UInt32 to the open file.
    +#
    +sub WriteUInt16 #(UInt16 value)
    +    {
    +    my ($self, $value) = @_;
    +    print FH_BINARYDATAFILE pack('n', $value);
    +    };
    +
    +#
    +#   Function: WriteUInt32
    +#   Writes a UInt32 to the open file.
    +#
    +sub WriteUInt32 #(UInt32 value)
    +    {
    +    my ($self, $value) = @_;
    +    print FH_BINARYDATAFILE pack('N', $value);
    +    };
    +
    +#
    +#   Function: WriteAString16
    +#   Writes an AString16 to the open file.  Supports undef strings.
    +#
    +sub WriteAString16 #(string value)
    +    {
    +    my ($self, $string) = @_;
    +
    +    if (length($string))
    +        {  print FH_BINARYDATAFILE pack('nA*', length($string), $string);  }
    +    else
    +        {  print FH_BINARYDATAFILE pack('n', 0);  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Builder.pm b/vendor/naturaldocs/Modules/NaturalDocs/Builder.pm
    new file mode 100644
    index 000000000..e8b1cd37a
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Builder.pm
    @@ -0,0 +1,281 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Builder
    +#
    +###############################################################################
    +#
    +#   A package that takes parsed source file and builds the output for it.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - <Add()> can be called immediately.
    +#       - <OutputPackages()> and <OutputPackageOf()> can be called once all sub-packages have been registered via <Add()>.
    +#         Since this is normally done in their INIT functions, they should be available to all normal functions immediately.
    +#
    +#       - Prior to calling <Run()>, <NaturalDocs::Settings>, <NaturalDocs::Project>, <NaturalDocs::Menu>, and
    +#         <NaturalDocs::Parser> must be initialized.  <NaturalDocs::Settings->GenerateDirectoryNames()> must be called.
    +#         <NaturalDocs::SymbolTable> and <NaturalDocs::ClassHierarchy> must be initialized and fully resolved.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::Builder::Base;
    +use NaturalDocs::Builder::HTML;
    +use NaturalDocs::Builder::FramedHTML;
    +
    +package NaturalDocs::Builder;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +#
    +#   Array: outputPackages
    +#
    +#   An array of the output packages available for use.
    +#
    +my @outputPackages;
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: OutputPackages
    +#
    +#   Returns an arrayref of the output packages available for use.  The arrayref is not a copy of the data, so don't change it.
    +#
    +#   Add output packages to this list with the <Add()> function.
    +#
    +sub OutputPackages
    +    {  return \@outputPackages;  };
    +
    +
    +#
    +#   Function: OutputPackageOf
    +#
    +#   Returns the output package corresponding to the passed command line option, or undef if none.
    +#
    +sub OutputPackageOf #(commandLineOption)
    +    {
    +    my ($self, $commandLineOption) = @_;
    +
    +    $commandLineOption = lc($commandLineOption);
    +
    +    foreach my $package (@outputPackages)
    +        {
    +        if (lc($package->CommandLineOption()) eq $commandLineOption)
    +            {  return $package;  };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +
    +#
    +#   Function: Add
    +#
    +#   Adds an output package to those available for use.  All output packages must call this function in order to be recognized.
    +#
    +#   Parameters:
    +#
    +#       package - The package name.
    +#
    +sub Add #(package)
    +    {
    +    my ($self, $package) = @_;
    +
    +    # Output packages shouldn't register themselves more than once, so we don't need to check for it.
    +    push @outputPackages, $package;
    +    };
    +
    +
    +#
    +#   Function: Run
    +#
    +#   Runs the build process.  This must be called *every time* Natural Docs is run, regardless of whether any source files changed
    +#   or not.  Some output packages have dependencies on files outside of the source tree that need to be checked.
    +#
    +#   Since there are multiple stages to the build process, this function will handle its own status messages.  There's no need to print
    +#   "Building files..." or something similar beforehand.
    +#
    +sub Run
    +    {
    +    my ($self) = @_;
    +
    +
    +    # Determine what we're doing.
    +
    +    my $buildTargets = NaturalDocs::Settings->BuildTargets();
    +
    +    my $filesToBuild = NaturalDocs::Project->FilesToBuild();
    +    my $numberOfFilesToBuild = (scalar keys %$filesToBuild) * (scalar @$buildTargets);
    +
    +    my $filesToPurge = NaturalDocs::Project->FilesToPurge();
    +    my $numberOfFilesToPurge = (scalar keys %$filesToPurge) * (scalar @$buildTargets);
    +
    +    my $imagesToUpdate = NaturalDocs::Project->ImageFilesToUpdate();
    +    my $numberOfImagesToUpdate = (scalar keys %$imagesToUpdate) * (scalar @$buildTargets);
    +
    +    my $imagesToPurge = NaturalDocs::Project->ImageFilesToPurge();
    +    my $numberOfImagesToPurge = (scalar keys %$imagesToPurge) * (scalar @$buildTargets);
    +
    +    my %indexesToBuild;
    +    my %indexesToPurge;
    +
    +    my $currentIndexes = NaturalDocs::Menu->Indexes();
    +    my $previousIndexes = NaturalDocs::Menu->PreviousIndexes();
    +
    +    foreach my $index (keys %$currentIndexes)
    +        {
    +        if (NaturalDocs::SymbolTable->IndexChanged($index) || !exists $previousIndexes->{$index})
    +            {
    +            $indexesToBuild{$index} = 1;
    +            };
    +        };
    +
    +    # All indexes that still exist should have been deleted.
    +    foreach my $index (keys %$previousIndexes)
    +        {
    +        if (!exists $currentIndexes->{$index})
    +            {
    +            $indexesToPurge{$index} = 1;
    +            };
    +        };
    +
    +    my $numberOfIndexesToBuild = (scalar keys %indexesToBuild) * (scalar @$buildTargets);
    +    my $numberOfIndexesToPurge = (scalar keys %indexesToPurge) * (scalar @$buildTargets);
    +
    +
    +    # Start the build process
    +
    +    foreach my $buildTarget (@$buildTargets)
    +        {
    +        $buildTarget->Builder()->BeginBuild( $numberOfFilesToBuild || $numberOfFilesToPurge ||
    +                                                               $numberOfImagesToUpdate || $numberOfImagesToPurge ||
    +                                                               $numberOfIndexesToBuild || $numberOfIndexesToPurge ||
    +                                                               NaturalDocs::Menu->HasChanged() );
    +        };
    +
    +    if ($numberOfFilesToPurge)
    +        {
    +        NaturalDocs::StatusMessage->Start('Purging ' . $numberOfFilesToPurge
    +                                                          . ' file' . ($numberOfFilesToPurge > 1 ? 's' : '') . '...',
    +                                                             scalar @$buildTargets);
    +
    +        foreach my $buildTarget (@$buildTargets)
    +            {
    +            $buildTarget->Builder()->PurgeFiles($filesToPurge);
    +            NaturalDocs::StatusMessage->CompletedItem();
    +            };
    +        };
    +
    +    if ($numberOfIndexesToPurge)
    +        {
    +        NaturalDocs::StatusMessage->Start('Purging ' . $numberOfIndexesToPurge
    +                                                           . ' index' . ($numberOfIndexesToPurge > 1 ? 'es' : '') . '...',
    +                                                             scalar @$buildTargets);
    +
    +        foreach my $buildTarget (@$buildTargets)
    +            {
    +            $buildTarget->Builder()->PurgeIndexes(\%indexesToPurge);
    +            NaturalDocs::StatusMessage->CompletedItem();
    +            };
    +        };
    +
    +    if ($numberOfImagesToPurge)
    +        {
    +        NaturalDocs::StatusMessage->Start('Purging ' . $numberOfImagesToPurge
    +                                                          . ' image' . ($numberOfImagesToPurge > 1 ? 's' : '') . '...',
    +                                                             scalar @$buildTargets);
    +
    +        foreach my $buildTarget (@$buildTargets)
    +            {
    +            $buildTarget->Builder()->PurgeImages($imagesToPurge);
    +            NaturalDocs::StatusMessage->CompletedItem();
    +            };
    +        };
    +
    +    if ($numberOfFilesToBuild)
    +        {
    +        NaturalDocs::StatusMessage->Start('Building ' . $numberOfFilesToBuild
    +                                                           . ' file' . ($numberOfFilesToBuild > 1 ? 's' : '') . '...',
    +                                                             $numberOfFilesToBuild);
    +
    +        foreach my $file (keys %$filesToBuild)
    +            {
    +            my $parsedFile = NaturalDocs::Parser->ParseForBuild($file);
    +
    +            NaturalDocs::Error->OnStartBuilding($file);
    +
    +            foreach my $buildTarget (@$buildTargets)
    +                {
    +                $buildTarget->Builder()->BuildFile($file, $parsedFile);
    +                NaturalDocs::StatusMessage->CompletedItem();
    +                };
    +
    +            NaturalDocs::Error->OnEndBuilding($file);
    +            };
    +        };
    +
    +    if ($numberOfIndexesToBuild)
    +        {
    +        NaturalDocs::StatusMessage->Start('Building ' . $numberOfIndexesToBuild
    +                                                          . ' index' . ($numberOfIndexesToBuild > 1 ? 'es' : '') . '...',
    +                                                             $numberOfIndexesToBuild);
    +
    +        foreach my $index (keys %indexesToBuild)
    +            {
    +            foreach my $buildTarget (@$buildTargets)
    +                {
    +                $buildTarget->Builder()->BuildIndex($index);
    +                NaturalDocs::StatusMessage->CompletedItem();
    +                };
    +            };
    +        };
    +
    +    if ($numberOfImagesToUpdate)
    +        {
    +        NaturalDocs::StatusMessage->Start('Updating ' . $numberOfImagesToUpdate
    +                                                          . ' image' . ($numberOfImagesToUpdate > 1 ? 's' : '') . '...',
    +                                                             $numberOfImagesToUpdate);
    +
    +        foreach my $image (keys %$imagesToUpdate)
    +            {
    +            foreach my $buildTarget (@$buildTargets)
    +                {
    +                $buildTarget->Builder()->UpdateImage($image);
    +                NaturalDocs::StatusMessage->CompletedItem();
    +                };
    +            };
    +        };
    +
    +    if (NaturalDocs::Menu->HasChanged())
    +        {
    +        if (!NaturalDocs::Settings->IsQuiet())
    +            {  print "Updating menu...\n";  };
    +
    +        foreach my $buildTarget (@$buildTargets)
    +            {  $buildTarget->Builder()->UpdateMenu();  };
    +        };
    +
    +    foreach my $buildTarget (@$buildTargets)
    +        {
    +        $buildTarget->Builder()->EndBuild($numberOfFilesToBuild || $numberOfFilesToPurge ||
    +                                                           $numberOfIndexesToBuild || $numberOfIndexesToPurge ||
    +                                                           $numberOfImagesToUpdate || $numberOfImagesToPurge ||
    +                                                           NaturalDocs::Menu->HasChanged());
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Builder/Base.pm b/vendor/naturaldocs/Modules/NaturalDocs/Builder/Base.pm
    new file mode 100644
    index 000000000..75b3fd310
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Builder/Base.pm
    @@ -0,0 +1,349 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Builder::Base
    +#
    +###############################################################################
    +#
    +#   A base class for all Builder output formats.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Builder::Base;
    +
    +
    +###############################################################################
    +# Group: Notes
    +
    +
    +#
    +#   Topic: Implementation
    +#
    +#   Builder packages are implemented as blessed arrayrefs, not hashrefs.  This is done for all objects in Natural Docs for
    +#   efficiency reasons.  You create members by defining constants via <NaturalDocs::DefineMembers> and using them as
    +#   indexes into the array.
    +#
    +
    +#
    +#   Topic: Function Order
    +#
    +#   The functions in the build process will always be called in the following order.
    +#
    +#   - <BeginBuild()> will always be called.
    +#   - <PurgeFiles()> will be called next only if there's files that need to be purged.
    +#   - <PurgeIndexes()> will be called next only if there's indexes that need to be purged.
    +#   - <PurgeImages()> will e called next only if there's images that need to be purged.
    +#   - <BuildFile()> will be called once for each file that needs to be built, if any.
    +#   - <BuildIndex()> will be called once for each index that changed and is part of the menu, if any.
    +#   - <UpdateImage()> will be called once for each image that needs to be updated, if any.
    +#   - <UpdateMenu()> will be called next only if the menu changed.
    +#   - <EndBuild()> will always be called.
    +#
    +
    +#
    +#   Topic: How to Approach
    +#
    +#   Here's an idea of how to approach making packages for different output types.
    +#
    +#
    +#   Multiple Output Files, Embedded Menu:
    +#
    +#       This example is for when you want to build one output file per source file, each with its own copy of the menu within it.
    +#       This is how <NaturalDocs::Builder::HTML> works.
    +#
    +#       Make sure you create a function that generates just the menu for a particular source file.  We'll need to generate menus for
    +#       both building a file from scratch and for updating the menu on an existing output file, so it's better to give it its own function.
    +#       You may want to surround it with something that can be easily detected in the output file to make replacing easier.
    +#
    +#       <BeginBuild()> isn't important.  You don't need to implement it.
    +#
    +#       Implement <PurgeFiles()> to delete the output files associated with the purged files.
    +#
    +#       Implement <PurgeIndexes()> to delete the output files associated with the purged indexes.
    +#
    +#       Implement <BuildFile()> to create an output file for the parsed source file.  Use the menu function described earlier.
    +#
    +#       Implement <BuildIndex()> to create an output file for each index.  Use the menu function described earlier for each page.
    +#
    +#       Implement <UpdateMenu()> to go through the list of unbuilt files and update their menus.  You can get the list from
    +#       <NaturalDocs::Project->UnbuiltFilesWithContent()>.  You need to open their output files, replace the menu, and save it back
    +#       to disk.  Yes, it would be simpler from a programmer's point of view to just rebuild the file completely, but that would be
    +#       _very_ inefficient since there could potentially be a _lot_ of files in this group.
    +#
    +#       Also make sure <UpdateMenu()> goes through the unchanged indexes and updates them as well.
    +#
    +#       <EndBuild()> isn't important.  You don't need to implement it.
    +#
    +#
    +#   Multiple Output Files, Menu in File:
    +#
    +#       This example is for when you want to build one output file per source file, but keep the menu in its own separate file.  This
    +#       is how <NaturalDocs::Builder::FramedHTML> works.
    +#
    +#       <BeginBuild()> isn't important.  You don't need to implement it.
    +#
    +#       Implement <PurgeFiles()> to delete the output files associated with the purged files.
    +#
    +#       Implement <PurgeIndexes()> to delete the output files associated with the purged indexes.
    +#
    +#       Implement <BuildFile()> to generate an output file from the parsed source file.
    +#
    +#       Implement <BuildIndex()> to generate an output file for each index.
    +#
    +#       Implement <UpdateMenu()> to rebuild the menu file.
    +#
    +#       <EndBuild()> isn't important.  You don't need to implement it.
    +#
    +#
    +#   Single Output File using Intermediate Files:
    +#
    +#       This example is for when you want to build one output file, such as a PDF file, but use intermediate files to handle differential
    +#       building.  This would be much like how a compiler compiles each source file into a object file, and then a linker stitches them
    +#       all together into the final executable file.
    +#
    +#       <BeginBuild()> isn't important.  You don't need to implement it.
    +#
    +#       Implement <PurgeFiles()> to delete the intermediate files associated with the purged files.
    +#
    +#       Implement <PurgeIndexes()> to delete the intermediate files associated with the purged indexes.
    +#
    +#       Implement <BuildFile()> to generate an intermediate file from the parsed source file.
    +#
    +#       Implement <BuildIndex()> to generate an intermediate file for the specified index.
    +#
    +#       Implement <UpdateMenu()> to generate the intermediate file for the menu.
    +#
    +#       Implement <EndBuild()> so that if the project changed, it stitches the intermediate files together into the final
    +#       output file.  Make sure you check the parameter because the function will be called when nothing changes too.
    +#
    +#
    +#   Single Output File using Direct Changes:
    +#
    +#       This example is for when you want to build one output file, such as a PDF file, but engineering it in such a way that you don't
    +#       need to use intermediate files.  In other words, you're able to add, delete, and modify entries directly in the output file.
    +#
    +#       Implement <BeginBuild()> so that if the project changed, it opens the output file and does anything it needs to do
    +#       to get ready for editing.
    +#
    +#       Implement <PurgeFiles()> to remove the entries associated with the purged files.
    +#
    +#       Implement <PurgeIndexes()> to remove the entries associated with the purged indexes.
    +#
    +#       Implement <BuildFile()> to add or replace a section of the output file with a new one generated from the parsed file.
    +#
    +#       Implement <BuildIndex()> to add or replace an index in the output file with a new one generated from the specified index.
    +#
    +#       Implement <EndBuild()> so that if the project changed, it saves the output file to disk.
    +#
    +#       How you handle the menu depends on how the output file references other sections of itself.  If it can do so by name, then
    +#       you can implement <UpdateMenu()> to update the menu section of the file and you're done.  If it has to reference itself
    +#       by address or offset, it gets trickier.  You should skip <UpdateMenu()> and instead rebuild the menu in <EndBuild()> if
    +#       the parameter is true.  This lets you do it whenever anything changes in a file, rather than just when the menu
    +#       visibly changes.  How you keep track of the locations and how they change is your problem.
    +#
    +
    +
    +###############################################################################
    +#
    +#   Group: Required Interface Functions
    +#
    +#   All Builder classes *must* define these functions.
    +#
    +
    +
    +#
    +#   Function: INIT
    +#
    +#   Define this function to call <NaturalDocs::Builder->Add()> so that <NaturalDocs::Builder> knows about this package.
    +#   Packages are defined this way so that new ones can be added without messing around in other code.
    +#
    +
    +
    +#
    +#   Function: CommandLineOption
    +#
    +#   Define this function to return the text that should be put in the command line after -o to use this package.  It cannot have
    +#   spaces and is not case sensitive.
    +#
    +#   For example, <NaturalDocs::Builder::HTML> returns 'html' so someone could use -o html [directory] to use that package.
    +#
    +sub CommandLineOption
    +    {
    +    NaturalDocs::Error->SoftDeath($_[0] . " didn't define CommandLineOption().");
    +    };
    +
    +
    +#
    +#   Function: BuildFile
    +#
    +#   Define this function to convert a parsed file to this package's output format.  This function will be called once for every source
    +#   file that needs to be rebuilt.  However, if a file hasn't changed since the last time Natural Docs was run, it will not be sent to
    +#   this function.  All packages must support differential build.
    +#
    +#   Parameters:
    +#
    +#       sourceFile  - The name of the source file.
    +#       parsedFile  - The parsed source file, as an arrayref of <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +sub BuildFile #(sourceFile, parsedFile)
    +    {
    +    NaturalDocs::Error->SoftDeath($_[0] . " didn't define BuildFile().");
    +    };
    +
    +
    +###############################################################################
    +#
    +#   Group: Optional Interface Functions
    +#
    +#   These functions can be implemented but packages are not required to do so.
    +#
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Note that this is the only function where the first parameter will be the package name, not the object itself.
    +#
    +sub New
    +    {
    +    my $package = shift;
    +
    +    my $object = [ ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: BeginBuild
    +#
    +#   Define this function if the package needs to do anything at the beginning of the build process.  This function will be called
    +#   every time Natural Docs is run, even if the project hasn't changed.  This allows you to manage dependencies specific
    +#   to the output format that may change independently from the source tree and menu.  For example,
    +#   <NaturalDocs::Builder::HTML> needs to keep the CSS files in sync regardless of whether the source tree changed or not.
    +#
    +#   Parameters:
    +#
    +#       hasChanged - Whether the project has changed, such as source files or the menu file.  If false, nothing else is going to be
    +#                            called except <EndBuild()>.
    +#
    +sub BeginBuild #(hasChanged)
    +    {
    +    };
    +
    +
    +#
    +#   Function: EndBuild
    +#
    +#   Define this function if the package needs to do anything at the end of the build process.  This function will be called every time
    +#   Natural Docs is run, even if the project hasn't changed.  This allows you to manage dependencies specific to the output
    +#   format that may change independently from the source tree.  For example, <NaturalDocs::Builder::HTML> needs to keep the
    +#   CSS files in sync regardless of whether the source tree changed or not.
    +#
    +#   Parameters:
    +#
    +#       hasChanged - Whether the project has changed, such as source files or the menu file.  If false, the only other function that
    +#                            was called was <BeginBuild()>.
    +#
    +sub EndBuild #(hasChanged)
    +    {
    +    };
    +
    +
    +#
    +#   Function: BuildIndex
    +#
    +#   Define this function to create an index for the passed topic.  You can get the index from
    +#   <NaturalDocs::SymbolTable->Index()>.
    +#
    +#   The reason it's not passed directly to this function is because indexes may be time-consuming to create.  As such, they're
    +#   generated on demand because some output packages may choose not to implement them.
    +#
    +#   Parameters:
    +#
    +#       topic  - The <TopicType> to limit the index by.
    +#
    +sub BuildIndex #(topic)
    +    {
    +    };
    +
    +
    +#
    +#   Function: UpdateImage
    +#
    +#   Define this function to add or update the passed image in the output.
    +#
    +#   Parameters:
    +#
    +#       file - The image <FileName>
    +#
    +sub UpdateImage #(file)
    +    {
    +    };
    +
    +
    +#
    +#   Function: PurgeFiles
    +#
    +#   Define this function to make the package remove all output related to the passed files.  These files no longer have Natural Docs
    +#   content.
    +#
    +#   Parameters:
    +#
    +#       files - An existence hashref of the files to purge.
    +#
    +sub PurgeFiles #(files)
    +    {
    +    };
    +
    +
    +#
    +#   Function: PurgeIndexes
    +#
    +#   Define this function to make the package remove all output related to the passed indexes.  These indexes are no longer part
    +#   of the menu.
    +#
    +#   Parameters:
    +#
    +#       indexes  - An existence hashref of the <TopicTypes> of the indexes to purge.
    +#
    +sub PurgeIndexes #(indexes)
    +    {
    +    };
    +
    +
    +#
    +#   Function: PurgeImages
    +#
    +#   Define this function to make the package remove all output related to the passed image files.  These files are no longer used
    +#   by the documentation.
    +#
    +#   Parameters:
    +#
    +#       files - An existence hashref of the image <FileNames> to purge.
    +#
    +sub PurgeImages #(files)
    +    {
    +    };
    +
    +
    +#
    +#   Function: UpdateMenu
    +#
    +#   Define this function to make the package update the menu.  It will only be called if the menu changed.
    +#
    +sub UpdateMenu
    +    {
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Builder/FramedHTML.pm b/vendor/naturaldocs/Modules/NaturalDocs/Builder/FramedHTML.pm
    new file mode 100644
    index 000000000..c29528a39
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Builder/FramedHTML.pm
    @@ -0,0 +1,354 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Builder::FramedHTML
    +#
    +###############################################################################
    +#
    +#   A package that generates output in HTML with frames.
    +#
    +#   All functions are called with Package->Function() notation.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Builder::FramedHTML;
    +
    +use base 'NaturalDocs::Builder::HTMLBase';
    +
    +
    +###############################################################################
    +# Group: Implemented Interface Functions
    +
    +
    +#
    +#   Function: INIT
    +#
    +#   Registers the package with <NaturalDocs::Builder>.
    +#
    +sub INIT
    +    {
    +    NaturalDocs::Builder->Add(__PACKAGE__);
    +    };
    +
    +
    +#
    +#   Function: CommandLineOption
    +#
    +#   Returns the option to follow -o to use this package.  In this case, "html".
    +#
    +sub CommandLineOption
    +    {
    +    return 'FramedHTML';
    +    };
    +
    +
    +#
    +#   Function: BuildFile
    +#
    +#   Builds the output file from the parsed source file.
    +#
    +#   Parameters:
    +#
    +#       sourcefile       - The <FileName> of the source file.
    +#       parsedFile      - An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +sub BuildFile #(sourceFile, parsedFile)
    +    {
    +    my ($self, $sourceFile, $parsedFile) = @_;
    +
    +    my $outputFile = $self->OutputFileOf($sourceFile);
    +
    +
    +    # 99.99% of the time the output directory will already exist, so this will actually be more efficient.  It only won't exist
    +    # if a new file was added in a new subdirectory and this is the first time that file was ever parsed.
    +    if (!open(OUTPUTFILEHANDLE, '>' . $outputFile))
    +        {
    +        NaturalDocs::File->CreatePath( NaturalDocs::File->NoFileName($outputFile) );
    +
    +        open(OUTPUTFILEHANDLE, '>' . $outputFile)
    +            or die "Couldn't create output file " . $outputFile . "\n";
    +        };
    +
    +    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +    my $usePrettify = (NaturalDocs::Settings->HighlightCode() || NaturalDocs::Settings->HighlightAnonymous());
    +
    +
    +    print OUTPUTFILEHANDLE
    +
    +        # IE 6 doesn't like any doctype here at all.  Add one (strict or transitional doesn't matter) and it makes the page slightly too
    +        # wide for the frame.  Mozilla and Opera handle it like champs either way because they Don't Suck(tm).
    +
    +        # '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
    +        # . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n"
    +
    +        '<html><head>'
    +
    +        	. '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<title>'
    +                . $self->BuildTitle($sourceFile)
    +            . '</title>'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '"></script>';
    +
    +            if ($usePrettify)
    +            	{
    +            	print OUTPUTFILEHANDLE
    +	            '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->PrettifyJavaScriptFile(), 1) . '">'
    +	            . '</script>';
    +            	}
    +
    +        print OUTPUTFILEHANDLE
    +        '</head><body class="FramedContentPage" onLoad="NDOnLoad();' . ($usePrettify ? 'prettyPrint();' : '') . '">'
    +            . $self->OpeningBrowserStyles()
    +
    +            . $self->StandardComments()
    +
    +            . "\n\n\n"
    +                . $self->BuildContent($sourceFile, $parsedFile)
    +            . "\n\n\n"
    +
    +            . $self->BuildToolTips()
    +
    +            . $self->ClosingBrowserStyles()
    +        . '</body></html>';
    +
    +
    +    close(OUTPUTFILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: BuildIndex
    +#
    +#   Builds an index for the passed type.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> to limit the index to, or undef if none.
    +#
    +sub BuildIndex #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    my $indexTitle = $self->IndexTitleOf($type);
    +    my $indexFile = $self->IndexFileOf($type);
    +
    +    my $startIndexPage =
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +        	. '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<title>';
    +
    +            if (defined NaturalDocs::Menu->Title())
    +                {  $startIndexPage .= $self->StringToHTML(NaturalDocs::Menu->Title()) . ' - ';  };
    +
    +                $startIndexPage .=
    +                $indexTitle
    +            . '</title>'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($indexFile, $self->MainCSSFile(), 1) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($indexFile, $self->MainJavaScriptFile(), 1) . '"></script>'
    +
    +        . '</head><body class="FramedIndexPage" onLoad="NDOnLoad()">'
    +            . $self->OpeningBrowserStyles()
    +
    +            . "\n\n\n"
    +                . $self->StandardComments()
    +            . "\n\n\n"
    +                . '<div id=Index>'
    +                    . '<div class=IPageTitle>'
    +                        . $indexTitle
    +                    . '</div>';
    +
    +
    +    my $endIndexPage =
    +
    +                    '</div><!--Index-->'
    +                . "\n\n\n"
    +
    +                . $self->ClosingBrowserStyles()
    +
    +       . '</body></html>';
    +
    +    my $startSearchResultsPage =
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +            . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($indexFile, $self->MainCSSFile(), 1) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($indexFile, $self->MainJavaScriptFile(), 1) . '"></script>'
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($indexFile, $self->SearchDataJavaScriptFile(), 1) . '">'
    +                . '</script>'
    +
    +        . '</head><body class="FramedSearchResultsPage" onLoad="NDOnLoad()">'
    +            . $self->OpeningBrowserStyles()
    +
    +            . "\n\n\n"
    +                . $self->StandardComments()
    +            . "\n\n\n"
    +
    +                . '<div id=Index>'
    +                    . '<div class=IPageTitle>'
    +                        . 'Search Results'
    +                    . '</div>';
    +
    +    my $endSearchResultsPage =
    +
    +                    '</div><!--Index-->'
    +                . "\n\n\n"
    +
    +                . $self->ClosingBrowserStyles()
    +
    +       . '</body></html>';
    +
    +    my $indexContent = NaturalDocs::SymbolTable->Index($type);
    +    my $pageCount = $self->BuildIndexPages($type, $indexContent, $startIndexPage, $endIndexPage,
    +                                                                  $startSearchResultsPage, $endSearchResultsPage);
    +    $self->PurgeIndexFiles($type, $indexContent, $pageCount + 1);
    +    };
    +
    +
    +#
    +#   Function: UpdateMenu
    +#
    +#   Builds the menu file.  Also generates index.html.
    +#
    +sub UpdateMenu
    +    {
    +    my $self = shift;
    +
    +    my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);
    +    my $outputFile = NaturalDocs::File->JoinPaths($outputDirectory, 'menu.html');
    +
    +
    +    open(OUTPUTFILEHANDLE, '>' . $outputFile)
    +        or die "Couldn't create output file " . $outputFile . "\n";
    +
    +    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +
    +    my $title = 'Menu';
    +    if (defined $title)
    +        {  $title .= ' - ' . NaturalDocs::Menu->Title();  };
    +
    +    $title = $self->StringToHTML($title);
    +
    +
    +    print OUTPUTFILEHANDLE
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +          	. '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<title>'
    +                . $title
    +            . '</title>'
    +
    +            . '<base target="Content">'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '"></script>'
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->SearchDataJavaScriptFile(), 1) . '">'
    +                . '</script>'
    +
    +        . '</head><body class="FramedMenuPage" onLoad="NDOnLoad()">'
    +            . $self->OpeningBrowserStyles()
    +
    +            . $self->StandardComments()
    +
    +            . "\n\n\n"
    +                . $self->BuildMenu(undef, undef)
    +            . "\n\n\n"
    +                . $self->BuildFooter(1)
    +            . "\n\n\n"
    +
    +            . $self->ClosingBrowserStyles()
    +        . '</body></html>';
    +
    +
    +    close(OUTPUTFILEHANDLE);
    +
    +
    +    # Update index.html
    +
    +    my $firstMenuEntry = $self->FindFirstFile();
    +    my $indexFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html' );
    +
    +    # We have to check because it's possible that there may be no files with Natural Docs content and thus no files on the menu.
    +    if (defined $firstMenuEntry)
    +        {
    +        open(INDEXFILEHANDLE, '>' . $indexFile)
    +            or die "Couldn't create output file " . $indexFile . ".\n";
    +
    +	    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +
    +        print INDEXFILEHANDLE
    +
    +            '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" '
    +                . '"http://www.w3.org/TR/REC-html40/frameset.dtd">'
    +
    +            . '<html>'
    +
    +                . '<head>'
    +
    +                    . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +                    . '<title>'
    +                        . $self->StringToHTML(NaturalDocs::Menu->Title())
    +                    . '</title>'
    +
    +                . '</head>'
    +
    +                . $self->StandardComments()
    +
    +                . '<frameset cols="185,*">'
    +                    . '<frame name=Menu src="menu.html">'
    +                    . '<frame name=Content src="'
    +                        . $self->MakeRelativeURL($indexFile, $self->OutputFileOf($firstMenuEntry->Target()), 1) . '">'
    +                . '</frameset>'
    +
    +                . '<noframes>'
    +                    . 'This documentation was designed for use with frames.  However, you can still use it by '
    +                    . '<a href="menu.html">starting from the menu page</a>.'
    +                    . "<script language=JavaScript><!--\n"
    +                        . 'location.href="menu.html";'
    +                    . "\n// --></script>"
    +                . '</noframes>'
    +
    +            . '</html>';
    +
    +        close INDEXFILEHANDLE;
    +        }
    +
    +    elsif (-e $indexFile)
    +        {
    +        unlink($indexFile);
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTML.pm b/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTML.pm
    new file mode 100644
    index 000000000..86bd8bf90
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTML.pm
    @@ -0,0 +1,414 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Builder::HTML
    +#
    +###############################################################################
    +#
    +#   A package that generates output in HTML.
    +#
    +#   All functions are called with Package->Function() notation.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Builder::HTML;
    +
    +use base 'NaturalDocs::Builder::HTMLBase';
    +
    +
    +###############################################################################
    +# Group: Implemented Interface Functions
    +
    +
    +#
    +#   Function: INIT
    +#
    +#   Registers the package with <NaturalDocs::Builder>.
    +#
    +sub INIT
    +    {
    +    NaturalDocs::Builder->Add(__PACKAGE__);
    +    };
    +
    +
    +#
    +#   Function: CommandLineOption
    +#
    +#   Returns the option to follow -o to use this package.  In this case, "html".
    +#
    +sub CommandLineOption
    +    {
    +    return 'HTML';
    +    };
    +
    +
    +#
    +#   Function: BuildFile
    +#
    +#   Builds the output file from the parsed source file.
    +#
    +#   Parameters:
    +#
    +#       sourcefile       - The <FileName> of the source file.
    +#       parsedFile      - An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +sub BuildFile #(sourceFile, parsedFile)
    +    {
    +    my ($self, $sourceFile, $parsedFile) = @_;
    +
    +    my $outputFile = $self->OutputFileOf($sourceFile);
    +
    +
    +    # 99.99% of the time the output directory will already exist, so this will actually be more efficient.  It only won't exist
    +    # if a new file was added in a new subdirectory and this is the first time that file was ever parsed.
    +    if (!open(OUTPUTFILEHANDLE, '>' . $outputFile))
    +        {
    +        NaturalDocs::File->CreatePath( NaturalDocs::File->NoFileName($outputFile) );
    +
    +        open(OUTPUTFILEHANDLE, '>' . $outputFile)
    +            or die "Couldn't create output file " . $outputFile . "\n";
    +        };
    +
    +    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +    my $usePrettify = (NaturalDocs::Settings->HighlightCode() || NaturalDocs::Settings->HighlightAnonymous());
    +
    +
    +    print OUTPUTFILEHANDLE
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +            . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<title>'
    +                . $self->BuildTitle($sourceFile)
    +            . '</title>'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '">'
    +                . '</script>';
    +
    +			if ($usePrettify)
    +				{
    +				print OUTPUTFILEHANDLE
    +	            '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->PrettifyJavaScriptFile(), 1) . '">'
    +	                . '</script>';
    +                }
    +
    +            print OUTPUTFILEHANDLE
    +            '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->SearchDataJavaScriptFile(), 1) . '">'
    +                . '</script>'
    +
    +	        . '</head><body class="ContentPage" onLoad="NDOnLoad();' . ($usePrettify ? 'prettyPrint();' : '') . '">'
    +            . $self->OpeningBrowserStyles()
    +
    +            . $self->StandardComments()
    +
    +            . "\n\n\n"
    +                . $self->BuildContent($sourceFile, $parsedFile)
    +            . "\n\n\n"
    +                . $self->BuildFooter()
    +            . "\n\n\n"
    +                . $self->BuildMenu($sourceFile, undef)
    +            . "\n\n\n"
    +                . $self->BuildToolTips()
    +            . "\n\n\n"
    +                . '<div id=MSearchResultsWindow>'
    +                    . '<iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe>'
    +                    . '<a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a>'
    +                . '</div>'
    +            . "\n\n\n"
    +
    +            . $self->ClosingBrowserStyles()
    +        . '</body></html>';
    +
    +
    +    close(OUTPUTFILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: BuildIndex
    +#
    +#   Builds an index for the passed type.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> to limit the index to, or undef if none.
    +#
    +sub BuildIndex #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    my $indexTitle = $self->IndexTitleOf($type);
    +
    +    my $startIndexPage =
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +            . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<title>'
    +                . $indexTitle;
    +
    +                if (defined NaturalDocs::Menu->Title())
    +                    {  $startIndexPage .= ' - ' . $self->StringToHTML(NaturalDocs::Menu->Title());  };
    +
    +            $startIndexPage .=
    +            '</title>'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($self->IndexDirectory(),
    +                                                                                                                       $self->MainCSSFile()) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->IndexDirectory(),
    +                                                                                                        $self->MainJavaScriptFile()) . '"></script>'
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->IndexDirectory(),
    +                                                                                                        $self->SearchDataJavaScriptFile()) . '">'
    +                . '</script>'
    +
    +        . '</head><body class="IndexPage" onLoad="NDOnLoad()">'
    +            . $self->OpeningBrowserStyles()
    +
    +        . $self->StandardComments()
    +
    +        . "\n\n\n"
    +
    +        . '<div id=Index>'
    +            . '<div class=IPageTitle>'
    +                . $indexTitle
    +            . '</div>';
    +
    +    my $endIndexPage =
    +            '</div><!--Index-->'
    +
    +            . "\n\n\n"
    +                . $self->BuildFooter()
    +            . "\n\n\n"
    +                . $self->BuildMenu(undef, $type)
    +            . "\n\n\n"
    +                . '<div id=MSearchResultsWindow>'
    +                    . '<iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe>'
    +                    . '<a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a>'
    +                . '</div>'
    +            . "\n\n\n"
    +
    +            . $self->ClosingBrowserStyles()
    +        . '</body></html>';
    +
    +
    +    my $startSearchResultsPage =
    +
    +        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" '
    +            . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n"
    +
    +        . '<html><head>'
    +
    +            . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
    +
    +            . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($self->SearchResultsDirectory(),
    +                                                                                                                       $self->MainCSSFile()) . '">'
    +
    +            . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->SearchResultsDirectory(),
    +                                                                                                        $self->MainJavaScriptFile()) . '"></script>'
    +
    +        . '</head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()">'
    +            . $self->OpeningBrowserStyles()
    +
    +        . $self->StandardComments()
    +
    +        . "\n\n\n"
    +
    +        . '<div id=Index>';
    +
    +
    +    my $endSearchResultsPage =
    +        '</div>'
    +        . $self->ClosingBrowserStyles()
    +   . '</body></html>';
    +
    +    my $indexContent = NaturalDocs::SymbolTable->Index($type);
    +    my $pageCount = $self->BuildIndexPages($type, $indexContent, $startIndexPage, $endIndexPage,
    +                                                                  $startSearchResultsPage, $endSearchResultsPage);
    +    $self->PurgeIndexFiles($type, $indexContent, $pageCount + 1);
    +    };
    +
    +
    +#
    +#   Function: UpdateMenu
    +#
    +#   Updates the menu in all the output files that weren't rebuilt.  Also generates index.html.
    +#
    +sub UpdateMenu
    +    {
    +    my $self = shift;
    +
    +
    +    # Update the menu on unbuilt files.
    +
    +    my $filesToUpdate = NaturalDocs::Project->UnbuiltFilesWithContent();
    +
    +    foreach my $sourceFile (keys %$filesToUpdate)
    +        {
    +        $self->UpdateFile($sourceFile);
    +        };
    +
    +
    +    # Update the menu on unchanged index files.
    +
    +    my $indexes = NaturalDocs::Menu->Indexes();
    +
    +    foreach my $index (keys %$indexes)
    +        {
    +        if (!NaturalDocs::SymbolTable->IndexChanged($index))
    +            {
    +            $self->UpdateIndex($index);
    +            };
    +        };
    +
    +
    +    # Update index.html
    +
    +    my $firstMenuEntry = $self->FindFirstFile();
    +    my $indexFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html' );
    +
    +    # We have to check because it's possible that there may be no files with Natural Docs content and thus no files on the menu.
    +    if (defined $firstMenuEntry)
    +        {
    +        open(INDEXFILEHANDLE, '>' . $indexFile)
    +            or die "Couldn't create output file " . $indexFile . ".\n";
    +
    +	    binmode(INDEXFILEHANDLE, ':encoding(UTF-8)');
    +
    +        print INDEXFILEHANDLE
    +        '<html><head>'
    +             . '<meta http-equiv="Refresh" CONTENT="0; URL='
    +                 . $self->MakeRelativeURL( NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html'),
    +                                                        $self->OutputFileOf($firstMenuEntry->Target()), 1 ) . '">'
    +        . '</head></html>';
    +
    +        close INDEXFILEHANDLE;
    +        }
    +
    +    elsif (-e $indexFile)
    +        {
    +        unlink($indexFile);
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: UpdateFile
    +#
    +#   Updates an output file.  Replaces the menu, HTML title, and footer.  It opens the output file, makes the changes, and saves it
    +#   back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName>.
    +#
    +#   Dependencies:
    +#
    +#       - Requires <Builder::BuildMenu()> to surround its content with the exact strings "<div id=Menu>" and "</div><!--Menu-->".
    +#       - Requires <Builder::BuildFooter()> to surround its content with the exact strings "<div id=Footer>" and
    +#         "</div><!--Footer-->".
    +#
    +sub UpdateFile #(sourceFile)
    +    {
    +    my ($self, $sourceFile) = @_;
    +
    +    my $outputFile = $self->OutputFileOf($sourceFile);
    +
    +    if (open(OUTPUTFILEHANDLE, '<' . $outputFile))
    +        {
    +        my $content;
    +
    +	    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +        read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE);
    +        close(OUTPUTFILEHANDLE);
    +
    +
    +        $content =~ s{<title>[^<]*<\/title>}{'<title>' . $self->BuildTitle($sourceFile) . '</title>'}e;
    +
    +        $content =~ s/<div id=Menu>.*?<\/div><!--Menu-->/$self->BuildMenu($sourceFile, undef)/es;
    +
    +        $content =~ s/<div id=Footer>.*?<\/div><!--Footer-->/$self->BuildFooter()/e;
    +
    +
    +        open(OUTPUTFILEHANDLE, '>' . $outputFile);
    +	    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +        print OUTPUTFILEHANDLE $content;
    +        close(OUTPUTFILEHANDLE);
    +        };
    +    };
    +
    +
    +#
    +#   Function: UpdateIndex
    +#
    +#   Updates an index's output file.  Replaces the menu and footer.  It opens the output file, makes the changes, and saves it
    +#   back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed.
    +#
    +#   Parameters:
    +#
    +#       type - The index <TopicType>, or undef if none.
    +#
    +sub UpdateIndex #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    my $page = 1;
    +
    +    my $outputFile = $self->IndexFileOf($type, $page);
    +
    +    my $newMenu = $self->BuildMenu(undef, $type);
    +    my $newFooter = $self->BuildFooter();
    +
    +    while (-e $outputFile)
    +        {
    +        open(OUTPUTFILEHANDLE, '<' . $outputFile)
    +            or die "Couldn't open output file " . $outputFile . ".\n";
    +
    +        my $content;
    +
    +	    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +        read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE);
    +        close(OUTPUTFILEHANDLE);
    +
    +
    +        $content =~ s/<div id=Menu>.*?<\/div><!--Menu-->/$newMenu/es;
    +
    +        $content =~ s/<div id=Footer>.*<\/div><!--Footer-->/$newFooter/e;
    +
    +
    +        open(OUTPUTFILEHANDLE, '>' . $outputFile)
    +            or die "Couldn't save output file " . $outputFile . ".\n";
    +
    +	    binmode(OUTPUTFILEHANDLE, ':encoding(UTF-8)');
    +        print OUTPUTFILEHANDLE $content;
    +        close(OUTPUTFILEHANDLE);
    +
    +        $page++;
    +        $outputFile = $self->IndexFileOf($type, $page);
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTMLBase.pm b/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTMLBase.pm
    new file mode 100644
    index 000000000..9d7dab0b0
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Builder/HTMLBase.pm
    @@ -0,0 +1,3745 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Builder::HTMLBase
    +#
    +###############################################################################
    +#
    +#   A base package for all the shared functionality in <NaturalDocs::Builder::HTML> and
    +#   <NaturalDocs::Builder::FramedHTML>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use Tie::RefHash;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Builder::HTMLBase;
    +
    +use base 'NaturalDocs::Builder::Base';
    +
    +use NaturalDocs::DefineMembers 'MADE_EMPTY_SEARCH_RESULTS_PAGE', 'MadeEmptySearchResultsPage()',
    +                                                 'SetMadeEmptySearchResultsPage()';
    +
    +
    +
    +###############################################################################
    +# Group: Object Variables
    +
    +
    +#
    +#   Constants: Members
    +#
    +#   The object is implemented as a blessed arrayref, with the follow constants as indexes.
    +#
    +#   MADE_EMPTY_SEARCH_RESULTS_PAGE - Whether the search results page for searches with no results was generated.
    +#
    +
    +#
    +#   Constants: NDMarkupToHTML Styles
    +#
    +#   These are the styles used with <NDMarkupToHTML()>.
    +#
    +#   NDMARKUPTOHTML_GENERAL - General style.
    +#   NDMARKUPTOHTML_SUMMARY - For summaries.
    +#   NDMARKUPTOHTML_TOOLTIP - For tooltips.
    +#
    +use constant NDMARKUPTOHTML_GENERAL => undef;
    +use constant NDMARKUPTOHTML_SUMMARY => 1;
    +use constant NDMARKUPTOHTML_TOOLTIP => 2;
    +
    +
    +
    +###############################################################################
    +# Group: Package Variables
    +# These variables are shared by all instances of the package so don't change them.
    +
    +
    +#
    +#   handle: FH_CSS_FILE
    +#
    +#   The file handle to use when updating CSS files.
    +#
    +
    +
    +#
    +#   Hash: abbreviations
    +#
    +#   An existence hash of acceptable abbreviations.  These are words that <AddDoubleSpaces()> won't put a second space
    +#   after when followed by period-whitespace-capital letter.  Yes, this is seriously over-engineered.
    +#
    +my %abbreviations = ( mr => 1, mrs => 1, ms => 1, dr => 1,
    +                                  rev => 1, fr => 1, 'i.e' => 1,
    +                                  maj => 1, gen => 1, pres => 1, sen => 1, rep => 1,
    +                                  n => 1, s => 1, e => 1, w => 1, ne => 1, se => 1, nw => 1, sw => 1 );
    +
    +#
    +#   array: indexHeadings
    +#
    +#   An array of the headings of all the index sections.  First is for symbols, second for numbers, and the rest for each letter.
    +#
    +my @indexHeadings = ( '$#!', '0-9', 'A' .. 'Z' );
    +
    +#
    +#   array: indexAnchors
    +#
    +#   An array of the HTML anchors of all the index sections.  First is for symbols, second for numbers, and the rest for each letter.
    +#
    +my @indexAnchors = ( 'Symbols', 'Numbers', 'A' .. 'Z' );
    +
    +#
    +#   array: searchExtensions
    +#
    +#   An array of the search file name extensions for all the index sections.  First is for symbols, second for numbers, and the rest
    +#   for each letter.
    +#
    +my @searchExtensions = ( 'Symbols', 'Numbers', 'A' .. 'Z' );
    +
    +#
    +#   bool: saidUpdatingCSSFile
    +#
    +#   Whether the status message "Updating CSS file..." has been displayed.  We only want to print it once, no matter how many
    +#   HTML-based targets we are building.
    +#
    +my $saidUpdatingCSSFile;
    +
    +#
    +#   constant: ADD_HIDDEN_BREAKS
    +#
    +#   Just a synonym for "1" so that setting the flag on <StringToHTML()> is clearer in the calling code.
    +#
    +use constant ADD_HIDDEN_BREAKS => 1;
    +
    +
    +###############################################################################
    +# Group: ToolTip Package Variables
    +#
    +#   These variables are for the tooltip generation functions only.  Since they're reset on every call to <BuildContent()> and
    +#   <BuildIndexSections()>, and are only used by them and their support functions, they can be shared by all instances of the
    +#   package.
    +
    +#
    +#   int: tooltipLinkNumber
    +#
    +#   A number used as part of the ID for each link that has a tooltip.  Should be incremented whenever one is made.
    +#
    +my $tooltipLinkNumber;
    +
    +#
    +#   int: tooltipNumber
    +#
    +#   A number used as part of the ID for each tooltip.  Should be incremented whenever one is made.
    +#
    +my $tooltipNumber;
    +
    +#
    +#   hash: tooltipSymbolsToNumbers
    +#
    +#   A hash that maps the tooltip symbols to their assigned numbers.
    +#
    +my %tooltipSymbolsToNumbers;
    +
    +#
    +#   string: tooltipHTML
    +#
    +#   The generated tooltip HTML.
    +#
    +my $tooltipHTML;
    +
    +
    +###############################################################################
    +# Group: Menu Package Variables
    +#
    +#   These variables are for the menu generation functions only.  Since they're reset on every call to <BuildMenu()> and are
    +#   only used by it and its support functions, they can be shared by all instances of the package.
    +#
    +
    +
    +#
    +#   hash: prebuiltMenus
    +#
    +#   A hash that maps output directonies to menu HTML already built for it.  There will be no selection or JavaScript in the menus.
    +#
    +my %prebuiltMenus;
    +
    +
    +#
    +#   bool: menuNumbersAndLengthsDone
    +#
    +#   Set when the variables that only need to be calculated for the menu once are done.  This includes <menuGroupNumber>,
    +#   <menuLength>, <menuGroupLengths>, and <menuGroupNumbers>, and <menuRootLength>.
    +#
    +my $menuNumbersAndLengthsDone;
    +
    +
    +#
    +#   int: menuGroupNumber
    +#
    +#   The current menu group number.  Each time a group is created, this is incremented so that each one will be unique.
    +#
    +my $menuGroupNumber;
    +
    +
    +#
    +#   int: menuLength
    +#
    +#   The length of the entire menu, fully expanded.  The value is computed from the <Menu Length Constants>.
    +#
    +my $menuLength;
    +
    +
    +#
    +#   hash: menuGroupLengths
    +#
    +#   A hash of the length of each group, *not* including any subgroup contents.  The keys are references to each groups'
    +#   <NaturalDocs::Menu::Entry> object, and the values are their lengths computed from the <Menu Length Constants>.
    +#
    +my %menuGroupLengths;
    +tie %menuGroupLengths, 'Tie::RefHash';
    +
    +
    +#
    +#   hash: menuGroupNumbers
    +#
    +#   A hash of the number of each group, as managed by <menuGroupNumber>.  The keys are references to each groups'
    +#   <NaturalDocs::Menu::Entry> object, and the values are the number.
    +#
    +my %menuGroupNumbers;
    +tie %menuGroupNumbers, 'Tie::RefHash';
    +
    +
    +#
    +#   int: menuRootLength
    +#
    +#   The length of the top-level menu entries without expansion.  The value is computed from the <Menu Length Constants>.
    +#
    +my $menuRootLength;
    +
    +
    +#
    +#   constants: Menu Length Constants
    +#
    +#   Constants used to approximate the lengths of the menu or its groups.
    +#
    +#   MENU_TITLE_LENGTH       - The length of the title.
    +#   MENU_SUBTITLE_LENGTH - The length of the subtitle.
    +#   MENU_FILE_LENGTH         - The length of one file entry.
    +#   MENU_GROUP_LENGTH     - The length of one group entry.
    +#   MENU_TEXT_LENGTH        - The length of one text entry.
    +#   MENU_LINK_LENGTH        - The length of one link entry.
    +#
    +#   MENU_LENGTH_LIMIT    - The limit of the menu's length.  If the total length surpasses this limit, groups that aren't required
    +#                                       to be open to show the selection will default to closed on browsers that support it.
    +#
    +use constant MENU_TITLE_LENGTH => 3;
    +use constant MENU_SUBTITLE_LENGTH => 1;
    +use constant MENU_FILE_LENGTH => 1;
    +use constant MENU_GROUP_LENGTH => 2; # because it's a line and a blank space
    +use constant MENU_TEXT_LENGTH => 1;
    +use constant MENU_LINK_LENGTH => 1;
    +use constant MENU_INDEX_LENGTH => 1;
    +
    +use constant MENU_LENGTH_LIMIT => 35;
    +
    +
    +###############################################################################
    +# Group: Image Package Variables
    +#
    +#   These variables are for the image generation functions only.  Since they're reset on every call to <BuildContent()>,
    +#   and are only used by it and its support functions, they can be shared by all instances of thepackage.
    +
    +
    +#
    +#   var: imageAnchorNumber
    +#   Incremented for each image link in the file that requires an anchor.
    +#
    +my $imageAnchorNumber;
    +
    +
    +#
    +#   var: imageContent
    +#
    +#   The actual embedded image HTML for all image links.  When generating an image link, the link HTML is returned and
    +#   the HTML for the target image is added here.  Periodically, such as after the end of the paragraph, this content should
    +#   be added to the page and the variable set to undef.
    +#
    +my $imageContent;
    +
    +
    +
    +###############################################################################
    +# Group: Search Package Variables
    +#
    +#   These variables are for the search generation functions only.  Since they're reset on every call to <BuildIndexSections()> and
    +#   are only used by them and their support functions, they can be shared by all instances of the package.
    +
    +
    +#
    +#   hash: searchResultIDs
    +#
    +#   A hash mapping lowercase-only search result IDs to the number of times they've been used.  This is to work around an IE
    +#   bug where it won't correctly reference IDs if they differ only in case.
    +#
    +my %searchResultIDs;
    +
    +
    +
    +###############################################################################
    +# Group: Object Functions
    +
    +
    +#
    +#   Function: New
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $class = shift;
    +
    +    my $object = $class->SUPER::New();
    +    $object->SetMadeEmptySearchResultsPage(0);
    +
    +    return $object;
    +    };
    +
    +
    +# Function: MadeEmptySearchResultsPage
    +# Returns whether the empty search results page was created or not.
    +
    +# Function: SetMadeEmptySearchResultsPage
    +# Sets whether the empty search results page was created or not.
    +
    +
    +
    +###############################################################################
    +# Group: Implemented Interface Functions
    +#
    +#   The behavior of these functions is shared between HTML output formats.
    +#
    +
    +
    +#
    +#   Function: UpdateImage
    +#
    +#   Define this function to add or update the passed image in the output.
    +#
    +#   Parameters:
    +#
    +#       file - The image <FileName>
    +#
    +sub UpdateImage #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    my $outputFile = $self->OutputImageOf($file);
    +    my $outputDirectory = NaturalDocs::File->NoFileName($outputFile);
    +
    +    if (!-d $outputDirectory)
    +        {  NaturalDocs::File->CreatePath($outputDirectory);  };
    +
    +    NaturalDocs::File->Copy($file, $outputFile);
    +    };
    +
    +
    +#
    +#   Function: PurgeFiles
    +#
    +#   Deletes the output files associated with the purged source files.
    +#
    +sub PurgeFiles #(filesToPurge)
    +    {
    +    my ($self, $filesToPurge) = @_;
    +
    +    # Combine directories into a hash to remove duplicate work.
    +    my %directoriesToPurge;
    +
    +    foreach my $file (keys %$filesToPurge)
    +        {
    +        # It's possible that there may be files there that aren't in a valid input directory anymore.  They won't generate an output
    +        # file name so we need to check for undef.
    +        my $outputFile = $self->OutputFileOf($file);
    +        if (defined $outputFile)
    +            {
    +            unlink($outputFile);
    +            $directoriesToPurge{ NaturalDocs::File->NoFileName($outputFile) } = 1;
    +            };
    +        };
    +
    +    foreach my $directory (keys %directoriesToPurge)
    +        {
    +        NaturalDocs::File->RemoveEmptyTree($directory, NaturalDocs::Settings->OutputDirectoryOf($self));
    +        };
    +    };
    +
    +
    +#
    +#   Function: PurgeIndexes
    +#
    +#   Deletes the output files associated with the purged source files.
    +#
    +#   Parameters:
    +#
    +#       indexes  - An existence hashref of the index types to purge.  The keys are the <TopicTypes> or * for the general index.
    +#
    +sub PurgeIndexes #(indexes)
    +    {
    +    my ($self, $indexes) = @_;
    +
    +    foreach my $index (keys %$indexes)
    +        {
    +        $self->PurgeIndexFiles($index, undef, undef);
    +        };
    +    };
    +
    +
    +#
    +#   Function: PurgeImages
    +#
    +#   Define this function to make the package remove all output related to the passed image files.  These files are no longer used
    +#   by the documentation.
    +#
    +#   Parameters:
    +#
    +#       files - An existence hashref of the image <FileNames> to purge.
    +#
    +sub PurgeImages #(files)
    +    {
    +    my ($self, $filesToPurge) = @_;
    +
    +    # Combine directories into a hash to remove duplicate work.
    +    my %directoriesToPurge;
    +
    +    foreach my $file (keys %$filesToPurge)
    +        {
    +        # It's possible that there may be files there that aren't in a valid input directory anymore.  They won't generate an output
    +        # file name so we need to check for undef.
    +        my $outputFile = $self->OutputImageOf($file);
    +        if (defined $outputFile)
    +            {
    +            unlink($outputFile);
    +            $directoriesToPurge{ NaturalDocs::File->NoFileName($outputFile) } = 1;
    +            };
    +        };
    +
    +    foreach my $directory (keys %directoriesToPurge)
    +        {
    +        NaturalDocs::File->RemoveEmptyTree($directory, NaturalDocs::Settings->OutputDirectoryOf($self));
    +        };
    +    };
    +
    +
    +#
    +#   Function: BeginBuild
    +#
    +#   Creates the necessary subdirectories in the output directory.
    +#
    +sub BeginBuild #(hasChanged)
    +    {
    +    my ($self, $hasChanged) = @_;
    +
    +    foreach my $directory ( $self->JavaScriptDirectory(), $self->CSSDirectory(), $self->IndexDirectory(),
    +                                       $self->SearchResultsDirectory() )
    +        {
    +        if (!-d $directory)
    +            {  NaturalDocs::File->CreatePath($directory);  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: EndBuild
    +#
    +#   Synchronizes the projects CSS and JavaScript files.  Also generates the search data JavaScript file.
    +#
    +sub EndBuild #(hasChanged)
    +    {
    +    my ($self, $hasChanged) = @_;
    +
    +
    +    # Update the style sheets.
    +
    +    my $styles = NaturalDocs::Settings->Styles();
    +    my $changed;
    +
    +    my $cssDirectory = $self->CSSDirectory();
    +    my $mainCSSFile = $self->MainCSSFile();
    +
    +    for (my $i = 0; $i < scalar @$styles; $i++)
    +        {
    +        my $outputCSSFile;
    +
    +        if (scalar @$styles == 1)
    +            {  $outputCSSFile = $mainCSSFile;  }
    +        else
    +            {  $outputCSSFile = NaturalDocs::File->JoinPaths($cssDirectory, ($i + 1) . '.css');  };
    +
    +
    +        my $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), $styles->[$i] . '.css' );
    +
    +        if (! -e $masterCSSFile)
    +            {  $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->StyleDirectory(), $styles->[$i] . '.css' );  };
    +
    +        # We check both the date and the size in case the user switches between two styles which just happen to have the same
    +        # date.  Should rarely happen, but it might.
    +        if (! -e $outputCSSFile ||
    +            (stat($masterCSSFile))[9] != (stat($outputCSSFile))[9] ||
    +             -s $masterCSSFile != -s $outputCSSFile)
    +            {
    +            if (!NaturalDocs::Settings->IsQuiet() && !$saidUpdatingCSSFile)
    +                {
    +                print "Updating CSS file...\n";
    +                $saidUpdatingCSSFile = 1;
    +                };
    +
    +            NaturalDocs::File->Copy($masterCSSFile, $outputCSSFile);
    +
    +            $changed = 1;
    +            };
    +        };
    +
    +
    +    my $deleteFrom;
    +
    +    if (scalar @$styles == 1)
    +        {  $deleteFrom = 1;  }
    +    else
    +        {  $deleteFrom = scalar @$styles + 1;  };
    +
    +    for (;;)
    +        {
    +        my $file = NaturalDocs::File->JoinPaths($cssDirectory, $deleteFrom . '.css');
    +
    +        if (! -e $file)
    +            {  last;  };
    +
    +        unlink ($file);
    +        $deleteFrom++;
    +
    +        $changed = 1;
    +        };
    +
    +
    +    if ($changed)
    +        {
    +        if (scalar @$styles > 1)
    +            {
    +            open(FH_CSS_FILE, '>' . $mainCSSFile);
    +		    binmode(FH_CSS_FILE, ':encoding(UTF-8)');
    +
    +            for (my $i = 0; $i < scalar @$styles; $i++)
    +                {
    +                print FH_CSS_FILE '@import URL("' . ($i + 1) . '.css");' . "\n";
    +                };
    +
    +            close(FH_CSS_FILE);
    +            };
    +        };
    +
    +
    +
    +    # Update the JavaScript files
    +
    +    my $jsMaster = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->JavaScriptDirectory(), 'NaturalDocs.js' );
    +    my $jsOutput = $self->MainJavaScriptFile();
    +
    +    # We check both the date and the size in case the user switches between two styles which just happen to have the same
    +    # date.  Should rarely happen, but it might.
    +    if (! -e $jsOutput ||
    +        (stat($jsMaster))[9] != (stat($jsOutput))[9] ||
    +         -s $jsMaster != -s $jsOutput)
    +        {
    +        NaturalDocs::File->Copy($jsMaster, $jsOutput);
    +        };
    +
    +
    +    my $prettifyOutput = $self->PrettifyJavaScriptFile();
    +
    +    if (NaturalDocs::Settings->HighlightCode() || NaturalDocs::Settings->HighlightAnonymous())
    +    	{
    +	    my $prettifyMaster = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->JavaScriptDirectory(), 'GooglePrettify.js' );
    +
    +	    # We check both the date and the size in case the user switches between two styles which just happen to have the same
    +	    # date.  Should rarely happen, but it might.
    +	    if (! -e $prettifyOutput ||
    +	        (stat($prettifyMaster))[9] != (stat($prettifyOutput))[9] ||
    +	         -s $prettifyMaster != -s $prettifyOutput)
    +	        {
    +	        NaturalDocs::File->Copy($prettifyMaster, $prettifyOutput);
    +	        };
    +	    }
    +	elsif (-e $prettifyOutput)
    +		{
    +		unlink $prettifyOutput;
    +		}
    +
    +
    +    my @indexes = keys %{NaturalDocs::Menu->Indexes()};
    +
    +    open(FH_INDEXINFOJS, '>' . NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'searchdata.js'));
    +    binmode(FH_INDEXINFOJS, ':encoding(UTF-8)');
    +
    +    print FH_INDEXINFOJS
    +    "var indexSectionsWithContent = {\n";
    +
    +    for (my $i = 0; $i < scalar @indexes; $i++)
    +        {
    +        if ($i != 0)
    +            {  print FH_INDEXINFOJS ",\n";  };
    +
    +        print FH_INDEXINFOJS '   "' . NaturalDocs::Topics->NameOfType($indexes[$i], 1, 1) . "\": {\n";
    +
    +        my $content = NaturalDocs::SymbolTable->IndexSectionsWithContent($indexes[$i]);
    +        for (my $contentIndex = 0; $contentIndex < 28; $contentIndex++)
    +            {
    +            if ($contentIndex != 0)
    +                {  print FH_INDEXINFOJS ",\n";  };
    +
    +            print FH_INDEXINFOJS '      "' . $searchExtensions[$contentIndex] . '": ' . ($content->[$contentIndex] ? 'true' : 'false');
    +            };
    +
    +        print FH_INDEXINFOJS "\n      }";
    +        };
    +
    +    print FH_INDEXINFOJS
    +    "\n   }";
    +
    +    close(FH_INDEXINFOJS);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Section Functions
    +
    +
    +#
    +#   Function: BuildTitle
    +#
    +#   Builds and returns the HTML page title of a file.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to build the title of.
    +#
    +#   Returns:
    +#
    +#       The source file's title in HTML.
    +#
    +sub BuildTitle #(sourceFile)
    +    {
    +    my ($self, $sourceFile) = @_;
    +
    +    # If we have a menu title, the page title is [menu title] - [file title].  Otherwise it is just [file title].
    +
    +    my $title = NaturalDocs::Project->DefaultMenuTitleOf($sourceFile);
    +
    +    my $menuTitle = NaturalDocs::Menu->Title();
    +    if (defined $menuTitle && $menuTitle ne $title)
    +        {  $title .= ' - ' . $menuTitle;  };
    +
    +    $title = $self->StringToHTML($title);
    +
    +    return $title;
    +    };
    +
    +#
    +#   Function: BuildMenu
    +#
    +#   Builds and returns the side menu of a file.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to use if you're looking for a source file.
    +#       indexType - The index <TopicType> to use if you're looking for an index.
    +#
    +#       Both sourceFile and indexType may be undef.
    +#
    +#   Returns:
    +#
    +#       The side menu in HTML.
    +#
    +#   Dependencies:
    +#
    +#       - <Builder::HTML::UpdateFile()> and <Builder::HTML::UpdateIndex()> require this section to be surrounded with the exact
    +#         strings "<div id=Menu>" and "</div><!--Menu-->".
    +#       - This function depends on the way <BuildMenuSegment()> formats file and index entries.
    +#
    +sub BuildMenu #(FileName sourceFile, TopicType indexType) -> string htmlMenu
    +    {
    +    my ($self, $sourceFile, $indexType) = @_;
    +
    +    if (!$menuNumbersAndLengthsDone)
    +        {
    +        $menuGroupNumber = 1;
    +        $menuLength = 0;
    +        %menuGroupLengths = ( );
    +        %menuGroupNumbers = ( );
    +        $menuRootLength = 0;
    +        };
    +
    +    my $outputDirectory;
    +
    +    if ($sourceFile)
    +        {  $outputDirectory = NaturalDocs::File->NoFileName( $self->OutputFileOf($sourceFile) );  }
    +    elsif ($indexType)
    +        {  $outputDirectory = NaturalDocs::File->NoFileName( $self->IndexFileOf($indexType) );  }
    +    else
    +        {  $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);  };
    +
    +
    +    # Comment needed for UpdateFile().
    +    my $output = '<div id=Menu>';
    +
    +
    +    if (!exists $prebuiltMenus{$outputDirectory})
    +        {
    +        my $segmentOutput;
    +
    +        ($segmentOutput, $menuRootLength) =
    +            $self->BuildMenuSegment($outputDirectory, NaturalDocs::Menu->Content(), 1);
    +
    +        my $titleOutput;
    +
    +        my $menuTitle = NaturalDocs::Menu->Title();
    +        if (defined $menuTitle)
    +            {
    +            if (!$menuNumbersAndLengthsDone)
    +                {  $menuLength += MENU_TITLE_LENGTH;  };
    +
    +            $menuRootLength += MENU_TITLE_LENGTH;
    +
    +            $titleOutput .=
    +            '<div class=MTitle>'
    +                . $self->StringToHTML($menuTitle);
    +
    +            my $menuSubTitle = NaturalDocs::Menu->SubTitle();
    +            if (defined $menuSubTitle)
    +                {
    +                if (!$menuNumbersAndLengthsDone)
    +                    {  $menuLength += MENU_SUBTITLE_LENGTH;  };
    +
    +                $menuRootLength += MENU_SUBTITLE_LENGTH;
    +
    +                $titleOutput .=
    +                '<div class=MSubTitle>'
    +                    . $self->StringToHTML($menuSubTitle)
    +                . '</div>';
    +                };
    +
    +            $titleOutput .=
    +            '</div>';
    +            };
    +
    +        my $searchOutput;
    +
    +        if (scalar keys %{NaturalDocs::Menu->Indexes()})
    +            {
    +            $searchOutput =
    +            '<script type="text/javascript"><!--' . "\n"
    +                . 'var searchPanel = new SearchPanel("searchPanel", "' . $self->CommandLineOption() . '", '
    +                    . '"' . $self->MakeRelativeURL($outputDirectory, $self->SearchResultsDirectory()) . '");' . "\n"
    +            . '--></script>'
    +
    +            . '<div id=MSearchPanel class=MSearchPanelInactive>'
    +                . '<input type=text id=MSearchField value=Search '
    +                    . 'onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" '
    +                    . 'onKeyUp="searchPanel.OnSearchFieldChange()">'
    +                . '<select id=MSearchType '
    +                    . 'onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" '
    +                    . 'onChange="searchPanel.OnSearchTypeChange()">';
    +
    +                my @indexes = keys %{NaturalDocs::Menu->Indexes()};
    +                @indexes = sort
    +                    {
    +                    if ($a eq ::TOPIC_GENERAL())  {  return -1;  }
    +                    elsif ($b eq ::TOPIC_GENERAL())  {  return 1;  }
    +                    else  {  return (NaturalDocs::Topics->NameOfType($a, 1) cmp NaturalDocs::Topics->NameOfType($b, 1))  };
    +                    }  @indexes;
    +
    +                foreach my $index (@indexes)
    +                    {
    +                    my ($name, $extra);
    +                    if ($index eq ::TOPIC_GENERAL())
    +                        {
    +                        $name = 'Everything';
    +                        $extra = ' id=MSearchEverything selected ';
    +                        }
    +                    else
    +                        {  $name = $self->ConvertAmpChars(NaturalDocs::Topics->NameOfType($index, 1));  }
    +
    +                    $searchOutput .=
    +                    '<option ' . $extra . 'value="' . NaturalDocs::Topics->NameOfType($index, 1, 1) . '">'
    +                        . $name
    +                    . '</option>';
    +                    };
    +
    +                $searchOutput .=
    +                '</select>'
    +            . '</div>';
    +            };
    +
    +        $prebuiltMenus{$outputDirectory} = $titleOutput . $segmentOutput . $searchOutput;
    +        $output .= $titleOutput . $segmentOutput . $searchOutput;
    +        }
    +    else
    +        {  $output .= $prebuiltMenus{$outputDirectory};  };
    +
    +
    +    # Highlight the menu selection.
    +
    +    if ($sourceFile)
    +        {
    +        # Dependency: This depends on how BuildMenuSegment() formats file entries.
    +        my $outputFile = $self->OutputFileOf($sourceFile);
    +        my $tag = '<div class=MFile><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">';
    +        my $tagIndex = index($output, $tag);
    +
    +        if ($tagIndex != -1)
    +            {
    +            my $endIndex = index($output, '</a>', $tagIndex);
    +
    +            substr($output, $endIndex, 4, '');
    +            substr($output, $tagIndex, length($tag), '<div class=MFile id=MSelected>');
    +            };
    +        }
    +    elsif ($indexType)
    +        {
    +        # Dependency: This depends on how BuildMenuSegment() formats index entries.
    +        my $outputFile = $self->IndexFileOf($indexType);
    +        my $tag = '<div class=MIndex><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">';
    +        my $tagIndex = index($output, $tag);
    +
    +        if ($tagIndex != -1)
    +            {
    +            my $endIndex = index($output, '</a>', $tagIndex);
    +
    +            substr($output, $endIndex, 4, '');
    +            substr($output, $tagIndex, length($tag), '<div class=MIndex id=MSelected>');
    +            };
    +        };
    +
    +
    +    # If the completely expanded menu is too long, collapse all the groups that aren't in the selection hierarchy or near the
    +    # selection.  By doing this instead of having them default to closed via CSS, any browser that doesn't support changing this at
    +    # runtime will keep the menu entirely open so that its still usable.
    +
    +    if ($menuLength > MENU_LENGTH_LIMIT())
    +        {
    +        my $menuSelectionHierarchy = $self->GetMenuSelectionHierarchy($sourceFile, $indexType);
    +
    +        my $toExpand = $self->ExpandMenu($sourceFile, $indexType, $menuSelectionHierarchy, $menuRootLength);
    +
    +        $output .=
    +
    +        '<script language=JavaScript><!--' . "\n"
    +
    +        . 'HideAllBut([' . join(', ', @$toExpand) . '], ' . $menuGroupNumber . ');'
    +
    +        . '// --></script>';
    +        };
    +
    +    $output .= '</div><!--Menu-->';
    +
    +    $menuNumbersAndLengthsDone = 1;
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildMenuSegment
    +#
    +#   A recursive function to build a segment of the menu.  *Remember to reset the <Menu Package Variables> before calling this
    +#   for the first time.*
    +#
    +#   Parameters:
    +#
    +#       outputDirectory - The output directory the menu is being built for.
    +#       menuSegment - An arrayref specifying the segment of the menu to build.  Either pass the menu itself or the contents
    +#                               of a group.
    +#       topLevel - Whether the passed segment is the top level segment or not.
    +#
    +#   Returns:
    +#
    +#       The array ( menuHTML, length ).
    +#
    +#       menuHTML - The menu segment in HTML.
    +#       groupLength - The length of the group, *not* including the contents of any subgroups, as computed from the
    +#                            <Menu Length Constants>.
    +#
    +#   Dependencies:
    +#
    +#       - <BuildMenu()> depends on the way this function formats file and index entries.
    +#
    +sub BuildMenuSegment #(outputDirectory, menuSegment, topLevel)
    +    {
    +    my ($self, $outputDirectory, $menuSegment, $topLevel) = @_;
    +
    +    my $output;
    +    my $groupLength = 0;
    +
    +    foreach my $entry (@$menuSegment)
    +        {
    +        if ($entry->Type() == ::MENU_GROUP())
    +            {
    +            my ($entryOutput, $entryLength) =
    +                $self->BuildMenuSegment($outputDirectory, $entry->GroupContent());
    +
    +            my $entryNumber;
    +
    +            if (!$menuNumbersAndLengthsDone)
    +                {
    +                $entryNumber = $menuGroupNumber;
    +                $menuGroupNumber++;
    +
    +                $menuGroupLengths{$entry} = $entryLength;
    +                $menuGroupNumbers{$entry} = $entryNumber;
    +                }
    +            else
    +                {  $entryNumber = $menuGroupNumbers{$entry};  };
    +
    +            $output .=
    +            '<div class=MEntry>'
    +                . '<div class=MGroup>'
    +
    +                    . '<a href="javascript:ToggleMenu(\'MGroupContent' . $entryNumber . '\')"'
    +                         . ($self->CommandLineOption() eq 'FramedHTML' ? ' target="_self"' : '') . '>'
    +                        . $self->StringToHTML($entry->Title())
    +                    . '</a>'
    +
    +                    . '<div class=MGroupContent id=MGroupContent' . $entryNumber . '>'
    +                        . $entryOutput
    +                    . '</div>'
    +
    +                . '</div>'
    +            . '</div>';
    +
    +            $groupLength += MENU_GROUP_LENGTH;
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_FILE())
    +            {
    +            my $targetOutputFile = $self->OutputFileOf($entry->Target());
    +
    +        # Dependency: BuildMenu() depends on how this formats file entries.
    +            $output .=
    +            '<div class=MEntry>'
    +                . '<div class=MFile>'
    +                    . '<a href="' . $self->MakeRelativeURL($outputDirectory, $targetOutputFile) . '">'
    +                        . $self->StringToHTML( $entry->Title(), ADD_HIDDEN_BREAKS)
    +                    . '</a>'
    +                . '</div>'
    +            . '</div>';
    +
    +            $groupLength += MENU_FILE_LENGTH;
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_TEXT())
    +            {
    +            $output .=
    +            '<div class=MEntry>'
    +                . '<div class=MText>'
    +                    . $self->StringToHTML( $entry->Title() )
    +                . '</div>'
    +            . '</div>';
    +
    +            $groupLength += MENU_TEXT_LENGTH;
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_LINK())
    +            {
    +            $output .=
    +            '<div class=MEntry>'
    +                . '<div class=MLink>'
    +                    . '<a href="' . $entry->Target() . '"' . ($self->CommandLineOption() eq 'FramedHTML' ? ' target="_top"' : '') . '>'
    +                        . $self->StringToHTML( $entry->Title() )
    +                    . '</a>'
    +                . '</div>'
    +            . '</div>';
    +
    +            $groupLength += MENU_LINK_LENGTH;
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_INDEX())
    +            {
    +            my $indexFile = $self->IndexFileOf($entry->Target);
    +
    +        # Dependency: BuildMenu() depends on how this formats index entries.
    +            $output .=
    +            '<div class=MEntry>'
    +                . '<div class=MIndex>'
    +                    . '<a href="' . $self->MakeRelativeURL( $outputDirectory, $self->IndexFileOf($entry->Target()) ) . '">'
    +                        . $self->StringToHTML( $entry->Title() )
    +                    . '</a>'
    +                . '</div>'
    +            . '</div>';
    +
    +            $groupLength += MENU_INDEX_LENGTH;
    +            };
    +        };
    +
    +
    +    if (!$menuNumbersAndLengthsDone)
    +        {  $menuLength += $groupLength;  };
    +
    +    return ($output, $groupLength);
    +    };
    +
    +
    +#
    +#   Function: BuildContent
    +#
    +#   Builds and returns the main page content.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName>.
    +#       parsedFile - The parsed source file as an arrayref of <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +#   Returns:
    +#
    +#       The page content in HTML.
    +#
    +sub BuildContent #(sourceFile, parsedFile)
    +    {
    +    my ($self, $sourceFile, $parsedFile) = @_;
    +
    +    $self->ResetToolTips();
    +    $imageAnchorNumber = 1;
    +    $imageContent = undef;
    +
    +    my $output = '<div id=Content>';
    +    my $i = 0;
    +
    +    while ($i < scalar @$parsedFile)
    +        {
    +        my $anchor = $self->SymbolToHTMLSymbol($parsedFile->[$i]->Symbol());
    +
    +        my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$i]->Type())->Scope();
    +
    +
    +        # The anchors are closed, but not around the text, so the :hover CSS style won't accidentally kick in.
    +
    +        my $headerType;
    +
    +        if ($i == 0)
    +            {  $headerType = 'h1';  }
    +        elsif ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +            {  $headerType = 'h2';  }
    +        else
    +            {  $headerType = 'h3';  };
    +
    +        $output .=
    +
    +        '<div class="C' . NaturalDocs::Topics->NameOfType($parsedFile->[$i]->Type(), 0, 1) . '">'
    +            . '<div class=CTopic' . ($i == 0 ? ' id=MainTopic' : '') . '>'
    +
    +                . '<' . $headerType . ' class=CTitle>'
    +                    . '<a name="' . $anchor . '"></a>'
    +                    . $self->StringToHTML( $parsedFile->[$i]->Title(), ADD_HIDDEN_BREAKS)
    +                . '</' . $headerType . '>';
    +
    +
    +        my $hierarchy;
    +        if (NaturalDocs::Topics->TypeInfo( $parsedFile->[$i]->Type() )->ClassHierarchy())
    +            {
    +            $hierarchy = $self->BuildClassHierarchy($sourceFile, $parsedFile->[$i]->Symbol());
    +            };
    +
    +        my $summary;
    +        if ($i == 0 || $scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +            {
    +            $summary .= $self->BuildSummary($sourceFile, $parsedFile, $i);
    +            };
    +
    +        my $hasBody;
    +        if (defined $hierarchy || defined $summary ||
    +            defined $parsedFile->[$i]->Prototype() || defined $parsedFile->[$i]->Body())
    +            {
    +            $output .= '<div class=CBody>';
    +            $hasBody = 1;
    +            };
    +
    +        $output .= $hierarchy;
    +
    +        if (defined $parsedFile->[$i]->Prototype())
    +            {
    +            $output .= $self->BuildPrototype($parsedFile->[$i]->Type(), $parsedFile->[$i]->Prototype(), $sourceFile);
    +            };
    +
    +        if (defined $parsedFile->[$i]->Body())
    +            {
    +            $output .= $self->NDMarkupToHTML( $sourceFile, $parsedFile->[$i]->Body(), $parsedFile->[$i]->Symbol(),
    +                                                                  $parsedFile->[$i]->Package(), $parsedFile->[$i]->Type(),
    +                                                                  $parsedFile->[$i]->Using() );
    +            };
    +
    +        $output .= $summary;
    +
    +
    +        if ($hasBody)
    +            {  $output .= '</div>';  };
    +
    +        $output .=
    +            '</div>' # CTopic
    +        . '</div>' # CType
    +        . "\n\n";
    +
    +        $i++;
    +        };
    +
    +    $output .= '</div><!--Content-->';
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildSummary
    +#
    +#   Builds a summary, either for the entire file or the current class/section.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> the summary appears in.
    +#
    +#       parsedFile - A reference to the parsed source file.
    +#
    +#       index   - The index into the parsed file to start at.  If undef or zero, it builds a summary for the entire file.  If it's the
    +#                    index of a <TopicType> that starts or ends a scope, it builds a summary for that scope
    +#
    +#   Returns:
    +#
    +#       The summary in HTML.
    +#
    +sub BuildSummary #(sourceFile, parsedFile, index)
    +    {
    +    my ($self, $sourceFile, $parsedFile, $index) = @_;
    +    my $completeSummary;
    +
    +    if (!defined $index || $index == 0)
    +        {
    +        $index = 0;
    +        $completeSummary = 1;
    +        }
    +    else
    +        {
    +        # Skip the scope entry.
    +        $index++;
    +        };
    +
    +    if ($index + 1 >= scalar @$parsedFile)
    +        {  return undef;  };
    +
    +
    +    my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$index]->Type())->Scope();
    +
    +    # Return nothing if there's only one entry.
    +    if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) )
    +        {  return undef;  };
    +
    +
    +    my $indent = 0;
    +    my $inGroup;
    +
    +    my $isMarked = 0;
    +
    +    my $output =
    +    '<!--START_ND_SUMMARY-->'
    +    . '<div class=Summary><div class=STitle>Summary</div>'
    +
    +        # Not all browsers get table padding right, so we need a div to apply the border.
    +        . '<div class=SBorder>'
    +        . '<table border=0 cellspacing=0 cellpadding=0 class=STable>';
    +
    +        while ($index < scalar @$parsedFile)
    +            {
    +            my $topic = $parsedFile->[$index];
    +            my $scope = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope();
    +
    +            if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) )
    +                {  last;  };
    +
    +
    +            # Remove modifiers as appropriate for the current entry.
    +
    +            if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +                {
    +                $indent = 0;
    +                $inGroup = 0;
    +                $isMarked = 0;
    +                }
    +            elsif ($topic->Type() eq ::TOPIC_GROUP())
    +                {
    +                if ($inGroup)
    +                    {  $indent--;  };
    +
    +                $inGroup = 0;
    +                $isMarked = 0;
    +                };
    +
    +
    +            $output .=
    +             '<tr class="S' . ($index == 0 ? 'Main' : NaturalDocs::Topics->NameOfType($topic->Type(), 0, 1))
    +                . ($indent ? ' SIndent' . $indent : '') . ($isMarked ? ' SMarked' : '') .'">'
    +                . '<td class=SEntry>';
    +
    +           # Add the entry itself.
    +
    +            my $toolTipProperties;
    +
    +            # We only want a tooltip here if there's a protoype.  Otherwise it's redundant.
    +
    +            if (defined $topic->Prototype())
    +                {
    +                my $tooltipID = $self->BuildToolTip($topic->Symbol(), $sourceFile, $topic->Type(),
    +                                                                     $topic->Prototype(), $topic->Summary());
    +                $toolTipProperties = $self->BuildToolTipLinkProperties($tooltipID);
    +                };
    +
    +            $output .=
    +            '<a href="#' . $self->SymbolToHTMLSymbol($parsedFile->[$index]->Symbol()) . '" ' . $toolTipProperties . '>'
    +                . $self->StringToHTML( $parsedFile->[$index]->Title(), ADD_HIDDEN_BREAKS)
    +            . '</a>';
    +
    +
    +            $output .=
    +            '</td><td class=SDescription>';
    +
    +            if (defined $parsedFile->[$index]->Body())
    +                {
    +                $output .= $self->NDMarkupToHTML($sourceFile, $parsedFile->[$index]->Summary(),
    +                                                                     $parsedFile->[$index]->Symbol(), $parsedFile->[$index]->Package(),
    +                                                                     $parsedFile->[$index]->Type(), $parsedFile->[$index]->Using(),
    +                                                                     NDMARKUPTOHTML_SUMMARY);
    +                };
    +
    +
    +            $output .=
    +            '</td></tr>';
    +
    +
    +            # Prepare the modifiers for the next entry.
    +
    +            if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +                {
    +                $indent = 1;
    +                $inGroup = 0;
    +                }
    +            elsif ($topic->Type() eq ::TOPIC_GROUP())
    +                {
    +                if (!$inGroup)
    +                    {
    +                    $indent++;
    +                    $inGroup = 1;
    +                    };
    +                };
    +
    +            $isMarked ^= 1;
    +            $index++;
    +            };
    +
    +        $output .=
    +        '</table>'
    +    . '</div>' # Body
    +    . '</div>' # Summary
    +    . "<!--END_ND_SUMMARY-->";
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildPrototype
    +#
    +#   Builds and returns the prototype as HTML.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> the prototype is from.
    +#       prototype - The prototype to format.
    +#       file - The <FileName> the prototype was defined in.
    +#
    +#   Returns:
    +#
    +#       The prototype in HTML.
    +#
    +sub BuildPrototype #(type, prototype, file)
    +    {
    +    my ($self, $type, $prototype, $file) = @_;
    +
    +    my $language = NaturalDocs::Languages->LanguageOf($file);
    +    my $prototypeObject = $language->ParsePrototype($type, $prototype);
    +
    +    my $output;
    +
    +    if ($prototypeObject->OnlyBeforeParameters())
    +        {
    +        $output =
    +        # A blockquote to scroll it if it's too long.
    +        '<blockquote>'
    +            # A surrounding table as a hack to make the div form-fit.
    +            . '<table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr>'
    +            	. '<td' . (NaturalDocs::Settings->HighlightCode() ? ' class="prettyprint"' : '') . '>'
    +	                . $self->ConvertAmpChars($prototypeObject->BeforeParameters())
    +	            . '</td>'
    +	        . '</tr></table>'
    +        . '</blockquote>';
    +        }
    +
    +    else
    +        {
    +        my $params = $prototypeObject->Parameters();
    +        my $beforeParams = $prototypeObject->BeforeParameters();
    +        my $afterParams = $prototypeObject->AfterParameters();
    +
    +	    my $highlightClass = (NaturalDocs::Settings->HighlightCode() ? ' prettyprint ' : '');
    +
    +        # Determine what features the prototype has and its length.
    +
    +        my ($hasType, $hasTypePrefix, $hasNamePrefix, $hasDefaultValue, $hasDefaultValuePrefix);
    +        my $maxParamLength = 0;
    +
    +        foreach my $param (@$params)
    +            {
    +            my $paramLength = length($param->Name());
    +
    +            if ($param->Type())
    +                {
    +                $hasType = 1;
    +                $paramLength += length($param->Type()) + 1;
    +                };
    +            if ($param->TypePrefix())
    +                {
    +                $hasTypePrefix = 1;
    +                $paramLength += length($param->TypePrefix()) + 1;
    +                };
    +            if ($param->NamePrefix())
    +                {
    +                $hasNamePrefix = 1;
    +                $paramLength += length($param->NamePrefix());
    +                };
    +            if ($param->DefaultValue())
    +                {
    +                $hasDefaultValue = 1;
    +
    +                # The length of the default value part is either the longest word, or 1/3 the total, whichever is longer.  We do this
    +                # because we don't want parameter lines wrapping to more than three lines, and there's no guarantee that the line will
    +                # wrap at all.  There's a small possibility that it could still wrap to four lines with this code, but we don't need to go
    +                # crazy(er) here.
    +
    +                my $thirdLength = length($param->DefaultValue()) / 3;
    +
    +                my @words = split(/ +/, $param->DefaultValue());
    +                my $maxWordLength = 0;
    +
    +                foreach my $word (@words)
    +                    {
    +                    if (length($word) > $maxWordLength)
    +                        {  $maxWordLength = length($word);  };
    +                    };
    +
    +                $paramLength += ($maxWordLength > $thirdLength ? $maxWordLength : $thirdLength) + 1;
    +                };
    +            if ($param->DefaultValuePrefix())
    +                {
    +                $hasDefaultValuePrefix = 1;
    +                $paramLength += length($param->DefaultValuePrefix()) + 1;
    +                };
    +
    +            if ($paramLength > $maxParamLength)
    +                {  $maxParamLength = $paramLength;  };
    +            };
    +
    +        my $useCondensed = (length($beforeParams) + $maxParamLength + length($afterParams) > 80 ? 1 : 0);
    +        my $parameterColumns = 1 + $hasType + $hasTypePrefix + $hasNamePrefix +
    +                                               $hasDefaultValue + $hasDefaultValuePrefix + $useCondensed;
    +
    +        $output =
    +        '<blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td>'
    +
    +            # Stupid hack to get it to work right in IE.
    +            . '<table border=0 cellspacing=0 cellpadding=0><tr>'
    +
    +            . '<td class="PBeforeParameters ' . $highlightClass . '"' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>'
    +                . $self->ConvertAmpChars($beforeParams);
    +
    +                if ($beforeParams && $beforeParams !~ /[\(\[\{\<]$/)
    +                    {  $output .= '&nbsp;';  };
    +
    +            $output .=
    +            '</td>';
    +
    +            for (my $i = 0; $i < scalar @$params; $i++)
    +                {
    +                if ($useCondensed)
    +                    {
    +                    $output .= '</tr><tr><td>&nbsp;&nbsp;&nbsp;</td>';
    +                    }
    +                elsif ($i > 0)
    +                    {
    +                    # Go to the next row and and skip the BeforeParameters cell.
    +                    $output .= '</tr><tr><td></td>';
    +                    };
    +
    +                if ($language->TypeBeforeParameter())
    +                    {
    +                    if ($hasTypePrefix)
    +                        {
    +                        my $htmlTypePrefix = $self->ConvertAmpChars($params->[$i]->TypePrefix());
    +                        $htmlTypePrefix =~ s/ $/&nbsp;/;
    +
    +                        $output .=
    +                        '<td class="PTypePrefix ' . $highlightClass . '" nowrap>'
    +                            . $htmlTypePrefix
    +                        . '</td>';
    +                        };
    +
    +                    if ($hasType)
    +                        {
    +                        $output .=
    +                        '<td class="PType ' . $highlightClass . '" nowrap>'
    +                            . $self->ConvertAmpChars($params->[$i]->Type()) . '&nbsp;'
    +                        . '</td>';
    +                        };
    +
    +                    if ($hasNamePrefix)
    +                        {
    +                        $output .=
    +                        '<td class="PParameterPrefix ' . $highlightClass . '" nowrap>'
    +                            . $self->ConvertAmpChars($params->[$i]->NamePrefix())
    +                        . '</td>';
    +                        };
    +
    +                    $output .=
    +                    '<td class="PParameter ' . $highlightClass . '" nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>'
    +                        . $self->ConvertAmpChars($params->[$i]->Name())
    +                    . '</td>';
    +                    }
    +
    +                else # !$language->TypeBeforeParameter()
    +                    {
    +                    $output .=
    +                    '<td class="PParameter ' . $highlightClass . '" nowrap>'
    +                        . $self->ConvertAmpChars( $params->[$i]->NamePrefix() . $params->[$i]->Name() )
    +                    . '</td>';
    +
    +                    if ($hasType || $hasTypePrefix)
    +                        {
    +                        my $typePrefix = $params->[$i]->TypePrefix();
    +                        if ($typePrefix)
    +                            {  $typePrefix .= ' ';  };
    +
    +                        $output .=
    +                        '<td class="PType ' . $highlightClass . '" nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>'
    +                            . '&nbsp;' . $self->ConvertAmpChars( $typePrefix . $params->[$i]->Type() )
    +                        . '</td>';
    +                        };
    +                    };
    +
    +                if ($hasDefaultValuePrefix)
    +                    {
    +                    $output .=
    +                    '<td class="PDefaultValuePrefix ' . $highlightClass . '">'
    +
    +                       . '&nbsp;' . $self->ConvertAmpChars( $params->[$i]->DefaultValuePrefix() ) . '&nbsp;'
    +                    . '</td>';
    +                    };
    +
    +                if ($hasDefaultValue)
    +                    {
    +                    $output .=
    +                    '<td class="PDefaultValue ' . $highlightClass . '" width=100%>'
    +                        . ($hasDefaultValuePrefix ? '' : '&nbsp;') . $self->ConvertAmpChars( $params->[$i]->DefaultValue() )
    +                    . '</td>';
    +                    };
    +                };
    +
    +            if ($useCondensed)
    +                {  $output .= '</tr><tr>';  };
    +
    +            $output .=
    +            '<td class="PAfterParameters ' . $highlightClass . '"' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>'
    +                 . $self->ConvertAmpChars($afterParams);
    +
    +                if ($afterParams && $afterParams !~ /^[\)\]\}\>]/)
    +                    {  $output .= '&nbsp;';  };
    +
    +            $output .=
    +            '</td>'
    +        . '</tr></table>'
    +
    +        # Hack.
    +        . '</td></tr></table></blockquote>';
    +       };
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildFooter
    +#
    +#   Builds and returns the HTML footer for the page.
    +#
    +#   Parameters:
    +#
    +#       multiline - Whether it should be formatted on multiple lines or not.
    +#
    +#   Dependencies:
    +#
    +#       <Builder::HTML::UpdateFile()> and <Builder::HTML::UpdateIndex()> require this section to be surrounded with the exact
    +#       strings "<div id=Footer>" and "</div><!--Footer-->".
    +#
    +sub BuildFooter #(bool multiline)
    +    {
    +    my ($self, $multiline) = @_;
    +
    +    my $footer = NaturalDocs::Menu->Footer();
    +    my $timestamp = NaturalDocs::Menu->TimeStamp();
    +    my $divider;
    +
    +    if ($multiline)
    +        {  $divider = '</p><p>';  }
    +    else
    +        {  $divider = '&nbsp; &middot;&nbsp; ';  };
    +
    +
    +    my $output = '<div id=Footer>';
    +    if ($multiline)
    +        {  $output .= '<p>';  };
    +
    +    if (defined $footer)
    +        {
    +        $footer =~ s/\(c\)/&copy;/gi;
    +        $footer =~ s/\(tm\)/&trade;/gi;
    +        $footer =~ s/\(r\)/&reg;/gi;
    +
    +        $output .= $footer . $divider;
    +        };
    +
    +    if (defined $timestamp)
    +        {
    +        $output .= $timestamp . $divider;
    +        };
    +
    +    $output .=
    +    '<a href="' . NaturalDocs::Settings->AppURL() . '">'
    +        . 'Generated by Natural Docs'
    +    . '</a>';
    +
    +    if ($multiline)
    +        {  $output .= '</p>';  };
    +
    +    $output .=
    +    '</div><!--Footer-->';
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildToolTip
    +#
    +#   Builds the HTML for a symbol's tooltip and stores it in <tooltipHTML>.
    +#
    +#   Parameters:
    +#
    +#       symbol - The target <SymbolString>.
    +#       file - The <FileName> the target's defined in.
    +#       type - The symbol <TopicType>.
    +#       prototype - The target prototype, or undef for none.
    +#       summary - The target summary, or undef for none.
    +#
    +#   Returns:
    +#
    +#       If a tooltip is necessary for the link, returns the tooltip ID.  If not, returns undef.
    +#
    +sub BuildToolTip #(symbol, file, type, prototype, summary)
    +    {
    +    my ($self, $symbol, $file, $type, $prototype, $summary) = @_;
    +
    +    if (defined $prototype || defined $summary)
    +        {
    +        my $htmlSymbol = $self->SymbolToHTMLSymbol($symbol);
    +        my $number = $tooltipSymbolsToNumbers{$htmlSymbol};
    +
    +        if (!defined $number)
    +            {
    +            $number = $tooltipNumber;
    +            $tooltipNumber++;
    +
    +            $tooltipSymbolsToNumbers{$htmlSymbol} = $number;
    +
    +            $tooltipHTML .=
    +            '<div class=CToolTip id="tt' . $number . '">'
    +                . '<div class=C' . NaturalDocs::Topics->NameOfType($type, 0, 1) . '>';
    +
    +            if (defined $prototype)
    +                {
    +                $tooltipHTML .= $self->BuildPrototype($type, $prototype, $file);
    +                };
    +
    +            if (defined $summary)
    +                {
    +                # The fact that we don't have scope or using shouldn't matter because links shouldn't be included in the style anyway.
    +                $summary = $self->NDMarkupToHTML($file, $summary, undef, undef, $type, undef, NDMARKUPTOHTML_TOOLTIP);
    +                $tooltipHTML .= $summary;
    +                };
    +
    +            $tooltipHTML .=
    +                '</div>'
    +            . '</div>';
    +            };
    +
    +        return 'tt' . $number;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +#
    +#   Function: BuildToolTips
    +#
    +#   Builds and returns the tooltips for the page in HTML.
    +#
    +sub BuildToolTips
    +    {
    +    my $self = shift;
    +    return "\n<!--START_ND_TOOLTIPS-->\n" . $tooltipHTML . "<!--END_ND_TOOLTIPS-->\n\n";
    +    };
    +
    +#
    +#   Function: BuildClassHierarchy
    +#
    +#   Builds and returns a class hierarchy diagram for the passed class, if applicable.
    +#
    +#   Parameters:
    +#
    +#       file - The source <FileName>.
    +#       class - The class <SymbolString> to build the hierarchy of.
    +#
    +sub BuildClassHierarchy #(file, symbol)
    +    {
    +    my ($self, $file, $symbol) = @_;
    +
    +    my @parents = NaturalDocs::ClassHierarchy->ParentsOf($symbol);
    +    @parents = sort { ::StringCompare($a, $b) } @parents;
    +
    +    my @children = NaturalDocs::ClassHierarchy->ChildrenOf($symbol);
    +    @children = sort { ::StringCompare($a, $b) } @children;
    +
    +    if (!scalar @parents && !scalar @children)
    +        {  return undef;  };
    +
    +    my $output =
    +    '<div class=ClassHierarchy>';
    +
    +    if (scalar @parents)
    +        {
    +        $output .='<table border=0 cellspacing=0 cellpadding=0><tr><td>';
    +
    +        foreach my $parent (@parents)
    +            {
    +            $output .= $self->BuildClassHierarchyEntry($file, $parent, 'CHParent', 1);
    +            };
    +
    +        $output .= '</td></tr></table><div class=CHIndent>';
    +        };
    +
    +    $output .=
    +    '<table border=0 cellspacing=0 cellpadding=0><tr><td>'
    +        . $self->BuildClassHierarchyEntry($file, $symbol, 'CHCurrent', undef)
    +    . '</td></tr></table>';
    +
    +    if (scalar @children)
    +        {
    +        $output .=
    +        '<div class=CHIndent>'
    +            . '<table border=0 cellspacing=0 cellpadding=0><tr><td>';
    +
    +        if (scalar @children <= 5)
    +            {
    +            for (my $i = 0; $i < scalar @children; $i++)
    +                {  $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1);  };
    +            }
    +        else
    +            {
    +            for (my $i = 0; $i < 4; $i++)
    +                {  $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1);  };
    +
    +           $output .= '<div class=CHChildNote><div class=CHEntry>' . (scalar @children - 4) . ' other children</div></div>';
    +            };
    +
    +        $output .=
    +        '</td></tr></table>'
    +        . '</div>';
    +        };
    +
    +    if (scalar @parents)
    +        {  $output .= '</div>';  };
    +
    +    $output .=
    +    '</div>';
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildClassHierarchyEntry
    +#
    +#   Builds and returns a single class hierarchy entry.
    +#
    +#   Parameters:
    +#
    +#       file - The source <FileName>.
    +#       symbol - The class <SymbolString> whose entry is getting built.
    +#       style - The style to apply to the entry, such as <CHParent>.
    +#       link - Whether to build a link for this class or not.  When building the selected class' entry, this should be false.  It will
    +#               automatically handle whether the symbol is defined or not.
    +#
    +sub BuildClassHierarchyEntry #(file, symbol, style, link)
    +    {
    +    my ($self, $file, $symbol, $style, $link) = @_;
    +
    +    my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
    +    my $name = join(NaturalDocs::Languages->LanguageOf($file)->PackageSeparator(), @identifiers);
    +    $name = $self->StringToHTML($name);
    +
    +    my $output = '<div class=' . $style . '><div class=CHEntry>';
    +
    +    if ($link)
    +        {
    +        my $target = NaturalDocs::SymbolTable->Lookup($symbol, $file);
    +
    +        if (defined $target)
    +            {
    +            my $targetFile;
    +
    +            if ($target->File() ne $file)
    +                {  $targetFile = $self->MakeRelativeURL( $self->OutputFileOf($file), $self->OutputFileOf($target->File()), 1 );  };
    +            # else leave it undef
    +
    +            my $targetTooltipID = $self->BuildToolTip($symbol, $target->File(), $target->Type(),
    +                                                                          $target->Prototype(), $target->Summary());
    +
    +            my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID);
    +
    +            $output .= '<a href="' . $targetFile . '#' . $self->SymbolToHTMLSymbol($symbol) . '" '
    +                            . 'class=L' . NaturalDocs::Topics->NameOfType($target->Type(), 0, 1) . ' ' . $toolTipProperties . '>'
    +                            . $name . '</a>';
    +            }
    +        else
    +            {  $output .= $name;  };
    +        }
    +    else
    +        {  $output .= $name;  };
    +
    +    $output .= '</div></div>';
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: OpeningBrowserStyles
    +#
    +#   Returns the JavaScript that will add opening browser styles if necessary.
    +#
    +sub OpeningBrowserStyles
    +    {
    +    my $self = shift;
    +
    +    return
    +
    +    '<script language=JavaScript><!--' . "\n"
    +
    +        # IE 4 and 5 don't understand 'undefined', so you can't say '!= undefined'.
    +        . 'if (browserType) {'
    +            . 'document.write("<div class=" + browserType + ">");'
    +            . 'if (browserVer) {'
    +                . 'document.write("<div class=" + browserVer + ">"); }'
    +            . '}'
    +
    +    . '// --></script>';
    +    };
    +
    +
    +#
    +#   Function: ClosingBrowserStyles
    +#
    +#   Returns the JavaScript that will close browser styles if necessary.
    +#
    +sub ClosingBrowserStyles
    +    {
    +    my $self = shift;
    +
    +    return
    +
    +    '<script language=JavaScript><!--' . "\n"
    +
    +        . 'if (browserType) {'
    +            . 'if (browserVer) {'
    +                . 'document.write("</div>"); }'
    +            . 'document.write("</div>");'
    +            . '}'
    +
    +    . '// --></script>';
    +    };
    +
    +
    +#
    +#   Function: StandardComments
    +#
    +#   Returns the standard HTML comments that should be included in every generated file.  This includes <IEWebMark()>, so this
    +#   really is required for proper functionality.
    +#
    +sub StandardComments
    +    {
    +    my $self = shift;
    +
    +    return "\n\n"
    +
    +        . '<!--  Generated by Natural Docs, version ' . NaturalDocs::Settings->TextAppVersion() . ' -->' . "\n"
    +        . '<!--  ' . NaturalDocs::Settings->AppURL() . '  -->' . "\n\n"
    +        . $self->IEWebMark() . "\n\n";
    +    };
    +
    +
    +#
    +#   Function: IEWebMark
    +#
    +#   Returns the HTML comment necessary to get around the security warnings in IE starting with Windows XP Service Pack 2.
    +#
    +#   With this mark, the HTML page is treated as if it were in the Internet security zone instead of the Local Machine zone.  This
    +#   prevents the lockdown on scripting that causes an error message to appear with each page.
    +#
    +#   More Information:
    +#
    +#       - http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/sp2brows.mspx#EHAA
    +#       - http://www.phdcc.com/xpsp2.htm#markoftheweb
    +#
    +sub IEWebMark
    +    {
    +    my $self = shift;
    +
    +    return '<!-- saved from url=(0026)http://www.naturaldocs.org -->';
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Index Functions
    +
    +
    +#
    +#   Function: BuildIndexPages
    +#
    +#   Builds an index file or files.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> the index is limited to, or undef for none.
    +#       indexSections  - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement>
    +#                               objects.  The first section is for symbols, the second for numbers, and the rest for A through Z.
    +#       beginIndexPage - All the content of the HTML page up to where the index content should appear.
    +#       endIndexPage - All the content of the HTML page past where the index should appear.
    +#       beginSearchResultsPage - All the content of the HTML page up to where the search results content should appear.
    +#       endSearchResultsPage - All the content of the HTML page past where the search results content should appear.
    +#
    +#   Returns:
    +#
    +#       The number of pages in the index.
    +#
    +sub BuildIndexPages #(TopicType type, NaturalDocs::SymbolTable::IndexElement[] indexSections, string beginIndexPage, string endIndexPage, string beginSearchResultsPage, string endSearchResultsPage) => int
    +    {
    +    my ($self, $type, $indexSections, $beginIndexPage, $endIndexPage, $beginSearchResultsPage, $endSearchResultsPage) = @_;
    +
    +
    +    # Build the content.
    +
    +    my ($indexHTMLSections, $tooltipHTMLSections, $searchResultsHTMLSections) = $self->BuildIndexSections($indexSections);
    +
    +
    +    # Generate the search result pages.
    +
    +   for (my $i = 0; $i < 28; $i++)
    +        {
    +        if ($searchResultsHTMLSections->[$i])
    +            {
    +            my $searchResultsFileName = $self->SearchResultsFileOf($type, $searchExtensions[$i]);
    +
    +            open(INDEXFILEHANDLE, '>' . $searchResultsFileName)
    +                or die "Couldn't create output file " . $searchResultsFileName . ".\n";
    +
    +		    binmode(INDEXFILEHANDLE, ':encoding(UTF-8)');
    +
    +            print INDEXFILEHANDLE
    +
    +            $beginSearchResultsPage
    +
    +            . '<div class=SRStatus id=Loading>Loading...</div>'
    +
    +            . '<table border=0 cellspacing=0 cellpadding=0>'
    +                . $searchResultsHTMLSections->[$i]
    +            . '</table>'
    +
    +            . '<div class=SRStatus id=Searching>Searching...</div>'
    +            . '<div class=SRStatus id=NoMatches>No Matches</div>'
    +
    +            . '<script type="text/javascript"><!--' . "\n"
    +                . 'document.getElementById("Loading").style.display="none";' . "\n"
    +                . 'document.getElementById("NoMatches").style.display="none";' . "\n"
    +
    +                . 'var searchResults = new SearchResults("searchResults", "' . $self->CommandLineOption() . '");' . "\n"
    +                . 'searchResults.Search();' . "\n"
    +            . '--></script>'
    +
    +            . $endSearchResultsPage;
    +
    +            close(INDEXFILEHANDLE);
    +            };
    +        };
    +
    +    if (!$self->MadeEmptySearchResultsPage())
    +        {
    +        my $emptySearchResultsFileName = NaturalDocs::File->JoinPaths( $self->SearchResultsDirectory(), 'NoResults.html' );
    +
    +        open(INDEXFILEHANDLE, '>' . $emptySearchResultsFileName)
    +            or die "Couldn't create output file " . $emptySearchResultsFileName . ".\n";
    +
    +	    binmode(INDEXFILEHANDLE, ':encoding(UTF-8)');
    +
    +        print INDEXFILEHANDLE
    +
    +        $beginSearchResultsPage
    +        . '<div class=SRStatus id=NoMatches>No Matches</div>'
    +        . $endSearchResultsPage;
    +
    +        close(INDEXFILEHANDLE);
    +
    +        $self->SetMadeEmptySearchResultsPage(1);
    +        };
    +
    +
    +    # Generate the index pages.
    +
    +    my $page = 1;
    +    my $pageSize = 0;
    +    my @pageLocations;
    +
    +    # The maximum page size acceptable before starting a new page.  Note that this doesn't include beginPage and endPage,
    +    # because we don't want something like a large menu screwing up the calculations.
    +    use constant PAGESIZE_LIMIT => 50000;
    +
    +
    +    # File the pages.
    +
    +    for (my $i = 0; $i < scalar @$indexHTMLSections; $i++)
    +        {
    +        if (!defined $indexHTMLSections->[$i])
    +            {  next;  };
    +
    +        $pageSize += length($indexHTMLSections->[$i]) + length($tooltipHTMLSections->[$i]);
    +        $pageLocations[$i] = $page;
    +
    +        if ($pageSize + length($indexHTMLSections->[$i+1]) + length($tooltipHTMLSections->[$i+1]) > PAGESIZE_LIMIT)
    +            {
    +            $page++;
    +            $pageSize = 0;
    +            };
    +        };
    +
    +
    +    # Build the pages.
    +
    +    my $indexFileName;
    +    $page = -1;
    +    my $oldPage = -1;
    +    my $tooltips;
    +    my $firstHeading;
    +
    +    for (my $i = 0; $i < scalar @$indexHTMLSections; $i++)
    +        {
    +        if (!defined $indexHTMLSections->[$i])
    +            {  next;  };
    +
    +        $page = $pageLocations[$i];
    +
    +        # Switch files if we need to.
    +
    +        if ($page != $oldPage)
    +            {
    +            if ($oldPage != -1)
    +                {
    +                print INDEXFILEHANDLE '</table>' . $tooltips . $endIndexPage;
    +                close(INDEXFILEHANDLE);
    +                $tooltips = undef;
    +                };
    +
    +            $indexFileName = $self->IndexFileOf($type, $page);
    +
    +            open(INDEXFILEHANDLE, '>' . $indexFileName)
    +                or die "Couldn't create output file " . $indexFileName . ".\n";
    +
    +		    binmode(INDEXFILEHANDLE, ':encoding(UTF-8)');
    +
    +            print INDEXFILEHANDLE $beginIndexPage . $self->BuildIndexNavigationBar($type, $page, \@pageLocations)
    +                                              . '<table border=0 cellspacing=0 cellpadding=0>';
    +
    +            $oldPage = $page;
    +            $firstHeading = 1;
    +            };
    +
    +        print INDEXFILEHANDLE
    +        '<tr>'
    +            . '<td class=IHeading' . ($firstHeading ? ' id=IFirstHeading' : '') . '>'
    +                . '<a name="' . $indexAnchors[$i] . '"></a>'
    +                 . $indexHeadings[$i]
    +            . '</td>'
    +            . '<td></td>'
    +        . '</tr>'
    +
    +        . $indexHTMLSections->[$i];
    +
    +        $firstHeading = 0;
    +        $tooltips .= $tooltipHTMLSections->[$i];
    +        };
    +
    +    if ($page != -1)
    +        {
    +        print INDEXFILEHANDLE '</table>' . $tooltips . $endIndexPage;
    +        close(INDEXFILEHANDLE);
    +        }
    +
    +    # Build a dummy page so there's something at least.
    +    else
    +        {
    +        $indexFileName = $self->IndexFileOf($type, 1);
    +
    +        open(INDEXFILEHANDLE, '>' . $indexFileName)
    +            or die "Couldn't create output file " . $indexFileName . ".\n";
    +
    +    	binmode(INDEXFILEHANDLE, ':encoding(UTF-8)');
    +
    +        print INDEXFILEHANDLE
    +            $beginIndexPage
    +            . $self->BuildIndexNavigationBar($type, 1, \@pageLocations)
    +            . 'There are no entries in the ' . lc( NaturalDocs::Topics->NameOfType($type) ) . ' index.'
    +            . $endIndexPage;
    +
    +        close(INDEXFILEHANDLE);
    +        };
    +
    +
    +    return $page;
    +    };
    +
    +
    +#
    +#   Function: BuildIndexSections
    +#
    +#   Builds and returns the index and search results sections in HTML.
    +#
    +#   Parameters:
    +#
    +#       index  - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement> objects.
    +#                   The first section is for symbols, the second for numbers, and the rest for A through Z.
    +#
    +#   Returns:
    +#
    +#       The arrayref ( indexSections, tooltipSections, searchResultsSections ).
    +#
    +#       Index 0 is the symbols, index 1 is the numbers, and each following index is A through Z.  The content of each section
    +#       is its HTML, or undef if there is nothing for that section.
    +#
    +sub BuildIndexSections #(NaturalDocs::SymbolTable::IndexElement[] index) => ( string[], string[], string[] )
    +    {
    +    my ($self, $indexSections) = @_;
    +
    +    $self->ResetToolTips();
    +    %searchResultIDs = ( );
    +
    +    my $contentSections = [ ];
    +    my $tooltipSections = [ ];
    +    my $searchResultsSections = [ ];
    +
    +    for (my $section = 0; $section < scalar @$indexSections; $section++)
    +        {
    +        if (defined $indexSections->[$section])
    +            {
    +            my $total = scalar @{$indexSections->[$section]};
    +
    +            for (my $i = 0; $i < $total; $i++)
    +                {
    +                my $id;
    +
    +                if ($i == 0)
    +                    {
    +                    if ($total == 1)
    +                        {  $id = 'IOnlySymbolPrefix';  }
    +                    else
    +                        {  $id = 'IFirstSymbolPrefix';  };
    +                    }
    +                elsif ($i == $total - 1)
    +                    {  $id = 'ILastSymbolPrefix';  };
    +
    +                my ($content, $searchResult) = $self->BuildIndexElement($indexSections->[$section]->[$i], $id);
    +                $contentSections->[$section] .= $content;
    +                $searchResultsSections->[$section] .= $searchResult;
    +                };
    +
    +            $tooltipSections->[$section] .= $self->BuildToolTips();
    +            $self->ResetToolTips(1);
    +            };
    +        };
    +
    +
    +    return ( $contentSections, $tooltipSections, $searchResultsSections );
    +    };
    +
    +
    +#
    +#   Function: BuildIndexElement
    +#
    +#   Converts a <NaturalDocs::SymbolTable::IndexElement> to HTML and returns it.  It will handle all sub-elements automatically.
    +#
    +#   Parameters:
    +#
    +#       element - The <NaturalDocs::SymbolTable::IndexElement> to build.
    +#       cssID - The CSS ID to apply to the prefix.
    +#
    +#   Recursion-Only Parameters:
    +#
    +#       These parameters are used internally for recursion, and should not be set.
    +#
    +#       symbol - If the element is below symbol level, the <SymbolString> to use.
    +#       package - If the element is below package level, the package <SymbolString> to use.
    +#       hasPackage - Whether the element is below package level.  Is necessary because package may need to be undef.
    +#
    +#   Returns:
    +#
    +#       The array ( indexHTML, searchResultHTML ) which is the element in the respective HTML forms.
    +#
    +sub BuildIndexElement #(NaturalDocs::SymbolTable::IndexElement element, string cssID, SymbolString symbol, SymbolString package, bool hasPackage) => ( string, string )
    +    {
    +    my ($self, $element, $cssID, $symbol, $package, $hasPackage) = @_;
    +
    +
    +    # If we're doing a file sub-index entry...
    +
    +    if ($hasPackage)
    +        {
    +        my ($inputDirectory, $relativePath) = NaturalDocs::Settings->SplitFromInputDirectory($element->File());
    +
    +        return $self->BuildIndexLink($self->StringToHTML($relativePath, ADD_HIDDEN_BREAKS), $symbol,
    +                                                                                 $package, $element->File(), $element->Type(),
    +                                                                                 $element->Prototype(), $element->Summary(), 'IFile');
    +        }
    +
    +
    +    # If we're doing a package sub-index entry...
    +
    +    elsif (defined $symbol)
    +
    +        {
    +        my $text;
    +
    +        if ($element->Package())
    +            {
    +            $text = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator());
    +            $text = $self->StringToHTML($text, ADD_HIDDEN_BREAKS);
    +            }
    +        else
    +            {  $text = 'Global';  };
    +
    +        if (!$element->HasMultipleFiles())
    +            {
    +            return $self->BuildIndexLink($text, $symbol, $element->Package(), $element->File(), $element->Type(),
    +                                                      $element->Prototype(), $element->Summary(), 'IParent');
    +            }
    +
    +        else
    +            {
    +            my $indexHTML =
    +            '<span class=IParent>'
    +                . $text
    +            . '</span>'
    +            . '<div class=ISubIndex>';
    +
    +            my $searchResultHTML = $indexHTML;
    +
    +            my $fileElements = $element->File();
    +            foreach my $fileElement (@$fileElements)
    +                {
    +                my ($i, $s) = $self->BuildIndexElement($fileElement, $cssID, $symbol, $element->Package(), 1);
    +                $indexHTML .= $i;
    +                $searchResultHTML .= $s;
    +                };
    +
    +            $indexHTML .= '</div>';
    +            $searchResultHTML .= '</div>';
    +
    +            return ($indexHTML, $searchResultHTML);
    +            };
    +        }
    +
    +
    +    # If we're doing a top-level symbol entry...
    +
    +    else
    +        {
    +        my $symbolText = $self->StringToHTML($element->SortableSymbol(), ADD_HIDDEN_BREAKS);
    +        my $symbolPrefix = $self->StringToHTML($element->IgnoredPrefix());
    +        my $searchResultID = $self->StringToSearchResultID($element->SortableSymbol());
    +
    +        my $indexHTML =
    +        '<tr>'
    +            . '<td class=ISymbolPrefix' . ($cssID ? ' id=' . $cssID : '') . '>'
    +                . ($symbolPrefix || '&nbsp;')
    +            . '</td><td class=IEntry>';
    +
    +        my $searchResultsHTML =
    +        '<div class=SRResult id=' . $searchResultID . '><div class=IEntry>';
    +
    +            if ($symbolPrefix)
    +                {  $searchResultsHTML .= '<span class=ISymbolPrefix>' . $symbolPrefix . '</span>';  };
    +
    +        if (!$element->HasMultiplePackages())
    +            {
    +            my $packageText;
    +
    +            if (defined $element->Package())
    +                {
    +                $packageText = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator());
    +                $packageText = $self->StringToHTML($packageText, ADD_HIDDEN_BREAKS);
    +                };
    +
    +            if (!$element->HasMultipleFiles())
    +                {
    +                my ($i, $s) =
    +                    $self->BuildIndexLink($symbolText, $element->Symbol(), $element->Package(), $element->File(),
    +                                                     $element->Type(), $element->Prototype(), $element->Summary(), 'ISymbol');
    +                $indexHTML .= $i;
    +                $searchResultsHTML .= $s;
    +
    +                if (defined $packageText)
    +                    {
    +                    $indexHTML .=
    +                    ', <span class=IParent>'
    +                        . $packageText
    +                    . '</span>';
    +
    +                    $searchResultsHTML .=
    +                    ', <span class=IParent>'
    +                        . $packageText
    +                    . '</span>';
    +                    };
    +                }
    +            else # hasMultipleFiles but not multiplePackages
    +                {
    +                $indexHTML .=
    +                '<span class=ISymbol>'
    +                    . $symbolText
    +                . '</span>';
    +
    +                $searchResultsHTML .=
    +                q{<a href="javascript:searchResults.Toggle('} . $searchResultID . q{')" class=ISymbol>}
    +                    . $symbolText
    +                . '</a>';
    +
    +                my $output;
    +
    +                if (defined $packageText)
    +                    {
    +                    $output .=
    +                    ', <span class=IParent>'
    +                        . $packageText
    +                    . '</span>';
    +                    };
    +
    +                $output .=
    +                '<div class=ISubIndex>';
    +
    +                $indexHTML .= $output;
    +                $searchResultsHTML .= $output;
    +
    +                my $fileElements = $element->File();
    +                foreach my $fileElement (@$fileElements)
    +                    {
    +                    my ($i, $s) = $self->BuildIndexElement($fileElement, $cssID, $element->Symbol(), $element->Package(), 1);
    +                    $indexHTML .= $i;
    +                    $searchResultsHTML .= $s;
    +                    };
    +
    +                $indexHTML .= '</div>';
    +                $searchResultsHTML .= '</div>';
    +                };
    +            }
    +
    +        else # hasMultiplePackages
    +            {
    +            $indexHTML .=
    +            '<span class=ISymbol>'
    +                . $symbolText
    +            . '</span>'
    +            . '<div class=ISubIndex>';
    +
    +            $searchResultsHTML .=
    +            q{<a href="javascript:searchResults.Toggle('} . $searchResultID . q{')" class=ISymbol>}
    +                . $symbolText
    +            . '</a>'
    +            . '<div class=ISubIndex>';
    +
    +            my $packageElements = $element->Package();
    +            foreach my $packageElement (@$packageElements)
    +                {
    +                my ($i, $s) = $self->BuildIndexElement($packageElement, $cssID, $element->Symbol());
    +                $indexHTML .= $i;
    +                $searchResultsHTML .= $s;
    +                };
    +
    +            $indexHTML .= '</div>';
    +            $searchResultsHTML .= '</div>';
    +            };
    +
    +        $indexHTML .= '</td></tr>';
    +        $searchResultsHTML .= '</div></div>';
    +
    +        return ($indexHTML, $searchResultsHTML);
    +        };
    +    };
    +
    +
    +#
    +#   Function: BuildIndexLink
    +#
    +#   Builds and returns the HTML associated with an index link.  The HTML will be the a href tag, the text, and the closing tag.
    +#
    +#   Parameters:
    +#
    +#       text - The text of the link *in HTML*.  Use <IndexSymbolToHTML()> if necessary.
    +#       symbol - The partial <SymbolString> to link to.
    +#       package - The package <SymbolString> of the symbol.
    +#       file - The <FileName> the symbol is defined in.
    +#       type - The <TopicType> of the symbol.
    +#       prototype - The prototype of the symbol, or undef if none.
    +#       summary - The summary of the symbol, or undef if none.
    +#       style - The CSS style to apply to the link.
    +#
    +#   Returns:
    +#
    +#       The array ( indexHTML, searchResultHTML ) which is the link in the respective forms.
    +#
    +sub BuildIndexLink #(string text, SymbolString symbol, SymbolString package, FileName file, TopicType type, string prototype, string summary, string style) => ( string, string )
    +    {
    +    my ($self, $text, $symbol, $package, $file, $type, $prototype, $summary, $style) = @_;
    +
    +    $symbol = NaturalDocs::SymbolString->Join($package, $symbol);
    +
    +    my $targetTooltipID = $self->BuildToolTip($symbol, $file, $type, $prototype, $summary);
    +    my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID);
    +
    +    my $indexHTML = '<a href="' . $self->MakeRelativeURL( $self->IndexDirectory(), $self->OutputFileOf($file) )
    +                                         . '#' . $self->SymbolToHTMLSymbol($symbol) . '" ' . $toolTipProperties . ' '
    +                                . 'class=' . $style . '>' . $text . '</a>';
    +    my $searchResultHTML = '<a href="' . $self->MakeRelativeURL( $self->SearchResultsDirectory(), $self->OutputFileOf($file) )
    +                                         . '#' . $self->SymbolToHTMLSymbol($symbol) . '" '
    +                                         . ($self->CommandLineOption eq 'HTML' ? 'target=_parent ' : '')
    +                                . 'class=' . $style . '>' . $text . '</a>';
    +
    +    return ($indexHTML, $searchResultHTML);
    +    };
    +
    +
    +#
    +#   Function: BuildIndexNavigationBar
    +#
    +#   Builds a navigation bar for a page of the index.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the index, or undef for general.
    +#       page - The page of the index the navigation bar is for.
    +#       locations - An arrayref of the locations of each section.  Index 0 is for the symbols, index 1 for the numbers, and the rest
    +#                       for each letter.  The values are the page numbers where the sections are located.
    +#
    +sub BuildIndexNavigationBar #(type, page, locations)
    +    {
    +    my ($self, $type, $page, $locations) = @_;
    +
    +    my $output = '<div class=INavigationBar>';
    +
    +    for (my $i = 0; $i < scalar @indexHeadings; $i++)
    +        {
    +        if ($i != 0)
    +            {  $output .= ' &middot; ';  };
    +
    +        if (defined $locations->[$i])
    +            {
    +            $output .= '<a href="';
    +
    +            if ($locations->[$i] != $page)
    +                {  $output .= $self->RelativeIndexFileOf($type, $locations->[$i]);  };
    +
    +            $output .= '#' . $indexAnchors[$i] . '">' . $indexHeadings[$i] . '</a>';
    +            }
    +        else
    +            {
    +            $output .= $indexHeadings[$i];
    +            };
    +        };
    +
    +    $output .= '</div>';
    +
    +    return $output;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: PurgeIndexFiles
    +#
    +#   Removes all or some of the output files for an index.
    +#
    +#   Parameters:
    +#
    +#       type  - The index <TopicType>.
    +#       indexSections  - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement>
    +#                               objects.  The first section is for symbols, the second for numbers, and the rest for A through Z.  May be
    +#                               undef.
    +#       startingPage - If defined, only pages starting with this number will be removed.  Otherwise all pages will be removed.
    +#
    +sub PurgeIndexFiles #(TopicType type, optional NaturalDocs::SymbolTable::IndexElement[] indexSections, optional int startingPage)
    +    {
    +    my ($self, $type, $indexSections, $page) = @_;
    +
    +    # First the regular index pages.
    +
    +    if (!defined $page)
    +        {  $page = 1;  };
    +
    +    for (;;)
    +        {
    +        my $file = $self->IndexFileOf($type, $page);
    +
    +        if (-e $file)
    +            {
    +            unlink($file);
    +            $page++;
    +            }
    +        else
    +            {
    +            last;
    +            };
    +        };
    +
    +
    +    # Next the search results.
    +
    +    for (my $i = 0; $i < 28; $i++)
    +        {
    +        if (!$indexSections || !$indexSections->[$i])
    +            {
    +            my $file = $self->SearchResultsFileOf($type, $searchExtensions[$i]);
    +
    +            if (-e $file)
    +                {  unlink($file);  };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: OutputFileOf
    +#
    +#   Returns the output file name of the source file.  Will be undef if it is not a file from a valid input directory.
    +#
    +sub OutputFileOf #(sourceFile)
    +    {
    +    my ($self, $sourceFile) = @_;
    +
    +    my ($inputDirectory, $relativeSourceFile) = NaturalDocs::Settings->SplitFromInputDirectory($sourceFile);
    +    if (!defined $inputDirectory)
    +        {  return undef;  };
    +
    +    my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);
    +    my $inputDirectoryName = NaturalDocs::Settings->InputDirectoryNameOf($inputDirectory);
    +
    +    $outputDirectory = NaturalDocs::File->JoinPaths( $outputDirectory,
    +                                                                            'files' . ($inputDirectoryName != 1 ? $inputDirectoryName : ''), 1 );
    +
    +    # We need to change any extensions to dashes because Apache will think file.pl.html is a script.
    +    # We also need to add a dash if the file doesn't have an extension so there'd be no conflicts with index.html,
    +    # FunctionIndex.html, etc.
    +
    +    if (!($relativeSourceFile =~ tr/./-/))
    +        {  $relativeSourceFile .= '-';  };
    +
    +    $relativeSourceFile =~ tr/ &?(){};#/_/;
    +    $relativeSourceFile .= '.html';
    +
    +    return NaturalDocs::File->JoinPaths($outputDirectory, $relativeSourceFile);
    +    };
    +
    +
    +#
    +#   Function: OutputImageOf
    +#
    +#   Returns the output image file name of the source image file.  Will be undef if it is not a file from a valid input directory.
    +#
    +sub OutputImageOf #(sourceImageFile)
    +    {
    +    my ($self, $sourceImageFile) = @_;
    +
    +    my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self);
    +    my $topLevelDirectory;
    +
    +    my ($inputDirectory, $relativeImageFile) = NaturalDocs::Settings->SplitFromInputDirectory($sourceImageFile);
    +
    +    if (defined $inputDirectory)
    +        {
    +        my $inputDirectoryName = NaturalDocs::Settings->InputDirectoryNameOf($inputDirectory);
    +        $topLevelDirectory = 'files' . ($inputDirectoryName != 1 ? $inputDirectoryName : '');
    +        }
    +    else
    +        {
    +        ($inputDirectory, $relativeImageFile) = NaturalDocs::Settings->SplitFromImageDirectory($sourceImageFile);
    +
    +        if (!defined $inputDirectory)
    +            {  return undef;  };
    +
    +        my $inputDirectoryName = NaturalDocs::Settings->ImageDirectoryNameOf($inputDirectory);
    +        $topLevelDirectory = 'images' . ($inputDirectoryName != 1 ? $inputDirectoryName : '');
    +        }
    +
    +
    +    $outputDirectory = NaturalDocs::File->JoinPaths($outputDirectory, $topLevelDirectory, 1);
    +
    +    $relativeImageFile =~ tr/ /_/;
    +
    +    return NaturalDocs::File->JoinPaths($outputDirectory, $relativeImageFile);
    +    };
    +
    +
    +#
    +#   Function: IndexDirectory
    +#
    +#   Returns the directory of the index files.
    +#
    +sub IndexDirectory
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index', 1);
    +    };
    +
    +
    +#
    +#   Function: IndexFileOf
    +#
    +#   Returns the output file name of the index file.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> of the index.
    +#       page  - The page number.  Undef is the same as one.
    +#
    +sub IndexFileOf #(type, page)
    +    {
    +    my ($self, $type, $page) = @_;
    +    return NaturalDocs::File->JoinPaths( $self->IndexDirectory(), $self->RelativeIndexFileOf($type, $page) );
    +    };
    +
    +
    +#
    +#   Function: RelativeIndexFileOf
    +#
    +#   Returns the output file name of the index file, relative to other index files.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> of the index.
    +#       page  - The page number.  Undef is the same as one.
    +#
    +sub RelativeIndexFileOf #(type, page)
    +    {
    +    my ($self, $type, $page) = @_;
    +    return NaturalDocs::Topics->NameOfType($type, 1, 1) . (defined $page && $page != 1 ? $page : '') . '.html';
    +    };
    +
    +
    +#
    +#   Function: SearchResultsDirectory
    +#
    +#   Returns the directory of the search results files.
    +#
    +sub SearchResultsDirectory
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'search', 1);
    +    };
    +
    +
    +#
    +#   Function: SearchResultsFileOf
    +#
    +#   Returns the output file name of the search result file.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> of the index.
    +#       extra - The string to add to the end of the file name, such as "A" or "Symbols".
    +#
    +sub SearchResultsFileOf #(TopicType type, string extra)
    +    {
    +    my ($self, $type, $extra) = @_;
    +
    +    my $fileName = NaturalDocs::Topics->NameOfType($type, 1, 1) . $extra . '.html';
    +
    +    return NaturalDocs::File->JoinPaths( $self->SearchResultsDirectory(), $fileName );
    +    };
    +
    +
    +#
    +#   Function: CSSDirectory
    +#
    +#   Returns the directory of the CSS files.
    +#
    +sub CSSDirectory
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'styles', 1);
    +    };
    +
    +
    +#
    +#   Function: MainCSSFile
    +#
    +#   Returns the location of the main CSS file.
    +#
    +sub MainCSSFile
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( $self->CSSDirectory(), 'main.css' );
    +    };
    +
    +
    +#
    +#   Function: JavaScriptDirectory
    +#
    +#   Returns the directory of the JavaScript files.
    +#
    +sub JavaScriptDirectory
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'javascript', 1);
    +    };
    +
    +
    +#
    +#   Function: MainJavaScriptFile
    +#
    +#   Returns the location of the main JavaScript file.
    +#
    +sub MainJavaScriptFile
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'main.js' );
    +    };
    +
    +
    +#
    +#   Function: PrettifyJavaScriptFile
    +#
    +#   Returns the location of the Google Prettify JavaScript file.
    +#
    +sub PrettifyJavaScriptFile
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'prettify.js' );
    +    };
    +
    +
    +#
    +#   Function: SearchDataJavaScriptFile
    +#
    +#   Returns the location of the search data JavaScript file.
    +#
    +sub SearchDataJavaScriptFile
    +    {
    +    my $self = shift;
    +    return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'searchdata.js' );
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: IndexTitleOf
    +#
    +#   Returns the page title of the index file.
    +#
    +#   Parameters:
    +#
    +#       type  - The type of index.
    +#
    +sub IndexTitleOf #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    return ($type eq ::TOPIC_GENERAL() ? '' : NaturalDocs::Topics->NameOfType($type) . ' ') . 'Index';
    +    };
    +
    +#
    +#   Function: MakeRelativeURL
    +#
    +#   Returns a relative path between two files in the output tree and returns it in URL format.
    +#
    +#   Parameters:
    +#
    +#       baseFile    - The base <FileName> in local format, *not* in URL format.
    +#       targetFile  - The target <FileName> of the link in local format, *not* in URL format.
    +#       baseHasFileName - Whether baseFile has a file name attached or is just a path.
    +#
    +#   Returns:
    +#
    +#       The relative URL to the target.
    +#
    +sub MakeRelativeURL #(FileName baseFile, FileName targetFile, bool baseHasFileName) -> string relativeURL
    +    {
    +    my ($self, $baseFile, $targetFile, $baseHasFileName) = @_;
    +
    +    if ($baseHasFileName)
    +        {  $baseFile = NaturalDocs::File->NoFileName($baseFile)  };
    +
    +    my $relativePath = NaturalDocs::File->MakeRelativePath($baseFile, $targetFile);
    +
    +    return $self->ConvertAmpChars( NaturalDocs::File->ConvertToURL($relativePath) );
    +    };
    +
    +#
    +#   Function: StringToHTML
    +#
    +#   Converts a text string to HTML.  Does not apply paragraph tags or accept formatting tags.
    +#
    +#   Parameters:
    +#
    +#       string - The string to convert.
    +#       addHiddenBreaks - Whether to add hidden breaks to the string.  You can use <ADD_HIDDEN_BREAKS> for this parameter
    +#                                   if you want to make the calling code clearer.
    +#
    +#   Returns:
    +#
    +#       The string in HTML.
    +#
    +sub StringToHTML #(string, addHiddenBreaks)
    +    {
    +    my ($self, $string, $addHiddenBreaks) = @_;
    +
    +    $string =~ s/&/&amp;/g;
    +    $string =~ s/</&lt;/g;
    +    $string =~ s/>/&gt;/g;
    +
    +    # Me likey the fancy quotes.  They work in IE 4+, Mozilla, and Opera 5+.  We've already abandoned NS4 with the CSS
    +    # styles, so might as well.
    +    $string =~ s/^\'/&lsquo;/gm;
    +    $string =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
    +    $string =~ s/\'/&rsquo;/g;
    +
    +    $string =~ s/^\"/&ldquo;/gm;
    +    $string =~ s/([\ \(\[\{])\"/$1&ldquo;/g;
    +    $string =~ s/\"/&rdquo;/g;
    +
    +    # Me likey the double spaces too.  As you can probably tell, I like print-formatting better than web-formatting.  The indented
    +    # paragraphs without blank lines in between them do become readable when you have fancy quotes and double spaces too.
    +    $string = $self->AddDoubleSpaces($string);
    +
    +    if ($addHiddenBreaks)
    +        {  $string = $self->AddHiddenBreaks($string);  };
    +
    +    return $string;
    +    };
    +
    +
    +#
    +#   Function: SymbolToHTMLSymbol
    +#
    +#   Converts a <SymbolString> to a HTML symbol, meaning one that is safe to include in anchor and link tags.  You don't need
    +#   to pass the result to <ConvertAmpChars()>.
    +#
    +sub SymbolToHTMLSymbol #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +
    +    my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
    +    my $htmlSymbol = join('.', @identifiers);
    +
    +    # If only Mozilla was nice about putting special characters in URLs like IE and Opera are, I could leave spaces in and replace
    +    # "<>& with their amp chars.  But alas, Mozilla shows them as %20, etc. instead.  It would have made for nice looking URLs.
    +    $htmlSymbol =~ tr/ \"<>\?&%/_/d;
    +
    +    return $htmlSymbol;
    +    };
    +
    +
    +#
    +#   Function: StringToSearchResultID
    +#
    +#   Takes a text string and translates it into something that can be used as a CSS ID.
    +#
    +#   Parameters:
    +#
    +#       string - The string to convert
    +#       dontIncrement - If set, it reuses the last generated ID.  Otherwise it generates a new one if it matches a previously
    +#                               generated one in a case-insensitive way.
    +#
    +sub StringToSearchResultID #(string string, bool dontIncrement = 0) => string
    +    {
    +    my ($self, $string, $dontIncrement) = @_;
    +
    +    $string =~ s/\_/_und/g;
    +    $string =~ s/ +/_spc/g;
    +
    +    my %translation = ( '~' => '_til', '!' => '_exc', '@' => '_att', '#' => '_num', '$' => '_dol', '%' => '_pct', '^' => '_car',
    +                                  '&' => '_amp', '*' => '_ast', '(' => '_lpa', ')' => '_rpa', '-' => '_min', '+' => '_plu', '=' => '_equ',
    +                                  '{' => '_lbc', '}' => '_rbc', '[' => '_lbk', ']' => '_rbk', ':' => '_col', ';' => '_sco', '"' => '_quo',
    +                                  '\'' => '_apo', '<' => '_lan', '>' => '_ran', ',' => '_com', '.' => '_per', '?' => '_que', '/' => '_sla' );
    +
    +    $string =~ s/([\~\!\@\#\$\%\^\&\*\(\)\-\+\=\{\}\[\]\:\;\"\'\<\>\,\.\?\/])/$translation{$1}/ge;
    +    $string =~ s/[^a-z0-9_]/_zzz/gi;
    +
    +    my $number = $searchResultIDs{lc($string)};
    +
    +    if (!$number)
    +        {  $number = 1;  }
    +    elsif (!$dontIncrement)
    +        {  $number++;  };
    +
    +    $searchResultIDs{lc($string)} = $number;
    +
    +    return 'SR' . ($number == 1 ? '' : $number) . '_' . $string;
    +    };
    +
    +
    +#
    +#   Function: NDMarkupToHTML
    +#
    +#   Converts a block of <NDMarkup> to HTML.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> the <NDMarkup> appears in.
    +#       text    - The <NDMarkup> text to convert.
    +#       symbol - The topic <SymbolString> the <NDMarkup> appears in.
    +#       package  - The package <SymbolString> the <NDMarkup> appears in.
    +#       type - The <TopicType> the <NDMarkup> appears in.
    +#       using - An arrayref of scope <SymbolStrings> the <NDMarkup> also has access to, or undef if none.
    +#       style - Set to one of the <NDMarkupToHTML Styles> or leave undef for general.
    +#
    +#   Returns:
    +#
    +#       The text in HTML.
    +#
    +sub NDMarkupToHTML #(sourceFile, text, symbol, package, type, using, style)
    +    {
    +    my ($self, $sourceFile, $text, $symbol, $package, $type, $using, $style) = @_;
    +
    +    my $dlSymbolBehavior;
    +
    +    if ($type eq ::TOPIC_ENUMERATION())
    +        {  $dlSymbolBehavior = NaturalDocs::Languages->LanguageOf($sourceFile)->EnumValues();  }
    +   elsif (NaturalDocs::Topics->TypeInfo($type)->Scope() == ::SCOPE_ALWAYS_GLOBAL())
    +        {  $dlSymbolBehavior = ::ENUM_GLOBAL();  }
    +    else
    +        {  $dlSymbolBehavior = ::ENUM_UNDER_PARENT();  };
    +
    +    my $output;
    +    my $inCode;
    +
    +    my @splitText = split(/(<\/?code(?: type="[^"]+")?>)/, $text);
    +
    +    while (scalar @splitText)
    +        {
    +        $text = shift @splitText;
    +
    +        if ($text =~ /<code type="([^"]+)">/)
    +            {
    +            my $codeType = $1;
    +
    +            my $highlight = ( ($codeType eq "code" && NaturalDocs::Settings->HighlightCode()) ||
    +            						  ($codeType eq "anonymous" && NaturalDocs::Settings->HighlightAnonymous()) );
    +
    +            $output .= '<blockquote><pre' . ($highlight ? ' class="prettyprint"' : '') . '>';
    +            $inCode = 1;
    +            }
    +        elsif ($text eq '</code>')
    +            {
    +            $output .= '</pre></blockquote>';
    +            $inCode = undef;
    +            }
    +        elsif ($inCode)
    +            {
    +            # Leave line breaks in.
    +            $output .= $text;
    +            }
    +        else
    +            {
    +            # Format non-code text.
    +
    +            # Convert linked images.
    +            if ($text =~ /<img mode=\"link\"/)
    +                {
    +                if ($style == NDMARKUPTOHTML_GENERAL)
    +                    {
    +                    # Split by tags we would want to see the linked images appear after.  For example, an image link appearing in
    +                    # the middle of a paragraph would appear after the end of that paragraph.
    +                    my @imageBlocks = split(/(<p>.*?<\/p>|<dl>.*?<\/dl>|<ul>.*?<\/ul>)/, $text);
    +                    $text = undef;
    +
    +                    foreach my $imageBlock (@imageBlocks)
    +                        {
    +                        $imageBlock =~ s{<img mode=\"link\" target=\"([^\"]*)\" original=\"([^\"]*)\">}
    +                                                {$self->BuildImage($sourceFile, 'link', $1, $2)}ge;
    +
    +                        $text .= $imageBlock . $imageContent;
    +                        $imageContent = undef;
    +                        };
    +                    }
    +
    +                # Use only the text for tooltips and summaries.
    +                else
    +                    {
    +                    $text =~ s{<img mode=\"link\" target=\"[^\"]*\" original=\"([^\"]*)\">}{$1}g;
    +                    };
    +                };
    +
    +            # Convert quotes to fancy quotes.  This has to be done before links because some of them may have JavaScript
    +            # attributes that use the apostrophe character.
    +            $text =~ s/^\'/&lsquo;/gm;
    +            $text =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
    +            $text =~ s/\'/&rsquo;/g;
    +
    +            $text =~ s/^&quot;/&ldquo;/gm;
    +            $text =~ s/([\ \(\[\{])&quot;/$1&ldquo;/g;
    +            $text =~ s/&quot;/&rdquo;/g;
    +
    +            # Resolve and convert links, except for tooltips.
    +            if ($style != NDMARKUPTOHTML_TOOLTIP)
    +                {
    +                $text =~ s{<link target=\"([^\"]*)\" name=\"([^\"]*)\" original=\"([^\"]*)\">}
    +                               {$self->BuildTextLink($1, $2, $3, $package, $using, $sourceFile)}ge;
    +                $text =~ s/<url target=\"([^\"]*)\" name=\"([^\"]*)\">/$self->BuildURLLink($1, $2)/ge;
    +                }
    +            else
    +                {
    +                $text =~ s{<link target=\"[^\"]*\" name=\"([^\"]*)\" original=\"[^\"]*\">}{$1}g;
    +                $text =~ s{<url target=\"[^\"]*\" name=\"([^\"]*)\">}{$1}g;
    +                };
    +
    +            # We do full e-mail links anyway just so the obfuscation remains.
    +            $text =~ s/<email target=\"([^\"]*)\" name=\"([^\"]*)\">/$self->BuildEMailLink($1, $2)/ge;
    +
    +
    +            # Convert inline images, but only for the general style.
    +            if ($style == NDMARKUPTOHTML_GENERAL)
    +                {
    +                $text =~ s{<img mode=\"inline\" target=\"([^\"]*)\" original=\"([^\"]*)\">}
    +                               {$self->BuildImage($sourceFile, 'inline', $1, $2)}ge;
    +                }
    +            else
    +                {
    +                $text =~ s{<img mode=\"inline\" target=\"[^\"]*\" original=\"([^\"]*)\">}{$1}g;
    +                };
    +
    +            # Copyright symbols.  Prevent conversion when part of (a), (b), (c) lists.
    +            if ($text !~ /\(a\)/i)
    +                {  $text =~ s/\(c\)/&copy;/gi;  };
    +
    +            # Trademark symbols.
    +            $text =~ s/\(tm\)/&trade;/gi;
    +            $text =~ s/\(r\)/&reg;/gi;
    +
    +            # Add double spaces too.
    +            $text = $self->AddDoubleSpaces($text);
    +
    +            # Headings
    +            $text =~ s/<h>/<h4 class=CHeading>/g;
    +            $text =~ s/<\/h>/<\/h4>/g;
    +
    +            # Description Lists
    +            $text =~ s/<dl>/<table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList>/g;
    +            $text =~ s/<\/dl>/<\/table>/g;
    +
    +            $text =~ s/<de>/<tr><td class=CDLEntry>/g;
    +            $text =~ s/<\/de>/<\/td>/g;
    +
    +            if ($dlSymbolBehavior == ::ENUM_GLOBAL())
    +                {  $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol(undef, $1)/ge;  }
    +            elsif ($dlSymbolBehavior == ::ENUM_UNDER_PARENT())
    +                {  $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($package, $1)/ge;  }
    +            else # ($dlSymbolBehavior == ::ENUM_UNDER_TYPE())
    +                {  $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($symbol, $1)/ge;  }
    +
    +            sub MakeDescriptionListSymbol #(package, text)
    +                {
    +                my ($self, $package, $text) = @_;
    +
    +                $text = NaturalDocs::NDMarkup->RestoreAmpChars($text);
    +                my $symbol = NaturalDocs::SymbolString->FromText($text);
    +
    +                if (defined $package)
    +                    {  $symbol = NaturalDocs::SymbolString->Join($package, $symbol);  };
    +
    +                return
    +                '<tr>'
    +                    . '<td class=CDLEntry>'
    +                        # The anchors are closed, but not around the text, to prevent the :hover CSS style from kicking in.
    +                        . '<a name="' . $self->SymbolToHTMLSymbol($symbol) . '"></a>'
    +                        . $text
    +                    . '</td>';
    +                };
    +
    +            $text =~ s/<dd>/<td class=CDLDescription>/g;
    +            $text =~ s/<\/dd>/<\/td><\/tr>/g;
    +
    +            $output .= $text;
    +            };
    +        };
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildTextLink
    +#
    +#   Creates a HTML link to a symbol, if it exists.
    +#
    +#   Parameters:
    +#
    +#       target  - The link text.
    +#       name - The link name.
    +#       original - The original text as it appears in the source.
    +#       package  - The package <SymbolString> the link appears in, or undef if none.
    +#       using - An arrayref of additional scope <SymbolStrings> the link has access to, or undef if none.
    +#       sourceFile  - The <FileName> the link appears in.
    +#
    +#       Target, name, and original are assumed to still have <NDMarkup> amp chars.
    +#
    +#   Returns:
    +#
    +#       The link in HTML, including tags.  If the link doesn't resolve to anything, returns the HTML that should be substituted for it.
    +#
    +sub BuildTextLink #(target, name, original, package, using, sourceFile)
    +    {
    +    my ($self, $target, $name, $original, $package, $using, $sourceFile) = @_;
    +
    +    my $plainTarget = $self->RestoreAmpChars($target);
    +
    +    my $symbol = NaturalDocs::SymbolString->FromText($plainTarget);
    +    my $symbolTarget = NaturalDocs::SymbolTable->References(::REFERENCE_TEXT(), $symbol, $package, $using, $sourceFile);
    +
    +    if (defined $symbolTarget)
    +        {
    +        my $symbolTargetFile;
    +
    +        if ($symbolTarget->File() ne $sourceFile)
    +            {
    +            $symbolTargetFile = $self->MakeRelativeURL( $self->OutputFileOf($sourceFile),
    +                                                                               $self->OutputFileOf($symbolTarget->File()), 1 );
    +            };
    +        # else leave it undef
    +
    +        my $symbolTargetTooltipID = $self->BuildToolTip($symbolTarget->Symbol(), $sourceFile, $symbolTarget->Type(),
    +                                                                                 $symbolTarget->Prototype(), $symbolTarget->Summary());
    +
    +        my $toolTipProperties = $self->BuildToolTipLinkProperties($symbolTargetTooltipID);
    +
    +        return '<a href="' . $symbolTargetFile . '#' . $self->SymbolToHTMLSymbol($symbolTarget->Symbol()) . '" '
    +                    . 'class=L' . NaturalDocs::Topics->NameOfType($symbolTarget->Type(), 0, 1) . ' ' . $toolTipProperties . '>'
    +                        . $name
    +                    . '</a>';
    +        }
    +    else
    +        {
    +        return $original;
    +        };
    +    };
    +
    +
    +#
    +#   Function: BuildURLLink
    +#
    +#   Creates a HTML link to an external URL.  Long URLs will have hidden breaks to allow them to wrap.
    +#
    +#   Parameters:
    +#
    +#       target - The URL to link to.
    +#       name - The label of the link.
    +#
    +#       Both are assumed to still have <NDMarkup> amp chars.
    +#
    +#   Returns:
    +#
    +#       The HTML link, complete with tags.
    +#
    +sub BuildURLLink #(target, name)
    +    {
    +    my ($self, $target, $name) = @_;
    +
    +    # Don't restore amp chars on the target.
    +
    +    if (length $name < 50 || $name ne $target)
    +        {  return '<a href="' . $target . '" class=LURL target=_top>' . $name . '</a>';  };
    +
    +    my @segments = split(/([\,\/]|&amp;)/, $target);
    +    my $output = '<a href="' . $target . '" class=LURL target=_top>';
    +
    +    # Get past the first batch of slashes, since we don't want to break on things like http://.
    +
    +    $output .= $segments[0];
    +
    +    my $i = 1;
    +    while ($i < scalar @segments && ($segments[$i] eq '/' || !$segments[$i]))
    +        {
    +        $output .= $segments[$i];
    +        $i++;
    +        };
    +
    +    # Now break on each one of those symbols.
    +
    +    while ($i < scalar @segments)
    +        {
    +        if ($segments[$i] eq ',' || $segments[$i] eq '/' || $segments[$i] eq '&amp;')
    +            {  $output .= '<wbr>';  };
    +
    +        $output .= $segments[$i];
    +        $i++;
    +        };
    +
    +    $output .= '</a>';
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildEMailLink
    +#
    +#   Creates a HTML link to an e-mail address.  The address will be transparently munged to protect it (hopefully) from spambots.
    +#
    +#   Parameters:
    +#
    +#       target  - The e-mail address.
    +#       name - The label of the link.
    +#
    +#       Both are assumed to still have <NDMarkup> amp chars.
    +#
    +#   Returns:
    +#
    +#       The HTML e-mail link, complete with tags.
    +#
    +sub BuildEMailLink #(target, name)
    +    {
    +    my ($self, $target, $name) = @_;
    +    my @splitAddress;
    +
    +
    +    # Hack the address up.  We want two user pieces and two host pieces.
    +
    +    my ($user, $host) = split(/\@/, $self->RestoreAmpChars($target));
    +
    +    my $userSplit = length($user) / 2;
    +
    +    push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($user, 0, $userSplit) );
    +    push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($user, $userSplit) );
    +
    +    push @splitAddress, '@';
    +
    +    my $hostSplit = length($host) / 2;
    +
    +    push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($host, 0, $hostSplit) );
    +    push @splitAddress, NaturalDocs::NDMarkup->ConvertAmpChars( substr($host, $hostSplit) );
    +
    +
    +    # Now put it back together again.  We'll use spans to split the text transparently and JavaScript to split and join the link.
    +
    +    my $output =
    +    "<a href=\"#\" onClick=\"location.href='mai' + 'lto:' + '" . join("' + '", @splitAddress) . "'; return false;\" class=LEMail>";
    +
    +    if ($name eq $target)
    +        {
    +        $output .=
    +        $splitAddress[0] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[1]
    +        . '<span>@</span>'
    +        . $splitAddress[3] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[4];
    +        }
    +    else
    +        {  $output .= $name;  };
    +
    +    $output .= '</a>';
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: BuildImage
    +#
    +#   Builds the HTML for an image.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> this image appears in.
    +#       mode - Either "inline" or "link".
    +#       target - The target.
    +#       original - The original text.
    +#
    +#       All are assumed to still have <NDMarkup> amp chars.
    +#
    +#   Returns:
    +#
    +#       The result in HTML.  If the mode was "link", the target image's HTML is added to <imageContent>.
    +#
    +sub BuildImage #(sourceFile, mode, target, original)
    +    {
    +    my ($self, $sourceFile, $mode, $target, $original) = @_;
    +
    +    my $targetNoAmp = $self->RestoreAmpChars($target);
    +
    +    my $image = NaturalDocs::ImageReferenceTable->GetReferenceTarget($sourceFile, $targetNoAmp);
    +
    +    if ($image)
    +        {
    +        my ($width, $height) = NaturalDocs::Project->ImageFileDimensions($image);
    +
    +        if ($mode eq 'inline')
    +            {
    +            return
    +            '<img src="' . $self->MakeRelativeURL($self->OutputFileOf($sourceFile),
    +                                                                       $self->OutputImageOf($image), 1) . '"'
    +
    +            . ($width && $height ? ' width="' . $width . '" height="' . $height . '"' : '')
    +            . '>';
    +            }
    +        else # link
    +            {
    +            # Make the text a little more friendly in the output by removing any folders and file extensions.
    +            # (see images/Table1.gif) will be turned into (see Table1).
    +            my $originalNoAmp = $self->RestoreAmpChars($original);
    +            my $targetIndex = index($originalNoAmp, $targetNoAmp);
    +            my ($shortTarget, $shortTargetNoAmp, $shortOriginal);
    +
    +            if ($targetIndex != -1)
    +                {
    +                $shortTargetNoAmp = (NaturalDocs::File->SplitPath($targetNoAmp))[2];
    +                $shortTargetNoAmp = NaturalDocs::File->NoExtension($shortTargetNoAmp);
    +
    +                substr($originalNoAmp, $targetIndex, length($targetNoAmp), $shortTargetNoAmp);
    +
    +                $shortOriginal = NaturalDocs::NDMarkup->ConvertAmpChars($originalNoAmp);
    +                $shortTarget = NaturalDocs::NDMarkup->ConvertAmpChars($shortTargetNoAmp);
    +                };
    +
    +            my $output =
    +            '<a href="#Image' . $imageAnchorNumber . '" class=CImageLink>'
    +                . ($shortOriginal || $original)
    +            . '</a>';
    +
    +            $imageContent .=
    +            '<blockquote>'
    +            . '<div class=CImage>'
    +                . '<a name="Image' . $imageAnchorNumber . '"></a>'
    +                . '<div class=CImageCaption>' . ($shortTarget || $target) . '</div>'
    +                . '<img src="' . $self->MakeRelativeURL($self->OutputFileOf($sourceFile),
    +                                                                           $self->OutputImageOf($image), 1) . '"'
    +
    +                . ($width && $height ? ' width="' . $width . '" height="' . $height . '"' : '')
    +                . '>'
    +
    +            . '</div></blockquote>';
    +
    +            $imageAnchorNumber++;
    +            return $output;
    +            };
    +        }
    +    else # !$image
    +        {
    +        if ($mode eq 'inline')
    +            {  return '<p>' . $original . '</p>';  }
    +        else #($mode eq 'link')
    +            {  return $original;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: BuildToolTipLinkProperties
    +#
    +#   Returns the properties that should go in the link tag to add a tooltip to it.  Because the function accepts undef, you can
    +#   call it without checking if <BuildToolTip()> returned undef or not.
    +#
    +#   Parameters:
    +#
    +#       toolTipID - The ID of the tooltip.  If undef, the function will return undef.
    +#
    +#   Returns:
    +#
    +#       The properties that should be put in the link tag, or undef if toolTipID wasn't specified.
    +#
    +sub BuildToolTipLinkProperties #(toolTipID)
    +    {
    +    my ($self, $toolTipID) = @_;
    +
    +    if (defined $toolTipID)
    +        {
    +        my $currentNumber = $tooltipLinkNumber;
    +        $tooltipLinkNumber++;
    +
    +        return 'id=link' . $currentNumber . ' '
    +                . 'onMouseOver="ShowTip(event, \'' . $toolTipID . '\', \'link' . $currentNumber . '\')" '
    +                . 'onMouseOut="HideTip(\'' . $toolTipID . '\')"';
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: AddDoubleSpaces
    +#
    +#   Adds second spaces after the appropriate punctuation with &nbsp; so they show up in HTML.  They don't occur if there isn't at
    +#   least one space after the punctuation, so things like class.member notation won't be affected.
    +#
    +#   Parameters:
    +#
    +#       text - The text to convert.
    +#
    +#   Returns:
    +#
    +#       The text with double spaces as necessary.
    +#
    +sub AddDoubleSpaces #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    # Question marks and exclamation points get double spaces unless followed by a lowercase letter.
    +
    +    $text =~ s/  ([^\ \t\r\n] [\!\?])  # Must appear after a non-whitespace character to apply.
    +
    +                      (&quot;|&[lr][sd]quo;|[\'\"\]\}\)]?)  # Tolerate closing quotes, parenthesis, etc.
    +                      ((?:<[^>]+>)*)  # Tolerate tags
    +
    +                      \   # The space
    +                      (?![a-z])  # Not followed by a lowercase character.
    +
    +                   /$1$2$3&nbsp;\ /gx;
    +
    +
    +    # Periods get double spaces if it's not followed by a lowercase letter.  However, if it's followed by a capital letter and the
    +    # preceding word is in the list of acceptable abbreviations, it won't get the double space.  Yes, I do realize I am seriously
    +    # over-engineering this.
    +
    +    $text =~ s/  ([^\ \t\r\n]+)  # The word prior to the period.
    +
    +                      \.
    +
    +                      (&quot;|&[lr][sd]quo;|[\'\"\]\}\)]?)  # Tolerate closing quotes, parenthesis, etc.
    +                      ((?:<[^>]+>)*)  # Tolerate tags
    +
    +                      \   # The space
    +                      ([^a-z])   # The next character, if it's not a lowercase letter.
    +
    +                  /$1 . '.' . $2 . $3 . MaybeExpand($1, $4) . $4/gex;
    +
    +    sub MaybeExpand #(leadWord, nextLetter)
    +        {
    +        my ($leadWord, $nextLetter) = @_;
    +
    +        if ($nextLetter =~ /^[A-Z]$/ && exists $abbreviations{ lc($leadWord) } )
    +            { return ' '; }
    +        else
    +            { return '&nbsp; '; };
    +        };
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: ConvertAmpChars
    +#
    +#   Converts certain characters to their HTML amp char equivalents.
    +#
    +#   Parameters:
    +#
    +#       text - The text to convert.
    +#
    +#   Returns:
    +#
    +#       The converted text.
    +#
    +sub ConvertAmpChars #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ s/&/&amp;/g;
    +    $text =~ s/\"/&quot;/g;
    +    $text =~ s/</&lt;/g;
    +    $text =~ s/>/&gt;/g;
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: RestoreAmpChars
    +#
    +#   Restores all amp characters to their original state.  This works with both <NDMarkup> amp chars and fancy quotes.
    +#
    +#   Parameters:
    +#
    +#       text - The text to convert.
    +#
    +#   Returns:
    +#
    +#       The converted text.
    +#
    +sub RestoreAmpChars #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text = NaturalDocs::NDMarkup->RestoreAmpChars($text);
    +    $text =~ s/&[lr]squo;/\'/g;
    +    $text =~ s/&[lr]dquo;/\"/g;
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: AddHiddenBreaks
    +#
    +#   Adds hidden breaks to symbols.  Puts them after symbol and directory separators so long names won't screw up the layout.
    +#
    +#   Parameters:
    +#
    +#       string - The string to break.
    +#
    +#   Returns:
    +#
    +#       The string with hidden breaks.
    +#
    +sub AddHiddenBreaks #(string)
    +    {
    +    my ($self, $string) = @_;
    +
    +    # \.(?=.{5,}) instead of \. so file extensions don't get breaks.
    +    # :+ instead of :: because Mac paths are separated by a : and we want to get those too.
    +
    +    $string =~ s/(\w(?:\.(?=.{5,})|:+|->|\\|\/))(\w)/$1 . '<wbr>' . $2/ge;
    +
    +    return $string;
    +    };
    +
    +
    +#
    +#   Function: FindFirstFile
    +#
    +#   A function that finds and returns the first file entry in the menu, or undef if none.
    +#
    +sub FindFirstFile
    +    {
    +    # Hidden parameter: arrayref
    +    # Used for recursion only.
    +
    +    my ($self, $arrayref) = @_;
    +
    +    if (!defined $arrayref)
    +        {  $arrayref = NaturalDocs::Menu->Content();  };
    +
    +    foreach my $entry (@$arrayref)
    +        {
    +        if ($entry->Type() == ::MENU_FILE())
    +            {
    +            return $entry;
    +            }
    +        elsif ($entry->Type() == ::MENU_GROUP())
    +            {
    +            my $result = $self->FindFirstFile($entry->GroupContent());
    +            if (defined $result)
    +                {  return $result;  };
    +            };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: ExpandMenu
    +#
    +#   Determines which groups should be expanded.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to use if you're looking for a source file.
    +#       indexType - The index <TopicType> to use if you're looking for an index.
    +#       selectionHierarchy - The <FileName> the menu is being built for.  Does not have to be on the menu itself.
    +#       rootLength - The length of the menu's root group, *not* including the contents of subgroups.
    +#
    +#   Returns:
    +#
    +#       An arrayref of all the group numbers that should be expanded.  At minimum, it will contain the numbers of the groups
    +#       present in <menuSelectionHierarchy>, though it may contain more.
    +#
    +sub ExpandMenu #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] selectionHierarchy, int rootLength) -> int[] groupsToExpand
    +    {
    +    my ($self, $sourceFile, $indexType, $menuSelectionHierarchy, $rootLength) = @_;
    +
    +    my $toExpand = [ ];
    +
    +
    +    # First expand everything in the selection hierarchy.
    +
    +    my $length = $rootLength;
    +
    +    foreach my $entry (@$menuSelectionHierarchy)
    +        {
    +        $length += $menuGroupLengths{$entry};
    +        push @$toExpand, $menuGroupNumbers{$entry};
    +        };
    +
    +
    +    # Now do multiple passes of group expansion as necessary.  We start from bottomIndex and expand outwards.  We stop going
    +    # in a direction if a group there is too long -- we do not skip over it and check later groups as well.  However, if one direction
    +    # stops, the other can keep going.
    +
    +    my $pass = 1;
    +    my $hasSubGroups;
    +
    +    while ($length < MENU_LENGTH_LIMIT)
    +        {
    +        my $content;
    +        my $topIndex;
    +        my $bottomIndex;
    +
    +
    +        if ($pass == 1)
    +            {
    +            # First pass, we expand the selection's siblings.
    +
    +            if (scalar @$menuSelectionHierarchy)
    +                {  $content = $menuSelectionHierarchy->[0]->GroupContent();  }
    +            else
    +                {  $content = NaturalDocs::Menu->Content();  };
    +
    +            $bottomIndex = 0;
    +
    +            while ($bottomIndex < scalar @$content &&
    +                     !($content->[$bottomIndex]->Type() == ::MENU_FILE() &&
    +                       $content->[$bottomIndex]->Target() eq $sourceFile) &&
    +                     !($content->[$bottomIndex]->Type() != ::MENU_INDEX() &&
    +                       $content->[$bottomIndex]->Target() eq $indexType) )
    +                {  $bottomIndex++;  };
    +
    +            if ($bottomIndex == scalar @$content)
    +                {  $bottomIndex = 0;  };
    +            $topIndex = $bottomIndex - 1;
    +            }
    +
    +        elsif ($pass == 2)
    +            {
    +            # If the section we just expanded had no sub-groups, do another pass trying to expand the parent's sub-groups.  The
    +            # net effect is that groups won't collapse as much unnecessarily.  Someone can click on a file in a sub-group and the
    +            # groups in the parent will stay open.
    +
    +            if (!$hasSubGroups && scalar @$menuSelectionHierarchy)
    +                {
    +                if (scalar @$menuSelectionHierarchy > 1)
    +                    {  $content = $menuSelectionHierarchy->[1]->GroupContent();  }
    +                else
    +                    {  $content = NaturalDocs::Menu->Content();  };
    +
    +                $bottomIndex = 0;
    +
    +                while ($bottomIndex < scalar @$content &&
    +                         $content->[$bottomIndex] != $menuSelectionHierarchy->[0])
    +                    {  $bottomIndex++;  };
    +
    +                $topIndex = $bottomIndex - 1;
    +                $bottomIndex++;  # Increment past our own group.
    +                $hasSubGroups = undef;
    +                }
    +            else
    +                {  last;  };
    +            }
    +
    +        # No more passes.
    +        else
    +            {  last;  };
    +
    +
    +        while ( ($topIndex >= 0 || $bottomIndex < scalar @$content) && $length < MENU_LENGTH_LIMIT)
    +            {
    +            # We do the bottom first.
    +
    +            while ($bottomIndex < scalar @$content && $content->[$bottomIndex]->Type() != ::MENU_GROUP())
    +                {  $bottomIndex++;  };
    +
    +            if ($bottomIndex < scalar @$content)
    +                {
    +                my $bottomEntry = $content->[$bottomIndex];
    +                $hasSubGroups = 1;
    +
    +                if ($length + $menuGroupLengths{$bottomEntry} <= MENU_LENGTH_LIMIT)
    +                    {
    +                    $length += $menuGroupLengths{$bottomEntry};
    +                    push @$toExpand, $menuGroupNumbers{$bottomEntry};
    +                    $bottomIndex++;
    +                    }
    +                else
    +                    {  $bottomIndex = scalar @$content;  };
    +                };
    +
    +            # Top next.
    +
    +            while ($topIndex >= 0 && $content->[$topIndex]->Type() != ::MENU_GROUP())
    +                {  $topIndex--;  };
    +
    +            if ($topIndex >= 0)
    +                {
    +                my $topEntry = $content->[$topIndex];
    +                $hasSubGroups = 1;
    +
    +                if ($length + $menuGroupLengths{$topEntry} <= MENU_LENGTH_LIMIT)
    +                    {
    +                    $length += $menuGroupLengths{$topEntry};
    +                    push @$toExpand, $menuGroupNumbers{$topEntry};
    +                    $topIndex--;
    +                    }
    +                else
    +                    {  $topIndex = -1;  };
    +                };
    +            };
    +
    +
    +        $pass++;
    +        };
    +
    +    return $toExpand;
    +    };
    +
    +
    +#
    +#   Function: GetMenuSelectionHierarchy
    +#
    +#   Finds the sequence of menu groups that contain the current selection.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to use if you're looking for a source file.
    +#       indexType - The index <TopicType> to use if you're looking for an index.
    +#
    +#   Returns:
    +#
    +#       An arrayref of the <NaturalDocs::Menu::Entry> objects of each group surrounding the selected menu item.  First entry is the
    +#       group immediately encompassing it, and each subsequent entry works its way towards the outermost group.
    +#
    +sub GetMenuSelectionHierarchy #(FileName sourceFile, TopicType indexType) -> NaturalDocs::Menu::Entry[] selectionHierarchy
    +    {
    +    my ($self, $sourceFile, $indexType) = @_;
    +
    +    my $hierarchy = [ ];
    +
    +    $self->FindMenuSelection($sourceFile, $indexType, $hierarchy, NaturalDocs::Menu->Content());
    +
    +    return $hierarchy;
    +    };
    +
    +
    +#
    +#   Function: FindMenuSelection
    +#
    +#   A recursive function that deterimes if it or any of its sub-groups has the menu selection.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to use if you're looking for a source file.
    +#       indexType - The index <TopicType> to use if you're looking for an index.
    +#       hierarchyRef - A reference to the menu selection hierarchy.
    +#       entries - An arrayref of <NaturalDocs::Menu::Entries> to search.
    +#
    +#   Returns:
    +#
    +#       Whether this group or any of its subgroups had the selection.  If true, it will add any subgroups to the menu selection
    +#       hierarchy but not itself.  This prevents the topmost entry from being added.
    +#
    +sub FindMenuSelection #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] hierarchyRef, NaturalDocs::Menu::Entry[] entries) -> bool hasSelection
    +    {
    +    my ($self, $sourceFile, $indexType, $hierarchyRef, $entries) = @_;
    +
    +    foreach my $entry (@$entries)
    +        {
    +        if ($entry->Type() == ::MENU_GROUP())
    +            {
    +            # If the subgroup has the selection...
    +            if ( $self->FindMenuSelection($sourceFile, $indexType, $hierarchyRef, $entry->GroupContent()) )
    +                {
    +                push @$hierarchyRef, $entry;
    +                return 1;
    +                };
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_FILE())
    +            {
    +            if ($sourceFile eq $entry->Target())
    +                {  return 1;  };
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_INDEX())
    +            {
    +            if ($indexType eq $entry->Target)
    +                {  return 1;  };
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +#
    +#   Function: ResetToolTips
    +#
    +#   Resets the <ToolTip Package Variables> for a new page.
    +#
    +#   Parameters:
    +#
    +#       samePage  - Set this flag if there's the possibility that the next batch of tooltips may be on the same page as the last.
    +#
    +sub ResetToolTips #(samePage)
    +    {
    +    my ($self, $samePage) = @_;
    +
    +    if (!$samePage)
    +        {
    +        $tooltipLinkNumber = 1;
    +        $tooltipNumber = 1;
    +        };
    +
    +    $tooltipHTML = undef;
    +    %tooltipSymbolsToNumbers = ( );
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy.pm b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy.pm
    new file mode 100644
    index 000000000..4b01b9a02
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy.pm
    @@ -0,0 +1,861 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ClassHierarchy
    +#
    +###############################################################################
    +#
    +#   A package that handles all the gory details of managing the class hierarchy.  It handles the hierarchy itself, which files define
    +#   them, rebuilding the files that are affected by changes, and loading and saving them to a file.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - <NaturalDocs::Settings> and <NaturalDocs::Project> must be initialized before use.
    +#
    +#       - <NaturalDocs::SymbolTable> must be initialized before <Load()> is called.  It must reflect the state as of the last time
    +#          Natural Docs was run.
    +#
    +#       - <Load()> must be called to initialize the package.  At this point, the <Information Functions> will return the state as
    +#         of the last time Natural Docs was run.  You are free to resolve <NaturalDocs::SymbolTable()> afterwards.
    +#
    +#       - <Purge()> must be called, and then <NaturalDocs::Parser->ParseForInformation()> must be called on all files that
    +#         have changed so it can fully resolve the hierarchy via the <Modification Functions()>.  Afterwards the
    +#         <Information Functions> will reflect the current state of the code.
    +#
    +#       - <Save()> must be called to commit any changes to the symbol table back to disk.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::ClassHierarchy::Class;
    +use NaturalDocs::ClassHierarchy::File;
    +
    +package NaturalDocs::ClassHierarchy;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +#
    +#   handle: CLASS_HIERARCHY_FILEHANDLE
    +#   The file handle used with <ClassHierarchy.nd>.
    +#
    +
    +#
    +#   hash: classes
    +#
    +#   A hash of all the classes.  The keys are the class <SymbolStrings> and the values are <NaturalDocs::ClassHierarchy::Classes>.
    +#
    +my %classes;
    +
    +#
    +#   hash: files
    +#
    +#   A hash of the hierarchy information referenced by file.  The keys are the <FileNames>, and the values are
    +#   <NaturalDocs::ClassHierarchy::File>s.
    +#
    +my %files;
    +
    +#
    +#   hash: parentReferences
    +#
    +#   A hash of all the parent reference strings and what they resolve to.  The keys are the <ReferenceStrings> and the values are
    +#   the class <SymbolStrings> that they resolve to.
    +#
    +my %parentReferences;
    +
    +#
    +#   object: watchedFile
    +#
    +#   A <NaturalDocs::ClassHierarchy::File> object of the file being watched for changes.  This is compared to the version in <files>
    +#   to see if anything was changed since the last parse.
    +#
    +my $watchedFile;
    +
    +#
    +#   string: watchedFileName
    +#
    +#   The <FileName> of the watched file, if any.  If there is no watched file, this will be undef.
    +#
    +my $watchedFileName;
    +
    +#
    +#   bool: dontRebuildFiles
    +#
    +#   A bool to set if you don't want changes in the hierarchy to cause files to be rebuilt.
    +#
    +my $dontRebuildFiles;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: ClassHierarchy.nd
    +#
    +#   Stores the class hierarchy on disk.
    +#
    +#   Format:
    +#
    +#       > [BINARY_FORMAT]
    +#       > [VersionInt: app version]
    +#
    +#       The standard <BINARY_FORMAT> and <VersionInt> header.
    +#
    +#       > [SymbolString: class or undef to end]
    +#
    +#       Next we begin a class segment with its <SymbolString>.  These continue until the end of the file.  Only defined classes are
    +#       included.
    +#
    +#       > [UInt32: number of files]
    +#       > [AString16: file] [AString16: file] ...
    +#
    +#       Next there is the number of files that define that class.  It's a UInt32, which seems like overkill, but I could imagine every
    +#       file in a huge C++ project being under the same namespace, and thus contributing its own definition.  It's theoretically
    +#       possible.
    +#
    +#       Following the number is that many file names.  You must remember the index of each file, as they will be important later.
    +#       Indexes start at one because zero has a special meaning.
    +#
    +#       > [UInt8: number of parents]
    +#       > ( [ReferenceString (no type): parent]
    +#       >   [UInt32: file index] [UInt32: file index] ... [UInt32: 0] ) ...
    +#
    +#       Next there is the number of parents defined for this class.  For each one, we define a parent segment, which consists of
    +#       its <ReferenceString>, and then a zero-terminated string of indexes of the files that define that parent as part of that class.
    +#       The indexes start at one, and are into the list of files we saw previously.
    +#
    +#       Note that we do store class segments for classes without parents, but not for undefined classes.
    +#
    +#       This concludes a class segment.  These segments continue until an undef <SymbolString>.
    +#
    +#   See Also:
    +#
    +#       <File Format Conventions>
    +#
    +#   Revisions:
    +#
    +#       1.22:
    +#
    +#           - Classes and parents switched from AString16s to <SymbolStrings> and <ReferenceStrings>.
    +#           - A ending undef <SymbolString> was added to the end.  Previously it stopped when the file ran out.
    +#
    +#       1.2:
    +#
    +#           - This file was introduced in 1.2.
    +#
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads the class hierarchy from disk.
    +#
    +sub Load
    +    {
    +    my ($self) = @_;
    +
    +    $dontRebuildFiles = 1;
    +
    +    my $fileIsOkay;
    +    my $fileName = NaturalDocs::Project->DataFile('ClassHierarchy.nd');
    +
    +    if (!NaturalDocs::Settings->RebuildData() && open(CLASS_HIERARCHY_FILEHANDLE, '<' . $fileName))
    +        {
    +        # See if it's binary.
    +        binmode(CLASS_HIERARCHY_FILEHANDLE);
    +
    +        my $firstChar;
    +        read(CLASS_HIERARCHY_FILEHANDLE, $firstChar, 1);
    +
    +        if ($firstChar != ::BINARY_FORMAT())
    +            {
    +            close(CLASS_HIERARCHY_FILEHANDLE);
    +            }
    +        else
    +            {
    +            my $version = NaturalDocs::Version->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE);
    +
    +            # Minor bugs were fixed in 1.33 that may affect the stored data.
    +
    +            if (NaturalDocs::Version->CheckFileFormat( $version, NaturalDocs::Version->FromString('1.33') ))
    +                {  $fileIsOkay = 1;  }
    +            else
    +                {  close(CLASS_HIERARCHY_FILEHANDLE);  };
    +            };
    +        };
    +
    +
    +    if (!$fileIsOkay)
    +        {
    +        NaturalDocs::Project->ReparseEverything();
    +        }
    +    else
    +        {
    +        my $raw;
    +
    +        for (;;)
    +            {
    +            # [SymbolString: class or undef to end]
    +
    +            my $class = NaturalDocs::SymbolString->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE);
    +
    +            if (!defined $class)
    +                {  last;  };
    +
    +            # [UInt32: number of files]
    +
    +            read(CLASS_HIERARCHY_FILEHANDLE, $raw, 4);
    +            my $numberOfFiles = unpack('N', $raw);
    +
    +            my @files;
    +
    +            while ($numberOfFiles)
    +                {
    +                # [AString16: file]
    +
    +                read(CLASS_HIERARCHY_FILEHANDLE, $raw, 2);
    +                my $fileLength = unpack('n', $raw);
    +
    +                my $file;
    +                read(CLASS_HIERARCHY_FILEHANDLE, $file, $fileLength);
    +
    +                push @files, $file;
    +                $self->AddClass($file, $class, NaturalDocs::Languages->LanguageOf($file)->Name());
    +
    +                $numberOfFiles--;
    +                };
    +
    +            # [UInt8: number of parents]
    +
    +            read(CLASS_HIERARCHY_FILEHANDLE, $raw, 1);
    +            my $numberOfParents = unpack('C', $raw);
    +
    +            while ($numberOfParents)
    +                {
    +                # [ReferenceString (no type): parent]
    +
    +                my $parent = NaturalDocs::ReferenceString->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE,
    +                                                                                                         ::BINARYREF_NOTYPE(),
    +                                                                                                         ::REFERENCE_CH_PARENT());
    +
    +                for (;;)
    +                    {
    +                    # [UInt32: file index or 0]
    +
    +                    read(CLASS_HIERARCHY_FILEHANDLE, $raw, 4);
    +                    my $fileIndex = unpack('N', $raw);
    +
    +                    if ($fileIndex == 0)
    +                        {  last;  }
    +
    +                    $self->AddParentReference( $files[$fileIndex - 1], $class, $parent );
    +                    };
    +
    +                $numberOfParents--;
    +                };
    +            };
    +
    +        close(CLASS_HIERARCHY_FILEHANDLE);
    +        };
    +
    +    $dontRebuildFiles = undef;
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves the class hierarchy to disk.
    +#
    +sub Save
    +    {
    +    my ($self) = @_;
    +
    +    open (CLASS_HIERARCHY_FILEHANDLE, '>' . NaturalDocs::Project->DataFile('ClassHierarchy.nd'))
    +        or die "Couldn't save " . NaturalDocs::Project->DataFile('ClassHierarchy.nd') . ".\n";
    +
    +    binmode(CLASS_HIERARCHY_FILEHANDLE);
    +
    +    print CLASS_HIERARCHY_FILEHANDLE '' . ::BINARY_FORMAT();
    +    NaturalDocs::Version->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, NaturalDocs::Settings->AppVersion());
    +
    +    while (my ($class, $classObject) = each %classes)
    +        {
    +        if ($classObject->IsDefined())
    +            {
    +            # [SymbolString: class or undef to end]
    +
    +            NaturalDocs::SymbolString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, $class);
    +
    +            # [UInt32: number of files]
    +
    +            my @definitions = $classObject->Definitions();
    +            my %definitionIndexes;
    +
    +            print CLASS_HIERARCHY_FILEHANDLE pack('N', scalar @definitions);
    +
    +            for (my $i = 0; $i < scalar @definitions; $i++)
    +                {
    +                # [AString16: file]
    +                print CLASS_HIERARCHY_FILEHANDLE pack('nA*', length($definitions[$i]), $definitions[$i]);
    +                $definitionIndexes{$definitions[$i]} = $i + 1;
    +                };
    +
    +            # [UInt8: number of parents]
    +
    +            my @parents = $classObject->ParentReferences();
    +            print CLASS_HIERARCHY_FILEHANDLE pack('C', scalar @parents);
    +
    +            foreach my $parent (@parents)
    +                {
    +                # [ReferenceString (no type): parent]
    +
    +                NaturalDocs::ReferenceString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, $parent, ::BINARYREF_NOTYPE());
    +
    +                # [UInt32: file index]
    +
    +                my @parentDefinitions = $classObject->ParentReferenceDefinitions($parent);
    +
    +                foreach my $parentDefinition (@parentDefinitions)
    +                    {
    +                    print CLASS_HIERARCHY_FILEHANDLE pack('N', $definitionIndexes{$parentDefinition});
    +                    };
    +
    +                # [UInt32: 0]
    +                print CLASS_HIERARCHY_FILEHANDLE pack('N', 0);
    +                };
    +            };
    +        };
    +
    +    # [SymbolString: class or undef to end]
    +
    +    NaturalDocs::SymbolString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, undef);
    +
    +    close(CLASS_HIERARCHY_FILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: Purge
    +#
    +#   Purges the hierarchy of files that no longer have Natural Docs content.
    +#
    +sub Purge
    +    {
    +    my ($self) = @_;
    +
    +    my $filesToPurge = NaturalDocs::Project->FilesToPurge();
    +
    +    foreach my $file (keys %$filesToPurge)
    +        {
    +        $self->DeleteFile($file);
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: OnInterpretationChange
    +#
    +#   Called by <NaturalDocs::SymbolTable> whenever a class hierarchy reference's intepretation changes, meaning it switched
    +#   from one symbol to another.
    +#
    +#       reference - The <ReferenceString> whose current interpretation changed.
    +#
    +sub OnInterpretationChange #(reference)
    +    {
    +    my ($self, $reference) = @_;
    +
    +    if (NaturalDocs::ReferenceString->TypeOf($reference) == ::REFERENCE_CH_PARENT())
    +        {
    +        # The approach here is simply to completely delete the reference and readd it.  This is less than optimal efficiency, since it's
    +        # being removed and added from %files too, even though that isn't required.  However, the simpler code is worth it
    +        # considering this will only happen when a parent reference becomes defined or undefined, or on the rare languages (like C#)
    +        # that allow relative parent references.
    +
    +        my $oldTargetSymbol = $parentReferences{$reference};
    +        my $oldTargetObject = $classes{$oldTargetSymbol};
    +
    +        my @classesWithReferenceParent = $oldTargetObject->Children();
    +
    +        # Each entry is an arrayref of file names.  Indexes are the same as classesWithReferenceParent's.
    +        my @filesDefiningReferenceParent;
    +
    +        foreach my $classWithReferenceParent (@classesWithReferenceParent)
    +            {
    +            my $fileList = [ $classes{$classWithReferenceParent}->ParentReferenceDefinitions($reference) ];
    +            push @filesDefiningReferenceParent, $fileList;
    +
    +            foreach my $fileDefiningReferenceParent (@$fileList)
    +                {
    +                $self->DeleteParentReference($fileDefiningReferenceParent, $classWithReferenceParent, $reference);
    +                };
    +            };
    +
    +
    +        # This will force the reference to be reinterpreted on the next add.
    +
    +        delete $parentReferences{$reference};
    +
    +
    +        # Now we can just readd it.
    +
    +        for (my $i = 0; $i < scalar @classesWithReferenceParent; $i++)
    +            {
    +            foreach my $file (@{$filesDefiningReferenceParent[$i]})
    +                {
    +                $self->AddParentReference($file, $classesWithReferenceParent[$i], $reference);
    +                };
    +            };
    +        };
    +
    +    # The only way for a REFERENCE_CH_CLASS reference to change is if the symbol is deleted.  That will be handled by
    +    # <AnalyzeChanges()>, so we don't need to do anything here.
    +    };
    +
    +
    +#
    +#   Function: OnTargetSymbolChange
    +#
    +#   Called by <NaturalDocs::SymbolTable> whenever a class hierarchy reference's target symbol changes, but the reference
    +#   still resolves to the same symbol.
    +#
    +#   Parameters:
    +#
    +#       reference - The <ReferenceString> that was affected by the change.
    +#
    +sub OnTargetSymbolChange #(reference)
    +    {
    +    my ($self, $reference) = @_;
    +
    +    my $type = NaturalDocs::ReferenceString->TypeOf($reference);
    +    my $class;
    +
    +    if ($type == ::REFERENCE_CH_PARENT())
    +        {  $class = $parentReferences{$reference};  }
    +    else # ($type == ::REFERENCE_CH_CLASS())
    +        {
    +        # Class references are global absolute, so we can just yank the symbol.
    +        (undef, $class, undef, undef, undef, undef) = NaturalDocs::ReferenceString->InformationOf($reference);
    +        };
    +
    +    $self->RebuildFilesFor($class, 1, 0, 1);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +
    +#
    +#   Function: AddClass
    +#
    +#   Adds a class to the hierarchy.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> the class was defined in.
    +#       class - The class <SymbolString>.
    +#       languageName - The name of the language this applies to.
    +#
    +#   Note:
    +#
    +#       The file parameter must be defined when using this function externally.  It may be undef for internal use only.
    +#
    +sub AddClass #(file, class, languageName)
    +    {
    +    my ($self, $file, $class, $languageName) = @_;
    +
    +    if (!exists $classes{$class})
    +        {
    +        $classes{$class} = NaturalDocs::ClassHierarchy::Class->New();
    +        NaturalDocs::SymbolTable->AddReference($self->ClassReferenceOf($class, $languageName), $file)
    +        };
    +
    +    if (defined $file)
    +        {
    +        # If this was the first definition for this class...
    +        if ($classes{$class}->AddDefinition($file))
    +            {  $self->RebuildFilesFor($class, 1, 1, 1);  };
    +
    +        if (!exists $files{$file})
    +            {  $files{$file} = NaturalDocs::ClassHierarchy::File->New();  };
    +
    +        $files{$file}->AddClass($class);
    +
    +        if (defined $watchedFileName)
    +            {  $watchedFile->AddClass($class);  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: AddParentReference
    +#
    +#   Adds a class-parent relationship to the hierarchy.  The classes will be created if they don't already exist.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> the reference was defined in.
    +#       class - The class <SymbolString>.
    +#       symbol - The parent class <SymbolString>.
    +#       scope - The package <SymbolString> that the reference appeared in.
    +#       using - An arrayref of package <SymbolStrings> that the reference has access to via "using" statements.
    +#       resolvingFlags - Any <Resolving Flags> to be used when resolving the reference.
    +#
    +#   Alternate Parameters:
    +#
    +#       file - The <FileName> the reference was defined in.
    +#       class - The class <SymbolString>.
    +#       reference - The parent <ReferenceString>.
    +#
    +sub AddParentReference #(file, class, symbol, scope, using, resolvingFlags) or (file, class, reference)
    +    {
    +    my ($self, $file, $class, $symbol, $parentReference);
    +
    +    if (scalar @_ == 7)
    +        {
    +        my ($scope, $using, $resolvingFlags);
    +        ($self, $file, $class, $symbol, $scope, $using, $resolvingFlags) = @_;
    +
    +        $parentReference = NaturalDocs::ReferenceString->MakeFrom(::REFERENCE_CH_PARENT(), $symbol,
    +                                                                                                    NaturalDocs::Languages->LanguageOf($file)->Name(),
    +                                                                                                    $scope, $using, $resolvingFlags);
    +        }
    +    else
    +        {
    +        ($self, $file, $class, $parentReference) = @_;
    +        $symbol = (NaturalDocs::ReferenceString->InformationOf($parentReference))[1];
    +        };
    +
    +
    +    # In case it doesn't already exist.
    +    $self->AddClass($file, $class);
    +
    +    my $parent;
    +    if (exists $parentReferences{$parentReference})
    +        {
    +        $parent = $parentReferences{$parentReference};
    +        }
    +    else
    +        {
    +        NaturalDocs::SymbolTable->AddReference($parentReference, $file);
    +        my $parentTarget = NaturalDocs::SymbolTable->References($parentReference);
    +
    +        if (defined $parentTarget)
    +            {  $parent = $parentTarget->Symbol();  }
    +        else
    +            {  $parent = $symbol;  };
    +
    +        # In case it doesn't already exist.
    +        $self->AddClass(undef, $parent);
    +
    +        $parentReferences{$parentReference} = $parent;
    +        };
    +
    +
    +    # If this defined a new parent...
    +    if ($classes{$class}->AddParentReference($parentReference, $file, \%parentReferences))
    +        {
    +        $classes{$parent}->AddChild($class);
    +
    +        $self->RebuildFilesFor($class, 0, 1, 0);
    +        $self->RebuildFilesFor($parent, 0, 1, 0);
    +        };
    +
    +    $files{$file}->AddParentReference($class, $parentReference);
    +
    +    if (defined $watchedFileName)
    +        {  $watchedFile->AddParentReference($class, $parentReference);  };
    +    };
    +
    +
    +#
    +#   Function: WatchFileForChanges
    +#
    +#   Watches a file for changes, which can then be applied by <AnalyzeChanges()>.  Definitions are not deleted via a DeleteClass()
    +#   function.  Instead, a file is watched for changes, reparsed, and then a comparison is made to look for definitions that
    +#   disappeared and any other relevant changes.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to watch.
    +#
    +sub WatchFileForChanges #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    $watchedFile = NaturalDocs::ClassHierarchy::File->New();
    +    $watchedFileName = $file;
    +    };
    +
    +
    +#
    +#   Function: AnalyzeChanges
    +#
    +#   Checks the watched file for any changes that occured since the last time is was parsed, and updates the hierarchy as
    +#   necessary.  Also sends any files that are affected to <NaturalDocs::Project->RebuildFile()>.
    +#
    +sub AnalyzeChanges
    +    {
    +    my ($self) = @_;
    +
    +    # If the file didn't have any classes before, and it still doesn't, it wont be in %files.
    +    if (exists $files{$watchedFileName})
    +        {
    +        my @originalClasses = $files{$watchedFileName}->Classes();
    +
    +        foreach my $originalClass (@originalClasses)
    +            {
    +            # If the class isn't there the second time around...
    +            if (!$watchedFile->HasClass($originalClass))
    +                {  $self->DeleteClass($watchedFileName, $originalClass);  }
    +
    +            else
    +                {
    +                my @originalParents = $files{$watchedFileName}->ParentReferencesOf($originalClass);
    +
    +                foreach my $originalParent (@originalParents)
    +                    {
    +                    # If the parent reference wasn't there the second time around...
    +                    if (!$watchedFile->HasParentReference($originalClass, $originalParent))
    +                        {  $self->DeleteParentReference($watchedFileName, $originalClass, $originalParent);  };
    +                    };
    +                };
    +            };
    +        };
    +
    +
    +    $watchedFile = undef;
    +    $watchedFileName = undef;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +
    +#
    +#   Function: ParentsOf
    +#   Returns a <SymbolString> array of the passed class' parents, or an empty array if none.  Note that not all of them may be
    +#   defined.
    +#
    +sub ParentsOf #(class)
    +    {
    +    my ($self, $class) = @_;
    +
    +    if (exists $classes{$class})
    +        {  return $classes{$class}->Parents();  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +#
    +#   Function: ChildrenOf
    +#   Returns a <SymbolString> array of the passed class' children, or an empty array if none.  Note that not all of them may be
    +#   defined.
    +#
    +sub ChildrenOf #(class)
    +    {
    +    my ($self, $class) = @_;
    +
    +    if (exists $classes{$class})
    +        {  return $classes{$class}->Children();  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: DeleteFile
    +#
    +#   Deletes a file and everything defined in it.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName>.
    +#
    +sub DeleteFile #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (!exists $files{$file})
    +        {  return;  };
    +
    +    my @classes = $files{$file}->Classes();
    +    foreach my $class (@classes)
    +        {
    +        $self->DeleteClass($file, $class);
    +        };
    +
    +    delete $files{$file};
    +    };
    +
    +#
    +#   Function: DeleteClass
    +#
    +#   Deletes a class definition from a file.  Will also delete any parent references from this class and file.  Will rebuild any file
    +#   affected unless <dontRebuildFiles> is set.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> that defines the class.
    +#       class - The class <SymbolString>.
    +#
    +sub DeleteClass #(file, class)
    +    {
    +    my ($self, $file, $class) = @_;
    +
    +    my @parents = $files{$file}->ParentReferencesOf($class);
    +    foreach my $parent (@parents)
    +        {
    +        $self->DeleteParentReference($file, $class, $parent);
    +        };
    +
    +    $files{$file}->DeleteClass($class);
    +
    +    # If we're deleting the last definition of this class.
    +    if ($classes{$class}->DeleteDefinition($file))
    +        {
    +        if (!$classes{$class}->HasChildren())
    +            {
    +            delete $classes{$class};
    +
    +            if (!$dontRebuildFiles)
    +                {  NaturalDocs::Project->RebuildFile($file);  };
    +            }
    +        else
    +            {  $self->RebuildFilesFor($class, 0, 1, 1);  };
    +
    +        };
    +    };
    +
    +
    +#
    +#   Function: DeleteParentReference
    +#
    +#   Deletes a class' parent reference and returns whether it resulted in the loss of a parent class.  Will rebuild any file affected
    +#   unless <dontRebuildFiles> is set.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> that defines the reference.
    +#       class - The class <SymbolString>.
    +#       reference - The parent <ReferenceString>.
    +#
    +#   Returns:
    +#
    +#       If the class lost a parent as a result of this, it will return its <SymbolString>.  It will return undef otherwise.
    +#
    +sub DeleteParentReference #(file, class, reference)
    +    {
    +    my ($self, $file, $class, $reference) = @_;
    +
    +    if (!exists $classes{$class})
    +        {  return;  };
    +
    +    $files{$file}->DeleteParentReference($class, $reference);
    +
    +    my $deletedParent = $classes{$class}->DeleteParentReference($reference, $file, \%parentReferences);
    +
    +    if (defined $deletedParent)
    +        {
    +        my $deletedParentObject = $classes{$deletedParent};
    +
    +        $deletedParentObject->DeleteChild($class);
    +
    +        $self->RebuildFilesFor($deletedParent, 0, 1, 0);
    +        $self->RebuildFilesFor($class, 0, 1, 0);
    +
    +        if (!$deletedParentObject->HasChildren() && !$deletedParentObject->IsDefined())
    +            {
    +            delete $classes{$deletedParent};
    +            NaturalDocs::SymbolTable->DeleteReference(
    +                $self->ClassReferenceOf($class, NaturalDocs::Languages->LanguageOf($file)->Name()) );
    +            };
    +
    +        return $deletedParent;
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: ClassReferenceOf
    +#
    +#   Returns the <REFERENCE_CH_CLASS> <ReferenceString> of the passed class <SymbolString>.
    +#
    +sub ClassReferenceOf #(class, languageName)
    +    {
    +    my ($self, $class, $languageName) = @_;
    +
    +    return NaturalDocs::ReferenceString->MakeFrom(::REFERENCE_CH_CLASS(), $class, $languageName, undef, undef,
    +                                                                            ::RESOLVE_ABSOLUTE() | ::RESOLVE_NOPLURAL());
    +    };
    +
    +
    +#
    +#   Function: RebuildFilesFor
    +#
    +#   Calls <NaturalDocs::Project->RebuildFile()> for every file defining the passed class, its parents, and/or its children.
    +#   Returns without doing anything if <dontRebuildFiles> is set.
    +#
    +#   Parameters:
    +#
    +#       class - The class <SymbolString>.
    +#       rebuildParents - Whether to rebuild the class' parents.
    +#       rebuildSelf - Whether to rebuild the class.
    +#       rebuildChildren - Whether to rebuild the class' children.
    +#
    +sub RebuildFilesFor #(class, rebuildParents, rebuildSelf, rebuildChildren)
    +    {
    +    my ($self, $class, $rebuildParents, $rebuildSelf, $rebuildChildren) = @_;
    +
    +    if ($dontRebuildFiles)
    +        {  return;  };
    +
    +    my @classesToBuild;
    +
    +    if ($rebuildParents)
    +        {  @classesToBuild = $classes{$class}->Parents();  };
    +    if ($rebuildSelf)
    +        {  push @classesToBuild, $class;  };
    +    if ($rebuildChildren)
    +        {  push @classesToBuild, $classes{$class}->Children();  };
    +
    +    foreach my $classToBuild (@classesToBuild)
    +        {
    +        my @definitions = $classes{$classToBuild}->Definitions();
    +
    +        foreach my $definition (@definitions)
    +            {  NaturalDocs::Project->RebuildFile($definition);  };
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/Class.pm b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/Class.pm
    new file mode 100644
    index 000000000..48ed6e28e
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/Class.pm
    @@ -0,0 +1,413 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::ClassHierarchy::Class
    +#
    +###############################################################################
    +#
    +#   An object that stores information about a class in the hierarchy.  It does not store its <SymbolString>; it assumes that it will
    +#   be stored in a hashref where the key is the <SymbolString>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::ClassHierarchy::Class;
    +
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The keys are the constants below.
    +#
    +#   DEFINITIONS - An existence hashref of all the <FileNames> which define this class.  Undef if none.
    +#   PARENTS - An existence hashref of the <SymbolStrings> of all the parents this class has.
    +#   CHILDREN - An existence hashref of the <SymbolStrings> of all the children this class has.
    +#   PARENT_REFERENCES - A hashref of the parent <ReferenceStrings> this class has.  The keys are the <ReferenceStrings>,
    +#                                      and the values are existence hashrefs of all the <FileNames> that define them.  Undef if none.
    +#
    +use NaturalDocs::DefineMembers 'DEFINITIONS', 'PARENTS', 'CHILDREN', 'PARENT_REFERENCES';
    +# Dependency: New() depends on the order of these constants, as well as the class not being derived from any other.
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new class.
    +#
    +sub New
    +    {
    +    # Dependency: This function depends on the order of the constants, as well as the class not being derived from any other.
    +    my ($package, $definitionFile) = @_;
    +
    +    my $object = [ undef, undef, undef, undef ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a rew definition of this class and returns if that was the first definition.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> the definition appears in.
    +#
    +#   Returns:
    +#
    +#       Whether this was the first definition of this class.
    +#
    +sub AddDefinition #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    my $wasFirst;
    +
    +    if (!defined $self->[DEFINITIONS])
    +        {
    +        $self->[DEFINITIONS] = { };
    +        $wasFirst = 1;
    +        };
    +
    +    $self->[DEFINITIONS]->{$file} = 1;
    +
    +    return $wasFirst;
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes the definition of this class and returns if there are no more definitions.  Note that if there are no more
    +#   definitions, you may still want to keep the object around if <HasChildren()> returns true.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> the definition appears in.
    +#
    +#   Returns:
    +#
    +#       Whether this deleted the last definition of this class.
    +#
    +sub DeleteDefinition #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {
    +        delete $self->[DEFINITIONS]->{$file};
    +
    +        if (!scalar keys %{$self->[DEFINITIONS]})
    +            {
    +            $self->[DEFINITIONS] = undef;
    +            return 1;
    +            };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: AddParentReference
    +#
    +#   Adds a parent reference to the class and return whether it resulted in a new parent class.
    +#
    +#   Parameters:
    +#
    +#       reference - The <ReferenceString> used to determine the parent.
    +#       file - The <FileName> the parent reference is in.
    +#       referenceTranslations - A hashref of what each reference currently resolves to.  The keys are the
    +#                                         <ReferenceStrings> and the values are class <SymbolStrings>.  It should include an entry for
    +#                                         the reference parameter above.
    +#
    +#   Returns:
    +#
    +#       If the reference adds a new parent, it will return that parent's <SymbolString>.  Otherwise it will return undef.
    +#
    +sub AddParentReference #(reference, file, referenceTranslations)
    +    {
    +    my ($self, $reference, $file, $referenceTranslations) = @_;
    +
    +    if (!defined $self->[PARENT_REFERENCES])
    +        {  $self->[PARENT_REFERENCES] = { };  };
    +    if (!defined $self->[PARENTS])
    +        {  $self->[PARENTS] = { };  };
    +
    +
    +    if (!exists $self->[PARENT_REFERENCES]->{$reference})
    +        {
    +        $self->[PARENT_REFERENCES]->{$reference} = { $file => 1 };
    +
    +        my $symbol = $referenceTranslations->{$reference};
    +
    +        if (!exists $self->[PARENTS]->{$symbol})
    +            {
    +            $self->[PARENTS]->{$symbol} = 1;
    +            return $symbol;
    +            }
    +        else
    +            {  return undef;  };
    +        }
    +    else
    +        {
    +        $self->[PARENT_REFERENCES]->{$reference}->{$file} = 1;
    +        return undef;
    +        };
    +    };
    +
    +#
    +#   Function: DeleteParentReference
    +#
    +#   Deletes a parent reference from the class and return whether it resulted in a loss of a parent class.
    +#
    +#   Parameters:
    +#
    +#       reference - The <ReferenceString> used to determine the parent.
    +#       file - The <FileName> the parent declaration is in.
    +#       referenceTranslations - A hashref of what each reference currently resolves to.  The keys are the
    +#                                         <ReferenceStrings> and the values are class <SymbolStrings>.  It should include an entry for
    +#                                         the reference parameter above.
    +#
    +#   Returns:
    +#
    +#       If this causes a parent class to be lost, it will return that parent's <SymbolString>.  Otherwise it will return undef.
    +#
    +sub DeleteParentReference #(reference, file, referenceTranslations)
    +    {
    +    my ($self, $reference, $file, $referenceTranslations) = @_;
    +
    +    if (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference} &&
    +        exists $self->[PARENT_REFERENCES]->{$reference}->{$file})
    +        {
    +        delete $self->[PARENT_REFERENCES]->{$reference}->{$file};
    +
    +        # Quit if there are other definitions of this reference.
    +        if (scalar keys %{$self->[PARENT_REFERENCES]->{$reference}})
    +            {  return undef;  };
    +
    +        delete $self->[PARENT_REFERENCES]->{$reference};
    +
    +        if (!scalar keys %{$self->[PARENT_REFERENCES]})
    +            {  $self->[PARENT_REFERENCES] = undef;  };
    +
    +        my $parent = $referenceTranslations->{$reference};
    +
    +        # Check if any other references resolve to the same parent.
    +        if (defined $self->[PARENT_REFERENCES])
    +            {
    +            foreach my $parentReference (keys %{$self->[PARENT_REFERENCES]})
    +                {
    +                if ($referenceTranslations->{$parentReference} eq $parent)
    +                    {  return undef;  };
    +                };
    +            };
    +
    +        # If we got this far, no other parent references resolve to this symbol.
    +
    +        delete $self->[PARENTS]->{$parent};
    +
    +        if (!scalar keys %{$self->[PARENTS]})
    +            {  $self->[PARENTS] = undef;  };
    +
    +        return $parent;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: AddChild
    +#   Adds a child <SymbolString> to the class.  Unlike <AddParentReference()>, this does not keep track of anything other than
    +#   whether it has it or not.
    +#
    +#   Parameters:
    +#
    +#       child - The <SymbolString> to add.
    +#
    +sub AddChild #(child)
    +    {
    +    my ($self, $child) = @_;
    +
    +    if (!defined $self->[CHILDREN])
    +        {  $self->[CHILDREN] = { };  };
    +
    +    $self->[CHILDREN]->{$child} = 1;
    +    };
    +
    +#
    +#   Function: DeleteChild
    +#   Deletes a child <SymbolString> from the class.  Unlike <DeleteParentReference()>, this does not keep track of anything other
    +#   than whether it has it or not.
    +#
    +#   Parameters:
    +#
    +#       child - The <SymbolString> to delete.
    +#
    +sub DeleteChild #(child)
    +    {
    +    my ($self, $child) = @_;
    +
    +    if (defined $self->[CHILDREN])
    +        {
    +        delete $self->[CHILDREN]->{$child};
    +
    +        if (!scalar keys %{$self->[CHILDREN]})
    +            {  $self->[CHILDREN] = undef;  };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +#
    +#   Function: Definitions
    +#   Returns an array of the <FileNames> that define this class, or an empty array if none.
    +#
    +sub Definitions
    +    {
    +    my ($self) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {  return keys %{$self->[DEFINITIONS]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +#
    +#   Function: IsDefinedIn
    +#   Returns whether the class is defined in the passed <FileName>.
    +#
    +sub IsDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {  return exists $self->[DEFINITIONS]->{$file};  }
    +    else
    +        {  return 0;  };
    +    };
    +
    +#
    +#   Function: IsDefined
    +#   Returns whether the class is defined in any files.
    +#
    +sub IsDefined
    +    {
    +    my ($self) = @_;
    +    return defined $self->[DEFINITIONS];
    +    };
    +
    +#
    +#   Function: ParentReferences
    +#   Returns an array of the parent <ReferenceStrings>, or an empty array if none.
    +#
    +sub ParentReferences
    +    {
    +    my ($self) = @_;
    +
    +    if (defined $self->[PARENT_REFERENCES])
    +        {  return keys %{$self->[PARENT_REFERENCES]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +#
    +#   Function: HasParentReference
    +#   Returns whether the class has the passed parent <ReferenceString>.
    +#
    +sub HasParentReference #(reference)
    +    {
    +    my ($self, $reference) = @_;
    +    return (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference});
    +    };
    +
    +#
    +#   Function: HasParentReferences
    +#   Returns whether the class has any parent <ReferenceStrings>.
    +#
    +sub HasParentReferences
    +    {
    +    my ($self) = @_;
    +    return defined $self->[PARENT_REFERENCES];
    +    };
    +
    +#
    +#   Function: Parents
    +#   Returns an array of the parent <SymbolStrings>, or an empty array if none.
    +#
    +sub Parents
    +    {
    +    my ($self) = @_;
    +
    +    if (defined $self->[PARENTS])
    +        {  return keys %{$self->[PARENTS]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +#
    +#   Function: HasParents
    +#   Returns whether the class has any parent <SymbolStrings> defined.
    +#
    +sub HasParents
    +    {
    +    my ($self) = @_;
    +    return defined $self->[PARENTS];
    +    };
    +
    +#
    +#   Function: Children
    +#   Returns an array of the child <SymbolStrings>, or an empty array if none.
    +#
    +sub Children
    +    {
    +    my ($self) = @_;
    +
    +    if (defined $self->[CHILDREN])
    +        {  return keys %{$self->[CHILDREN]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +#
    +#   Function: HasChildren
    +#   Returns whether any child <SymbolStrings> are defined.
    +#
    +sub HasChildren
    +    {
    +    my ($self) = @_;
    +    return defined $self->[CHILDREN];
    +    };
    +
    +
    +#
    +#   Function: ParentReferenceDefinitions
    +#   Returns an array of the <FileNames> which define the passed parent <ReferenceString>, or an empty array if none.
    +#
    +sub ParentReferenceDefinitions #(reference)
    +    {
    +    my ($self, $reference) = @_;
    +
    +    if (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference})
    +        {  return keys %{$self->[PARENT_REFERENCES]->{$reference}};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/File.pm b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/File.pm
    new file mode 100644
    index 000000000..0f7b3226a
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ClassHierarchy/File.pm
    @@ -0,0 +1,158 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::ClassHierarchy::File
    +#
    +###############################################################################
    +#
    +#   An object that stores information about what hierarchy information is present in a file.  It does not store its <FileName>; it
    +#   assumes that it will be stored in a hashref where the key is the <FileName>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::ClassHierarchy::File;
    +
    +
    +#
    +#   Topic: Implementation
    +#
    +#   Since there's only one member in the class, and it's a hashref, the class is simply the hashref itself blessed as a class.
    +#   The keys are the class <SymbolStrings> that are defined in the file, and the values are existence hashrefs of each class'
    +#   parent <ReferenceStrings>, or undef if none.
    +#
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new class.
    +#
    +sub New
    +    {
    +    my ($package) = @_;
    +
    +    my $object = { };
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +#
    +#   Function: AddClass
    +#   Adds a rew class <SymbolString> to the file.
    +#
    +sub AddClass #(class)
    +    {
    +    my ($self, $class) = @_;
    +
    +    if (!exists $self->{$class})
    +        {  $self->{$class} = undef;  };
    +    };
    +
    +#
    +#   Function: DeleteClass
    +#   Deletes a class <SymbolString> from the file.
    +#
    +sub DeleteClass #(class)
    +    {
    +    my ($self, $class) = @_;
    +    delete $self->{$class};
    +    };
    +
    +#
    +#   Function: AddParentReference
    +#   Adds a parent <ReferenceString> to a class <SymbolString>.
    +#
    +sub AddParentReference #(class, parentReference)
    +    {
    +    my ($self, $class, $parent) = @_;
    +
    +    if (!exists $self->{$class} || !defined $self->{$class})
    +        {  $self->{$class} = { };  };
    +
    +    $self->{$class}->{$parent} = 1;
    +    };
    +
    +#
    +#   Function: DeleteParentReference
    +#   Deletes a parent <ReferenceString> from a class <SymbolString>.
    +#
    +sub DeleteParentReference #(class, parent)
    +    {
    +    my ($self, $class, $parent) = @_;
    +
    +    if (exists $self->{$class})
    +        {
    +        delete $self->{$class}->{$parent};
    +
    +        if (!scalar keys %{$self->{$class}})
    +            {  $self->{$class} = undef;  };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +
    +#
    +#   Function: Classes
    +#   Returns an array of the class <SymbolStrings> that are defined by this file, or an empty array if none.
    +#
    +sub Classes
    +    {
    +    my ($self) = @_;
    +    return keys %{$self};
    +    };
    +
    +#
    +#   Function: HasClass
    +#   Returns whether the file defines the passed class <SymbolString>.
    +#
    +sub HasClass #(class)
    +    {
    +    my ($self, $class) = @_;
    +    return exists $self->{$class};
    +    };
    +
    +#
    +#   Function: ParentReferencesOf
    +#   Returns an array of the parent <ReferenceStrings> that are defined by the class, or an empty array if none.
    +#
    +sub ParentReferencesOf #(class)
    +    {
    +    my ($self, $class) = @_;
    +
    +    if (!exists $self->{$class} || !defined $self->{$class})
    +        {  return ( );  }
    +    else
    +        {  return keys %{$self->{$class}};  };
    +    };
    +
    +#
    +#   Function: HasParentReference
    +#   Returns whether the file defines the passed class <SymbolString> and parent <ReferenceString>.
    +#
    +sub HasParentReference #(class, parent)
    +    {
    +    my ($self, $class, $parent) = @_;
    +
    +    if (!$self->HasClass($class))
    +        {  return undef;  };
    +
    +    return exists $self->{$class}->{$parent};
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ConfigFile.pm b/vendor/naturaldocs/Modules/NaturalDocs/ConfigFile.pm
    new file mode 100644
    index 000000000..e9fd7cc46
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ConfigFile.pm
    @@ -0,0 +1,508 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ConfigFile
    +#
    +###############################################################################
    +#
    +#   A package to manage Natural Docs' configuration files.
    +#
    +#   Usage:
    +#
    +#       - Only one configuration file can be managed with this package at a time.  You must close the file before opening another
    +#         one.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::ConfigFile;
    +
    +
    +
    +#
    +#   Topic: Format
    +#
    +#   All configuration files are text files.
    +#
    +#   > # [comment]
    +#
    +#   Comments start with the # character.
    +#
    +#   > Format: [version]
    +#
    +#   All configuration files *must* have a format line as its first line containing content.  Whitespace and comments are permitted
    +#   ahead of it.
    +#
    +#   > [keyword]: [value]
    +#
    +#   Keywords can only contain <CFChars>.  Keywords are not case sensitive.  Values can be anything and run until the end of
    +#   the line or a comment.
    +#
    +#   > [value]
    +#
    +#   Lines that don't start with a valid keyword format are considered to be all value.
    +#
    +#   > [line] { [line] } [line]
    +#
    +#   Files supporting brace groups (specified in <Open()>) may also have braces that can appear anywhere.  It allows more than
    +#   one thing to appear per line, which isn't supported otherwise.  Consequently, values may not have braces.
    +#
    +
    +
    +#
    +#   Type: CFChars
    +#
    +#   The characters that can appear in configuration file keywords and user-defined element names: letters, numbers, spaces,
    +#   dashes, slashes, apostrophes, and periods.
    +#
    +#   Although the list above is exhaustive, it should be noted that you especially can *not* use colons (messes up keyword: value
    +#   sequences) commas (messes up item, item, item list sequences) and hashes (messes up comment detection.)
    +#
    +#   You can search the source code for [CFChars] to find all the instances where this definition is used.
    +#
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +#
    +#   handle: CONFIG_FILEHANDLE
    +#
    +#   The file handle used for the configuration file.
    +#
    +
    +
    +#
    +#   string: file
    +#
    +#   The <FileName> for the current configuration file being parsed.
    +#
    +my $file;
    +
    +
    +#
    +#	var: lineReader
    +#
    +#	The <LineReader> used to read the configuration file.
    +#
    +my $lineReader;
    +
    +
    +#
    +#   array: errors
    +#
    +#   An array of errors added by <AddError()>.  Every odd entry is the line number, and every even entry following is the
    +#   error message.
    +#
    +my @errors;
    +
    +
    +#
    +#   var: lineNumber
    +#
    +#   The current line number for the configuration file.
    +#
    +my $lineNumber;
    +
    +
    +#
    +#   bool: hasBraceGroups
    +#
    +#   Whether the file has brace groups or not.
    +#
    +my $hasBraceGroups;
    +
    +
    +#
    +#   array: virtualLines
    +#
    +#   An array of virtual lines if a line from the file contained more than one.
    +#
    +#   Files with brace groups may have more than one virtual line per actual file line, such as "Group: A { Group: B".  When that
    +#   happens, any extra virtual lines are put into here so they can be returned on the next call.
    +#
    +my @virtualLines;
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: Open
    +#
    +#   Opens a configuration file for parsing and returns the format <VersionInt>.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to parse.
    +#       hasBraceGroups - Whether the file supports brace groups or not.  If so, lines with braces will be split apart behind the
    +#                                  scenes.
    +#
    +#   Returns:
    +#
    +#       The <VersionInt> of the file, or undef if the file doesn't exist.
    +#
    +sub Open #(file, hasBraceGroups)
    +    {
    +    my $self;
    +    ($self, $file, $hasBraceGroups) = @_;
    +
    +    @errors = ( );
    +
    +    # It will be incremented to one when the first line is read from the file.
    +    $lineNumber = 0;
    +
    +    open(CONFIG_FILEHANDLE, '<' . $file) or return undef;
    +    $lineReader = NaturalDocs::LineReader->New(\*CONFIG_FILEHANDLE);
    +
    +
    +    # Get the format line.
    +
    +    my ($keyword, $value, $comment) = $self->GetLine();
    +
    +    if ($keyword eq 'format')
    +        {  return NaturalDocs::Version->FromString($value);  }
    +    else
    +        {  die "The first content line in " . $file . " must be the Format: line.\n";  };
    +    };
    +
    +
    +#
    +#   Function: Close
    +#
    +#   Closes the current configuration file.
    +#
    +sub Close
    +    {
    +    my $self = shift;
    +    close(CONFIG_FILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: GetLine
    +#
    +#   Returns the next line containing content, or an empty array if none.
    +#
    +#   Returns:
    +#
    +#       Returns the array ( keyword, value, comment ), or an empty array if none.  All tabs will be converted to spaces, and all
    +#       whitespace will be condensed into a single space.
    +#
    +#       keyword - The keyword part of the line, if any.  Is converted to lowercase and doesn't include the colon.  If the file supports
    +#                       brace groups, opening and closing braces will be returned as keywords.
    +#       value - The value part of the line, minus any whitespace.  Keeps its original case.
    +#       comment - The comment following the line, if any.  This includes the # symbol and a leading space if there was
    +#                       any whitespace, since it may be significant.  Otherwise undef.  Used for lines where the # character needs to be
    +#                       accepted as part of the value.
    +#
    +sub GetLine
    +    {
    +    my $self = shift;
    +
    +    my ($line, $comment);
    +
    +
    +    # Get the next line with content.
    +
    +    do
    +        {
    +        # Get the next line.
    +
    +        my $isFileLine;
    +
    +        if (scalar @virtualLines)
    +            {
    +            $line = shift @virtualLines;
    +            $isFileLine = 0;
    +            }
    +        else
    +            {
    +            $line = $lineReader->Get();
    +            $lineNumber++;
    +
    +            if (!defined $line)
    +                {  return ( );  };
    +
    +            # Condense spaces and tabs into a single space.
    +            $line =~ tr/\t /  /s;
    +            $isFileLine = 1;
    +            };
    +
    +
    +        # Split off the comment.
    +
    +        if ($line =~ /^(.*?)( ?#.*)$/)
    +            {  ($line, $comment) = ($1, $2);  }
    +        else
    +            {  $comment = undef;  };
    +
    +
    +        # Split any brace groups.
    +
    +        if ($isFileLine && $hasBraceGroups && $line =~ /[\{\}]/)
    +            {
    +            ($line, @virtualLines) = split(/([\{\}])/, $line);
    +
    +            $virtualLines[-1] .= $comment;
    +            $comment = undef;
    +            };
    +
    +
    +        # Remove whitespace.
    +
    +        $line =~ s/^ //;
    +        $line =~ s/ $//;
    +        $comment =~ s/ $//;
    +        # We want to keep the leading space on a comment.
    +        }
    +    while (!$line);
    +
    +
    +    # Process the line.
    +
    +    if ($hasBraceGroups && ($line eq '{' || $line eq '}'))
    +        {
    +        return ($line, undef, undef);
    +        };
    +
    +
    +    if ($line =~ /^([a-z0-9\ \'\/\.\-]+?) ?: ?(.*)$/i) # [CFChars]
    +        {
    +        my ($keyword, $value) = ($1, $2);
    +        return (lc($keyword), $value, $comment);
    +        }
    +
    +    else
    +        {
    +        return (undef, $line, $comment);
    +        };
    +    };
    +
    +
    +#
    +#   Function: LineNumber
    +#
    +#   Returns the line number for the line last returned by <GetLine()>.
    +#
    +sub LineNumber
    +    {  return $lineNumber;  };
    +
    +
    +
    +###############################################################################
    +# Group: Error Functions
    +
    +
    +#
    +#   Function: AddError
    +#
    +#   Stores an error for the current configuration file.  Will be attached to the last line read by <GetLine()>.
    +#
    +#   Parameters:
    +#
    +#       message - The error message.
    +#       lineNumber - The line number to use.  If not specified, it will use the line number from the last call to <GetLine()>.
    +#
    +sub AddError #(message, lineNumber)
    +    {
    +    my ($self, $message, $messageLineNumber) = @_;
    +
    +    if (!defined $messageLineNumber)
    +        {  $messageLineNumber = $lineNumber;  };
    +
    +    push @errors, $messageLineNumber, $message;
    +    };
    +
    +
    +#
    +#   Function: ErrorCount
    +#
    +#   Returns how many errors the configuration file has.
    +#
    +sub ErrorCount
    +    {
    +    return (scalar @errors) / 2;
    +    };
    +
    +
    +#
    +#   Function: PrintErrorsAndAnnotateFile
    +#
    +#   Prints the errors to STDERR in the standard GNU format and annotates the configuration file with them.  It does *not* end
    +#   execution.  <Close()> *must* be called before this function.
    +#
    +sub PrintErrorsAndAnnotateFile
    +    {
    +    my ($self) = @_;
    +
    +    if (scalar @errors)
    +        {
    +        open(CONFIG_FILEHANDLE, '<' . $file);
    +
    +        my $lineReader = NaturalDocs::LineReader->New(\*CONFIG_FILEHANDLE);
    +        my @lines = $lineReader->GetAll();
    +
    +        close(CONFIG_FILEHANDLE);
    +
    +        # We need to keep track of both the real and the original line numbers.  The original line numbers are for matching errors in
    +        # the errors array, and don't include any comment lines added or deleted.  Line number is the current line number including
    +        # those comment lines for sending to the display.
    +        my $lineNumber = 1;
    +        my $originalLineNumber = 1;
    +
    +        open(CONFIG_FILEHANDLE, '>' . $file);
    +
    +        # We don't want to keep the old error header, if present.
    +        if ($lines[0] =~ /^\# There (?:is an error|are \d+ errors) in this file\./)
    +            {
    +            shift @lines;
    +            $originalLineNumber++;
    +
    +            # We want to drop the blank line after it as well.
    +            if ($lines[0] eq "\n")
    +                {
    +                shift @lines;
    +                $originalLineNumber++;
    +                };
    +            };
    +
    +        if ($self->ErrorCount() == 1)
    +            {
    +            print CONFIG_FILEHANDLE
    +            "# There is an error in this file.  Search for ERROR to find it.\n\n";
    +            }
    +        else
    +            {
    +            print CONFIG_FILEHANDLE
    +            "# There are " . $self->ErrorCount() . " errors in this file.  Search for ERROR to find them.\n\n";
    +            };
    +
    +        $lineNumber += 2;
    +
    +
    +        foreach my $line (@lines)
    +            {
    +            while (scalar @errors && $originalLineNumber == $errors[0])
    +                {
    +                my $errorLine = shift @errors;
    +                my $errorMessage = shift @errors;
    +
    +                print CONFIG_FILEHANDLE "# ERROR: " . $errorMessage . "\n";
    +
    +                # Use the GNU error format, which should make it easier to handle errors when Natural Docs is part of a build process.
    +                # See http://www.gnu.org/prep/standards_15.html
    +
    +                $errorMessage = lcfirst($errorMessage);
    +                $errorMessage =~ s/\.$//;
    +
    +                print STDERR 'NaturalDocs:' . $file . ':' . $lineNumber . ': ' . $errorMessage . "\n";
    +
    +                $lineNumber++;
    +                };
    +
    +            # We want to remove error lines from previous runs.
    +            if (substr($line, 0, 9) ne '# ERROR: ')
    +                {
    +                print CONFIG_FILEHANDLE $line;
    +                $lineNumber++;
    +                };
    +
    +            $originalLineNumber++;
    +            };
    +
    +        # Clean up any remaining errors.
    +        while (scalar @errors)
    +            {
    +            my $errorLine = shift @errors;
    +            my $errorMessage = shift @errors;
    +
    +            print CONFIG_FILEHANDLE "# ERROR: " . $errorMessage . "\n";
    +
    +            # Use the GNU error format, which should make it easier to handle errors when Natural Docs is part of a build process.
    +            # See http://www.gnu.org/prep/standards_15.html
    +
    +            $errorMessage = lcfirst($errorMessage);
    +            $errorMessage =~ s/\.$//;
    +
    +            print STDERR 'NaturalDocs:' . $file . ':' . $lineNumber . ': ' . $errorMessage . "\n";
    +            };
    +
    +        close(CONFIG_FILEHANDLE);
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Misc Functions
    +
    +
    +#
    +#   Function: HasOnlyCFChars
    +#
    +#   Returns whether the passed string contains only <CFChars>.
    +#
    +sub HasOnlyCFChars #(string)
    +    {
    +    my ($self, $string) = @_;
    +    return ($string =~ /^[a-z0-9\ \.\-\/\']*$/i);  # [CFChars]
    +    };
    +
    +
    +#
    +#   Function: CFCharNames
    +#
    +#   Returns a plain-english list of <CFChars> which can be embedded in a sentence.  For example, "You can only use
    +#   [CFCharsList()] in the name.
    +#
    +sub CFCharNames
    +    {
    +    # [CFChars]
    +    return 'letters, numbers, spaces, periods, dashes, slashes, and apostrophes';
    +    };
    +
    +
    +#
    +#   Function: Obscure
    +#
    +#   Obscures the passed text so that it is not user editable and returns it.  The encoding method is not secure; it is just designed
    +#   to be fast and to discourage user editing.
    +#
    +sub Obscure #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    # ` is specifically chosen to encode to space because of its rarity.  We don't want a trailing one to get cut off before decoding.
    +    $text =~ tr{a-zA-Z0-9\ \\\/\.\:\_\-\`}
    +                    {pY9fGc\`R8lAoE\\uIdH6tN\/7sQjKx0B5mW\.vZ41PyFg\:CrLaO\_eUi2DhT\-nSqJkXb3MwVz\ };
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: Unobscure
    +#
    +#   Restores text encoded with <Obscure()> and returns it.
    +#
    +sub Unobscure #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ tr{pY9fGc\`R8lAoE\\uIdH6tN\/7sQjKx0B5mW\.vZ41PyFg\:CrLaO\_eUi2DhT\-nSqJkXb3MwVz\ }
    +                    {a-zA-Z0-9\ \\\/\.\:\_\-\`};
    +
    +    return $text;
    +    };
    +
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Constants.pm b/vendor/naturaldocs/Modules/NaturalDocs/Constants.pm
    new file mode 100644
    index 000000000..66d934c29
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Constants.pm
    @@ -0,0 +1,166 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Constants
    +#
    +###############################################################################
    +#
    +#   Constants that are used throughout the script.  All are exported by default.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Constants;
    +
    +use vars qw(@EXPORT @ISA);
    +require Exporter;
    +@ISA = qw(Exporter);
    +
    +@EXPORT = ('MENU_TITLE', 'MENU_SUBTITLE', 'MENU_FILE', 'MENU_GROUP', 'MENU_TEXT', 'MENU_LINK', 'MENU_FOOTER',
    +                   'MENU_INDEX', 'MENU_FORMAT', 'MENU_ENDOFORIGINAL', 'MENU_DATA',
    +
    +                   'MENU_FILE_NOAUTOTITLE', 'MENU_GROUP_UPDATETITLES', 'MENU_GROUP_UPDATESTRUCTURE',
    +                   'MENU_GROUP_UPDATEORDER', 'MENU_GROUP_HASENDOFORIGINAL',
    +                   'MENU_GROUP_UNSORTED', 'MENU_GROUP_FILESSORTED',
    +                   'MENU_GROUP_FILESANDGROUPSSORTED', 'MENU_GROUP_EVERYTHINGSORTED',
    +                   'MENU_GROUP_ISINDEXGROUP',
    +
    +                   'FILE_NEW', 'FILE_CHANGED', 'FILE_SAME', 'FILE_DOESNTEXIST');
    +
    +#
    +#   Topic: Assumptions
    +#
    +#   - No constant here will ever be zero.
    +#   - All constants are exported by default.
    +#
    +
    +
    +###############################################################################
    +# Group: Virtual Types
    +# These are only groups of constants, but should be treated like typedefs or enums.  Each one represents a distinct type and
    +# their values should only be one of their constants or undef.
    +
    +
    +#
    +#   Constants: MenuEntryType
    +#
    +#   The types of entries that can appear in the menu.
    +#
    +#       MENU_TITLE         - The title of the menu.
    +#       MENU_SUBTITLE   - The sub-title of the menu.
    +#       MENU_FILE           - A source file, relative to the source directory.
    +#       MENU_GROUP       - A group.
    +#       MENU_TEXT          - Arbitrary text.
    +#       MENU_LINK           - A web link.
    +#       MENU_FOOTER      - Footer text.
    +#       MENU_INDEX        - An index.
    +#       MENU_FORMAT     - The version of Natural Docs the menu file was generated with.
    +#       MENU_ENDOFORIGINAL - A dummy entry that marks where the original group content ends.  This is used when automatically
    +#                                           changing the groups so that the alphabetization or lack thereof can be detected without being
    +#                                           affected by new entries tacked on to the end.
    +#       MENU_DATA - Data not meant for user editing.
    +#
    +#   Dependency:
    +#
    +#       <PreviousMenuState.nd> depends on these values all being able to fit into a UInt8, i.e. <= 255.
    +#
    +use constant MENU_TITLE => 1;
    +use constant MENU_SUBTITLE => 2;
    +use constant MENU_FILE => 3;
    +use constant MENU_GROUP => 4;
    +use constant MENU_TEXT => 5;
    +use constant MENU_LINK => 6;
    +use constant MENU_FOOTER => 7;
    +use constant MENU_INDEX => 8;
    +use constant MENU_FORMAT => 9;
    +use constant MENU_ENDOFORIGINAL => 10;
    +use constant MENU_DATA => 11;
    +
    +
    +#
    +#   Constants: FileStatus
    +#
    +#   What happened to a file since Natural Docs' last execution.
    +#
    +#       FILE_NEW                - The file has been added since the last run.
    +#       FILE_CHANGED        - The file has been modified since the last run.
    +#       FILE_SAME               - The file hasn't been modified since the last run.
    +#       FILE_DOESNTEXIST  - The file doesn't exist, or was deleted.
    +#
    +use constant FILE_NEW => 1;
    +use constant FILE_CHANGED => 2;
    +use constant FILE_SAME => 3;
    +use constant FILE_DOESNTEXIST => 4;
    +
    +
    +
    +###############################################################################
    +# Group: Flags
    +# These constants can be combined with each other.
    +
    +
    +#
    +#   Constants: Menu Entry Flags
    +#
    +#   The various flags that can apply to a menu entry.  You cannot mix flags of different types, since they may overlap.
    +#
    +#   File Flags:
    +#
    +#       MENU_FILE_NOAUTOTITLE - Whether the file is auto-titled or not.
    +#
    +#   Group Flags:
    +#
    +#       MENU_GROUP_UPDATETITLES - The group should have its auto-titles regenerated.
    +#       MENU_GROUP_UPDATESTRUCTURE - The group should be checked for structural changes, such as being removed or being
    +#                                                             split into subgroups.
    +#       MENU_GROUP_UPDATEORDER - The group should be resorted.
    +#
    +#       MENU_GROUP_HASENDOFORIGINAL - Whether the group contains a dummy <MENU_ENDOFORIGINAL> entry.
    +#       MENU_GROUP_ISINDEXGROUP - Whether the group is used primarily for <MENU_INDEX> entries.  <MENU_TEXT> entries
    +#                                                       are tolerated.
    +#
    +#       MENU_GROUP_UNSORTED - The group's contents are not sorted.
    +#       MENU_GROUP_FILESSORTED - The group's files are sorted alphabetically.
    +#       MENU_GROUP_FILESANDGROUPSSORTED - The group's files and sub-groups are sorted alphabetically.
    +#       MENU_GROUP_EVERYTHINGSORTED - All entries in the group are sorted alphabetically.
    +#
    +use constant MENU_FILE_NOAUTOTITLE => 0x0001;
    +
    +use constant MENU_GROUP_UPDATETITLES => 0x0001;
    +use constant MENU_GROUP_UPDATESTRUCTURE => 0x0002;
    +use constant MENU_GROUP_UPDATEORDER => 0x0004;
    +use constant MENU_GROUP_HASENDOFORIGINAL => 0x0008;
    +
    +# This could really be a two-bit field instead of four flags, but it's not worth the effort since it's only used internally.
    +use constant MENU_GROUP_UNSORTED => 0x0010;
    +use constant MENU_GROUP_FILESSORTED => 0x0020;
    +use constant MENU_GROUP_FILESANDGROUPSSORTED => 0x0040;
    +use constant MENU_GROUP_EVERYTHINGSORTED => 0x0080;
    +
    +use constant MENU_GROUP_ISINDEXGROUP => 0x0100;
    +
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: IsClassHierarchyReference
    +#   Returns whether the passed <ReferenceType> belongs to <NaturalDocs::ClassHierarchy>.
    +#
    +sub IsClassHierarchyReference #(reference)
    +    {
    +    my ($self, $reference) = @_;
    +    return ($reference == ::REFERENCE_CH_CLASS() || $reference == ::REFERENCE_CH_PARENT());
    +    };
    +
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/DefineMembers.pm b/vendor/naturaldocs/Modules/NaturalDocs/DefineMembers.pm
    new file mode 100644
    index 000000000..8e04d3ddd
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/DefineMembers.pm
    @@ -0,0 +1,101 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::DefineMembers
    +#
    +###############################################################################
    +#
    +#   A custom Perl pragma to define member constants and accessors for use in Natural Docs objects while supporting inheritance.
    +#
    +#   Each member will be defined as a numeric constant which should be used as that variable's index into the object arrayref.
    +#   They will be assigned sequentially from zero, and take into account any members defined this way in parent classes.  Note
    +#   that you can *not* use multiple inheritance with this method.
    +#
    +#   If a parameter ends in parenthesis, it will be generated as an accessor for the previous member.  If it also starts with "Set",
    +#   the accessor will accept a single parameter to replace the value with.  If it's followed with "duparrayref", it will assume the
    +#   parameter is either an arrayref or undef, and if the former, will duplicate it to set the value.
    +#
    +#   Example:
    +#
    +#   > package MyPackage;
    +#   >
    +#   > use NaturalDocs::DefineMembers 'VAR_A', 'VarA()', 'SetVarA()',
    +#   >                                'VAR_B', 'VarB()',
    +#   >                                'VAR_C',
    +#   >                                'VAR_D', 'VarD()', 'SetVarD() duparrayref';
    +#   >
    +#   > sub SetC #(C)
    +#   >    {
    +#   >    my ($self, $c) = @_;
    +#   >    $self->[VAR_C] = $c;
    +#   >    };
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +package NaturalDocs::DefineMembers;
    +
    +sub import #(member, member, ...)
    +    {
    +    my ($self, @parameters) = @_;
    +    my $package = caller();
    +
    +    no strict 'refs';
    +    my $parent = ${$package . '::ISA'}[0];
    +    use strict 'refs';
    +
    +    my $memberConstant = 0;
    +    my $lastMemberName;
    +
    +    if (defined $parent && $parent->can('END_OF_MEMBERS'))
    +        {  $memberConstant = $parent->END_OF_MEMBERS();  };
    +
    +    my $code = '{ package ' . $package . ";\n";
    +
    +    foreach my $parameter (@parameters)
    +        {
    +        if ($parameter =~ /^(.+)\(\) *(duparrayref)?$/i)
    +            {
    +            my ($functionName, $pragma) = ($1, lc($2));
    +
    +            if ($functionName =~ /^Set/)
    +                {
    +                if ($pragma eq 'duparrayref')
    +                    {
    +                    $code .=
    +                    'sub ' . $functionName . '
    +                        {
    +                        if (defined $_[1])
    +                            {  $_[0]->[' . $lastMemberName . '] = [ @{$_[1]} ];  }
    +                        else
    +                            {  $_[0]->[' . $lastMemberName . '] = undef;  };
    +                        };' . "\n";
    +                    }
    +                else
    +                    {
    +                    $code .= 'sub ' . $functionName . ' { $_[0]->[' . $lastMemberName . '] = $_[1];  };' . "\n";
    +                    };
    +                }
    +            else
    +                {
    +                $code .= 'sub ' . $functionName . ' { return $_[0]->[' . $lastMemberName . '];  };' . "\n";
    +                };
    +            }
    +        else
    +            {
    +            $code .= 'use constant ' . $parameter . ' => ' . $memberConstant . ";\n";
    +            $memberConstant++;
    +            $lastMemberName = $parameter;
    +            };
    +        };
    +
    +    $code .= 'use constant END_OF_MEMBERS => ' . $memberConstant . ";\n";
    +    $code .= '};';
    +
    +    eval $code;
    +    };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Error.pm b/vendor/naturaldocs/Modules/NaturalDocs/Error.pm
    new file mode 100644
    index 000000000..5b300607b
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Error.pm
    @@ -0,0 +1,306 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Error
    +#
    +###############################################################################
    +#
    +#   Manages all aspects of error handling in Natural Docs.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +$SIG{'__DIE__'} = \&NaturalDocs::Error::CatchDeath;
    +
    +
    +package NaturalDocs::Error;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   handle: FH_CRASHREPORT
    +#   The filehandle used for generating crash reports.
    +#
    +
    +
    +#
    +#   var: stackTrace
    +#   The stack trace generated by <CatchDeath()>.
    +#
    +my $stackTrace;
    +
    +
    +#
    +#   var: softDeath
    +#   Whether the program exited using <SoftDeath()>.
    +#
    +my $softDeath;
    +
    +
    +#
    +#   var: currentAction
    +#   What Natural Docs was doing when it crashed.  This stores strings generated by functions like <OnStartParsing()>.
    +#
    +my $currentAction;
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: SoftDeath
    +#
    +#   Generates a "soft" death, which means the program exits like with Perl's die(), but no crash report will be generated.
    +#
    +#   Parameter:
    +#
    +#       message - The error message to die with.
    +#
    +sub SoftDeath #(message)
    +    {
    +    my ($self, $message) = @_;
    +
    +    $softDeath = 1;
    +    if ($message !~ /\n$/)
    +        {  $message .= "\n";  };
    +
    +    die $message;
    +    };
    +
    +
    +#
    +#   Function: OnStartParsing
    +#
    +#   Called whenever <NaturalDocs::Parser> starts parsing a source file.
    +#
    +sub OnStartParsing #(FileName file)
    +    {
    +    my ($self, $file) = @_;
    +    $currentAction = 'Parsing ' . $file;
    +    };
    +
    +
    +#
    +#   Function: OnEndParsing
    +#
    +#   Called whenever <NaturalDocs::Parser> is done parsing a source file.
    +#
    +sub OnEndParsing #(FileName file)
    +    {
    +    my ($self, $file) = @_;
    +    $currentAction = undef;
    +    };
    +
    +
    +#
    +#   Function: OnStartBuilding
    +#
    +#   Called whenever <NaturalDocs::Builder> starts building a source file.
    +#
    +sub OnStartBuilding #(FileName file)
    +    {
    +    my ($self, $file) = @_;
    +    $currentAction = 'Building ' . $file;
    +    };
    +
    +
    +#
    +#   Function: OnEndBuilding
    +#
    +#   Called whenever <NaturalDocs::Builder> is done building a source file.
    +#
    +sub OnEndBuilding #(FileName file)
    +    {
    +    my ($self, $file) = @_;
    +    $currentAction = undef;
    +    };
    +
    +
    +#
    +#   Function: HandleDeath
    +#
    +#   Should be called whenever Natural Docs dies out of execution.
    +#
    +sub HandleDeath
    +    {
    +    my $self = shift;
    +
    +    my $reason = $::EVAL_ERROR;
    +    $reason =~ s/[\n\r]+$//;
    +
    +    my $errorMessage =
    +         "\n"
    +         . "Natural Docs encountered the following error and was stopped:\n"
    +         . "\n"
    +         . "   " . $reason . "\n"
    +         . "\n"
    +
    +         . "You can get help at the following web site:\n"
    +         . "\n"
    +         . "   " . NaturalDocs::Settings->AppURL() . "\n"
    +         . "\n";
    +
    +    if (!$softDeath)
    +        {
    +        my $crashReport = $self->GenerateCrashReport();
    +
    +        if ($crashReport)
    +            {
    +            $errorMessage .=
    +             "If sending an error report, please include the information found in the\n"
    +             . "following file:\n"
    +             . "\n"
    +             . "   " . $crashReport . "\n"
    +             . "\n";
    +            }
    +        else
    +            {
    +            $errorMessage .=
    +             "If sending an error report, please include the following information:\n"
    +             . "\n"
    +             . "   Natural Docs version: " . NaturalDocs::Settings->TextAppVersion() . "\n"
    +             . "   Perl version: " . $self->PerlVersion() . " on " . $::OSNAME . "\n"
    +             . "\n";
    +             };
    +        };
    +
    +    die $errorMessage;
    +    };
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: PerlVersion
    +#   Returns the current Perl version as a string.
    +#
    +sub PerlVersion
    +    {
    +    my $self = shift;
    +
    +    my $perlVersion;
    +
    +    if ($^V)
    +        {  $perlVersion = sprintf('%vd', $^V);  }
    +    if (!$perlVersion || substr($perlVersion, 0, 1) eq '%')
    +        {  $perlVersion = $];  };
    +
    +    return $perlVersion;
    +    };
    +
    +
    +#
    +#   Function: GenerateCrashReport
    +#
    +#   Generates a report and returns the <FileName> it's located at.  Returns undef if it could not generate one.
    +#
    +sub GenerateCrashReport
    +    {
    +    my $self = shift;
    +
    +    my $errorMessage = $::EVAL_ERROR;
    +    $errorMessage =~ s/[\r\n]+$//;
    +
    +    my $reportDirectory = NaturalDocs::Settings->ProjectDirectory();
    +
    +    if (!$reportDirectory || !-d $reportDirectory)
    +        {  return undef;  };
    +
    +    my $file = NaturalDocs::File->JoinPaths($reportDirectory, 'LastCrash.txt');
    +
    +    open(FH_CRASHREPORT, '>' . $file) or return undef;
    +
    +    print FH_CRASHREPORT
    +    'Crash Message:' . "\n\n"
    +    . '   ' . $errorMessage . "\n\n";
    +
    +    if ($currentAction)
    +        {
    +        print FH_CRASHREPORT
    +        'Current Action:' . "\n\n"
    +        . '   ' . $currentAction . "\n\n";
    +        };
    +
    +    print FH_CRASHREPORT
    +    'Natural Docs version ' . NaturalDocs::Settings->TextAppVersion() . "\n"
    +    . 'Perl version ' . $self->PerlVersion . ' on ' . $::OSNAME . "\n\n"
    +    . 'Command Line:' . "\n\n"
    +    . '   ' . join(' ', @ARGV) . "\n\n";
    +
    +    if ($stackTrace)
    +        {
    +        print FH_CRASHREPORT
    +        'Stack Trace:' . "\n\n"
    +        . $stackTrace;
    +        }
    +    else
    +        {
    +        print FH_CRASHREPORT
    +        'Stack Trace not available.' . "\n\n";
    +        };
    +
    +    close(FH_CRASHREPORT);
    +    return $file;
    +    };
    +
    +
    +###############################################################################
    +# Group: Signal Handlers
    +
    +
    +#
    +#   Function: CatchDeath
    +#
    +#   Catches Perl die calls.
    +#
    +#   *IMPORTANT:* This function is a signal handler and should not be called manually.  Also, because of this, it does not have
    +#   a $self parameter.
    +#
    +#   Parameters:
    +#
    +#       message - The error message to die with.
    +#
    +sub CatchDeath #(message)
    +    {
    +    # No $self because it's a signal handler.
    +    my $message = shift;
    +
    +    if (!$NaturalDocs::Error::softDeath)
    +        {
    +        my $i = 0;
    +        my ($lastPackage, $lastFile, $lastLine, $lastFunction);
    +
    +        while (my ($package, $file, $line, $function) = caller($i))
    +            {
    +            if ($i != 0)
    +                {  $stackTrace .= ', called from' . "\n";  };
    +
    +            $stackTrace .= '   ' . $function;
    +
    +            if (defined $lastLine)
    +                {
    +                $stackTrace .= ', line ' . $lastLine;
    +
    +                if ($function !~ /^NaturalDocs::/)
    +                    {  $stackTrace .= ' of ' . $lastFile;  };
    +                };
    +
    +            ($lastPackage, $lastFile, $lastLine, $lastFunction) = ($package, $file, $line, $function);
    +            $i++;
    +            };
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/File.pm b/vendor/naturaldocs/Modules/NaturalDocs/File.pm
    new file mode 100644
    index 000000000..9a13c99a2
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/File.pm
    @@ -0,0 +1,541 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::File
    +#
    +###############################################################################
    +#
    +#   A package to manage file access across platforms.  Incorporates functions from various standard File:: packages, but more
    +#   importantly, works around the glorious suckage present in File::Spec, at least in version 0.82 and earlier.  Read the "Why oh
    +#   why?" sections for why this package was necessary.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - The package doesn't depend on any other Natural Docs packages and is ready to use immediately.
    +#
    +#       - All functions except <CanonizePath()> assume that all parameters are canonized.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use File::Spec ();
    +use File::Path ();
    +use File::Copy ();
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::File;
    +
    +
    +#
    +#   Function: CheckCompatibility
    +#
    +#   Checks if the standard packages required by this one are up to snuff and dies if they aren't.  This is done because I can't
    +#   tell which versions of File::Spec have splitpath just by the version numbers.
    +#
    +sub CheckCompatibility
    +    {
    +    my ($self) = @_;
    +
    +    eval {
    +        File::Spec->splitpath('');
    +    };
    +
    +    if ($@)
    +        {
    +        NaturalDocs::Error->SoftDeath("Natural Docs requires a newer version of File::Spec than you have.  "
    +                                                    . "You must either upgrade it or upgrade Perl.");
    +        };
    +    };
    +
    +
    +###############################################################################
    +# Group: Path String Functions
    +
    +
    +#
    +#   Function: CanonizePath
    +#
    +#   Takes a path and returns a logically simplified version of it.
    +#
    +#   Why oh why?:
    +#
    +#       Because File::Spec->canonpath doesn't strip quotes on Windows.  So if you pass in "a b\c" or "a b"\c, they still end up as
    +#       different strings even though they're logically the same.
    +#
    +#       It also doesn't remove things like "..", so "a/b/../c" doesn't simplify to "a/c" like it should.
    +#
    +sub CanonizePath #(path)
    +    {
    +    my ($self, $path) = @_;
    +
    +    if ($::OSNAME eq 'MSWin32')
    +        {
    +        # We don't have to use a smarter algorithm for dropping quotes because they're invalid characters for actual file and
    +        # directory names.
    +        $path =~ s/\"//g;
    +        };
    +
    +    $path = File::Spec->canonpath($path);
    +
    +    # Condense a/b/../c into a/c.
    +
    +    my $upDir = File::Spec->updir();
    +    if (index($path, $upDir) != -1)
    +        {
    +        my ($volume, $directoryString, $file) = $self->SplitPath($path);
    +        my @directories = $self->SplitDirectories($directoryString);
    +
    +        my $i = 1;
    +        while ($i < scalar @directories)
    +            {
    +            if ($i > 0 && $directories[$i] eq $upDir && $directories[$i - 1] ne $upDir)
    +                {
    +                splice(@directories, $i - 1, 2);
    +                $i--;
    +                }
    +            else
    +                {  $i++;  };
    +            };
    +
    +        $directoryString = $self->JoinDirectories(@directories);
    +        $path = $self->JoinPath($volume, $directoryString, $file);
    +        };
    +
    +    return $path;
    +    };
    +
    +
    +#
    +#   Function: PathIsAbsolute
    +#
    +#   Returns whether the passed path is absolute.
    +#
    +sub PathIsAbsolute #(path)
    +    {
    +    my ($self, $path) = @_;
    +    return File::Spec->file_name_is_absolute($path);
    +    };
    +
    +
    +#
    +#   Function: JoinPath
    +#
    +#   Creates a path from its elements.
    +#
    +#   Parameters:
    +#
    +#       volume - The volume, such as the drive letter on Windows.  Undef if none.
    +#       dirString - The directory string.  Create with <JoinDirectories()> if necessary.
    +#       file - The file name, or undef if none.
    +#
    +#   Returns:
    +#
    +#       The joined path.
    +#
    +sub JoinPath #(volume, dirString, $file)
    +    {
    +    my ($self, $volume, $dirString, $file) = @_;
    +    return File::Spec->catpath($volume, $dirString, $file);
    +    };
    +
    +
    +#
    +#   Function: JoinPaths
    +#
    +#   Joins two paths.
    +#
    +#   Parameters:
    +#
    +#       basePath       - May be a relative path, an absolute path, or undef.
    +#       extraPath      - May be a relative path, a file, a relative path and file together, or undef.
    +#       noFileInExtra - Set this to true if extraPath is a relative path only, and doesn't have a file.
    +#
    +#   Returns:
    +#
    +#       The joined path.
    +#
    +#   Why oh why?:
    +#
    +#       Because nothing in File::Spec will simply slap two paths together.  They have to be split up for catpath/file, and rel2abs
    +#       requires the base to be absolute.
    +#
    +sub JoinPaths #(basePath, extraPath, noFileInExtra)
    +    {
    +    my ($self, $basePath, $extraPath, $noFileInExtra) = @_;
    +
    +    # If both are undef, it will return undef, which is what we want.
    +    if (!defined $basePath)
    +        {  return $extraPath;  }
    +    elsif (!defined $extraPath)
    +        {  return $basePath;  };
    +
    +    my ($baseVolume, $baseDirString, $baseFile) = File::Spec->splitpath($basePath, 1);
    +    my ($extraVolume, $extraDirString, $extraFile) = File::Spec->splitpath($extraPath, $noFileInExtra);
    +
    +    my @baseDirectories = $self->SplitDirectories($baseDirString);
    +    my @extraDirectories = $self->SplitDirectories($extraDirString);
    +
    +    my $fullDirString = $self->JoinDirectories(@baseDirectories, @extraDirectories);
    +
    +    my $fullPath = File::Spec->catpath($baseVolume, $fullDirString, $extraFile);
    +
    +    return $self->CanonizePath($fullPath);
    +    };
    +
    +
    +#
    +#   Function: SplitPath
    +#
    +#   Takes a path and returns its elements.
    +#
    +#   Parameters:
    +#
    +#       path - The path to split.
    +#       noFile - Set to true if the path doesn't have a file at the end.
    +#
    +#   Returns:
    +#
    +#       The array ( volume, directoryString, file ).  If any don't apply, they will be undef.  Use <SplitDirectories()> to split the
    +#       directory string if desired.
    +#
    +#   Why oh Why?:
    +#
    +#       Because File::Spec->splitpath may leave a trailing slash/backslash/whatever on the directory string, which makes
    +#       it a bit hard to match it with results from File::Spec->catdir.
    +#
    +sub SplitPath #(path, noFile)
    +    {
    +    my ($self, $path, $noFile) = @_;
    +
    +    my @segments = File::Spec->splitpath($path, $noFile);
    +
    +    if (!length $segments[0])
    +        {  $segments[0] = undef;  };
    +    if (!length $segments[2])
    +        {  $segments[2] = undef;  };
    +
    +    $segments[1] = File::Spec->catdir( File::Spec->splitdir($segments[1]) );
    +
    +    return @segments;
    +    };
    +
    +
    +#
    +#   Function: JoinDirectories
    +#
    +#   Creates a directory string from an array of directory names.
    +#
    +#   Parameters:
    +#
    +#       directory - A directory name.  There may be as many of these as desired.
    +#
    +sub JoinDirectories #(directory, directory, ...)
    +    {
    +    my ($self, @directories) = @_;
    +    return File::Spec->catdir(@directories);
    +    };
    +
    +
    +#
    +#   Function: SplitDirectories
    +#
    +#   Takes a string of directories and returns an array of its elements.
    +#
    +#   Why oh why?:
    +#
    +#       Because File::Spec->splitdir might leave an empty element at the end of the array, which screws up both joining in
    +#       <ConvertToURL> and navigation in <MakeRelativePath>.
    +#
    +sub SplitDirectories #(directoryString)
    +    {
    +    my ($self, $directoryString) = @_;
    +
    +    my @directories = File::Spec->splitdir($directoryString);
    +
    +    if (!length $directories[-1])
    +        {  pop @directories;  };
    +
    +    return @directories;
    +    };
    +
    +
    +#
    +#   Function: MakeRelativePath
    +#
    +#   Takes two paths and returns a relative path between them.
    +#
    +#   Parameters:
    +#
    +#       basePath    - The starting path.  May be relative or absolute, so long as the target path is as well.
    +#       targetPath  - The target path.  May be relative or absolute, so long as the base path is as well.
    +#
    +#       If both paths are relative, they are assumed to be relative to the same base.
    +#
    +#   Returns:
    +#
    +#       The target path relative to base.
    +#
    +#   Why oh why?:
    +#
    +#       First, there's nothing that gives a relative path between two relative paths.
    +#
    +#       Second, if target and base are absolute but on different volumes, File::Spec->abs2rel creates a totally non-functional
    +#       relative path.  It should return the target as is, since there is no relative path.
    +#
    +#       Third, File::Spec->abs2rel between absolute paths on the same volume, at least on Windows, leaves the drive letter
    +#       on.  So abs2rel('a:\b\c\d', 'a:\b') returns 'a:c\d' instead of the expected 'c\d'.  That makes no sense whatsoever.  It's
    +#       not like it was designed to handle only directory names, either; the documentation says 'path' and the code seems to
    +#       explicitly handle it.  There's just an 'unless' in there that tacks on the volume, defeating the purpose of a *relative* path
    +#       and making the function worthless.
    +#
    +sub MakeRelativePath #(basePath, targetPath)
    +    {
    +    my ($self, $basePath, $targetPath) = @_;
    +
    +    my ($baseVolume, $baseDirString, $baseFile) = $self->SplitPath($basePath, 1);
    +    my ($targetVolume, $targetDirString, $targetFile) = $self->SplitPath($targetPath);
    +
    +    # If the volumes are different, there is no possible relative path.
    +    if ($targetVolume ne $baseVolume)
    +        {  return $targetPath;  };
    +
    +    my @baseDirectories = $self->SplitDirectories($baseDirString);
    +    my @targetDirectories = $self->SplitDirectories($targetDirString);
    +
    +    # Skip the parts of the path that are the same.
    +    while (scalar @baseDirectories && @targetDirectories && $baseDirectories[0] eq $targetDirectories[0])
    +        {
    +        shift @baseDirectories;
    +        shift @targetDirectories;
    +        };
    +
    +    # Back out of the base path until it reaches where they were similar.
    +    for (my $i = 0; $i < scalar @baseDirectories; $i++)
    +        {
    +        unshift @targetDirectories, File::Spec->updir();
    +        };
    +
    +    $targetDirString = $self->JoinDirectories(@targetDirectories);
    +
    +    return File::Spec->catpath(undef, $targetDirString, $targetFile);
    +    };
    +
    +
    +#
    +#   Function: IsSubPathOf
    +#
    +#   Returns whether the path is a descendant of another path.
    +#
    +#   Parameters:
    +#
    +#       base - The base path to test against.
    +#       path - The possible subpath to test.
    +#
    +#   Returns:
    +#
    +#       Whether path is a descendant of base.
    +#
    +sub IsSubPathOf #(base, path)
    +    {
    +    my ($self, $base, $path) = @_;
    +
    +    # This is a quick test that should find a false quickly.
    +    if ($base eq substr($path, 0, length($base)))
    +        {
    +        # This doesn't guarantee true, because it could be "C:\A B" and "C:\A B C\File".  So we test for it by seeing if the last
    +        # directory in base is the same as the equivalent directory in path.
    +
    +        my ($baseVolume, $baseDirString, $baseFile) = NaturalDocs::File->SplitPath($base, 1);
    +        my @baseDirectories = NaturalDocs::File->SplitDirectories($baseDirString);
    +
    +        my ($pathVolume, $pathDirString, $pathFile) = NaturalDocs::File->SplitPath($path);
    +        my @pathDirectories = NaturalDocs::File->SplitDirectories($pathDirString);
    +
    +        return ( $baseDirectories[-1] eq $pathDirectories[ scalar @baseDirectories - 1 ] );
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: ConvertToURL
    +#
    +#   Takes a relative path and converts it from the native format to a relative URL.  Note that it _doesn't_ convert special characters
    +#   to amp chars.
    +#
    +sub ConvertToURL #(path)
    +    {
    +    my ($self, $path) = @_;
    +
    +    my ($pathVolume, $pathDirString, $pathFile) = $self->SplitPath($path);
    +    my @pathDirectories = $self->SplitDirectories($pathDirString);
    +
    +    my $i = 0;
    +    while ($i < scalar @pathDirectories && $pathDirectories[$i] eq File::Spec->updir())
    +        {
    +        $pathDirectories[$i] = '..';
    +        $i++;
    +        };
    +
    +    return join('/', @pathDirectories, $pathFile);
    +    };
    +
    +
    +#
    +#   Function: NoUpwards
    +#
    +#   Takes an array of directory entries and returns one without all the entries that refer to the parent directory, such as '.' and '..'.
    +#
    +sub NoUpwards #(array)
    +    {
    +    my ($self, @array) = @_;
    +    return File::Spec->no_upwards(@array);
    +    };
    +
    +
    +#
    +#   Function: NoFileName
    +#
    +#   Takes a path and returns a version without the file name.  Useful for sending paths to <CreatePath()>.
    +#
    +sub NoFileName #(path)
    +    {
    +    my ($self, $path) = @_;
    +
    +    my ($pathVolume, $pathDirString, $pathFile) = File::Spec->splitpath($path);
    +
    +    return File::Spec->catpath($pathVolume, $pathDirString, undef);
    +    };
    +
    +
    +#
    +#   Function: NoExtension
    +#
    +#   Returns the path without an extension.
    +#
    +sub NoExtension #(path)
    +    {
    +    my ($self, $path) = @_;
    +
    +    my $extension = $self->ExtensionOf($path);
    +
    +    if ($extension)
    +        {  $path = substr($path, 0, length($path) - length($extension) - 1);  };
    +
    +    return $path;
    +    };
    +
    +
    +#
    +#   Function: ExtensionOf
    +#
    +#   Returns the extension of the passed path, or undef if none.
    +#
    +sub ExtensionOf #(path)
    +    {
    +    my ($self, $path) = @_;
    +
    +    my ($pathVolume, $pathDirString, $pathFile) = File::Spec->splitpath($path);
    +
    +    # We need the leading dot in the regex so files that start with a dot but don't have an extension count as extensionless files.
    +    if ($pathFile =~ /.\.([^\.]+)$/)
    +        {  return $1;  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: IsCaseSensitive
    +#
    +#   Returns whether the current platform has case-sensitive paths.
    +#
    +sub IsCaseSensitive
    +    {
    +    return !(File::Spec->case_tolerant());
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Disk Functions
    +
    +
    +#
    +#   Function: CreatePath
    +#
    +#   Creates a directory tree corresponding to the passed path, regardless of how many directories do or do not already exist.
    +#   Do _not_ include a file name in the path.  Use <NoFileName()> first if you need to.
    +#
    +sub CreatePath #(path)
    +    {
    +    my ($self, $path) = @_;
    +    File::Path::mkpath($path);
    +    };
    +
    +
    +#
    +#   Function: RemoveEmptyTree
    +#
    +#   Removes an empty directory tree.  The passed directory will be removed if it's empty, and it will keep removing its parents
    +#   until it reaches one that's not empty or a set limit.
    +#
    +#   Parameters:
    +#
    +#       path - The path to start from.  It will try to remove this directory and work it's way down.
    +#       limit - The path to stop at if it doesn't find any non-empty directories first.  This path will *not* be removed.
    +#
    +sub RemoveEmptyTree #(path, limit)
    +    {
    +    my ($self, $path, $limit) = @_;
    +
    +    my ($volume, $directoryString) = $self->SplitPath($path, 1);
    +    my @directories = $self->SplitDirectories($directoryString);
    +
    +    my $directory = $path;
    +
    +    while (-d $directory && $directory ne $limit)
    +        {
    +        opendir FH_ND_FILE, $directory;
    +        my @entries = readdir FH_ND_FILE;
    +        closedir FH_ND_FILE;
    +
    +        @entries = $self->NoUpwards(@entries);
    +
    +        if (scalar @entries || !rmdir($directory))
    +            {  last;  };
    +
    +        pop @directories;
    +        $directoryString = $self->JoinDirectories(@directories);
    +        $directory = $self->JoinPath($volume, $directoryString);
    +        };
    +    };
    +
    +
    +#
    +#   Function: Copy
    +#
    +#   Copies a file from one path to another.  If the destination file exists, it is overwritten.
    +#
    +#   Parameters:
    +#
    +#       source       - The file to copy.
    +#       destination - The destination to copy to.
    +#
    +#   Returns:
    +#
    +#       Whether it succeeded
    +#
    +sub Copy #(source, destination) => bool
    +    {
    +    my ($self, $source, $destination) = @_;
    +    return File::Copy::copy($source, $destination);
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable.pm b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable.pm
    new file mode 100644
    index 000000000..df803e4b5
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable.pm
    @@ -0,0 +1,384 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ImageReferenceTable
    +#
    +###############################################################################
    +#
    +#   A <NaturalDocs::SourceDB>-based package that manages all the image references appearing in source files.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::ImageReferenceTable::String;
    +use NaturalDocs::ImageReferenceTable::Reference;
    +
    +
    +package NaturalDocs::ImageReferenceTable;
    +
    +use base 'NaturalDocs::SourceDB::Extension';
    +
    +
    +###############################################################################
    +# Group: Information
    +
    +#
    +#   Topic: Usage
    +#
    +#       - <NaturalDocs::Project> and <NaturalDocs::SourceDB> must be initialized before this package can be used.
    +#
    +#       - Call <Register()> before using.
    +#
    +#
    +#   Topic: Programming Notes
    +#
    +#       When working on this code, remember that there are three things it has to juggle.
    +#
    +#       - The information in <NaturalDocs::SourceDB>.
    +#       - Image file references in <NaturalDocs::Project>.
    +#       - Source file rebuilding on changes.
    +#
    +#       Managing the actual image files will be handled between <NaturalDocs::Project> and the <NaturalDocs::Builder>
    +#       sub-packages.
    +#
    +#
    +#   Topic: Implementation
    +#
    +#       Managing image references is simpler than managing the references in <NaturalDocs::SymbolTable>.  In SymbolTable,
    +#       you have to worry about reference targets popping into and out of existence.  A link may go to a file that hasn't been
    +#       reparsed yet and the target may no longer exist.  We have to deal with that when we know it, which may be after the
    +#       reference's file was parsed.  Also, a new definition may appear that serves as a better interpretation of a link than its
    +#       current target, and again we may only know that after the reference's file has been parsed already.  So we have to deal
    +#       with scores and potential symbols and each symbol knowing exactly what links to it and so forth.
    +#
    +#       Not so with image references.  All possible targets (all possible image files) are known by <NaturalDocs::Project> early
    +#       on and will remain consistent throughout execution.  So because of that, we can get away with only storing reference
    +#       counts with each image and determining exactly where a reference points to as we find them.
    +#
    +#       Reference counts are stored with the image file information in <NaturalDocs::Project>.  However, it is not loaded and
    +#       saved to disk by it.  Rather, it is regenerated by this package when it loads <ImageReferenceTable.nd>.
    +#       NaturalDocs::Project only stores the last modification time (so it can add files to the build list if they've changed) and
    +#       whether it had any references at all on the last run (so it knows whether it should care if they've changed.)
    +#       ImageReferenceTable.nd stores each reference's target, width, and height.  Whether their interpretations have changed is
    +#       dealt with in the <Load()> function, again since the list of targets (image files) is constant.
    +#
    +#       The package is based on <NaturalDocs::SourceDB>, so read it's documentation for more information on how it works.
    +#
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   var: extensionID
    +#   The <ExtensionID> granted by <NaturalDocs::SourceDB>.
    +#
    +my $extensionID;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: ImageReferenceTable.nd
    +#
    +#   The data file which stores all the image references from the last run of Natural Docs.
    +#
    +#   Format:
    +#
    +#       > [Standard Binary Header]
    +#
    +#       It starts with the standard binary header from <NaturalDocs::BinaryFile>.
    +#
    +#       > [Image Reference String or undef]
    +#       > [AString16: target file]
    +#       > [UInt16: target width or 0]
    +#       > [UInt16: target height or 0]
    +#
    +#       For each <ImageReferenceString>, it's target, width, and height are stored.  The target is needed so we can tell if it
    +#       changed from the last run, and the dimensions are needed because if the target hasn't changed but the file's dimensions
    +#       have, the source files need to be rebuilt.
    +#
    +#       <ImageReferenceStrings> are encoded by <NaturalDocs::ImageReferenceTable::String>.
    +#
    +#       > [AString16: definition file or undef] ...
    +#
    +#       Then comes a series of AString16s for all the files that define the reference until it hits an undef.
    +#
    +#       This whole series is repeated for each <ImageReferenceString> until it hits an undef.
    +#
    +#	Revisions:
    +#
    +#		1.4:
    +#
    +#			- The file was added to Natural Docs.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: Register
    +#   Registers the package with <NaturalDocs::SourceDB>.
    +#
    +sub Register
    +    {
    +    my $self = shift;
    +    $extensionID = NaturalDocs::SourceDB->RegisterExtension($self, 0);
    +    };
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads the data from <ImageReferenceTable.nd>.  Returns whether it was successful.
    +#
    +sub Load # => bool
    +    {
    +    my $self = shift;
    +
    +    if (NaturalDocs::Settings->RebuildData())
    +        {  return 0;  };
    +
    +    # The file format hasn't changed since it was introduced.
    +    if (!NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('ImageReferenceTable.nd') ))
    +        {  return 0;  };
    +
    +
    +    # [Image Reference String or undef]
    +    while (my $referenceString = NaturalDocs::ImageReferenceTable::String->FromBinaryFile())
    +        {
    +        NaturalDocs::SourceDB->AddItem($extensionID, $referenceString,
    +                                                           NaturalDocs::ImageReferenceTable::Reference->New());
    +
    +        # [AString16: target file]
    +        # [UInt16: target width or 0]
    +        # [UInt16: target height or 0]
    +
    +        my $targetFile = NaturalDocs::BinaryFile->GetAString16();
    +        my $width = NaturalDocs::BinaryFile->GetUInt16();
    +        my $height = NaturalDocs::BinaryFile->GetUInt16();
    +
    +        my $newTargetFile = $self->SetReferenceTarget($referenceString);
    +        my $newWidth;
    +        my $newHeight;
    +
    +        if ($newTargetFile)
    +            {
    +            NaturalDocs::Project->AddImageFileReference($newTargetFile);
    +            ($newWidth, $newHeight) = NaturalDocs::Project->ImageFileDimensions($newTargetFile);
    +            };
    +
    +        my $rebuildDefinitions = ($newTargetFile ne $targetFile || $newWidth != $width || $newHeight != $height);
    +
    +
    +        # [AString16: definition file or undef] ...
    +        while (my $definitionFile = NaturalDocs::BinaryFile->GetAString16())
    +            {
    +            NaturalDocs::SourceDB->AddDefinition($extensionID, $referenceString, $definitionFile);
    +
    +            if ($rebuildDefinitions)
    +                {  NaturalDocs::Project->RebuildFile($definitionFile);  };
    +            };
    +        };
    +
    +
    +    NaturalDocs::BinaryFile->Close();
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves the data to <ImageReferenceTable.nd>.
    +#
    +sub Save
    +    {
    +    my $self = shift;
    +
    +    my $references = NaturalDocs::SourceDB->GetAllItemsHashRef($extensionID);
    +
    +    NaturalDocs::BinaryFile->OpenForWriting( NaturalDocs::Project->DataFile('ImageReferenceTable.nd') );
    +
    +    while (my ($referenceString, $referenceObject) = each %$references)
    +        {
    +        # [Image Reference String or undef]
    +        # [AString16: target file]
    +        # [UInt16: target width or 0]
    +        # [UInt16: target height or 0]
    +
    +        NaturalDocs::ImageReferenceTable::String->ToBinaryFile($referenceString);
    +
    +        my $target = $referenceObject->Target();
    +        my ($width, $height);
    +
    +        if ($target)
    +            {  ($width, $height) = NaturalDocs::Project->ImageFileDimensions($target);  };
    +
    +        NaturalDocs::BinaryFile->WriteAString16( $referenceObject->Target() );
    +        NaturalDocs::BinaryFile->WriteUInt16( ($width || 0) );
    +        NaturalDocs::BinaryFile->WriteUInt16( ($height || 0) );
    +
    +        # [AString16: definition file or undef] ...
    +
    +        my $definitions = $referenceObject->GetAllDefinitionsHashRef();
    +
    +        foreach my $definition (keys %$definitions)
    +            {  NaturalDocs::BinaryFile->WriteAString16($definition);  };
    +
    +        NaturalDocs::BinaryFile->WriteAString16(undef);
    +        };
    +
    +    NaturalDocs::ImageReferenceTable::String->ToBinaryFile(undef);
    +
    +    NaturalDocs::BinaryFile->Close();
    +    };
    +
    +
    +#
    +#   Function: AddReference
    +#
    +#   Adds a new image reference.
    +#
    +sub AddReference #(FileName file, string referenceText)
    +    {
    +    my ($self, $file, $referenceText) = @_;
    +
    +    my $referenceString = NaturalDocs::ImageReferenceTable::String->Make($file, $referenceText);
    +
    +    if (!NaturalDocs::SourceDB->HasItem($extensionID, $referenceString))
    +        {
    +        my $referenceObject = NaturalDocs::ImageReferenceTable::Reference->New();
    +        NaturalDocs::SourceDB->AddItem($extensionID, $referenceString, $referenceObject);
    +
    +        my $target = $self->SetReferenceTarget($referenceString);
    +        if ($target)
    +            {  NaturalDocs::Project->AddImageFileReference($target);  };
    +        };
    +
    +    NaturalDocs::SourceDB->AddDefinition($extensionID, $referenceString, $file);
    +    };
    +
    +
    +#
    +#   Function: OnDeletedDefinition
    +#
    +#   Called for each definition deleted by <NaturalDocs::SourceDB>.  This is called *after* the definition has been deleted from
    +#   the database, so don't expect to be able to read it.
    +#
    +sub OnDeletedDefinition #(ImageReferenceString referenceString, FileName file, bool wasLastDefinition)
    +    {
    +    my ($self, $referenceString, $file, $wasLastDefinition) = @_;
    +
    +    if ($wasLastDefinition)
    +        {
    +        my $referenceObject = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString);
    +        my $target = $referenceObject->Target();
    +
    +        if ($target)
    +            {  NaturalDocs::Project->DeleteImageFileReference($target);  };
    +
    +        NaturalDocs::SourceDB->DeleteItem($extensionID, $referenceString);
    +        };
    +    };
    +
    +
    +#
    +#   Function: GetReferenceTarget
    +#
    +#   Returns the image file the reference resolves to, or undef if none.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> the reference appears in.
    +#       text - The reference text.
    +#
    +sub GetReferenceTarget #(FileName sourceFile, string text) => FileName
    +    {
    +    my ($self, $sourceFile, $text) = @_;
    +
    +    my $referenceString = NaturalDocs::ImageReferenceTable::String->Make($sourceFile, $text);
    +    my $reference = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString);
    +
    +    if (!defined $reference)
    +        {  return undef;  }
    +    else
    +        {  return $reference->Target();  };
    +    };
    +
    +
    +#
    +#   Function: SetReferenceTarget
    +#
    +#   Determines the best target for the passed <ImageReferenceString> and sets it on the
    +#   <NaturalDocs::ImageReferenceTable::Reference> object.  Returns the new target <FileName>.  Does *not* add any source
    +#   files to the bulid list.
    +#
    +sub SetReferenceTarget #(ImageReferenceString referenceString) => FileName
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    my $referenceObject = NaturalDocs::SourceDB->GetItem($extensionID, $referenceString);
    +    my ($sourcePath, $text) = NaturalDocs::ImageReferenceTable::String->InformationOf($referenceString);
    +
    +
    +    # Try the path relative to the source file first.
    +
    +    my $target;
    +
    +    my $imageFile = NaturalDocs::File->JoinPaths($sourcePath, $text);
    +    my $exists = NaturalDocs::Project->ImageFileExists($imageFile);
    +
    +
    +    # Then try relative image directories.
    +
    +    if (!$exists)
    +        {
    +        my $relativeImageDirectories = NaturalDocs::Settings->RelativeImageDirectories();
    +
    +        for (my $i = 0; $i < scalar @$relativeImageDirectories && !$exists; $i++)
    +            {
    +            $imageFile = NaturalDocs::File->JoinPaths($sourcePath, $relativeImageDirectories->[$i], 1);
    +            $imageFile = NaturalDocs::File->JoinPaths($imageFile, $text);
    +
    +            $exists = NaturalDocs::Project->ImageFileExists($imageFile);
    +            };
    +        };
    +
    +
    +    # Then try absolute image directories.
    +
    +    if (!$exists)
    +        {
    +        my $imageDirectories = NaturalDocs::Settings->ImageDirectories();
    +
    +        for (my $i = 0; $i < scalar @$imageDirectories && !$exists; $i++)
    +            {
    +            $imageFile = NaturalDocs::File->JoinPaths($imageDirectories->[$i], $text);
    +            $exists = NaturalDocs::Project->ImageFileExists($imageFile);
    +            };
    +        };
    +
    +
    +    if ($exists)
    +        {  $target = NaturalDocs::Project->ImageFileCapitalization($imageFile);  };
    +    #else leave it as undef.
    +
    +    $referenceObject->SetTarget($target);
    +    return $target;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm
    new file mode 100644
    index 000000000..3477435af
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm
    @@ -0,0 +1,45 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ImageReferenceTable::Reference
    +#
    +###############################################################################
    +#
    +#   A class for references being tracked in <NaturalDocs::SourceDB>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::ImageReferenceTable::Reference;
    +
    +use base 'NaturalDocs::SourceDB::Item';
    +
    +
    +use NaturalDocs::DefineMembers 'TARGET', 'Target()', 'SetTarget()',
    +                                                 'NEEDS_REBUILD', 'NeedsRebuild()', 'SetNeedsRebuild()';
    +
    +
    +#
    +#   Variables: Members
    +#
    +#   The following constants are indexes into the object array.
    +#
    +#   TARGET - The image <FileName> this reference resolves to, or undef if none.
    +#
    +
    +
    +#
    +#   Functions: Member Functions
    +#
    +#   Target - Returns the image <FileName> this reference resolves to, or undef if none.
    +#   SetTarget - Replaces the image <FileName> this reference resolves to, or undef if none.
    +#
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/String.pm b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/String.pm
    new file mode 100644
    index 000000000..27fbf7797
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ImageReferenceTable/String.pm
    @@ -0,0 +1,111 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ImageReferenceTable::String
    +#
    +###############################################################################
    +#
    +#   A package for creating and managing <ImageReferenceStrings>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::ImageReferenceTable::String;
    +
    +
    +#
    +#   Type: ImageReferenceString
    +#
    +#   A string representing a unique image reference.  It's composed of the reference text and the directory of the source file.
    +#   The source file name itself isn't included because two files in the same directory with the same reference text will always go
    +#   to the same targets.
    +#
    +
    +
    +#
    +#   Function: Make
    +#
    +#   Converts a source <FileName> and the reference text to an <ImageReferenceString>.
    +#
    +sub Make #(FileName sourceFile, string text) => ImageReferenceString
    +    {
    +    my ($self, $sourceFile, $text) = @_;
    +
    +    my $path = NaturalDocs::File->NoFileName($sourceFile);
    +
    +    # Condense whitespace and remove any separator characters.
    +    $path =~ tr/ \t\r\n\x1C/ /s;
    +    $text =~ tr/ \t\r\n\x1C/ /s;
    +
    +    return $path . "\x1C" . $text;
    +    };
    +
    +
    +#
    +#   Function: InformationOf
    +#
    +#   Returns the information contained in the <ImageReferenceString> as the array ( path, text ).
    +#
    +sub InformationOf #(ImageReferenceString referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    return split(/\x1C/, $referenceString);
    +    };
    +
    +
    +#
    +#   Function: ToBinaryFile
    +#
    +#   Writes an <ImageReferenceString> to <NaturalDocs::BinaryFile>.  Can also encode an undef.
    +#
    +#   Format:
    +#
    +#       > [AString16: path] [AString16: reference text] ...
    +#
    +#       Undef is represented by the first AString16 being undef.
    +#
    +sub ToBinaryFile #(ImageReferenceString referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    if (defined $referenceString)
    +        {
    +        my ($path, $text) = split(/\x1C/, $referenceString);
    +
    +        NaturalDocs::BinaryFile->WriteAString16($path);
    +        NaturalDocs::BinaryFile->WriteAString16($text);
    +        }
    +    else
    +        {
    +        NaturalDocs::BinaryFile->WriteAString16(undef);
    +        };
    +    };
    +
    +
    +#
    +#   Function: FromBinaryFile
    +#
    +#   Loads an <ImageReferenceString> or undef from <NaturalDocs::BinaryFile> and returns it.
    +#
    +sub FromBinaryFile
    +    {
    +    my $self = shift;
    +
    +    my $path = NaturalDocs::BinaryFile->GetAString16();
    +
    +    if (!defined $path)
    +        {  return undef;  };
    +
    +    my $text = NaturalDocs::BinaryFile->GetAString16();
    +
    +    return $path . "\x1C" . $text;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages.pm
    new file mode 100644
    index 000000000..a85f749f4
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages.pm
    @@ -0,0 +1,1476 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Languages
    +#
    +###############################################################################
    +#
    +#   A package to manage all the programming languages Natural Docs supports.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - Prior to use, <NaturalDocs::Settings> must be initialized and <Load()> must be called.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use Text::Wrap();
    +
    +use NaturalDocs::Languages::Prototype;
    +
    +use NaturalDocs::Languages::Base;
    +use NaturalDocs::Languages::Simple;
    +use NaturalDocs::Languages::Advanced;
    +
    +use NaturalDocs::Languages::Perl;
    +use NaturalDocs::Languages::CSharp;
    +use NaturalDocs::Languages::ActionScript;
    +
    +use NaturalDocs::Languages::Ada;
    +use NaturalDocs::Languages::PLSQL;
    +use NaturalDocs::Languages::Pascal;
    +use NaturalDocs::Languages::Tcl;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   handle: FH_LANGUAGES
    +#
    +#   The file handle used for writing to <Languages.txt>.
    +#
    +
    +
    +#
    +#   hash: languages
    +#
    +#   A hash of all the defined languages.  The keys are the all-lowercase language names, and the values are
    +#   <NaturalDocs::Languages::Base>-derived objects.
    +#
    +my %languages;
    +
    +#
    +#   hash: extensions
    +#
    +#   A hash of all the defined languages' extensions.  The keys are the all-lowercase extensions, and the values are the
    +#   all-lowercase names of the languages that defined them.
    +#
    +my %extensions;
    +
    +#
    +#   hash: shebangStrings
    +#
    +#   A hash of all the defined languages' strings to search for in the shebang (#!) line.  The keys are the all-lowercase strings, and
    +#   the values are the all-lowercase names of the languages that defined them.
    +#
    +my %shebangStrings;
    +
    +#
    +#   hash: shebangFiles
    +#
    +#   A hash of all the defined languages for files where it needs to be found via shebang strings.  The keys are the file names,
    +#   and the values are language names, or undef if the file isn't supported.  These values should be filled in the first time
    +#   each file is parsed for a shebang string so that it doesn't have to be done multiple times.
    +#
    +my %shebangFiles;
    +
    +#
    +#   array: mainLanguageNames
    +#
    +#   An array of the language names that are defined in the main <Languages.txt>.
    +#
    +my @mainLanguageNames;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: Languages.txt
    +#
    +#   The configuration file that defines or overrides the language definitions for Natural Docs.  One version sits in Natural Docs'
    +#   configuration directory, and another can be in a project directory to add to or override them.
    +#
    +#   > # [comments]
    +#
    +#   Everything after a # symbol is ignored.  However, for this particular file, comments can only appear on their own lines.
    +#   They cannot appear after content on the same line.
    +#
    +#   > Format: [version]
    +#
    +#   Specifies the file format version of the file.
    +#
    +#
    +#   Sections:
    +#
    +#       > Ignore[d] Extension[s]: [extension] [extension] ...
    +#
    +#       Causes the listed file extensions to be ignored, even if they were previously defined to be part of a language.  The list is
    +#       space-separated.  ex. "Ignore Extensions: cvs txt"
    +#
    +#
    +#       > Language: [name]
    +#
    +#       Creates a new language.  Everything underneath applies to this language until the next one.  Names can use any
    +#       characters.
    +#
    +#       The languages "Text File" and "Shebang Script" have special meanings.  Text files are considered all comment and don't
    +#       have comment symbols.  Shebang scripts have their language determined by the shebang string and automatically
    +#       include files with no extension in addition to the extensions defined.
    +#
    +#       If "Text File" doesn't define ignored prefixes, a package separator, or enum value behavior, those settings will be copied
    +#       from the language with the most files in the source tree.
    +#
    +#
    +#       > Alter Language: [name]
    +#
    +#       Alters an existing language.  Everything underneath it overrides the previous settings until the next one.  Note that if a
    +#       property has an [Add/Replace] form and that property has already been defined, you have to specify whether you're adding
    +#       to or replacing the defined list.
    +#
    +#
    +#   Language Properties:
    +#
    +#       > Extension[s]: [extension] [extension] ...
    +#       > [Add/Replace] Extension[s]: ...
    +#
    +#       Defines file extensions for the language's source files.  The list is space-separated.  ex. "Extensions: c cpp".  You can use
    +#       extensions that were previously used by another language to redefine them.
    +#
    +#
    +#       > Shebang String[s]: [string] [string] ...
    +#       > [Add/Replace] Shebang String[s]: ...
    +#
    +#       Defines a list of strings that can appear in the shebang (#!) line to designate that it's part of this language.  They can
    +#       appear anywhere in the line, so "php" will work for "#!/user/bin/php4".  You can use strings that were previously used by
    +#       another language to redefine them.
    +#
    +#
    +#       > Ignore[d] Prefix[es] in Index: [prefix] [prefix] ...
    +#       > Ignore[d] [Topic Type] Prefix[es] in Index: [prefix] [prefix] ...
    +#       > [Add/Replace] Ignore[d] Prefix[es] in Index: ...
    +#       > [Add/Replace] Ignore[d] [Topic Type] Prefix[es] in Index: ...
    +#
    +#       Specifies prefixes that should be ignored when sorting symbols for an index.  Can be specified in general or for a specific
    +#       <TopicType>.  The prefixes will still appear, the symbols will just be sorted as if they're not there.  For example, specifying
    +#       "ADO_" for functions will mean that "ADO_DoSomething" will appear under D instead of A.
    +#
    +#
    +#   Basic Language Support Properties:
    +#
    +#       These attributes are only available for languages with basic language support.
    +#
    +#
    +#       > Line Comment[s]: [symbol] [symbol] ...
    +#
    +#       Defines a space-separated list of symbols that are used for line comments, if any.  ex. "Line Comment: //".
    +#
    +#
    +#       > Block Comment[s]: [opening symbol] [closing symbol] [opening symbol] [closing symbol] ...
    +#
    +#       Defines a space-separated list of symbol pairs that are used for block comments, if any.  ex. "Block Comment: /* */".
    +#
    +#
    +#       > Package Separator: [symbol]
    +#
    +#       Defines the default package separator symbol, such as . or ::.  This is for presentation only and will not affect how
    +#       Natural Docs links are parsed.  The default is a dot.
    +#
    +#
    +#       > [Topic Type] Prototype Ender[s]: [symbol] [symbol] ...
    +#
    +#       When defined, Natural Docs will attempt to collect prototypes from the code following the specified <TopicType>.  It grabs
    +#       code until the first ender symbol or the next Natural Docs comment, and if it contains the topic name, it serves as its
    +#       prototype.  Use \n to specify a line break.  ex. "Function Prototype Enders: { ;", "Variable Prototype Enders: = ;".
    +#
    +#
    +#       > Line Extender: [symbol]
    +#
    +#       Defines the symbol that allows a prototype to span multiple lines if normally a line break would end it.
    +#
    +#
    +#       > Enum Values: [global|under type|under parent]
    +#
    +#       Defines how enum values are referenced.  The default is global.
    +#
    +#       global - Values are always global, referenced as 'value'.
    +#       under type - Values are under the enum type, referenced as 'package.enum.value'.
    +#       under parent - Values are under the enum's parent, referenced as 'package.value'.
    +#
    +#
    +#       > Perl Package: [perl package]
    +#
    +#       Specifies the Perl package used to fine-tune the language behavior in ways too complex to do in this file.
    +#
    +#
    +#   Full Language Support Properties:
    +#
    +#       These attributes are only available for languages with full language support.
    +#
    +#
    +#       > Full Language Support: [perl package]
    +#
    +#       Specifies the Perl package that has the parsing routines necessary for full language support.
    +#
    +#
    +#   Revisions:
    +#
    +#       1.32:
    +#
    +#           - Package Separator is now a basic language support only property.
    +#           - Added Enum Values setting.
    +#
    +#       1.3:
    +#
    +#           - The file was introduced.
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads both the master and the project version of <Languages.txt>.
    +#
    +sub Load
    +    {
    +    my $self = shift;
    +
    +    # Hashrefs where the keys are all-lowercase extensions/shebang strings, and the values are arrayrefs of the languages
    +    # that defined them, earliest first, all lowercase.
    +    my %tempExtensions;
    +    my %tempShebangStrings;
    +
    +    $self->LoadFile(1, \%tempExtensions, \%tempShebangStrings);  # Main
    +
    +    if (!exists $languages{'shebang script'})
    +        {  NaturalDocs::ConfigFile->AddError('You must define "Shebang Script" in the main languages file.');  };
    +    if (!exists $languages{'text file'})
    +        {  NaturalDocs::ConfigFile->AddError('You must define "Text File" in the main languages file.');  };
    +
    +    my $errorCount = NaturalDocs::ConfigFile->ErrorCount();
    +
    +    if ($errorCount)
    +        {
    +        NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile();
    +        NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors')
    +                                                    . ' in ' . NaturalDocs::Project->MainConfigFile('Languages.txt'));
    +        }
    +
    +
    +    $self->LoadFile(0, \%tempExtensions, \%tempShebangStrings);  # User
    +
    +    $errorCount = NaturalDocs::ConfigFile->ErrorCount();
    +
    +    if ($errorCount)
    +        {
    +        NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile();
    +        NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors')
    +                                                    . ' in ' . NaturalDocs::Project->UserConfigFile('Languages.txt'));
    +        };
    +
    +
    +    # Convert the temp hashes into the real ones.
    +
    +    while (my ($extension, $languages) = each %tempExtensions)
    +        {
    +        $extensions{$extension} = $languages->[-1];
    +        };
    +    while (my ($shebangString, $languages) = each %tempShebangStrings)
    +        {
    +        $shebangStrings{$shebangString} = $languages->[-1];
    +        };
    +    };
    +
    +
    +#
    +#   Function: LoadFile
    +#
    +#   Loads a particular version of <Languages.txt>.
    +#
    +#   Parameters:
    +#
    +#       isMain - Whether the file is the main file or not.
    +#       tempExtensions - A hashref where the keys are all-lowercase extensions, and the values are arrayrefs of the all-lowercase
    +#                                 names of the languages that defined them, earliest first.  It will be changed by this function.
    +#       tempShebangStrings - A hashref where the keys are all-lowercase shebang strings, and the values are arrayrefs of the
    +#                                        all-lowercase names of the languages that defined them, earliest first.  It will be changed by this
    +#                                        function.
    +#
    +sub LoadFile #(isMain, tempExtensions, tempShebangStrings)
    +    {
    +    my ($self, $isMain, $tempExtensions, $tempShebangStrings) = @_;
    +
    +    my ($file, $status);
    +
    +    if ($isMain)
    +        {
    +        $file = NaturalDocs::Project->MainConfigFile('Languages.txt');
    +        $status = NaturalDocs::Project->MainConfigFileStatus('Languages.txt');
    +        }
    +    else
    +        {
    +        $file = NaturalDocs::Project->UserConfigFile('Languages.txt');
    +        $status = NaturalDocs::Project->UserConfigFileStatus('Languages.txt');
    +        };
    +
    +
    +    my $version;
    +
    +    # An array of properties for the current language.  Each entry is the three consecutive values ( lineNumber, keyword, value ).
    +    my @properties;
    +
    +    if ($version = NaturalDocs::ConfigFile->Open($file))
    +        {
    +        # The format hasn't changed significantly since the file was introduced.
    +
    +        if ($status == ::FILE_CHANGED())
    +            {
    +            NaturalDocs::Project->ReparseEverything();
    +            NaturalDocs::SymbolTable->RebuildAllIndexes();  # Because the ignored prefixes could change.
    +            };
    +
    +        my ($keyword, $value, $comment);
    +
    +        while (($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine())
    +            {
    +            $value .= $comment;
    +            $value =~ s/^ //;
    +
    +            # Process previous properties.
    +            if (($keyword eq 'language' || $keyword eq 'alter language') && scalar @properties)
    +                {
    +                if ($isMain && $properties[1] eq 'language')
    +                    {  push @mainLanguageNames, $properties[2];  };
    +
    +                $self->ProcessProperties(\@properties, $version, $tempExtensions, $tempShebangStrings);
    +                @properties = ( );
    +                };
    +
    +            if ($keyword =~ /^ignored? extensions?$/)
    +                {
    +                $value =~ tr/.*//d;
    +                my @extensions = split(/ /, lc($value));
    +
    +                foreach my $extension (@extensions)
    +                    {  delete $tempExtensions->{$extension};  };
    +                }
    +            else
    +                {
    +                push @properties, NaturalDocs::ConfigFile->LineNumber(), $keyword, $value;
    +                };
    +            };
    +
    +        if (scalar @properties)
    +            {
    +            if ($isMain && $properties[1] eq 'language')
    +                {  push @mainLanguageNames, $properties[2];  };
    +
    +            $self->ProcessProperties(\@properties, $version, $tempExtensions, $tempShebangStrings);
    +            };
    +        }
    +
    +    else # couldn't open file
    +        {
    +        if ($isMain)
    +            {  die "Couldn't open languages file " . $file . "\n";  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: ProcessProperties
    +#
    +#   Processes an array of language properties from <Languages.txt>.
    +#
    +#   Parameters:
    +#
    +#       properties - An arrayref of properties where each entry is the three consecutive values ( lineNumber, keyword, value ).
    +#                         It must start with the Language or Alter Language property.
    +#       version - The <VersionInt> of the file.
    +#       tempExtensions - A hashref where the keys are all-lowercase extensions, and the values are arrayrefs of the all-lowercase
    +#                                 names of the languages that defined them, earliest first.  It will be changed by this function.
    +#       tempShebangStrings - A hashref where the keys are all-lowercase shebang strings, and the values are arrayrefs of the
    +#                                        all-lowercase names of the languages that defined them, earliest first.  It will be changed by this
    +#                                        function.
    +#
    +sub ProcessProperties #(properties, version, tempExtensions, tempShebangStrings)
    +    {
    +    my ($self, $properties, $version, $tempExtensions, $tempShebangStrings) = @_;
    +
    +
    +    # First validate the name and check whether the language has full support.
    +
    +    my $language;
    +    my $fullLanguageSupport;
    +    my ($lineNumber, $languageKeyword, $languageName) = @$properties[0..2];
    +    my $lcLanguageName = lc($languageName);
    +    my ($keyword, $value);
    +
    +    if ($languageKeyword eq 'alter language')
    +        {
    +        $language = $languages{$lcLanguageName};
    +
    +        if (!defined $language)
    +            {
    +            NaturalDocs::ConfigFile->AddError('The language ' . $languageName . ' is not defined.', $lineNumber);
    +            return;
    +            }
    +        else
    +            {
    +            $fullLanguageSupport = (!$language->isa('NaturalDocs::Languages::Simple'));
    +            };
    +        }
    +
    +    elsif ($languageKeyword eq 'language')
    +        {
    +        if (exists $languages{$lcLanguageName})
    +            {
    +            NaturalDocs::ConfigFile->AddError('The language ' . $value . ' is already defined.  Use "Alter Language" if you want '
    +                                                             . 'to override its settings.', $lineNumber);
    +            return;
    +            };
    +
    +        # Case is important with these two.
    +        if ($lcLanguageName eq 'shebang script')
    +            {  $languageName = 'Shebang Script';  }
    +        elsif ($lcLanguageName eq 'text file')
    +            {  $languageName = 'Text File';  };
    +
    +
    +        # Go through the properties looking for whether the language has basic or full support and which package to use to create
    +        # it.
    +
    +        for (my $i = 3; $i < scalar @$properties; $i += 3)
    +            {
    +            ($lineNumber, $keyword, $value) = @$properties[$i..$i+2];
    +
    +            if ($keyword eq 'full language support')
    +                {
    +                $fullLanguageSupport = 1;
    +
    +                eval
    +                    {
    +                    $language = $value->New($languageName);
    +                    };
    +                if ($::EVAL_ERROR)
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Could not create ' . $value . ' object.', $lineNumber);
    +                    return;
    +                    };
    +
    +                last;
    +                }
    +
    +            elsif ($keyword eq 'perl package')
    +                {
    +                eval
    +                    {
    +                    $language = $value->New($languageName);
    +                    };
    +                if ($::EVAL_ERROR)
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Could not create ' . $value . ' object.', $lineNumber);
    +                    return;
    +                    };
    +                };
    +            };
    +
    +        # If $language was not created by now, it's a generic basic support language.
    +        if (!defined $language)
    +            {  $language = NaturalDocs::Languages::Simple->New($languageName);  };
    +
    +        $languages{$lcLanguageName} = $language;
    +        }
    +
    +    else # not language or alter language
    +        {
    +        NaturalDocs::ConfigFile->AddError('You must start this line with "Language", "Alter Language", or "Ignore Extensions".',
    +                                                           $lineNumber);
    +        return;
    +        };
    +
    +
    +    # Decode the properties.
    +
    +    for (my $i = 3; $i < scalar @$properties; $i += 3)
    +        {
    +        ($lineNumber, $keyword, $value) = @$properties[$i..$i+2];
    +
    +        if ($keyword =~ /^(?:(add|replace) )?extensions?$/)
    +            {
    +            my $command = $1;
    +
    +
    +            # Remove old extensions.
    +
    +            if (defined $language->Extensions() && $command eq 'replace')
    +                {
    +                foreach my $extension (@{$language->Extensions()})
    +                    {
    +                    if (exists $tempExtensions->{$extension})
    +                        {
    +                        my $languages = $tempExtensions->{$extension};
    +                        my $i = 0;
    +
    +                        while ($i < scalar @$languages)
    +                            {
    +                            if ($languages->[$i] eq $lcLanguageName)
    +                                {  splice(@$languages, $i, 1);  }
    +                            else
    +                                {  $i++;  };
    +                            };
    +
    +                        if (!scalar @$languages)
    +                            {  delete $tempExtensions->{$extension};  };
    +                        };
    +                    };
    +                };
    +
    +
    +            # Add new extensions.
    +
    +            # Ignore stars and dots so people could use .ext or *.ext.
    +            $value =~ s/\*\.|\.//g;
    +
    +            my @extensions = split(/ /, lc($value));
    +
    +            foreach my $extension (@extensions)
    +                {
    +                if (!exists $tempExtensions->{$extension})
    +                    {  $tempExtensions->{$extension} = [ ];  };
    +
    +                push @{$tempExtensions->{$extension}}, $lcLanguageName;
    +                };
    +
    +
    +            # Set the extensions for the language object.
    +
    +            if (defined $language->Extensions())
    +                {
    +                if ($command eq 'add')
    +                    {  push @extensions, @{$language->Extensions()};  }
    +                elsif (!$command)
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of extensions.',
    +                                                                       $lineNumber);
    +                    };
    +                };
    +
    +            $language->SetExtensions(\@extensions);
    +            }
    +
    +        elsif ($keyword =~ /^(?:(add|replace) )?shebang strings?$/)
    +            {
    +            my $command = $1;
    +
    +
    +            # Remove old strings.
    +
    +            if (defined $language->ShebangStrings() && $command eq 'replace')
    +                {
    +                foreach my $shebangString (@{$language->ShebangStrings()})
    +                    {
    +                    if (exists $tempShebangStrings->{$shebangString})
    +                        {
    +                        my $languages = $tempShebangStrings->{$shebangString};
    +                        my $i = 0;
    +
    +                        while ($i < scalar @$languages)
    +                            {
    +                            if ($languages->[$i] eq $lcLanguageName)
    +                                {  splice(@$languages, $i, 1);  }
    +                            else
    +                                {  $i++;  };
    +                            };
    +
    +                        if (!scalar @$languages)
    +                            {  delete $tempShebangStrings->{$shebangString};  };
    +                        };
    +                    };
    +                };
    +
    +
    +            # Add new strings.
    +
    +            my @shebangStrings = split(/ /, lc($value));
    +
    +            foreach my $shebangString (@shebangStrings)
    +                {
    +                if (!exists $tempShebangStrings->{$shebangString})
    +                    {  $tempShebangStrings->{$shebangString} = [ ];  };
    +
    +                push @{$tempShebangStrings->{$shebangString}}, $lcLanguageName;
    +                };
    +
    +
    +            # Set the strings for the language object.
    +
    +            if (defined $language->ShebangStrings())
    +                {
    +                if ($command eq 'add')
    +                    {  push @shebangStrings, @{$language->ShebangStrings()};  }
    +                elsif (!$command)
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of shebang '
    +                                                                     . 'strings.', $lineNumber);
    +                    };
    +                };
    +
    +            $language->SetShebangStrings(\@shebangStrings);
    +            }
    +
    +        elsif ($keyword eq 'package separator')
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                # Prior to 1.32, package separator was used with full language support too.  Accept it without complaining, even though
    +                # we ignore it.
    +                if ($version >= NaturalDocs::Version->FromString('1.32'))
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                    };
    +                }
    +            else
    +                {  $language->SetPackageSeparator($value);  };
    +            }
    +
    +        elsif ($keyword =~ /^(?:(add|replace) )?ignored? (?:(.+) )?prefix(?:es)? in index$/)
    +            {
    +            my ($command, $topicName) = ($1, $2);
    +            my $topicType;
    +
    +            if ($topicName)
    +                {
    +                if (!( ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName) ))
    +                    {
    +                    NaturalDocs::ConfigFile->AddError($topicName . ' is not a defined topic type.', $lineNumber);
    +                    };
    +                }
    +            else
    +                {  $topicType = ::TOPIC_GENERAL();  };
    +
    +            if ($topicType)
    +                {
    +                my @prefixes;
    +
    +                if (defined $language->IgnoredPrefixesFor($topicType))
    +                    {
    +                    if ($command eq 'add')
    +                        {  @prefixes = @{$language->IgnoredPrefixesFor($topicType)};  }
    +                    elsif (!$command)
    +                        {
    +                        NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of '
    +                                                                         . 'ignored prefixes.', $lineNumber);
    +                        };
    +                    };
    +
    +                push @prefixes, split(/ /, $value);
    +                $language->SetIgnoredPrefixesFor($topicType, \@prefixes);
    +                };
    +            }
    +
    +        elsif ($keyword eq 'full language support' || $keyword eq 'perl package')
    +            {
    +            if ($languageKeyword eq 'alter language')
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot use ' . $keyword . ' with Alter Language.', $lineNumber);
    +                };
    +            # else ignore it.
    +            }
    +
    +        elsif ($keyword =~ /^line comments?$/)
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                }
    +            else
    +                {
    +                my @symbols = split(/ /, $value);
    +                $language->SetLineCommentSymbols(\@symbols);
    +                };
    +            }
    +
    +        elsif ($keyword =~ /^block comments?$/)
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                }
    +            else
    +                {
    +                my @symbols = split(/ /, $value);
    +
    +                if ((scalar @symbols) % 2 == 0)
    +                    {  $language->SetBlockCommentSymbols(\@symbols);  }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Block comment symbols must appear in pairs.', $lineNumber);  };
    +                };
    +            }
    +
    +        elsif ($keyword =~ /^(?:(.+) )?prototype enders?$/)
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                }
    +            else
    +                {
    +                my $topicName = $1;
    +                my $topicType;
    +
    +                if ($topicName)
    +                    {
    +                    if (!( ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName) ))
    +                        {
    +                        NaturalDocs::ConfigFile->AddError($topicName . ' is not a defined topic type.', $lineNumber);
    +                        };
    +                    }
    +                else
    +                    {  $topicType = ::TOPIC_GENERAL();  };
    +
    +                if ($topicType)
    +                    {
    +                    $value =~ s/\\n/\n/g;
    +                    my @symbols = split(/ /, $value);
    +                    $language->SetPrototypeEndersFor($topicType, \@symbols);
    +                    };
    +                };
    +            }
    +
    +        elsif ($keyword eq 'line extender')
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                }
    +            else
    +                {
    +                $language->SetLineExtender($value);
    +                };
    +            }
    +
    +        elsif ($keyword eq 'enum values')
    +            {
    +            if ($fullLanguageSupport)
    +                {
    +                NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber);
    +                }
    +            else
    +                {
    +                $value = lc($value);
    +                my $constant;
    +
    +                if ($value eq 'global')
    +                    {  $constant = ::ENUM_GLOBAL();  }
    +                elsif ($value eq 'under type')
    +                    {  $constant = ::ENUM_UNDER_TYPE();  }
    +                elsif ($value eq 'under parent')
    +                    {  $constant = ::ENUM_UNDER_PARENT();  };
    +
    +                if (defined $constant)
    +                    {  $language->SetEnumValues($constant);  }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Enum Values must be "Global", "Under Type", or "Under Parent".', $lineNumber);
    +                    };
    +                };
    +            }
    +
    +        else
    +            {
    +            NaturalDocs::ConfigFile->AddError($keyword . ' is not a valid keyword.', $lineNumber);
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves the main and user versions of <Languages.txt>.
    +#
    +sub Save
    +    {
    +    my $self = shift;
    +
    +    $self->SaveFile(1); # Main
    +    $self->SaveFile(0); # User
    +    };
    +
    +
    +#
    +#   Function: SaveFile
    +#
    +#   Saves a particular version of <Topics.txt>.
    +#
    +#   Parameters:
    +#
    +#       isMain - Whether the file is the main file or not.
    +#
    +sub SaveFile #(isMain)
    +    {
    +    my ($self, $isMain) = @_;
    +
    +    my $file;
    +
    +    if ($isMain)
    +        {
    +        if (NaturalDocs::Project->MainConfigFileStatus('Languages.txt') == ::FILE_SAME())
    +            {  return;  };
    +        $file = NaturalDocs::Project->MainConfigFile('Languages.txt');
    +        }
    +    else
    +        {
    +        # Have to check the main too because this file lists the languages defined there.
    +        if (NaturalDocs::Project->UserConfigFileStatus('Languages.txt') == ::FILE_SAME() &&
    +            NaturalDocs::Project->MainConfigFileStatus('Languages.txt') == ::FILE_SAME())
    +            {  return;  };
    +        $file = NaturalDocs::Project->UserConfigFile('Languages.txt');
    +        };
    +
    +
    +    # Array of segments, with each being groups of three consecutive entries.  The first is the keyword ('language' or
    +    # 'alter language'), the second is the value, and the third is a hashref of all the properties.
    +    # - For properties that can accept a topic type, the property values are hashrefs mapping topic types to the values.
    +    # - For properties that can accept 'add' or 'replace', there is an additional property ending in 'command' that stores it.
    +    # - For properties that can accept both, the 'command' thing is applied to the topic types rather than the properties.
    +    my @segments;
    +
    +    my @ignoredExtensions;
    +
    +    my $currentProperties;
    +    my $version;
    +
    +    if ($version = NaturalDocs::ConfigFile->Open($file))
    +        {
    +        # We can assume the file is valid.
    +
    +        while (my ($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine())
    +            {
    +            $value .= $comment;
    +            $value =~ s/^ //;
    +
    +            if ($keyword eq 'language')
    +                {
    +                $currentProperties = { };
    +
    +                # Case is important with these two.
    +                if (lc($value) eq 'shebang script')
    +                    {  $value = 'Shebang Script';  }
    +                elsif (lc($value) eq 'text file')
    +                    {  $value = 'Text File';  };
    +
    +                push @segments, 'language', $value, $currentProperties;
    +                }
    +
    +            elsif ($keyword eq 'alter language')
    +                {
    +                $currentProperties = { };
    +                push @segments, 'alter language', $languages{lc($value)}->Name(), $currentProperties;
    +                }
    +
    +            elsif ($keyword =~ /^ignored? extensions?$/)
    +                {
    +                $value =~ tr/*.//d;
    +                push @ignoredExtensions, split(/ /, $value);
    +                }
    +
    +            elsif ($keyword eq 'package separator' || $keyword eq 'full language support' || $keyword eq 'perl package' ||
    +                    $keyword eq 'line extender' || $keyword eq 'enum values')
    +                {
    +                $currentProperties->{$keyword} = $value;
    +                }
    +
    +            elsif ($keyword =~ /^line comments?$/)
    +                {
    +                $currentProperties->{'line comments'} = $value;
    +                }
    +            elsif ($keyword =~ /^block comments?$/)
    +                {
    +                $currentProperties->{'block comments'} = $value;
    +                }
    +
    +            elsif ($keyword =~ /^(?:(add|replace) )?extensions?$/)
    +                {
    +                my $command = $1;
    +
    +                if ($command eq 'add' && exists $currentProperties->{'extensions'})
    +                    {  $currentProperties->{'extensions'} .= ' ' . $value;  }
    +                else
    +                    {
    +                    $currentProperties->{'extensions'} = $value;
    +                    $currentProperties->{'extensions command'} = $command;
    +                    };
    +                }
    +
    +            elsif ($keyword =~ /^(?:(add|replace) )?shebang strings?$/)
    +                {
    +                my $command = $1;
    +
    +                if ($command eq 'add' && exists $currentProperties->{'shebang strings'})
    +                    {  $currentProperties->{'shebang strings'} .= ' ' . $value;  }
    +                else
    +                    {
    +                    $currentProperties->{'shebang strings'} = $value;
    +                    $currentProperties->{'shebang strings command'} = $command;
    +                    };
    +                }
    +
    +            elsif ($keyword =~ /^(?:(.+) )?prototype enders?$/)
    +                {
    +                my $topicName = $1;
    +                my $topicType;
    +
    +                if ($topicName)
    +                    {  ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName);  }
    +                else
    +                    {  $topicType = ::TOPIC_GENERAL();  };
    +
    +                my $currentTypeProperties = $currentProperties->{'prototype enders'};
    +
    +                if (!defined $currentTypeProperties)
    +                    {
    +                    $currentTypeProperties = { };
    +                    $currentProperties->{'prototype enders'} = $currentTypeProperties;
    +                    };
    +
    +                $currentTypeProperties->{$topicType} = $value;
    +                }
    +
    +            elsif ($keyword =~ /^(?:(add|replace) )?ignored? (?:(.+) )?prefix(?:es)? in index$/)
    +                {
    +                my ($command, $topicName) = ($1, $2);
    +                my $topicType;
    +
    +                if ($topicName)
    +                    {  ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName);  }
    +                else
    +                    {  $topicType = ::TOPIC_GENERAL();  };
    +
    +                my $currentTypeProperties = $currentProperties->{'ignored prefixes in index'};
    +
    +                if (!defined $currentTypeProperties)
    +                    {
    +                    $currentTypeProperties = { };
    +                    $currentProperties->{'ignored prefixes in index'} = $currentTypeProperties;
    +                    };
    +
    +                if ($command eq 'add' && exists $currentTypeProperties->{$topicType})
    +                    {  $currentTypeProperties->{$topicType} .= ' ' . $value;  }
    +                else
    +                    {
    +                    $currentTypeProperties->{$topicType} = $value;
    +                    $currentTypeProperties->{$topicType . ' command'} = $command;
    +                    };
    +                };
    +            };
    +
    +        NaturalDocs::ConfigFile->Close();
    +        };
    +
    +
    +    if (!open(FH_LANGUAGES, '>' . $file))
    +        {
    +        # The main file may be on a shared volume or some other place the user doesn't have write access to.  Since this is only to
    +        # reformat the file, we can ignore the failure.
    +        if ($isMain)
    +            {  return;  }
    +        else
    +            {  die "Couldn't save " . $file;  };
    +        };
    +
    +    print FH_LANGUAGES 'Format: ' . NaturalDocs::Settings->TextAppVersion() . "\n\n";
    +
    +    # Remember the 80 character limit.
    +
    +    if ($isMain)
    +        {
    +        print FH_LANGUAGES
    +        "# This is the main Natural Docs languages file.  If you change anything here,\n"
    +        . "# it will apply to EVERY PROJECT you use Natural Docs on.  If you'd like to\n"
    +        . "# change something for just one project, edit the Languages.txt in its project\n"
    +        . "# directory instead.\n";
    +        }
    +    else
    +        {
    +        print FH_LANGUAGES
    +        "# This is the Natural Docs languages file for this project.  If you change\n"
    +        . "# anything here, it will apply to THIS PROJECT ONLY.  If you'd like to change\n"
    +        . "# something for all your projects, edit the Languages.txt in Natural Docs'\n"
    +        . "# Config directory instead.\n\n\n";
    +
    +        if (scalar @ignoredExtensions == 1)
    +            {
    +            print FH_LANGUAGES
    +            'Ignore Extension: ' . $ignoredExtensions[0] . "\n";
    +            }
    +        elsif (scalar @ignoredExtensions)
    +            {
    +            print FH_LANGUAGES
    +            'Ignore Extensions: ' . join(' ', @ignoredExtensions) . "\n";
    +            }
    +        else
    +            {
    +            print FH_LANGUAGES
    +            "# You can prevent certain file extensions from being scanned like this:\n"
    +            . "# Ignore Extensions: [extension] [extension] ...\n"
    +            };
    +        };
    +
    +    print FH_LANGUAGES
    +    "\n\n"
    +    . "#-------------------------------------------------------------------------------\n"
    +    . "# SYNTAX:\n"
    +    . "#\n"
    +    . "# Unlike other Natural Docs configuration files, in this file all comments\n"
    +    . "# MUST be alone on a line.  Some languages deal with the # character, so you\n"
    +    . "# cannot put comments on the same line as content.\n"
    +    . "#\n"
    +    . "# Also, all lists are separated with spaces, not commas, again because some\n"
    +    . "# languages may need to use them.\n"
    +    . "#\n";
    +
    +    if ($isMain)
    +        {
    +        print FH_LANGUAGES
    +        "# Language: [name]\n"
    +        . "#    Defines a new language.  Its name can use any characters.\n"
    +        . "#\n";
    +        }
    +    else
    +        {
    +        print FH_LANGUAGES
    +        "# Language: [name]\n"
    +        . "# Alter Language: [name]\n"
    +        . "#    Defines a new language or alters an existing one.  Its name can use any\n"
    +        . "#    characters.  If any of the properties below have an add/replace form, you\n"
    +        . "#    must use that when using Alter Language.\n"
    +        . "#\n";
    +        };
    +
    +    print FH_LANGUAGES
    +    "#    The language Shebang Script is special.  It's entry is only used for\n"
    +    . "#    extensions, and files with those extensions have their shebang (#!) lines\n"
    +    . "#    read to determine the real language of the file.  Extensionless files are\n"
    +    . "#    always treated this way.\n"
    +    . "#\n"
    +    . "#    The language Text File is also special.  It's treated as one big comment\n"
    +    . "#    so you can put Natural Docs content in them without special symbols.  Also,\n"
    +    . "#    if you don't specify a package separator, ignored prefixes, or enum value\n"
    +    . "#    behavior, it will copy those settings from the language that is used most\n"
    +    . "#    in the source tree.\n"
    +    . "#\n"
    +    . "# Extensions: [extension] [extension] ...\n";
    +
    +    if ($isMain)
    +        {
    +        print FH_LANGUAGES
    +        "#    Defines the file extensions of the language's source files.  You can use *\n"
    +        . "#    to mean any undefined extension.\n"
    +        . "#\n"
    +        . "# Shebang Strings: [string] [string] ...\n"
    +        . "#    Defines a list of strings that can appear in the shebang (#!) line to\n"
    +        . "#    designate that it's part of the language.\n"
    +        . "#\n";
    +        }
    +    else
    +        {
    +        print FH_LANGUAGES
    +        "# [Add/Replace] Extensions: [extension] [extension] ...\n"
    +        . "#    Defines the file extensions of the language's source files.  You can\n"
    +        . "#    redefine extensions found in the main languages file.  You can use * to\n"
    +        . "#    mean any undefined extension.\n"
    +        . "#\n"
    +        . "# Shebang Strings: [string] [string] ...\n"
    +        . "# [Add/Replace] Shebang Strings: [string] [string] ...\n"
    +        . "#    Defines a list of strings that can appear in the shebang (#!) line to\n"
    +        . "#    designate that it's part of the language.  You can redefine strings found\n"
    +        . "#    in the main languages file.\n"
    +        . "#\n";
    +        };
    +
    +    print FH_LANGUAGES
    +    "# Ignore Prefixes in Index: [prefix] [prefix] ...\n"
    +    . (!$isMain ? "# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ...\n#\n" : '')
    +    . "# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...\n"
    +    . (!$isMain ? "# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ...\n" : '')
    +    . "#    Specifies prefixes that should be ignored when sorting symbols in an\n"
    +    . "#    index.  Can be specified in general or for a specific topic type.\n"
    +    . "#\n"
    +    . "#------------------------------------------------------------------------------\n"
    +    . "# For basic language support only:\n"
    +    . "#\n"
    +    . "# Line Comments: [symbol] [symbol] ...\n"
    +    . "#    Defines a space-separated list of symbols that are used for line comments,\n"
    +    . "#    if any.\n"
    +    . "#\n"
    +    . "# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...\n"
    +    . "#    Defines a space-separated list of symbol pairs that are used for block\n"
    +    . "#    comments, if any.\n"
    +    . "#\n"
    +    . "# Package Separator: [symbol]\n"
    +    . "#    Defines the default package separator symbol.  The default is a dot.\n"
    +    . "#\n"
    +    . "# [Topic Type] Prototype Enders: [symbol] [symbol] ...\n"
    +    . "#    When defined, Natural Docs will attempt to get a prototype from the code\n"
    +    . "#    immediately following the topic type.  It stops when it reaches one of\n"
    +    . "#    these symbols.  Use \\n for line breaks.\n"
    +    . "#\n"
    +    . "# Line Extender: [symbol]\n"
    +    . "#    Defines the symbol that allows a prototype to span multiple lines if\n"
    +    . "#    normally a line break would end it.\n"
    +    . "#\n"
    +    . "# Enum Values: [global|under type|under parent]\n"
    +    . "#    Defines how enum values are referenced.  The default is global.\n"
    +    . "#    global       - Values are always global, referenced as 'value'.\n"
    +    . "#    under type   - Values are under the enum type, referenced as\n"
    +    . "#               'package.enum.value'.\n"
    +    . "#    under parent - Values are under the enum's parent, referenced as\n"
    +    . "#               'package.value'.\n"
    +    . "#\n"
    +    . "# Perl Package: [perl package]\n"
    +    . "#    Specifies the Perl package used to fine-tune the language behavior in ways\n"
    +    . "#    too complex to do in this file.\n"
    +    . "#\n"
    +    . "#------------------------------------------------------------------------------\n"
    +    . "# For full language support only:\n"
    +    . "#\n"
    +    . "# Full Language Support: [perl package]\n"
    +    . "#    Specifies the Perl package that has the parsing routines necessary for full\n"
    +    . "#    language support.\n"
    +    . "#\n"
    +    . "#-------------------------------------------------------------------------------\n\n";
    +
    +    if ($isMain)
    +        {
    +        print FH_LANGUAGES
    +        "# The following languages MUST be defined in this file:\n"
    +        . "#\n"
    +        . "#    Text File, Shebang Script\n";
    +        }
    +    else
    +        {
    +        print FH_LANGUAGES
    +        "# The following languages are defined in the main file, if you'd like to alter\n"
    +        . "# them:\n"
    +        . "#\n"
    +        . Text::Wrap::wrap('#    ', '#    ', join(', ', @mainLanguageNames)) . "\n";
    +        };
    +
    +    print FH_LANGUAGES "\n"
    +    . "# If you add a language that you think would be useful to other developers\n"
    +    . "# and should be included in Natural Docs by default, please e-mail it to\n"
    +    . "# languages [at] naturaldocs [dot] org.\n";
    +
    +    my @topicTypeOrder = ( ::TOPIC_GENERAL(), ::TOPIC_CLASS(), ::TOPIC_FUNCTION(), ::TOPIC_VARIABLE(),
    +                                         ::TOPIC_PROPERTY(), ::TOPIC_TYPE(), ::TOPIC_CONSTANT() );
    +
    +    for (my $i = 0; $i < scalar @segments; $i += 3)
    +        {
    +        my ($keyword, $name, $properties) = @segments[$i..$i+2];
    +
    +        print FH_LANGUAGES "\n\n";
    +
    +        if ($keyword eq 'language')
    +            {  print FH_LANGUAGES 'Language: ' . $name . "\n\n";  }
    +        else
    +            {  print FH_LANGUAGES 'Alter Language: ' . $name . "\n\n";  };
    +
    +        if (exists $properties->{'extensions'})
    +            {
    +            print FH_LANGUAGES '   ';
    +
    +            if ($properties->{'extensions command'})
    +                {  print FH_LANGUAGES ucfirst($properties->{'extensions command'}) . ' ';  };
    +
    +            my @extensions = split(/ /, $properties->{'extensions'}, 2);
    +
    +            if (scalar @extensions == 1)
    +                {  print FH_LANGUAGES 'Extension: ';  }
    +            else
    +                {  print FH_LANGUAGES 'Extensions: ';  };
    +
    +            print FH_LANGUAGES lc($properties->{'extensions'}) . "\n";
    +            };
    +
    +        if (exists $properties->{'shebang strings'})
    +            {
    +            print FH_LANGUAGES '   ';
    +
    +            if ($properties->{'shebang strings command'})
    +                {  print FH_LANGUAGES ucfirst($properties->{'shebang strings command'}) . ' ';  };
    +
    +            my @shebangStrings = split(/ /, $properties->{'shebang strings'}, 2);
    +
    +            if (scalar @shebangStrings == 1)
    +                {  print FH_LANGUAGES 'Shebang String: ';  }
    +            else
    +                {  print FH_LANGUAGES 'Shebang Strings: ';  };
    +
    +            print FH_LANGUAGES lc($properties->{'shebang strings'}) . "\n";
    +            };
    +
    +        if (exists $properties->{'ignored prefixes in index'})
    +            {
    +            my $topicTypePrefixes = $properties->{'ignored prefixes in index'};
    +
    +            my %usedTopicTypes;
    +            my @topicTypes = ( @topicTypeOrder, keys %$topicTypePrefixes );
    +
    +            foreach my $topicType (@topicTypes)
    +                {
    +                if ($topicType !~ / command$/ &&
    +                    exists $topicTypePrefixes->{$topicType} &&
    +                    !exists $usedTopicTypes{$topicType})
    +                    {
    +                    print FH_LANGUAGES '   ';
    +
    +                    if ($topicTypePrefixes->{$topicType . ' command'})
    +                        {  print FH_LANGUAGES ucfirst($topicTypePrefixes->{$topicType . ' command'}) . ' Ignored ';  }
    +                    else
    +                        {  print FH_LANGUAGES 'Ignore ';  };
    +
    +                    if ($topicType ne ::TOPIC_GENERAL())
    +                        {  print FH_LANGUAGES NaturalDocs::Topics->TypeInfo($topicType)->Name() . ' ';  };
    +
    +                    my @prefixes = split(/ /, $topicTypePrefixes->{$topicType}, 2);
    +
    +                    if (scalar @prefixes == 1)
    +                        {  print FH_LANGUAGES 'Prefix in Index: ';  }
    +                    else
    +                        {  print FH_LANGUAGES 'Prefixes in Index: ';  };
    +
    +                    print FH_LANGUAGES $topicTypePrefixes->{$topicType} . "\n";
    +
    +                    $usedTopicTypes{$topicType} = 1;
    +                    };
    +                };
    +            };
    +
    +        if (exists $properties->{'line comments'})
    +            {
    +            my @comments = split(/ /, $properties->{'line comments'}, 2);
    +
    +            if (scalar @comments == 1)
    +                {  print FH_LANGUAGES '   Line Comment: ';  }
    +            else
    +                {  print FH_LANGUAGES '   Line Comments: ';  };
    +
    +            print FH_LANGUAGES $properties->{'line comments'} . "\n";
    +            };
    +
    +        if (exists $properties->{'block comments'})
    +            {
    +            my @comments = split(/ /, $properties->{'block comments'}, 3);
    +
    +            if (scalar @comments == 2)
    +                {  print FH_LANGUAGES '   Block Comment: ';  }
    +            else
    +                {  print FH_LANGUAGES '   Block Comments: ';  };
    +
    +            print FH_LANGUAGES $properties->{'block comments'} . "\n";
    +            };
    +
    +        if (exists $properties->{'package separator'})
    +            {
    +            # Prior to 1.32, Package Separator was allowed for full language support.  Ignore it when reformatting.
    +            if ($version >= NaturalDocs::Version->FromString('1.32') || !exists $properties->{'full language support'})
    +                {  print FH_LANGUAGES '   Package Separator: ' . $properties->{'package separator'} . "\n";  };
    +            };
    +
    +        if (exists $properties->{'enum values'})
    +            {
    +            print FH_LANGUAGES '   Enum Values: ' . ucfirst(lc($properties->{'enum values'})) . "\n";
    +            };
    +
    +        if (exists $properties->{'prototype enders'})
    +            {
    +            my $topicTypeEnders = $properties->{'prototype enders'};
    +
    +            my %usedTopicTypes;
    +            my @topicTypes = ( @topicTypeOrder, keys %$topicTypeEnders );
    +
    +            foreach my $topicType (@topicTypes)
    +                {
    +                if ($topicType !~ / command$/ &&
    +                    exists $topicTypeEnders->{$topicType} &&
    +                    !exists $usedTopicTypes{$topicType})
    +                    {
    +                    print FH_LANGUAGES '   ';
    +
    +                    if ($topicType ne ::TOPIC_GENERAL())
    +                        {  print FH_LANGUAGES NaturalDocs::Topics->TypeInfo($topicType)->Name() . ' ';  };
    +
    +                    my @enders = split(/ /, $topicTypeEnders->{$topicType}, 2);
    +
    +                    if (scalar @enders == 1)
    +                        {  print FH_LANGUAGES 'Prototype Ender: ';  }
    +                    else
    +                        {  print FH_LANGUAGES 'Prototype Enders: ';  };
    +
    +                    print FH_LANGUAGES $topicTypeEnders->{$topicType} . "\n";
    +
    +                    $usedTopicTypes{$topicType} = 1;
    +                    };
    +                };
    +            };
    +
    +        if (exists $properties->{'line extender'})
    +            {
    +            print FH_LANGUAGES '   Line Extender: ' . $properties->{'line extender'} . "\n";
    +            };
    +
    +        if (exists $properties->{'perl package'})
    +            {
    +            print FH_LANGUAGES '   Perl Package: ' . $properties->{'perl package'} . "\n";
    +            };
    +
    +        if (exists $properties->{'full language support'})
    +            {
    +            print FH_LANGUAGES '   Full Language Support: ' . $properties->{'full language support'} . "\n";
    +            };
    +        };
    +
    +    close(FH_LANGUAGES);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: LanguageOf
    +#
    +#   Returns the language of the passed source file.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to get the language of.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::Languages::Base>-derived object for the passed file, or undef if the file is not a recognized language.
    +#
    +sub LanguageOf #(sourceFile)
    +    {
    +    my ($self, $sourceFile) = @_;
    +
    +    my $extension = NaturalDocs::File->ExtensionOf($sourceFile);
    +    if (defined $extension)
    +        {  $extension = lc($extension);  };
    +
    +    my $languageName;
    +
    +    if (!defined $extension)
    +        {  $languageName = 'shebang script';  }
    +    else
    +        {  $languageName = $extensions{$extension};  };
    +
    +    if (!defined $languageName)
    +        {  $languageName = $extensions{'*'};  };
    +
    +    if (defined $languageName)
    +        {
    +        if ($languageName eq 'shebang script')
    +            {
    +            if (exists $shebangFiles{$sourceFile})
    +                {
    +                if (defined $shebangFiles{$sourceFile})
    +                    {  return $languages{$shebangFiles{$sourceFile}};  }
    +                else
    +                    {  return undef;  };
    +                }
    +
    +            else # (!exists $shebangFiles{$sourceFile})
    +                {
    +                my $shebangLine;
    +
    +                if (open(SOURCEFILEHANDLE, '<' . $sourceFile))
    +                	{
    +                    my $lineReader = NaturalDocs::LineReader->New(\*SOURCEFILEHANDLE);
    +                    $shebangLine = $lineReader->Get();
    +
    +	                if (substr($shebangLine, 0, 2) ne '#!')
    +	                    {  $shebangLine = undef;  };
    +
    +	                close (SOURCEFILEHANDLE);
    +	                }
    +	            elsif (defined $extension)
    +	            	{  die 'Could not open ' . $sourceFile;  }
    +	            # Ignore extensionless files that can't be opened.  They may be system files.
    +
    +                if (!defined $shebangLine)
    +                    {
    +                    $shebangFiles{$sourceFile} = undef;
    +                    return undef;
    +                    }
    +                else
    +                    {
    +                    $shebangLine = lc($shebangLine);
    +
    +                    foreach my $shebangString (keys %shebangStrings)
    +                        {
    +                        if (index($shebangLine, $shebangString) != -1)
    +                            {
    +                            $shebangFiles{$sourceFile} = $shebangStrings{$shebangString};
    +                            return $languages{$shebangStrings{$shebangString}};
    +                            };
    +                        };
    +
    +                    $shebangFiles{$sourceFile} = undef;
    +                    return undef;
    +                    };
    +                };
    +            }
    +
    +        else # language name ne 'shebang script'
    +            {  return $languages{$languageName};  };
    +        }
    +    else # !defined $language
    +        {
    +        return undef;
    +        };
    +    };
    +
    +
    +#
    +#   Function: OnMostUsedLanguageKnown
    +#
    +#   Called when the most used language is known.
    +#
    +sub OnMostUsedLanguageKnown
    +    {
    +    my $self = shift;
    +
    +    my $language = $languages{lc( NaturalDocs::Project->MostUsedLanguage() )};
    +
    +    if ($language)
    +        {
    +        if (!$languages{'text file'}->HasIgnoredPrefixes())
    +            {  $languages{'text file'}->CopyIgnoredPrefixesOf($language);  };
    +        if (!$languages{'text file'}->PackageSeparatorWasSet())
    +            {  $languages{'text file'}->SetPackageSeparator($language->PackageSeparator());  };
    +        if (!$languages{'text file'}->EnumValuesWasSet())
    +            {  $languages{'text file'}->SetEnumValues($language->EnumValues());  };
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/ActionScript.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/ActionScript.pm
    new file mode 100644
    index 000000000..b24ba9d6e
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/ActionScript.pm
    @@ -0,0 +1,1487 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::ActionScript
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of Flash ActionScript.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::ActionScript;
    +
    +use base 'NaturalDocs::Languages::Advanced';
    +
    +
    +################################################################################
    +# Group: Constants and Types
    +
    +
    +#
    +#   Constants: XML Tag Type
    +#
    +#   XML_OPENING_TAG - The tag is an opening one, such as <tag>.
    +#   XML_CLOSING_TAG - The tag is a closing one, such as </tag>.
    +#   XML_SELF_CONTAINED_TAG - The tag is self contained, such as <tag />.
    +#
    +use constant XML_OPENING_TAG => 1;
    +use constant XML_CLOSING_TAG => 2;
    +use constant XML_SELF_CONTAINED_TAG => 3;
    +
    +
    +################################################################################
    +# Group: Package Variables
    +
    +#
    +#   hash: classModifiers
    +#   An existence hash of all the acceptable class modifiers.  The keys are in all lowercase.
    +#
    +my %classModifiers = ( 'dynamic' => 1,
    +                                   'intrinsic' => 1,
    +                                   'final' => 1,
    +                                   'internal' => 1,
    +                                   'public' => 1 );
    +
    +#
    +#   hash: memberModifiers
    +#   An existence hash of all the acceptable class member modifiers.  The keys are in all lowercase.
    +#
    +my %memberModifiers = ( 'public' => 1,
    +                                        'private' => 1,
    +                                        'protected' => 1,
    +                                        'static' => 1,
    +                                        'internal' => 1,
    +                                        'override' => 1 );
    +
    +
    +#
    +#   hash: declarationEnders
    +#   An existence hash of all the tokens that can end a declaration.  This is important because statements don't require a semicolon
    +#   to end.  The keys are in all lowercase.
    +#
    +my %declarationEnders = ( ';' => 1,
    +                                        '}' => 1,
    +                                        '{' => 1,
    +                                        'public' => 1,
    +                                        'private' => 1,
    +                                        'protected' => 1,
    +                                        'static' => 1,
    +                                        'internal' => 1,
    +                                        'dynamic' => 1,
    +                                        'intrinsic' => 1,
    +                                        'final' => 1,
    +                                        'override' => 1,
    +                                        'class' => 1,
    +                                        'interface' => 1,
    +                                        'var' => 1,
    +                                        'function' => 1,
    +                                        'const' => 1,
    +                                        'namespace' => 1,
    +                                        'import' => 1 );
    +
    +
    +#
    +#   var: isEscaped
    +#   Whether the current file being parsed uses escapement.
    +#
    +my $isEscaped;
    +
    +
    +
    +################################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: PackageSeparator
    +#   Returns the package separator symbol.
    +#
    +sub PackageSeparator
    +    {  return '.';  };
    +
    +
    +#
    +#   Function: EnumValues
    +#   Returns the <EnumValuesType> that describes how the language handles enums.
    +#
    +sub EnumValues
    +    {  return ::ENUM_GLOBAL();  };
    +
    +
    +#
    +#   Function: ParseParameterLine
    +#   Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
    +#
    +sub ParseParameterLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    if ($line =~ /^ ?\.\.\.\ (.+)$/)
    +        {
    +        # This puts them in the wrong fields as $1 should be the name and ... should be the type.  However, this is necessary
    +        # because the order in the source is reversed from other parameter declarations and it's more important for the output
    +        # to match the source.
    +        return NaturalDocs::Languages::Prototype::Parameter->New($1, undef, '...', undef, undef, undef);
    +        }
    +    else
    +        {  return $self->ParsePascalParameterLine($line);  };
    +    };
    +
    +
    +#
    +#   Function: TypeBeforeParameter
    +#   Returns whether the type appears before the parameter in prototypes.
    +#
    +sub TypeBeforeParameter
    +    {  return 0;  };
    +
    +
    +#
    +#   Function: PreprocessFile
    +#
    +#   If the file is escaped, strips out all unescaped code.  Will translate any unescaped comments into comments surrounded by
    +#   "\x1C\x1D\x1E\x1F" and "\x1F\x1E\x1D" characters, so chosen because they are the same character lengths as <!-- and -->
    +#   and will not appear in normal code.
    +#
    +sub PreprocessFile
    +    {
    +    my ($self, $lines) = @_;
    +
    +    if (!$isEscaped)
    +        {  return;  };
    +
    +    use constant MODE_UNESCAPED_REGULAR => 1;
    +    use constant MODE_UNESCAPED_PI => 2;
    +    use constant MODE_UNESCAPED_CDATA => 3;
    +    use constant MODE_UNESCAPED_COMMENT => 4;
    +    use constant MODE_ESCAPED_UNKNOWN_CDATA => 5;
    +    use constant MODE_ESCAPED_CDATA => 6;
    +    use constant MODE_ESCAPED_NO_CDATA => 7;
    +
    +    my $mode = MODE_UNESCAPED_REGULAR;
    +
    +    for (my $i = 0; $i < scalar @$lines; $i++)
    +        {
    +        my @tokens = split(/(<[ \t]*\/?[ \t]*mx:Script[^>]*>|<\?|\?>|<\!--|-->|<\!\[CDATA\[|\]\]\>)/, $lines->[$i]);
    +        my $newLine;
    +
    +        foreach my $token (@tokens)
    +            {
    +            if ($mode == MODE_UNESCAPED_REGULAR)
    +                {
    +                if ($token eq '<?')
    +                    {  $mode = MODE_UNESCAPED_PI;  }
    +                elsif ($token eq '<![CDATA[')
    +                    {  $mode = MODE_UNESCAPED_CDATA;  }
    +                elsif ($token eq '<!--')
    +                    {
    +                    $mode = MODE_UNESCAPED_COMMENT;
    +                    $newLine .= "\x1C\x1D\x1E\x1F";
    +                    }
    +                elsif ($token =~ /^<[ \t]*mx:Script/)
    +                    {  $mode = MODE_ESCAPED_UNKNOWN_CDATA;  };
    +                }
    +
    +            elsif ($mode == MODE_UNESCAPED_PI)
    +                {
    +                if ($token eq '?>')
    +                    {  $mode = MODE_UNESCAPED_REGULAR;  };
    +                }
    +
    +            elsif ($mode == MODE_UNESCAPED_CDATA)
    +                {
    +                if ($token eq ']]>')
    +                    {  $mode = MODE_UNESCAPED_REGULAR;  };
    +                }
    +
    +            elsif ($mode == MODE_UNESCAPED_COMMENT)
    +                {
    +                if ($token eq '-->')
    +                    {
    +                    $mode = MODE_UNESCAPED_REGULAR;
    +                    $newLine .= "\x1F\x1E\x1D";
    +                    }
    +                else
    +                    {  $newLine .= $token;  };
    +                }
    +
    +            elsif ($mode == MODE_ESCAPED_UNKNOWN_CDATA)
    +                {
    +                if ($token eq '<![CDATA[')
    +                    {  $mode = MODE_ESCAPED_CDATA;  }
    +                elsif ($token =~ /^<[ \t]*\/[ \t]*mx:Script/)
    +                    {
    +                    $mode = MODE_UNESCAPED_REGULAR;
    +                    $newLine .= '; ';
    +                    }
    +                elsif ($token !~ /^[ \t]*$/)
    +                    {
    +                    $mode = MODE_ESCAPED_NO_CDATA;
    +                    $newLine .= $token;
    +                    };
    +                }
    +
    +            elsif ($mode == MODE_ESCAPED_CDATA)
    +                {
    +                if ($token eq ']]>')
    +                    {
    +                    $mode = MODE_UNESCAPED_REGULAR;
    +                    $newLine .= '; ';
    +                    }
    +                else
    +                    {  $newLine .= $token;  };
    +                }
    +
    +            else #($mode == MODE_ESCAPED_NO_CDATA)
    +                {
    +                if ($token =~ /^<[ \t]*\/[ \t]*mx:Script/)
    +                    {
    +                    $mode = MODE_UNESCAPED_REGULAR;
    +                    $newLine .= '; ';
    +                    }
    +                else
    +                    {  $newLine .= $token;  };
    +                };
    +
    +            };
    +
    +        $lines->[$i] = $newLine;
    +        };
    +    };
    +
    +
    +#
    +#   Function: ParseFile
    +#
    +#   Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The <FileName> to parse.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#
    +#   Returns:
    +#
    +#       The array ( autoTopics, scopeRecord ).
    +#
    +#       autoTopics - An arrayref of automatically generated topics from the file, or undef if none.
    +#       scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none.
    +#
    +sub ParseFile #(sourceFile, topicsList)
    +    {
    +    my ($self, $sourceFile, $topicsList) = @_;
    +
    +    # The \x1# comment symbols are inserted by PreprocessFile() to stand in for XML comments in escaped files.
    +    my @parseParameters = ( [ '//' ], [ '/*', '*/', "\x1C\x1D\x1E\x1F", "\x1F\x1E\x1D" ], [ '///' ], [ '/**', '*/' ] );
    +
    +    my $extension = lc(NaturalDocs::File->ExtensionOf($sourceFile));
    +    $isEscaped = ($extension eq 'mxml');
    +
    +    $self->ParseForCommentsAndTokens($sourceFile, @parseParameters);
    +
    +    my $tokens = $self->Tokens();
    +    my $index = 0;
    +    my $lineNumber = 1;
    +
    +    while ($index < scalar @$tokens)
    +        {
    +        if ($self->TryToSkipWhitespace(\$index, \$lineNumber) ||
    +            $self->TryToGetImport(\$index, \$lineNumber) ||
    +            $self->TryToGetClass(\$index, \$lineNumber) ||
    +            $self->TryToGetFunction(\$index, \$lineNumber) ||
    +            $self->TryToGetVariable(\$index, \$lineNumber) )
    +            {
    +            # The functions above will handle everything.
    +            }
    +
    +        elsif ($tokens->[$index] eq '{')
    +            {
    +            $self->StartScope('}', $lineNumber, undef, undef, undef);
    +            $index++;
    +            }
    +
    +        elsif ($tokens->[$index] eq '}')
    +            {
    +            if ($self->ClosingScopeSymbol() eq '}')
    +                {  $self->EndScope($lineNumber);  };
    +
    +            $index++;
    +            }
    +
    +        else
    +            {
    +            $self->SkipToNextStatement(\$index, \$lineNumber);
    +            };
    +        };
    +
    +
    +    # Don't need to keep these around.
    +    $self->ClearTokens();
    +
    +
    +    my $autoTopics = $self->AutoTopics();
    +
    +    my $scopeRecord = $self->ScopeRecord();
    +    if (defined $scopeRecord && !scalar @$scopeRecord)
    +        {  $scopeRecord = undef;  };
    +
    +    return ( $autoTopics, $scopeRecord );
    +    };
    +
    +
    +
    +################################################################################
    +# Group: Statement Parsing Functions
    +# All functions here assume that the current position is at the beginning of a statement.
    +#
    +# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as
    +# often as it should.  We're making use of the fact that Perl will always return undef in these cases to keep the code simpler.
    +
    +
    +#
    +#   Function: TryToGetIdentifier
    +#
    +#   Determines whether the position is at an identifier, and if so, skips it and returns the complete identifier as a string.  Returns
    +#   undef otherwise.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the current token index.
    +#       lineNumberRef - A reference to the current line number.
    +#       allowStar - If set, allows the last identifier to be a star.
    +#
    +sub TryToGetIdentifier #(indexRef, lineNumberRef, allowStar)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $allowStar) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +
    +    use constant MODE_IDENTIFIER_START => 1;
    +    use constant MODE_IN_IDENTIFIER => 2;
    +    use constant MODE_AFTER_STAR => 3;
    +
    +    my $identifier;
    +    my $mode = MODE_IDENTIFIER_START;
    +
    +    while ($index < scalar @$tokens)
    +        {
    +        if ($mode == MODE_IDENTIFIER_START)
    +            {
    +            if ($tokens->[$index] =~ /^[a-z\$\_]/i)
    +                {
    +                $identifier .= $tokens->[$index];
    +                $index++;
    +
    +                $mode = MODE_IN_IDENTIFIER;
    +                }
    +            elsif ($allowStar && $tokens->[$index] eq '*')
    +                {
    +                $identifier .= '*';
    +                $index++;
    +
    +                $mode = MODE_AFTER_STAR;
    +                }
    +            else
    +                {  return undef;  };
    +            }
    +
    +        elsif ($mode == MODE_IN_IDENTIFIER)
    +            {
    +            if ($tokens->[$index] eq '.')
    +                {
    +                $identifier .= '.';
    +                $index++;
    +
    +                $mode = MODE_IDENTIFIER_START;
    +                }
    +            elsif ($tokens->[$index] =~ /^[a-z0-9\$\_]/i)
    +                {
    +                $identifier .= $tokens->[$index];
    +                $index++;
    +                }
    +            else
    +                {  last;  };
    +            }
    +
    +        else #($mode == MODE_AFTER_STAR)
    +            {
    +            if ($tokens->[$index] =~ /^[a-z0-9\$\_\.]/i)
    +                {  return undef;  }
    +            else
    +                {  last;  };
    +            };
    +        };
    +
    +    # We need to check again because we may have run out of tokens after a dot.
    +    if ($mode != MODE_IDENTIFIER_START)
    +        {
    +        $$indexRef = $index;
    +        return $identifier;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToGetImport
    +#
    +#   Determines whether the position is at a import statement, and if so, adds it as a Using statement to the current scope, skips
    +#   it, and returns true.
    +#
    +sub TryToGetImport #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($tokens->[$index] ne 'import')
    +        {  return undef;  };
    +
    +    $index++;
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $identifier = $self->TryToGetIdentifier(\$index, \$lineNumber, 1);
    +    if (!$identifier)
    +        {  return undef;  };
    +
    +
    +    # Currently we implement importing by stripping the last package level and treating it as a using.  So "import p1.p2.p3" makes
    +    # p1.p2 the using path, which is over-tolerant but that's okay.  "import p1.p2.*" is treated the same way, but in this case it's
    +    # not over-tolerant.  If there's no dot, there's no point to including it.
    +
    +    if (index($identifier, '.') != -1)
    +        {
    +        $identifier =~ s/\.[^\.]+$//;
    +        $self->AddUsing( NaturalDocs::SymbolString->FromText($identifier) );
    +        };
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetClass
    +#
    +#   Determines whether the position is at a class declaration statement, and if so, generates a topic for it, skips it, and
    +#   returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Classes
    +#       - Interfaces
    +#       - Classes and interfaces with _global
    +#
    +sub TryToGetClass #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              exists $classModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    my $type;
    +
    +    if ($tokens->[$index] eq 'class' || $tokens->[$index] eq 'interface')
    +        {
    +        $type = $tokens->[$index];
    +
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        }
    +    else
    +        {  return undef;  };
    +
    +    my $className = $self->TryToGetIdentifier(\$index, \$lineNumber);
    +
    +    if (!$className)
    +        {  return undef;  };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my @parents;
    +
    +    if ($tokens->[$index] eq 'extends')
    +        {
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        # Interfaces can extend multiple other interfaces, which is NOT clearly mentioned in the docs.
    +
    +        for (;;)
    +        	{
    +	        my $parent = $self->TryToGetIdentifier(\$index, \$lineNumber);
    +	        if (!$parent)
    +	            {  return undef;  };
    +
    +	        push @parents, $parent;
    +
    +	        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            if ($tokens->[$index] ne ',')
    +                {  last;  }
    +            else
    +                {
    +                $index++;
    +                $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +                };
    +	        }
    +        };
    +
    +    if ($type eq 'class' && $tokens->[$index] eq 'implements')
    +        {
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        for (;;)
    +            {
    +            my $parent = $self->TryToGetIdentifier(\$index, \$lineNumber);
    +            if (!$parent)
    +                {  return undef;  };
    +
    +            push @parents, $parent;
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            if ($tokens->[$index] ne ',')
    +                {  last;  }
    +            else
    +                {
    +                $index++;
    +                $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +                };
    +            };
    +        };
    +
    +    if ($tokens->[$index] ne '{')
    +        {  return undef;  };
    +
    +    $index++;
    +
    +
    +    # If we made it this far, we have a valid class declaration.
    +
    +    my $topicType;
    +
    +    if ($type eq 'interface')
    +        {  $topicType = ::TOPIC_INTERFACE();  }
    +    else
    +        {  $topicType = ::TOPIC_CLASS();  };
    +
    +    $className =~ s/^_global.//;
    +
    +    my $autoTopic = NaturalDocs::Parser::ParsedTopic->New($topicType, $className,
    +                                                                                         undef, $self->CurrentUsing(),
    +                                                                                         undef,
    +                                                                                         undef, undef, $$lineNumberRef);
    +
    +    $self->AddAutoTopic($autoTopic);
    +    NaturalDocs::Parser->OnClass($autoTopic->Package());
    +
    +    foreach my $parent (@parents)
    +        {
    +        NaturalDocs::Parser->OnClassParent($autoTopic->Package(), NaturalDocs::SymbolString->FromText($parent),
    +                                                               undef, $self->CurrentUsing(), ::RESOLVE_ABSOLUTE());
    +        };
    +
    +    $self->StartScope('}', $lineNumber, $autoTopic->Package());
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetFunction
    +#
    +#   Determines if the position is on a function declaration, and if so, generates a topic for it, skips it, and returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Functions
    +#       - Constructors
    +#       - Properties
    +#       - Functions with _global
    +#       - Functions with namespaces
    +#
    +sub TryToGetFunction #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +    my $namespace;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i)
    +        {
    +        if ($tokens->[$index] eq 'function')
    +            {  last;  }
    +
    +        elsif (exists $memberModifiers{lc($tokens->[$index])})
    +            {
    +            push @modifiers, lc($tokens->[$index]);
    +            $index++;
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +
    +        elsif (!$namespace)
    +            {
    +            do
    +                {
    +                $namespace .= $tokens->[$index];
    +                $index++;
    +                }
    +            while ($tokens->[$index] =~ /^[a-z0-9_]/i);
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +
    +        else
    +            {  last;  };
    +        };
    +
    +    if ($tokens->[$index] ne 'function')
    +        {  return undef;  };
    +    $index++;
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $type;
    +
    +    if ($tokens->[$index] eq 'get' || $tokens->[$index] eq 'set')
    +        {
    +        # This can either be a property ("function get Something()") or a function name ("function get()").
    +
    +        my $nextIndex = $index;
    +        my $nextLineNumber = $lineNumber;
    +
    +        $nextIndex++;
    +        $self->TryToSkipWhitespace(\$nextIndex, \$nextLineNumber);
    +
    +        if ($tokens->[$nextIndex] eq '(')
    +            {
    +            $type = ::TOPIC_FUNCTION();
    +            # Ignore the movement and let the code ahead pick it up as the name.
    +            }
    +        else
    +            {
    +            $type = ::TOPIC_PROPERTY();
    +            $index = $nextIndex;
    +            $lineNumber = $nextLineNumber;
    +            };
    +        }
    +    else
    +        {  $type = ::TOPIC_FUNCTION();  };
    +
    +    my $name = $self->TryToGetIdentifier(\$index, \$lineNumber);
    +    if (!$name)
    +        {  return undef;  };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    if ($tokens->[$index] ne '(')
    +        {  return undef;  };
    +
    +    $index++;
    +    $self->GenericSkipUntilAfter(\$index, \$lineNumber, ')');
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    if ($tokens->[$index] eq ':')
    +        {
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        $self->TryToGetIdentifier(\$index, \$lineNumber, 1);
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +
    +    my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) );
    +
    +    if ($tokens->[$index] eq '{')
    +        {  $self->GenericSkip(\$index, \$lineNumber);  }
    +    elsif (!exists $declarationEnders{$tokens->[$index]})
    +        {  return undef;  };
    +
    +
    +    my $scope = $self->CurrentScope();
    +
    +    if ($name =~ s/^_global.//)
    +        {  $scope = undef;  };
    +    if ($namespace)
    +        {  $scope = NaturalDocs::SymbolString->Join($scope, $namespace);  };
    +
    +    $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $name,
    +                                                                                              $scope, $self->CurrentUsing(),
    +                                                                                              $prototype,
    +                                                                                              undef, undef, $startLine));
    +
    +
    +    # We succeeded if we got this far.
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetVariable
    +#
    +#   Determines if the position is on a variable declaration statement, and if so, generates a topic for each variable, skips the
    +#   statement, and returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Variables
    +#       - Variables with _global
    +#       - Variables with type * (untyped)
    +#       - Constants
    +#       - Variables and constants with namespaces
    +#
    +sub TryToGetVariable #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +    my $namespace;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i)
    +        {
    +        if ($tokens->[$index] eq 'var' || $tokens->[$index] eq 'const')
    +            {  last;  }
    +
    +        elsif (exists $memberModifiers{lc($tokens->[$index])})
    +            {
    +            push @modifiers, lc($tokens->[$index]);
    +            $index++;
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +
    +        elsif (!$namespace)
    +            {
    +            do
    +                {
    +                $namespace .= $tokens->[$index];
    +                $index++;
    +                }
    +            while ($tokens->[$index] =~ /^[a-z0-9_]/i);
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +
    +        else
    +            {  last;  };
    +        };
    +
    +    my $type;
    +
    +    if ($tokens->[$index] eq 'var')
    +        {  $type = ::TOPIC_VARIABLE();  }
    +    elsif ($tokens->[$index] eq 'const')
    +        {  $type = ::TOPIC_CONSTANT();  }
    +    else
    +        {  return undef;  };
    +    $index++;
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $endTypeIndex = $index;
    +    my @names;
    +    my @types;
    +
    +    for (;;)
    +        {
    +        my $name = $self->TryToGetIdentifier(\$index, \$lineNumber);
    +        if (!$name)
    +            {  return undef;  };
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        my $type;
    +
    +        if ($tokens->[$index] eq ':')
    +            {
    +            $index++;
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            $type = ': ' . $self->TryToGetIdentifier(\$index, \$lineNumber, 1);
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            };
    +
    +        if ($tokens->[$index] eq '=')
    +            {
    +            do
    +                {
    +                $self->GenericSkip(\$index, \$lineNumber);
    +                }
    +            while ($tokens->[$index] ne ',' && !exists $declarationEnders{$tokens->[$index]} && $index < scalar @$tokens);
    +            };
    +
    +        push @names, $name;
    +        push @types, $type;
    +
    +        if ($tokens->[$index] eq ',')
    +            {
    +            $index++;
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +        elsif (exists $declarationEnders{$tokens->[$index]})
    +            {  last;  }
    +        else
    +            {  return undef;  };
    +        };
    +
    +
    +    # We succeeded if we got this far.
    +
    +    my $prototypePrefix = $self->CreateString($startIndex, $endTypeIndex);
    +
    +    for (my $i = 0; $i < scalar @names; $i++)
    +        {
    +        my $prototype = $self->NormalizePrototype( $prototypePrefix . ' ' . $names[$i] . $types[$i]);
    +        my $scope = $self->CurrentScope();
    +
    +        if ($names[$i] =~ s/^_global.//)
    +            {  $scope = undef;  };
    +        if ($namespace)
    +            {  $scope = NaturalDocs::SymbolString->Join($scope, $namespace);  };
    +
    +        $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $names[$i],
    +                                                                                                  $scope, $self->CurrentUsing(),
    +                                                                                                  $prototype,
    +                                                                                                  undef, undef, $startLine));
    +        };
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +
    +################################################################################
    +# Group: Low Level Parsing Functions
    +
    +
    +#
    +#   Function: GenericSkip
    +#
    +#   Advances the position one place through general code.
    +#
    +#   - If the position is on a string, it will skip it completely.
    +#   - If the position is on an opening symbol, it will skip until the past the closing symbol.
    +#   - If the position is on whitespace (including comments), it will skip it completely.
    +#   - Otherwise it skips one token.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the current index.
    +#       lineNumberRef - A reference to the current line number.
    +#
    +sub GenericSkip #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway.
    +    if ($tokens->[$$indexRef] eq '{')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '(')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '[')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']');
    +        }
    +
    +    elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipString($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipRegExp($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipXML($indexRef, $lineNumberRef) )
    +        {
    +        }
    +
    +    else
    +        {  $$indexRef++;  };
    +    };
    +
    +
    +#
    +#   Function: GenericSkipUntilAfter
    +#
    +#   Advances the position via <GenericSkip()> until a specific token is reached and passed.
    +#
    +sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $token) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token)
    +        {  $self->GenericSkip($indexRef, $lineNumberRef);  };
    +
    +    if ($tokens->[$$indexRef] eq "\n")
    +        {  $$lineNumberRef++;  };
    +    $$indexRef++;
    +    };
    +
    +
    +#
    +#   Function: IndiscriminateSkipUntilAfterSequence
    +#
    +#   Advances the position indiscriminately until a specific token sequence is reached and passed.
    +#
    +sub IndiscriminateSkipUntilAfterSequence #(indexRef, lineNumberRef, token, token, ...)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, @sequence) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens && !$self->IsAtSequence($$indexRef, @sequence))
    +        {
    +        if ($tokens->[$$indexRef] eq "\n")
    +            {  $$lineNumberRef++;  };
    +        $$indexRef++;
    +        };
    +
    +    if ($self->IsAtSequence($$indexRef, @sequence))
    +        {
    +        $$indexRef += scalar @sequence;
    +        foreach my $token (@sequence)
    +            {
    +            if ($token eq "\n")
    +                {  $$lineNumberRef++;  };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: SkipToNextStatement
    +#
    +#   Advances the position via <GenericSkip()> until the next statement, which is defined as anything in <declarationEnders> not
    +#   appearing in brackets or strings.  It will always advance at least one token.
    +#
    +sub SkipToNextStatement #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq ';')
    +        {  $$indexRef++;  }
    +
    +    else
    +        {
    +        do
    +            {
    +            $self->GenericSkip($indexRef, $lineNumberRef);
    +            }
    +        while ( $$indexRef < scalar @$tokens &&
    +                  !exists $declarationEnders{$tokens->[$$indexRef]} );
    +        };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipRegExp
    +#   If the current position is on a regular expression, skip past it and return true.
    +#
    +sub TryToSkipRegExp #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '/')
    +        {
    +        # A slash can either start a regular expression or be a divide symbol.  Skip backwards to see what the previous symbol is.
    +        my $index = $$indexRef - 1;
    +
    +        while ($index >= 0 && $tokens->[$index] =~ /^(?: |\t|\n)/)
    +            {  $index--;  };
    +
    +        if ($index < 0 || $tokens->[$index] !~ /^[\:\=\(\[\,]/)
    +            {  return 0;  };
    +
    +        $$indexRef++;
    +
    +        while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne '/')
    +            {
    +            if ($tokens->[$$indexRef] eq '\\')
    +                {  $$indexRef += 2;  }
    +            elsif ($tokens->[$$indexRef] eq "\n")
    +                {
    +                $$indexRef++;
    +                $$lineNumberRef++;
    +                }
    +            else
    +                {  $$indexRef++;  }
    +            };
    +
    +        if ($$indexRef < scalar @$tokens)
    +            {
    +            $$indexRef++;
    +
    +            if ($tokens->[$$indexRef] =~ /^[gimsx]+$/i)
    +                {  $$indexRef++;  };
    +            };
    +
    +        return 1;
    +        }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipXML
    +#   If the current position is on an XML literal, skip past it and return true.
    +#
    +sub TryToSkipXML #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '<')
    +        {
    +        # A < can either start an XML literal or be a comparison or shift operator.  First check the next character for << or <=.
    +
    +        my $index = $$indexRef + 1;
    +
    +        while ($index < scalar @$tokens && $tokens->[$index] =~ /^[\=\<]$/)
    +            {  return 0;  };
    +
    +
    +        # Next try the previous character.
    +
    +        $index = $$indexRef - 1;
    +
    +        while ($index >= 0 && $tokens->[$index] =~ /^[ |\t|\n]/)
    +            {  $index--;  };
    +
    +        if ($index < 0 || $tokens->[$index] !~ /^[\=\(\[\,\>]/)
    +            {  return 0;  };
    +        }
    +    else
    +        {  return 0;  };
    +
    +
    +    # Only handle the tag here if it's not an irregular XML section.
    +    if (!$self->TryToSkipIrregularXML($indexRef, $lineNumberRef))
    +        {
    +        my @tagStack;
    +
    +        my ($tagType, $tagIdentifier) = $self->GetAndSkipXMLTag($indexRef, $lineNumberRef);
    +        if ($tagType == XML_OPENING_TAG)
    +            {  push @tagStack, $tagIdentifier;  };
    +
    +        while (scalar @tagStack && $$indexRef < scalar @$tokens)
    +            {
    +            $self->SkipToNextXMLTag($indexRef, $lineNumberRef);
    +            ($tagType, $tagIdentifier) = $self->GetAndSkipXMLTag($indexRef, $lineNumberRef);
    +
    +            if ($tagType == XML_OPENING_TAG)
    +                {  push @tagStack, $tagIdentifier;  }
    +            elsif ($tagType == XML_CLOSING_TAG && $tagIdentifier eq $tagStack[-1])
    +                {  pop @tagStack;  };
    +            };
    +        };
    +
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipIrregularXML
    +#
    +#   If the current position is on an irregular XML tag, skip past it and return true.  Irregular XML tags are defined as
    +#
    +#       CDATA - <![CDATA[ ... ]]>
    +#       Comments - <!-- ... -->
    +#       PI - <? ... ?>
    +#
    +sub TryToSkipIrregularXML #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +
    +    if ($self->IsAtSequence($$indexRef, '<', '!', '[', 'CDATA', '['))
    +        {
    +        $$indexRef += 5;
    +        $self->IndiscriminateSkipUntilAfterSequence($indexRef, $lineNumberRef, ']', ']', '>');
    +        return 1;
    +        }
    +
    +    elsif ($self->IsAtSequence($$indexRef, '<', '!', '-', '-'))
    +        {
    +        $$indexRef += 4;
    +        $self->IndiscriminateSkipUntilAfterSequence($indexRef, $lineNumberRef, '-', '-', '>');
    +        return 1;
    +        }
    +
    +    elsif ($self->IsAtSequence($$indexRef, '<', '?'))
    +        {
    +        $$indexRef += 2;
    +        $self->IndiscriminateSkipUntilAfterSequence($indexRef, $lineNumberRef, '?', '>');
    +        return 1;
    +        }
    +
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: GetAndSkipXMLTag
    +#
    +#   Processes the XML tag at the current position, moves beyond it, and returns information about it.  Assumes the position is on
    +#   the opening angle bracket of the tag and the tag is a normal XML tag, not one of the ones handled by
    +#   <TryToSkipIrregularXML()>.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the index of the position of the opening angle bracket.
    +#       lineNumberRef - A reference to the line number of the position of the opening angle bracket.
    +#
    +#   Returns:
    +#
    +#       The array ( tagType, name ).
    +#
    +#       tagType - One of the <XML Tag Type> constants.
    +#       identifier - The identifier of the tag.  If it's an empty tag (<> or </>), this will be "(anonymous)".
    +#
    +sub GetAndSkipXMLTag #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne '<')
    +        {  die "Tried to call GetXMLTag when the position isn't on an opening bracket.";  };
    +
    +    # Get the anonymous ones out of the way so we don't have to worry about them below, since they're rather exceptional.
    +
    +    if ($self->IsAtSequence($$indexRef, '<', '>'))
    +        {
    +        $$indexRef += 2;
    +        return ( XML_OPENING_TAG, '(anonymous)' );
    +        }
    +    elsif ($self->IsAtSequence($$indexRef, '<', '/', '>'))
    +        {
    +        $$indexRef += 3;
    +        return ( XML_CLOSING_TAG, '(anonymous)' );
    +        };
    +
    +
    +    # Grab the identifier.
    +
    +    my $tagType = XML_OPENING_TAG;
    +    my $identifier;
    +
    +    $$indexRef++;
    +
    +    if ($tokens->[$$indexRef] eq '/')
    +        {
    +        $$indexRef++;
    +        $tagType = XML_CLOSING_TAG;
    +        };
    +
    +    $self->TryToSkipXMLWhitespace($indexRef, $lineNumberRef);
    +
    +
    +    # The identifier could be a native expression in braces.
    +
    +    if ($tokens->[$$indexRef] eq '{')
    +        {
    +        my $startOfIdentifier = $$indexRef;
    +
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +
    +        $identifier = $self->CreateString($startOfIdentifier, $$indexRef);
    +        }
    +
    +
    +    # Otherwise just grab content until whitespace or the end of the tag.
    +
    +    else
    +        {
    +        while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] !~ /^[\/\>\ \t]$/)
    +            {
    +            $identifier .= $tokens->[$$indexRef];
    +            $$indexRef++;
    +            };
    +        };
    +
    +
    +    # Skip to the end of the tag.
    +
    +    while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] !~ /^[\/\>]$/)
    +        {
    +        if ($tokens->[$$indexRef] eq '{')
    +            {
    +            $$indexRef++;
    +            $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +            }
    +
    +        elsif ($self->TryToSkipXMLWhitespace($indexRef, $lineNumberRef))
    +            {  }
    +
    +        # We don't need to do special handling for attribute quotes or anything like that because there's no backslashing in
    +        # XML.  It's all handled with entity characters.
    +        else
    +            {  $$indexRef++;  };
    +        };
    +
    +
    +    if ($tokens->[$$indexRef] eq '/')
    +        {
    +        if ($tagType == XML_OPENING_TAG)
    +            {  $tagType = XML_SELF_CONTAINED_TAG;  };
    +
    +        $$indexRef++;
    +        };
    +
    +    if ($tokens->[$$indexRef] eq '>')
    +        {  $$indexRef++;  };
    +
    +    if (!$identifier)
    +        {  $identifier = '(anonymous)';  };
    +
    +
    +    return ( $tagType, $identifier );
    +    };
    +
    +
    +#
    +#   Function: SkipToNextXMLTag
    +#   Skips to the next normal XML tag.  It will not stop at elements handled by <TryToSkipIrregularXML()>.  Note that if the
    +#   position is already at an XML tag, it will not move.
    +#
    +sub SkipToNextXMLTag #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        if ($tokens->[$$indexRef] eq '{')
    +            {
    +            $$indexRef++;
    +            $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +            }
    +
    +        elsif ($self->TryToSkipIrregularXML($indexRef, $lineNumberRef))
    +            {  }
    +
    +        elsif ($tokens->[$$indexRef] eq '<')
    +            {  last;  }
    +
    +        else
    +            {
    +            if ($tokens->[$$indexRef] eq "\n")
    +                {  $$lineNumberRef++;  };
    +
    +            $$indexRef++;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipXMLWhitespace
    +#   If the current position is on XML whitespace, skip past it and return true.
    +#
    +sub TryToSkipXMLWhitespace #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $result;
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        if ($tokens->[$$indexRef] =~ /^[ \t]/)
    +            {
    +            $$indexRef++;
    +            $result = 1;
    +            }
    +        elsif ($tokens->[$$indexRef] eq "\n")
    +            {
    +            $$indexRef++;
    +            $$lineNumberRef++;
    +            $result = 1;
    +            }
    +        else
    +            {  last;  };
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipString
    +#   If the current position is on a string delimiter, skip past the string and return true.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the index of the position to start at.
    +#       lineNumberRef - A reference to the line number of the position.
    +#
    +#   Returns:
    +#
    +#       Whether the position was at a string.
    +#
    +#   Syntax Support:
    +#
    +#       - Supports quotes and apostrophes.
    +#
    +sub TryToSkipString #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +
    +    return ($self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'') ||
    +               $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"') );
    +    };
    +
    +
    +#
    +#   Function: TryToSkipWhitespace
    +#   If the current position is on a whitespace token, a line break token, or a comment, it skips them and returns true.  If there are
    +#   a number of these in a row, it skips them all.
    +#
    +sub TryToSkipWhitespace #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $result;
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        if ($tokens->[$$indexRef] =~ /^[ \t]/)
    +            {
    +            $$indexRef++;
    +            $result = 1;
    +            }
    +        elsif ($tokens->[$$indexRef] eq "\n")
    +            {
    +            $$indexRef++;
    +            $$lineNumberRef++;
    +            $result = 1;
    +            }
    +        elsif ($self->TryToSkipComment($indexRef, $lineNumberRef))
    +            {
    +            $result = 1;
    +            }
    +        else
    +            {  last;  };
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipComment
    +#   If the current position is on a comment, skip past it and return true.
    +#
    +sub TryToSkipComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +
    +    return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) ||
    +                $self->TryToSkipMultilineComment($indexRef, $lineNumberRef) );
    +    };
    +
    +
    +#
    +#   Function: TryToSkipLineComment
    +#   If the current position is on a line comment symbol, skip past it and return true.
    +#
    +sub TryToSkipLineComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '/')
    +        {
    +        $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipMultilineComment
    +#   If the current position is on an opening comment symbol, skip past it and return true.
    +#
    +sub TryToSkipMultilineComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '*')
    +        {
    +        $self->SkipUntilAfter($indexRef, $lineNumberRef, '*', '/');
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Ada.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Ada.pm
    new file mode 100644
    index 000000000..062c5ae97
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Ada.pm
    @@ -0,0 +1,39 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Ada
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of Ada
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Ada;
    +
    +use base 'NaturalDocs::Languages::Simple';
    +
    +
    +#
    +#   Function: ParseParameterLine
    +#   Overridden because Ada uses Pascal-style parameters
    +#
    +sub ParseParameterLine #(...)
    +    {
    +    my ($self, @params) = @_;
    +    return $self->SUPER::ParsePascalParameterLine(@params);
    +    };
    +
    +sub TypeBeforeParameter
    +    {
    +    return 0;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced.pm
    new file mode 100644
    index 000000000..d6cafa7eb
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced.pm
    @@ -0,0 +1,817 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Advanced
    +#
    +###############################################################################
    +#
    +#   The base class for all languages that have full support in Natural Docs.  Each one will have a custom parser capable
    +#   of documenting undocumented aspects of the code.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::Languages::Advanced::Scope;
    +use NaturalDocs::Languages::Advanced::ScopeChange;
    +
    +package NaturalDocs::Languages::Advanced;
    +
    +use base 'NaturalDocs::Languages::Base';
    +
    +
    +#############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are used as indexes.
    +#
    +#   TOKENS - An arrayref of tokens used in all the <Parsing Functions>.
    +#   SCOPE_STACK - An arrayref of <NaturalDocs::Languages::Advanced::Scope> objects serving as a scope stack for parsing.
    +#                            There will always be one available, with a symbol of undef, for the top level.
    +#   SCOPE_RECORD - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChange> objects, as generated by the scope
    +#                              stack.  If there is more than one change per line, only the last is stored.
    +#   AUTO_TOPICS - An arrayref of <NaturalDocs::Parser::ParsedTopics> generated automatically from the code.
    +#
    +use NaturalDocs::DefineMembers 'TOKENS', 'SCOPE_STACK', 'SCOPE_RECORD', 'AUTO_TOPICS';
    +
    +
    +#############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       name - The name of the language.
    +#
    +sub New #(name)
    +    {
    +    my ($package, @parameters) = @_;
    +
    +    my $object = $package->SUPER::New(@parameters);
    +    $object->[TOKENS] = undef;
    +    $object->[SCOPE_STACK] = undef;
    +    $object->[SCOPE_RECORD] = undef;
    +
    +    return $object;
    +    };
    +
    +
    +# Function: Tokens
    +# Returns the tokens found by <ParseForCommentsAndTokens()>.
    +sub Tokens
    +    {  return $_[0]->[TOKENS];  };
    +
    +# Function: SetTokens
    +# Replaces the tokens.
    +sub SetTokens #(tokens)
    +    {  $_[0]->[TOKENS] = $_[1];  };
    +
    +# Function: ClearTokens
    +#  Resets the token list.  You may want to do this after parsing is over to save memory.
    +sub ClearTokens
    +    {  $_[0]->[TOKENS] = undef;  };
    +
    +# Function: AutoTopics
    +# Returns the arrayref of automatically generated topics, or undef if none.
    +sub AutoTopics
    +    {  return $_[0]->[AUTO_TOPICS];  };
    +
    +# Function: AddAutoTopic
    +# Adds a <NaturalDocs::Parser::ParsedTopic> to <AutoTopics()>.
    +sub AddAutoTopic #(topic)
    +    {
    +    my ($self, $topic) = @_;
    +    if (!defined $self->[AUTO_TOPICS])
    +        {  $self->[AUTO_TOPICS] = [ ];  };
    +    push @{$self->[AUTO_TOPICS]}, $topic;
    +    };
    +
    +# Function: ClearAutoTopics
    +# Resets the automatic topic list.  Not necessary if you call <ParseForCommentsAndTokens()>.
    +sub ClearAutoTopics
    +    {  $_[0]->[AUTO_TOPICS] = undef;  };
    +
    +# Function: ScopeRecord
    +# Returns an arrayref of <NaturalDocs::Languages::Advanced::ScopeChange> objects describing how and when the scope
    +# changed thoughout the file.  There will always be at least one entry, which will be for line 1 and undef as the scope.
    +sub ScopeRecord
    +    {  return $_[0]->[SCOPE_RECORD];  };
    +
    +
    +
    +###############################################################################
    +#
    +#   Group: Parsing Functions
    +#
    +#   These functions are good general language building blocks.  Use them to create your language-specific parser.
    +#
    +#   All functions work on <Tokens()> and assume it is set by <ParseForCommentsAndTokens()>.
    +#
    +
    +
    +#
    +#   Function: ParseForCommentsAndTokens
    +#
    +#   Loads the passed file, sends all appropriate comments to <NaturalDocs::Parser->OnComment()>, and breaks the rest into
    +#   an arrayref of tokens.  Tokens are defined as
    +#
    +#   - All consecutive alphanumeric and underscore characters.
    +#   - All consecutive whitespace.
    +#   - A single line break.  It will always be "\n"; you don't have to worry about platform differences.
    +#   - A single character not included above, which is usually a symbol.  Multiple consecutive ones each get their own token.
    +#
    +#   The result will be placed in <Tokens()>.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The source <FileName> to load and parse.
    +#       lineCommentSymbols - An arrayref of symbols that designate line comments, or undef if none.
    +#       blockCommentSymbols - An arrayref of symbol pairs that designate multiline comments, or undef if none.  Symbol pairs are
    +#                                            designated as two consecutive array entries, the opening symbol appearing first.
    +#       javadocLineCommentSymbols - An arrayref of symbols that designate the start of a JavaDoc comment, or undef if none.
    +#       javadocBlockCommentSymbols - An arrayref of symbol pairs that designate multiline JavaDoc comments, or undef if none.
    +#
    +#   Notes:
    +#
    +#       - This function automatically calls <ClearAutoTopics()> and <ClearScopeStack()>.  You only need to call those functions
    +#         manually if you override this one.
    +#       - To save parsing time, all comment lines sent to <NaturalDocs::Parser->OnComment()> will be replaced with blank lines
    +#         in <Tokens()>.  It's all the same to most languages.
    +#
    +sub ParseForCommentsAndTokens #(FileName sourceFile, string[] lineCommentSymbols, string[] blockCommentSymbols, string[] javadocLineCommentSymbols, string[] javadocBlockCommentSymbols)
    +    {
    +    my ($self, $sourceFile, $lineCommentSymbols, $blockCommentSymbols,
    +           $javadocLineCommentSymbols, $javadocBlockCommentSymbols) = @_;
    +
    +    open(SOURCEFILEHANDLE, '<' . $sourceFile)
    +        or die "Couldn't open input file " . $sourceFile . "\n";
    +
    +    my $lineReader = NaturalDocs::LineReader->New(\*SOURCEFILEHANDLE);
    +
    +    my $tokens = [ ];
    +    $self->SetTokens($tokens);
    +
    +    # For convenience.
    +    $self->ClearAutoTopics();
    +    $self->ClearScopeStack();
    +
    +
    +    # Load and preprocess the file
    +
    +    my @lines = $lineReader->GetAll();
    +    close(SOURCEFILEHANDLE);
    +
    +    $self->PreprocessFile(\@lines);
    +
    +
    +    # Go through the file
    +
    +    my $lineIndex = 0;
    +
    +    while ($lineIndex < scalar @lines)
    +        {
    +        my $line = $lines[$lineIndex];
    +
    +        my @commentLines;
    +        my $commentLineNumber;
    +        my $isJavaDoc;
    +        my $closingSymbol;
    +
    +
    +        # Retrieve single line comments.  This leaves $lineIndex at the next line.
    +
    +        if ( ($isJavaDoc = $self->StripOpeningJavaDocSymbols(\$line, $javadocLineCommentSymbols)) ||
    +              $self->StripOpeningSymbols(\$line, $lineCommentSymbols))
    +            {
    +            $commentLineNumber = $lineIndex + 1;
    +
    +            do
    +                {
    +                push @commentLines, $line;
    +                push @$tokens, "\n";
    +
    +                $lineIndex++;
    +
    +                if ($lineIndex >= scalar @lines)
    +                    {  goto EndDo;  };
    +
    +                $line = $lines[$lineIndex];
    +                }
    +            while ($self->StripOpeningSymbols(\$line, $lineCommentSymbols));
    +
    +            EndDo:  # I hate Perl sometimes.
    +            }
    +
    +
    +        # Retrieve multiline comments.  This leaves $lineIndex at the next line.
    +
    +        elsif ( ($isJavaDoc = $self->StripOpeningJavaDocBlockSymbols(\$line, $javadocBlockCommentSymbols)) ||
    +                 ($closingSymbol = $self->StripOpeningBlockSymbols(\$line, $blockCommentSymbols)) )
    +            {
    +            $commentLineNumber = $lineIndex + 1;
    +
    +            if ($isJavaDoc)
    +                {  $closingSymbol = $isJavaDoc;  };
    +
    +            # Note that it is possible for a multiline comment to start correctly but not end so.  We want those comments to stay in
    +            # the code.  For example, look at this prototype with this splint annotation:
    +            #
    +            # int get_array(integer_t id,
    +            #                    /*@out@*/ array_t array);
    +            #
    +            # The annotation starts correctly but doesn't end so because it is followed by code on the same line.
    +
    +            my ($lineRemainder, $isMultiLine);
    +
    +            for (;;)
    +                {
    +                $lineRemainder = $self->StripClosingSymbol(\$line, $closingSymbol);
    +
    +                push @commentLines, $line;
    +
    +                #  If we found an end comment symbol...
    +                if (defined $lineRemainder)
    +                    {  last;  };
    +
    +                push @$tokens, "\n";
    +                $lineIndex++;
    +                $isMultiLine = 1;
    +
    +                if ($lineIndex >= scalar @lines)
    +                    {  last;  };
    +
    +                $line = $lines[$lineIndex];
    +                };
    +
    +            if ($lineRemainder !~ /^[ \t]*$/)
    +                {
    +                # If there was something past the closing symbol this wasn't an acceptable comment.
    +
    +                if ($isMultiLine)
    +                    {  $self->TokenizeLine($lineRemainder);  }
    +                else
    +                    {
    +                    # We go back to the original line if it wasn't a multiline comment because we want the comment to stay in the
    +                    # code.  Otherwise the /*@out@*/ from the example would be removed.
    +                    $self->TokenizeLine($lines[$lineIndex]);
    +                    };
    +
    +                @commentLines = ( );
    +                }
    +            else
    +                {
    +                push @$tokens, "\n";
    +                };
    +
    +            $lineIndex++;
    +            }
    +
    +
    +        # Otherwise just add it to the code.
    +
    +        else
    +            {
    +            $self->TokenizeLine($line);
    +            $lineIndex++;
    +            };
    +
    +
    +        # If there were comments, send them to Parser->OnComment().
    +
    +        if (scalar @commentLines)
    +            {
    +            NaturalDocs::Parser->OnComment(\@commentLines, $commentLineNumber, $isJavaDoc);
    +            @commentLines = ( );
    +            $isJavaDoc = undef;
    +            };
    +
    +        # $lineIndex was incremented by the individual code paths above.
    +
    +        };  # while ($lineIndex < scalar @lines)
    +    };
    +
    +
    +#
    +#   Function: PreprocessFile
    +#
    +#   An overridable function if you'd like to preprocess the file before it goes into <ParseForCommentsAndTokens()>.
    +#
    +#   Parameters:
    +#
    +#       lines - An arrayref to the file's lines.  Each line has its line break stripped off, but is otherwise untouched.
    +#
    +sub PreprocessFile #(lines)
    +    {
    +    };
    +
    +
    +#
    +#   Function: TokenizeLine
    +#
    +#   Converts the passed line to tokens as described in <ParseForCommentsAndTokens> and adds them to <Tokens()>.  Also
    +#   adds a line break token after it.
    +#
    +sub TokenizeLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +    push @{$self->Tokens()}, $line =~ /(\w+|[ \t]+|.)/g, "\n";
    +    };
    +
    +
    +#
    +#   Function: TryToSkipString
    +#
    +#   If the position is on a string delimiter, moves the position to the token following the closing delimiter, or past the end of the
    +#   tokens if there is none.  Assumes all other characters are allowed in the string, the delimiter itself is allowed if it's preceded by
    +#   a backslash, and line breaks are allowed in the string.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the position's index into <Tokens()>.
    +#       lineNumberRef - A reference to the position's line number.
    +#       openingDelimiter - The opening string delimiter, such as a quote or an apostrophe.
    +#       closingDelimiter - The closing string delimiter, if different.  If not defined, assumes the same as openingDelimiter.
    +#       startContentIndexRef - A reference to a variable in which to store the index of the first token of the string's content.
    +#                                         May be undef.
    +#       endContentIndexRef - A reference to a variable in which to store the index of the end of the string's content, which is one
    +#                                        past the last index of content.  May be undef.
    +#
    +#   Returns:
    +#
    +#       Whether the position was on the passed delimiter or not.  The index, line number, and content index ref variables will be
    +#       updated only if true.
    +#
    +sub TryToSkipString #(indexRef, lineNumberRef, openingDelimiter, closingDelimiter, startContentIndexRef, endContentIndexRef)
    +    {
    +    my ($self, $index, $lineNumber, $openingDelimiter, $closingDelimiter, $startContentIndexRef, $endContentIndexRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if (!defined $closingDelimiter)
    +        {  $closingDelimiter = $openingDelimiter;  };
    +
    +    if ($tokens->[$$index] ne $openingDelimiter)
    +        {  return undef;  };
    +
    +
    +    $$index++;
    +    if (defined $startContentIndexRef)
    +        {  $$startContentIndexRef = $$index;  };
    +
    +    while ($$index < scalar @$tokens)
    +        {
    +        if ($tokens->[$$index] eq "\\")
    +            {
    +            # Skip the token after it.
    +            $$index += 2;
    +            }
    +        elsif ($tokens->[$$index] eq "\n")
    +            {
    +            $$lineNumber++;
    +            $$index++;
    +            }
    +        elsif ($tokens->[$$index] eq $closingDelimiter)
    +            {
    +            if (defined $endContentIndexRef)
    +                {  $$endContentIndexRef = $$index;  };
    +
    +            $$index++;
    +            last;
    +            }
    +        else
    +            {
    +            $$index++;
    +            };
    +        };
    +
    +    if ($$index >= scalar @$tokens && defined $endContentIndexRef)
    +        {  $$endContentIndexRef = scalar @$tokens;  };
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: SkipRestOfLine
    +#
    +#   Moves the position to the token following the next line break, or past the end of the tokens array if there is none.  Useful for
    +#   line comments.
    +#
    +#   Note that it skips blindly.  It assumes there cannot be anything of interest, such as a string delimiter, between the position
    +#   and the end of the line.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the position's index into <Tokens()>.
    +#       lineNumberRef - A reference to the position's line number.
    +
    +sub SkipRestOfLine #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $index, $lineNumber) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$index < scalar @$tokens)
    +        {
    +        if ($tokens->[$$index] eq "\n")
    +            {
    +            $$lineNumber++;
    +            $$index++;
    +            last;
    +            }
    +        else
    +            {
    +            $$index++;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: SkipUntilAfter
    +#
    +#   Moves the position to the token following the next occurance of a particular token sequence, or past the end of the tokens
    +#   array if it never occurs.  Useful for multiline comments.
    +#
    +#   Note that it skips blindly.  It assumes there cannot be anything of interest, such as a string delimiter, between the position
    +#   and the end of the line.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the position's index.
    +#       lineNumberRef - A reference to the position's line number.
    +#       token - A token that must be matched.  Can be specified multiple times to match a sequence of tokens.
    +#
    +sub SkipUntilAfter #(indexRef, lineNumberRef, token, token, ...)
    +    {
    +    my ($self, $index, $lineNumber, @target) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$index < scalar @$tokens)
    +        {
    +        if ($tokens->[$$index] eq $target[0] && ($$index + scalar @target) <= scalar @$tokens)
    +            {
    +            my $match = 1;
    +
    +            for (my $i = 1; $i < scalar @target; $i++)
    +                {
    +                if ($tokens->[$$index+$i] ne $target[$i])
    +                    {
    +                    $match = 0;
    +                    last;
    +                    };
    +                };
    +
    +            if ($match)
    +                {
    +                $$index += scalar @target;
    +                return;
    +                };
    +            };
    +
    +        if ($tokens->[$$index] eq "\n")
    +            {
    +            $$lineNumber++;
    +            $$index++;
    +            }
    +        else
    +            {
    +            $$index++;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: IsFirstLineToken
    +#
    +#   Returns whether the position is at the first token of a line, not including whitespace.
    +#
    +#   Parameters:
    +#
    +#       index - The index of the position.
    +#
    +sub IsFirstLineToken #(index)
    +    {
    +    my ($self, $index) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($index == 0)
    +        {  return 1;  };
    +
    +    $index--;
    +
    +    if ($tokens->[$index] =~ /^[ \t]/)
    +        {  $index--;  };
    +
    +    if ($index <= 0 || $tokens->[$index] eq "\n")
    +        {  return 1;  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: IsLastLineToken
    +#
    +#   Returns whether the position is at the last token of a line, not including whitespace.
    +#
    +#   Parameters:
    +#
    +#       index - The index of the position.
    +#
    +sub IsLastLineToken #(index)
    +    {
    +    my ($self, $index) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    do
    +        {  $index++;  }
    +    while ($index < scalar @$tokens && $tokens->[$index] =~ /^[ \t]/);
    +
    +    if ($index >= scalar @$tokens || $tokens->[$index] eq "\n")
    +        {  return 1;  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: IsAtSequence
    +#
    +#   Returns whether the position is at a sequence of tokens.
    +#
    +#   Parameters:
    +#
    +#       index - The index of the position.
    +#       token - A token to match.  Specify multiple times to specify the sequence.
    +#
    +sub IsAtSequence #(index, token, token, token ...)
    +    {
    +    my ($self, $index, @target) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($index + scalar @target > scalar @$tokens)
    +        {  return undef;  };
    +
    +    for (my $i = 0; $i < scalar @target; $i++)
    +        {
    +        if ($tokens->[$index + $i] ne $target[$i])
    +            {  return undef;  };
    +        };
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: IsBackslashed
    +#
    +#   Returns whether the position is after a backslash.
    +#
    +#   Parameters:
    +#
    +#       index - The index of the postition.
    +#
    +sub IsBackslashed #(index)
    +    {
    +    my ($self, $index) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($index > 0 && $tokens->[$index - 1] eq "\\")
    +        {  return 1;  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +
    +###############################################################################
    +#
    +#   Group: Scope Functions
    +#
    +#   These functions provide a nice scope stack implementation for language-specific parsers to use.  The default implementation
    +#   makes the following assumptions.
    +#
    +#   - Packages completely replace one another, rather than concatenating.  You need to concatenate manually if that's the
    +#     behavior.
    +#
    +#   - Packages inherit, so if a scope level doesn't set its own, the package is the same as the parent scope's.
    +#
    +
    +
    +#
    +#   Function: ClearScopeStack
    +#
    +#   Clears the scope stack for a new file.  Not necessary if you call <ParseForCommentsAndTokens()>.
    +#
    +sub ClearScopeStack
    +    {
    +    my ($self) = @_;
    +    $self->[SCOPE_STACK] = [ NaturalDocs::Languages::Advanced::Scope->New(undef, undef) ];
    +    $self->[SCOPE_RECORD] = [ NaturalDocs::Languages::Advanced::ScopeChange->New(undef, 1) ];
    +    };
    +
    +
    +#
    +#   Function: StartScope
    +#
    +#   Records a new scope level.
    +#
    +#   Parameters:
    +#
    +#       closingSymbol - The closing symbol of the scope.
    +#       lineNumber - The line number where the scope begins.
    +#       package - The package <SymbolString> of the scope.  Undef means no change.
    +#
    +sub StartScope #(closingSymbol, lineNumber, package)
    +    {
    +    my ($self, $closingSymbol, $lineNumber, $package) = @_;
    +
    +    push @{$self->[SCOPE_STACK]},
    +            NaturalDocs::Languages::Advanced::Scope->New($closingSymbol, $package, $self->CurrentUsing());
    +
    +    $self->AddToScopeRecord($self->CurrentScope(), $lineNumber);
    +    };
    +
    +
    +#
    +#   Function: EndScope
    +#
    +#   Records the end of the current scope level.  Note that this is blind; you need to manually check <ClosingScopeSymbol()> if
    +#   you need to determine if it is correct to do so.
    +#
    +#   Parameters:
    +#
    +#       lineNumber - The line number where the scope ends.
    +#
    +sub EndScope #(lineNumber)
    +    {
    +    my ($self, $lineNumber) = @_;
    +
    +    if (scalar @{$self->[SCOPE_STACK]} > 1)
    +        {  pop @{$self->[SCOPE_STACK]};  };
    +
    +    $self->AddToScopeRecord($self->CurrentScope(), $lineNumber);
    +    };
    +
    +
    +#
    +#   Function: ClosingScopeSymbol
    +#
    +#   Returns the symbol that ends the current scope level, or undef if we are at the top level.
    +#
    +sub ClosingScopeSymbol
    +    {
    +    my ($self) = @_;
    +    return $self->[SCOPE_STACK]->[-1]->ClosingSymbol();
    +    };
    +
    +
    +#
    +#   Function: CurrentScope
    +#
    +#   Returns the current calculated scope, or undef if global.  The default implementation just returns <CurrentPackage()>.  This
    +#   is a separate function because C++ may need to track namespaces and classes separately, and so the current scope would
    +#   be a concatenation of them.
    +#
    +sub CurrentScope
    +    {
    +    return $_[0]->CurrentPackage();
    +    };
    +
    +
    +#
    +#   Function: CurrentPackage
    +#
    +#   Returns the current calculated package or class, or undef if none.
    +#
    +sub CurrentPackage
    +    {
    +    my ($self) = @_;
    +
    +    my $package;
    +
    +    for (my $index = scalar @{$self->[SCOPE_STACK]} - 1; $index >= 0 && !defined $package; $index--)
    +        {
    +        $package = $self->[SCOPE_STACK]->[$index]->Package();
    +        };
    +
    +    return $package;
    +    };
    +
    +
    +#
    +#   Function: SetPackage
    +#
    +#   Sets the package for the current scope level.
    +#
    +#   Parameters:
    +#
    +#       package - The new package <SymbolString>.
    +#       lineNumber - The line number the new package starts on.
    +#
    +sub SetPackage #(package, lineNumber)
    +    {
    +    my ($self, $package, $lineNumber) = @_;
    +    $self->[SCOPE_STACK]->[-1]->SetPackage($package);
    +
    +    $self->AddToScopeRecord($self->CurrentScope(), $lineNumber);
    +    };
    +
    +
    +#
    +#   Function: CurrentUsing
    +#
    +#   Returns the current calculated arrayref of <SymbolStrings> from Using statements, or undef if none.
    +#
    +sub CurrentUsing
    +    {
    +    my ($self) = @_;
    +    return $self->[SCOPE_STACK]->[-1]->Using();
    +    };
    +
    +
    +#
    +#   Function: AddUsing
    +#
    +#   Adds a Using <SymbolString> to the current scope.
    +#
    +sub AddUsing #(using)
    +    {
    +    my ($self, $using) = @_;
    +    $self->[SCOPE_STACK]->[-1]->AddUsing($using);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: AddToScopeRecord
    +#
    +#   Adds a change to the scope record, condensing unnecessary entries.
    +#
    +#   Parameters:
    +#
    +#       newScope - What the scope <SymbolString> changed to.
    +#       lineNumber - Where the scope changed.
    +#
    +sub AddToScopeRecord #(newScope, lineNumber)
    +    {
    +    my ($self, $scope, $lineNumber) = @_;
    +    my $scopeRecord = $self->ScopeRecord();
    +
    +    if ($scope ne $scopeRecord->[-1]->Scope())
    +        {
    +        if ($scopeRecord->[-1]->LineNumber() == $lineNumber)
    +            {  $scopeRecord->[-1]->SetScope($scope);  }
    +        else
    +            {  push @$scopeRecord, NaturalDocs::Languages::Advanced::ScopeChange->New($scope, $lineNumber);  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: CreateString
    +#
    +#   Converts the specified tokens into a string and returns it.
    +#
    +#   Parameters:
    +#
    +#       startIndex - The starting index to convert.
    +#       endIndex - The ending index, which is *not inclusive*.
    +#
    +#   Returns:
    +#
    +#       The string.
    +#
    +sub CreateString #(startIndex, endIndex)
    +    {
    +    my ($self, $startIndex, $endIndex) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $string;
    +
    +    while ($startIndex < $endIndex && $startIndex < scalar @$tokens)
    +        {
    +        $string .= $tokens->[$startIndex];
    +        $startIndex++;
    +        };
    +
    +    return $string;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm
    new file mode 100644
    index 000000000..39218107b
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm
    @@ -0,0 +1,96 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Advanced::Scope
    +#
    +###############################################################################
    +#
    +#   A class used to store a scope level.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Advanced::Scope;
    +
    +#
    +#   Constants: Implementation
    +#
    +#   The object is implemented as a blessed arrayref.  The constants below are used as indexes.
    +#
    +#   CLOSING_SYMBOL - The closing symbol character of the scope.
    +#   PACKAGE - The package <SymbolString> of the scope.
    +#   USING - An arrayref of <SymbolStrings> for using statements, or undef if none.
    +#
    +use NaturalDocs::DefineMembers 'CLOSING_SYMBOL', 'PACKAGE', 'USING';
    +# Dependency: New() depends on the order of these constants as well as that there is no inherited members.
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       closingSymbol - The closing symbol character of the scope.
    +#       package - The package <SymbolString> of the scope.
    +#       using - An arrayref of using <SymbolStrings>, or undef if none.  The contents of the array will be duplicated.
    +#
    +#       If package is set to undef, it is assumed that it inherits the value of the previous scope on the stack.
    +#
    +sub New #(closingSymbol, package, using)
    +    {
    +    # Dependency: This depends on the order of the parameters matching the constants, and that there are no inherited
    +    # members.
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    if (defined $object->[USING])
    +        {  $object->[USING] = [ @{$object->[USING]} ];  };
    +
    +    return $object;
    +    };
    +
    +
    +# Function: ClosingSymbol
    +# Returns the closing symbol character of the scope.
    +sub ClosingSymbol
    +    {  return $_[0]->[CLOSING_SYMBOL];  };
    +
    +# Function: Package
    +# Returns the package <SymbolString> of the scope, or undef if none.
    +sub Package
    +    {  return $_[0]->[PACKAGE];  };
    +
    +# Function: SetPackage
    +# Sets the package <SymbolString> of the scope.
    +sub SetPackage #(package)
    +    {  $_[0]->[PACKAGE] = $_[1];  };
    +
    +# Function: Using
    +# Returns an arrayref of <SymbolStrings> for using statements, or undef if none
    +sub Using
    +    {  return $_[0]->[USING];  };
    +
    +# Function: AddUsing
    +# Adds a <SymbolString> to the <Using()> array.
    +sub AddUsing #(using)
    +    {
    +    my ($self, $using) = @_;
    +
    +    if (!defined $self->[USING])
    +        {  $self->[USING] = [ ];  };
    +
    +    push @{$self->[USING]}, $using;
    +    };
    +
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm
    new file mode 100644
    index 000000000..8774d5b59
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm
    @@ -0,0 +1,71 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Advanced::ScopeChange
    +#
    +###############################################################################
    +#
    +#   A class used to store a scope change.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Advanced::ScopeChange;
    +
    +#
    +#   Constants: Implementation
    +#
    +#   The object is implemented as a blessed arrayref.  The constants below are used as indexes.
    +#
    +#   SCOPE - The new scope <SymbolString>.
    +#   LINE_NUMBER - The line number of the change.
    +#
    +use NaturalDocs::DefineMembers 'SCOPE', 'LINE_NUMBER';
    +# Dependency: New() depends on the order of these constants as well as that there is no inherited members.
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       scope - The <SymbolString> the scope was changed to.
    +#       lineNumber - What line it occurred on.
    +#
    +sub New #(scope, lineNumber)
    +    {
    +    # Dependency: This depends on the order of the parameters matching the constants, and that there are no inherited
    +    # members.
    +    my $self = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $self;
    +
    +    return $object;
    +    };
    +
    +
    +# Function: Scope
    +# Returns the <SymbolString> the scope was changed to.
    +sub Scope
    +    {  return $_[0]->[SCOPE];  };
    +
    +# Function: SetScope
    +# Replaces the <SymbolString> the scope was changed to.
    +sub SetScope #(scope)
    +    {  $_[0]->[SCOPE] = $_[1];  };
    +
    +# Function: LineNumber
    +# Returns the line number of the change.
    +sub LineNumber
    +    {  return $_[0]->[LINE_NUMBER];  };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Base.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Base.pm
    new file mode 100644
    index 000000000..15880c437
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Base.pm
    @@ -0,0 +1,833 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Base
    +#
    +###############################################################################
    +#
    +#   A base class for all programming language parsers.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Base;
    +
    +use NaturalDocs::DefineMembers 'NAME', 'Name()',
    +                                                 'EXTENSIONS', 'Extensions()', 'SetExtensions() duparrayref',
    +                                                 'SHEBANG_STRINGS', 'ShebangStrings()', 'SetShebangStrings() duparrayref',
    +                                                 'IGNORED_PREFIXES',
    +                                                 'ENUM_VALUES';
    +
    +use base 'Exporter';
    +our @EXPORT = ('ENUM_GLOBAL', 'ENUM_UNDER_TYPE', 'ENUM_UNDER_PARENT');
    +
    +
    +#
    +#   Constants: EnumValuesType
    +#
    +#   How enum values are handled in the language.
    +#
    +#   ENUM_GLOBAL - Values are always global and thus 'value'.
    +#   ENUM_UNDER_TYPE - Values are under the type in the hierarchy, and thus 'package.enum.value'.
    +#   ENUM_UNDER_PARENT - Values are under the parent in the hierarchy, putting them on the same level as the enum itself.  Thus
    +#                                       'package.value'.
    +#
    +use constant ENUM_GLOBAL => 1;
    +use constant ENUM_UNDER_TYPE => 2;
    +use constant ENUM_UNDER_PARENT => 3;
    +
    +
    +#
    +#   Handle: SOURCEFILEHANDLE
    +#
    +#   The handle of the source file currently being parsed.
    +#
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       name - The name of the language.
    +#
    +sub New #(name)
    +    {
    +    my ($selfPackage, $name) = @_;
    +
    +    my $object = [ ];
    +
    +    $object->[NAME] = $name;
    +
    +    bless $object, $selfPackage;
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Members
    +#
    +#   Name - Returns the language's name.
    +#   Extensions - Returns an arrayref of the language's file extensions, or undef if none.
    +#   SetExtensions - Replaces the arrayref of the language's file extensions.
    +#   ShebangStrings - Returns an arrayref of the language's shebang strings, or undef if none.
    +#   SetShebangStrings - Replaces the arrayref of the language's shebang strings.
    +#
    +
    +#
    +#   Function: PackageSeparator
    +#   Returns the language's package separator string.
    +#
    +sub PackageSeparator
    +    {  return '.';  };
    +
    +#
    +#   Function: PackageSeparatorWasSet
    +#   Returns whether the language's package separator string was ever changed from the default.
    +#
    +sub PackageSeparatorWasSet
    +    {  return 0;  };
    +
    +
    +#
    +#   Function: EnumValues
    +#   Returns the <EnumValuesType> that describes how the language handles enums.
    +#
    +sub EnumValues
    +    {  return ENUM_GLOBAL;  };
    +
    +
    +#
    +#   Function: IgnoredPrefixesFor
    +#
    +#   Returns an arrayref of ignored prefixes for the passed <TopicType>, or undef if none.  The array is sorted so that the longest
    +#   prefixes are first.
    +#
    +sub IgnoredPrefixesFor #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    if (defined $self->[IGNORED_PREFIXES])
    +        {  return $self->[IGNORED_PREFIXES]->{$type};  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: SetIgnoredPrefixesFor
    +#
    +#   Replaces the arrayref of ignored prefixes for the passed <TopicType>.
    +#
    +sub SetIgnoredPrefixesFor #(type, prefixes)
    +    {
    +    my ($self, $type, $prefixesRef) = @_;
    +
    +    if (!defined $self->[IGNORED_PREFIXES])
    +        {  $self->[IGNORED_PREFIXES] = { };  };
    +
    +    if (!defined $prefixesRef)
    +        {  delete $self->[IGNORED_PREFIXES]->{$type};  }
    +    else
    +        {
    +        my $prefixes = [ @$prefixesRef ];
    +
    +        # Sort prefixes to be longest to shortest.
    +        @$prefixes = sort { length $b <=> length $a } @$prefixes;
    +
    +        $self->[IGNORED_PREFIXES]->{$type} = $prefixes;
    +        };
    +    };
    +
    +
    +#
    +#   Function: HasIgnoredPrefixes
    +#
    +#   Returns whether the language has any ignored prefixes at all.
    +#
    +sub HasIgnoredPrefixes
    +    {  return defined $_[0]->[IGNORED_PREFIXES];  };
    +
    +
    +#
    +#   Function: CopyIgnoredPrefixesOf
    +#
    +#   Copies all the ignored prefix settings of the passed <NaturalDocs::Languages::Base> object.
    +#
    +sub CopyIgnoredPrefixesOf #(language)
    +    {
    +    my ($self, $language) = @_;
    +
    +    if ($language->HasIgnoredPrefixes())
    +        {
    +        $self->[IGNORED_PREFIXES] = { };
    +
    +        while (my ($topicType, $prefixes) = each %{$language->[IGNORED_PREFIXES]})
    +            {
    +            $self->[IGNORED_PREFIXES]->{$topicType} = [ @$prefixes ];
    +            };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Parsing Functions
    +
    +
    +#
    +#   Function: ParseFile
    +#
    +#   Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>.
    +#   This *must* be defined by a subclass.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The <FileName> of the source file to parse.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#
    +#   Returns:
    +#
    +#       The array ( autoTopics, scopeRecord ).
    +#
    +#       autoTopics - An arrayref of automatically generated <NaturalDocs::Parser::ParsedTopics> from the file, or undef if none.
    +#       scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none.
    +#
    +
    +
    +#
    +#   Function: ParsePrototype
    +#
    +#   Parses the prototype and returns it as a <NaturalDocs::Languages::Prototype> object.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType>.
    +#       prototype - The text prototype.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::Languages::Prototype> object.
    +#
    +sub ParsePrototype #(type, prototype)
    +    {
    +    my ($self, $type, $prototype) = @_;
    +
    +    my $isClass = NaturalDocs::Topics->TypeInfo($type)->ClassHierarchy();
    +
    +    if ($prototype !~ /\(.*[^ ].*\)/ && (!$isClass || $prototype !~ /\{.*[^ ].*\}/))
    +        {
    +        my $object = NaturalDocs::Languages::Prototype->New($prototype);
    +        return $object;
    +        };
    +
    +
    +    # Parse the parameters out of the prototype.
    +
    +    my @tokens = $prototype =~ /([^\(\)\[\]\{\}\<\>\'\"\,\;]+|.)/g;
    +
    +    my $parameter;
    +    my @parameterLines;
    +
    +    my @symbolStack;
    +    my $finishedParameters;
    +
    +    my ($beforeParameters, $afterParameters);
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($finishedParameters)
    +            {  $afterParameters .= $token;  }
    +
    +        elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
    +            {
    +            if ($symbolStack[0] eq '(' || ($isClass && $symbolStack[0] eq '{'))
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +
    +            if ($token eq $symbolStack[-1])
    +                {  pop @symbolStack;  };
    +            }
    +
    +        elsif ($token =~ /^[\(\[\{\<\'\"]$/)
    +            {
    +            if ($symbolStack[0] eq '(' || ($isClass && $symbolStack[0] eq '{'))
    +                {  $parameter .= $token;   }
    +            else
    +                {  $beforeParameters .= $token;  };
    +
    +            push @symbolStack, $token;
    +            }
    +
    +        elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
    +                 ($token eq ']' && $symbolStack[-1] eq '[') ||
    +                 ($token eq '}' && $symbolStack[-1] eq '{') ||
    +                 ($token eq '>' && $symbolStack[-1] eq '<') )
    +            {
    +            if ($symbolStack[0] eq '(')
    +                {
    +                if ($token eq ')' && scalar @symbolStack == 1)
    +                    {
    +                    if ($parameter ne ' ')
    +                        {  push @parameterLines, $parameter;  };
    +
    +                    $finishedParameters = 1;
    +                    $afterParameters .= $token;
    +                    }
    +                else
    +                    {  $parameter .= $token;  };
    +                }
    +            elsif ($isClass && $symbolStack[0] eq '{')
    +                {
    +                if ($token eq '}' && scalar @symbolStack == 1)
    +                    {
    +                    if ($parameter ne ' ')
    +                        {  push @parameterLines, $parameter;  };
    +
    +                    $finishedParameters = 1;
    +                    $afterParameters .= $token;
    +                    }
    +                else
    +                    {  $parameter .= $token;  };
    +                }
    +            else
    +                {
    +                $beforeParameters .= $token;
    +                };
    +
    +            pop @symbolStack;
    +            }
    +
    +        elsif ($token eq ',' || $token eq ';')
    +            {
    +            if ($symbolStack[0] eq '(' || ($isClass && $symbolStack[0] eq '{'))
    +                {
    +                if (scalar @symbolStack == 1)
    +                    {
    +                    push @parameterLines, $parameter . $token;
    +                    $parameter = undef;
    +                    }
    +                else
    +                    {
    +                    $parameter .= $token;
    +                    };
    +                }
    +            else
    +                {
    +                $beforeParameters .= $token;
    +                };
    +            }
    +
    +        else
    +            {
    +            if ($symbolStack[0] eq '(' || ($isClass && $symbolStack[0] eq '{'))
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +            };
    +        };
    +
    +    foreach my $part (\$beforeParameters, \$afterParameters)
    +        {
    +        $$part =~ s/^ //;
    +        $$part =~ s/ $//;
    +        };
    +
    +    my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters);
    +
    +
    +    # Parse the actual parameters.
    +
    +    foreach my $parameterLine (@parameterLines)
    +        {
    +        $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) );
    +        };
    +
    +    return $prototypeObject;
    +    };
    +
    +
    +#
    +#   Function: ParseParameterLine
    +#
    +#   Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
    +#
    +#   This vesion assumes a C++ style line.  If you need a Pascal style line, override this function to forward to
    +#   <ParsePascalParameterLine()>.
    +#
    +#   > Function(parameter, type parameter, type parameter = value);
    +#
    +sub ParseParameterLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    $line =~ s/^ //;
    +    $line =~ s/ $//;
    +
    +    my @tokens = $line =~ /([^ \(\)\{\}\[\]\<\>\'\"\=]+|.)/g;
    +
    +    my @symbolStack;
    +    my @parameterWords = ( undef );
    +    my ($defaultValue, $defaultValuePrefix, $inDefaultValue);
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($inDefaultValue)
    +            {  $defaultValue .= $token;  }
    +
    +        elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
    +            {
    +            $parameterWords[-1] .= $token;
    +
    +            if ($token eq $symbolStack[-1])
    +                {  pop @symbolStack;  };
    +            }
    +
    +        elsif ($token =~ /^[\(\[\{\<\'\"]$/)
    +            {
    +            push @symbolStack, $token;
    +            $parameterWords[-1] .= $token;
    +            }
    +
    +        elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
    +                 ($token eq ']' && $symbolStack[-1] eq '[') ||
    +                 ($token eq '}' && $symbolStack[-1] eq '{') ||
    +                 ($token eq '>' && $symbolStack[-1] eq '<') )
    +            {
    +            pop @symbolStack;
    +            $parameterWords[-1] .= $token;
    +            }
    +
    +        elsif ($token eq ' ')
    +            {
    +            if (!scalar @symbolStack)
    +                {  push @parameterWords, undef;  }
    +            else
    +                {  $parameterWords[-1] .= $token;  };
    +            }
    +
    +        elsif ($token eq '=')
    +            {
    +            if (!scalar @symbolStack)
    +                {
    +                $defaultValuePrefix = $token;
    +                $inDefaultValue = 1;
    +                }
    +            else
    +                {  $parameterWords[-1] .= $token;  };
    +            }
    +
    +        else
    +            {
    +            $parameterWords[-1] .= $token;
    +            };
    +        };
    +
    +    my ($name, $namePrefix, $type, $typePrefix);
    +
    +    if (!$parameterWords[-1])
    +        {  pop @parameterWords;  };
    +
    +    $name = pop @parameterWords;
    +
    +    if ($parameterWords[-1]=~ /([\*\&]+)$/)
    +        {
    +        $namePrefix = $1;
    +        $parameterWords[-1] = substr($parameterWords[-1], 0, 0 - length($namePrefix));
    +        $parameterWords[-1] =~ s/ $//;
    +
    +        if (!$parameterWords[-1])
    +            {  pop @parameterWords;  };
    +        }
    +    elsif ($name =~ /^([\*\&]+)/)
    +        {
    +        $namePrefix = $1;
    +        $name = substr($name, length($namePrefix));
    +        $name =~ s/^ //;
    +        };
    +
    +    $type = pop @parameterWords;
    +    $typePrefix = join(' ', @parameterWords);
    +
    +    if ($typePrefix)
    +        {  $typePrefix .= ' ';  };
    +
    +    if ($type =~ /^([a-z0-9_\:\.]+(?:\.|\:\:))[a-z0-9_]/i)
    +        {
    +        my $attachedTypePrefix = $1;
    +
    +        $typePrefix .= $attachedTypePrefix;
    +        $type = substr($type, length($attachedTypePrefix));
    +        };
    +
    +    $defaultValue =~ s/ $//;
    +
    +    return NaturalDocs::Languages::Prototype::Parameter->New($type, $typePrefix, $name, $namePrefix,
    +                                                                                             $defaultValue, $defaultValuePrefix);
    +    };
    +
    +
    +#
    +#   Function: ParsePascalParameterLine
    +#
    +#   Parses a Pascal-like prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
    +#   Pascal lines are as follows:
    +#
    +#   > Function (name: type; name, name: type := value)
    +#
    +#   Also supports ActionScript lines
    +#
    +#   > Function (name: type, name, name: type = value)
    +#
    +sub ParsePascalParameterLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    $line =~ s/^ //;
    +    $line =~ s/ $//;
    +
    +    my @tokens = $line =~ /([^\(\)\{\}\[\]\<\>\'\"\=\:]+|\:\=|.)/g;
    +    my ($type, $name, $defaultValue, $defaultValuePrefix, $afterName, $afterDefaultValue);
    +    my @symbolStack;
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($afterDefaultValue)
    +            {  $defaultValue .= $token;  }
    +
    +        elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
    +            {
    +            if ($afterName)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +
    +            if ($token eq $symbolStack[-1])
    +                {  pop @symbolStack;  };
    +            }
    +
    +        elsif ($token =~ /^[\(\[\{\<\'\"]$/)
    +            {
    +            push @symbolStack, $token;
    +
    +            if ($afterName)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +            }
    +
    +        elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
    +                 ($token eq ']' && $symbolStack[-1] eq '[') ||
    +                 ($token eq '}' && $symbolStack[-1] eq '{') ||
    +                 ($token eq '>' && $symbolStack[-1] eq '<') )
    +            {
    +            pop @symbolStack;
    +
    +            if ($afterName)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +            }
    +
    +        elsif ($afterName)
    +            {
    +            if (($token eq ':=' || $token eq '=') && !scalar @symbolStack)
    +                {
    +                $defaultValuePrefix = $token;
    +                $afterDefaultValue = 1;
    +                }
    +            else
    +                {  $type .= $token;  };
    +            }
    +
    +        elsif ($token eq ':' && !scalar @symbolStack)
    +            {
    +            $name .= $token;
    +            $afterName = 1;
    +            }
    +
    +        else
    +            {  $name .= $token;  };
    +        };
    +
    +    foreach my $part (\$type, \$name, \$defaultValue)
    +        {
    +        $$part =~ s/^ //;
    +        $$part =~ s/ $//;
    +        };
    +
    +    return NaturalDocs::Languages::Prototype::Parameter->New($type, undef, $name, undef, $defaultValue, $defaultValuePrefix);
    +    };
    +
    +
    +#
    +#   Function: TypeBeforeParameter
    +#
    +#   Returns whether the type appears before the parameter in prototypes.
    +#
    +#   For example, it does in C++
    +#   > void Function (int a, int b)
    +#
    +#   but does not in Pascal
    +#   > function Function (a: int; b, c: int)
    +#
    +sub TypeBeforeParameter
    +    {
    +    return 1;
    +    };
    +
    +
    +
    +#
    +#   Function: IgnoredPrefixLength
    +#
    +#   Returns the length of the prefix that should be ignored in the index, or zero if none.
    +#
    +#   Parameters:
    +#
    +#       name - The name of the symbol.
    +#       type  - The symbol's <TopicType>.
    +#
    +#   Returns:
    +#
    +#       The length of the prefix to ignore, or zero if none.
    +#
    +sub IgnoredPrefixLength #(name, type)
    +    {
    +    my ($self, $name, $type) = @_;
    +
    +    foreach my $prefixes ($self->IgnoredPrefixesFor($type), $self->IgnoredPrefixesFor(::TOPIC_GENERAL()))
    +        {
    +        if (defined $prefixes)
    +            {
    +            foreach my $prefix (@$prefixes)
    +                {
    +                if (substr($name, 0, length($prefix)) eq $prefix)
    +                    {  return length($prefix);  };
    +                };
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: StripOpeningSymbols
    +#
    +#   Determines if the line starts with any of the passed symbols, and if so, replaces it with spaces.  This only happens
    +#   if the only thing before it on the line is whitespace.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A reference to the line to check.
    +#       symbols - An arrayref of the symbols to check for.
    +#
    +#   Returns:
    +#
    +#       If the line starts with any of the passed comment symbols, it will replace it in the line with spaces and return the symbol.
    +#       If the line doesn't, it will leave the line alone and return undef.
    +#
    +sub StripOpeningSymbols #(lineRef, symbols)
    +    {
    +    my ($self, $lineRef, $symbols) = @_;
    +
    +    if (!defined $symbols)
    +        {  return undef;  };
    +
    +    my ($index, $symbol) = ::FindFirstSymbol($$lineRef, $symbols);
    +
    +    if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/)
    +        {
    +        return substr($$lineRef, $index, length($symbol), ' ' x length($symbol));
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: StripOpeningJavaDocSymbols
    +#
    +#   Determines if the line starts with any of the passed symbols, and if so, replaces it with spaces.  This only happens
    +#   if the only thing before it on the line is whitespace and the next character after it is whitespace or the end of the line.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A reference to the line to check.
    +#       symbols - An arrayref of the symbols to check for.
    +#
    +#   Returns:
    +#
    +#       If the line starts with any of the passed comment symbols, it will replace it in the line with spaces and return the symbol.
    +#       If the line doesn't, it will leave the line alone and return undef.
    +#
    +sub StripOpeningJavaDocSymbols #(lineRef, symbols)
    +    {
    +    my ($self, $lineRef, $symbols) = @_;
    +
    +    if (!defined $symbols)
    +        {  return undef;  };
    +
    +    my ($index, $symbol) = ::FindFirstSymbol($$lineRef, $symbols);
    +
    +    if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/ && substr($$lineRef, $index + length($symbol), 1) =~ /^[ \t]?$/)
    +        {
    +        return substr($$lineRef, $index, length($symbol), ' ' x length($symbol));
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: StripOpeningBlockSymbols
    +#
    +#   Determines if the line starts with any of the opening symbols in the passed symbol pairs, and if so, replaces it with spaces.
    +#   This only happens if the only thing before it on the line is whitespace.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A reference to the line to check.
    +#       symbolPairs - An arrayref of the symbol pairs to check for.  Pairs are specified as two consecutive array entries, with the
    +#                            opening symbol first.
    +#
    +#   Returns:
    +#
    +#       If the line starts with any of the opening symbols, it will replace it in the line with spaces and return the closing symbol.
    +#       If the line doesn't, it will leave the line alone and return undef.
    +#
    +sub StripOpeningBlockSymbols #(lineRef, symbolPairs)
    +    {
    +    my ($self, $lineRef, $symbolPairs) = @_;
    +
    +    if (!defined $symbolPairs)
    +        {  return undef;  };
    +
    +    for (my $i = 0; $i < scalar @$symbolPairs; $i += 2)
    +        {
    +        my $index = index($$lineRef, $symbolPairs->[$i]);
    +
    +        if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/)
    +            {
    +            substr($$lineRef, $index, length($symbolPairs->[$i]), ' ' x length($symbolPairs->[$i]));
    +            return $symbolPairs->[$i + 1];
    +            };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: StripOpeningJavaDocBlockSymbols
    +#
    +#   Determines if the line starts with any of the opening symbols in the passed symbol pairs, and if so, replaces it with spaces.
    +#   This only happens if the only thing before it on the line is whitespace and the next character is whitespace or the end of the line.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A reference to the line to check.
    +#       symbolPairs - An arrayref of the symbol pairs to check for.  Pairs are specified as two consecutive array entries, with the
    +#                            opening symbol first.
    +#
    +#   Returns:
    +#
    +#       If the line starts with any of the opening symbols, it will replace it in the line with spaces and return the closing symbol.
    +#       If the line doesn't, it will leave the line alone and return undef.
    +#
    +sub StripOpeningJavaDocBlockSymbols #(lineRef, symbolPairs)
    +    {
    +    my ($self, $lineRef, $symbolPairs) = @_;
    +
    +    if (!defined $symbolPairs)
    +        {  return undef;  };
    +
    +    for (my $i = 0; $i < scalar @$symbolPairs; $i += 2)
    +        {
    +        my $index = index($$lineRef, $symbolPairs->[$i]);
    +
    +        if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/ &&
    +            substr($$lineRef, $index + length($symbolPairs->[$i]), 1) =~ /^[ \t]?$/)
    +            {
    +            substr($$lineRef, $index, length($symbolPairs->[$i]), ' ' x length($symbolPairs->[$i]));
    +            return $symbolPairs->[$i + 1];
    +            };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: StripClosingSymbol
    +#
    +#   Determines if the line contains a symbol, and if so, truncates it just before the symbol.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A reference to the line to check.
    +#       symbol - The symbol to check for.
    +#
    +#   Returns:
    +#
    +#       The remainder of the line, or undef if the symbol was not found.
    +#
    +sub StripClosingSymbol #(lineRef, symbol)
    +    {
    +    my ($self, $lineRef, $symbol) = @_;
    +
    +    my $index = index($$lineRef, $symbol);
    +
    +    if ($index != -1)
    +        {
    +        my $lineRemainder = substr($$lineRef, $index + length($symbol));
    +        $$lineRef = substr($$lineRef, 0, $index);
    +
    +        return $lineRemainder;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: NormalizePrototype
    +#
    +#   Normalizes a prototype.  Specifically, condenses spaces, tabs, and line breaks into single spaces and removes leading and
    +#   trailing ones.
    +#
    +#   Parameters:
    +#
    +#       prototype - The original prototype string.
    +#
    +#   Returns:
    +#
    +#       The normalized prototype.
    +#
    +sub NormalizePrototype #(prototype)
    +    {
    +    my ($self, $prototype) = @_;
    +
    +    $prototype =~ tr/ \t\r\n/ /s;
    +    $prototype =~ s/^ //;
    +    $prototype =~ s/ $//;
    +
    +    return $prototype;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/CSharp.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/CSharp.pm
    new file mode 100644
    index 000000000..13a0c94fb
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/CSharp.pm
    @@ -0,0 +1,1552 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::CSharp
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of C#.
    +#
    +#
    +#   Topic: Language Support
    +#
    +#       Supported:
    +#
    +#       - Classes
    +#       - Namespaces (no topic generated)
    +#       - Functions
    +#       - Constructors and Destructors
    +#       - Properties
    +#       - Indexers
    +#       - Operators
    +#       - Delegates
    +#       - Variables
    +#       - Constants
    +#       - Events
    +#       - Enums
    +#
    +#       Not supported yet:
    +#
    +#       - Autodocumenting enum members
    +#       - Using alias
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::CSharp;
    +
    +use base 'NaturalDocs::Languages::Advanced';
    +
    +
    +###############################################################################
    +# Group: Package Variables
    +
    +#
    +#   hash: classKeywords
    +#   An existence hash of all the acceptable class keywords.  The keys are in all lowercase.
    +#
    +my %classKeywords = ( 'class' => 1,
    +                                    'struct' => 1,
    +                                    'interface' => 1 );
    +
    +#
    +#   hash: classModifiers
    +#   An existence hash of all the acceptable class modifiers.  The keys are in all lowercase.
    +#
    +my %classModifiers = ( 'new' => 1,
    +                                   'public' => 1,
    +                                   'protected' => 1,
    +                                   'internal' => 1,
    +                                   'private' => 1,
    +                                   'abstract' => 1,
    +                                   'sealed' => 1,
    +                                   'unsafe' => 1,
    +                                   'static' => 1,
    +                                   'partial' => 1 );
    +
    +#
    +#   hash: functionModifiers
    +#   An existence hash of all the acceptable function modifiers.  Also applies to properties.  Also encompasses those for operators
    +#   and indexers, but have more than are valid for them.  The keys are in all lowercase.
    +#
    +my %functionModifiers = ( 'new' => 1,
    +                                       'public' => 1,
    +                                       'protected' => 1,
    +                                       'internal' => 1,
    +                                       'private' => 1,
    +                                       'static' => 1,
    +                                       'virtual' => 1,
    +                                       'sealed' => 1,
    +                                       'override' => 1,
    +                                       'abstract' => 1,
    +                                       'extern' => 1,
    +                                       'unsafe' => 1 );
    +
    +#
    +#   hash: variableModifiers
    +#   An existence hash of all the acceptable variable modifiers.  The keys are in all lowercase.
    +#
    +my %variableModifiers = ( 'new' => 1,
    +                                       'public' => 1,
    +                                       'protected' => 1,
    +                                       'internal' => 1,
    +                                       'private' => 1,
    +                                       'static' => 1,
    +                                       'readonly' => 1,
    +                                       'volatile' => 1,
    +                                       'unsafe' => 1 );
    +
    +#
    +#   hash: enumTypes
    +#   An existence hash of all the possible enum types.  The keys are in all lowercase.
    +#
    +my %enumTypes = ( 'sbyte' => 1,
    +                             'byte' => 1,
    +                             'short' => 1,
    +                             'ushort' => 1,
    +                             'int' => 1,
    +                             'uint' => 1,
    +                             'long' => 1,
    +                             'ulong' => 1 );
    +
    +#
    +#   hash: impossibleTypeWords
    +#   An existence hash of all the reserved words that cannot be in a type.  This includes 'enum' and all modifiers.  The keys are in
    +#   all lowercase.
    +#
    +my %impossibleTypeWords = ( 'abstract' => 1, 'as' => 1, 'base' => 1, 'break' => 1, 'case' => 1, 'catch' => 1,
    +                                              'checked' => 1, 'class' => 1, 'const' => 1, 'continue' => 1, 'default' => 1, 'delegate' => 1,
    +                                              'do' => 1, 'else' => 1, 'enum' => 1, 'event' => 1, 'explicit' => 1, 'extern' => 1,
    +                                              'false' => 1, 'finally' => 1, 'fixed' => 1, 'for' => 1, 'foreach' => 1, 'goto' => 1, 'if' => 1,
    +                                              'implicit' => 1, 'in' => 1, 'interface' => 1, 'internal' => 1, 'is' => 1, 'lock' => 1,
    +                                              'namespace' => 1, 'new' => 1, 'null' => 1, 'operator' => 1, 'out' => 1, 'override' => 1,
    +                                              'params' => 1, 'private' => 1, 'protected' => 1, 'public' => 1, 'readonly' => 1, 'ref' => 1,
    +                                              'return' => 1, 'sealed' => 1, 'sizeof' => 1, 'stackalloc' => 1, 'static' => 1,
    +                                              'struct' => 1, 'switch' => 1, 'this' => 1, 'throw' => 1, 'true' => 1, 'try' => 1, 'typeof' => 1,
    +                                              'unchecked' => 1, 'unsafe' => 1, 'using' => 1, 'virtual' => 1, 'volatile' => 1, 'while' => 1 );
    +# Deleted from the list: object, string, bool, decimal, sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, void
    +
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: PackageSeparator
    +#   Returns the package separator symbol.
    +#
    +sub PackageSeparator
    +    {  return '.';  };
    +
    +
    +#
    +#   Function: EnumValues
    +#   Returns the <EnumValuesType> that describes how the language handles enums.
    +#
    +sub EnumValues
    +    {  return ::ENUM_UNDER_TYPE();  };
    +
    +
    +#
    +#   Function: ParseFile
    +#
    +#   Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The <FileName> to parse.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#
    +#   Returns:
    +#
    +#       The array ( autoTopics, scopeRecord ).
    +#
    +#       autoTopics - An arrayref of automatically generated topics from the file, or undef if none.
    +#       scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none.
    +#
    +sub ParseFile #(sourceFile, topicsList)
    +    {
    +    my ($self, $sourceFile, $topicsList) = @_;
    +
    +    $self->ParseForCommentsAndTokens($sourceFile, [ '//' ], [ '/*', '*/' ], [ '///' ], [ '/**', '*/' ] );
    +
    +    my $tokens = $self->Tokens();
    +    my $index = 0;
    +    my $lineNumber = 1;
    +
    +    while ($index < scalar @$tokens)
    +        {
    +        if ($self->TryToSkipWhitespace(\$index, \$lineNumber) ||
    +            $self->TryToGetNamespace(\$index, \$lineNumber) ||
    +            $self->TryToGetUsing(\$index, \$lineNumber) ||
    +            $self->TryToGetClass(\$index, \$lineNumber) ||
    +            $self->TryToGetFunction(\$index, \$lineNumber) ||
    +            $self->TryToGetOverloadedOperator(\$index, \$lineNumber) ||
    +            $self->TryToGetVariable(\$index, \$lineNumber) ||
    +            $self->TryToGetEnum(\$index, \$lineNumber) )
    +            {
    +            # The functions above will handle everything.
    +            }
    +
    +        elsif ($tokens->[$index] eq '{')
    +            {
    +            $self->StartScope('}', $lineNumber, undef, undef, undef);
    +            $index++;
    +            }
    +
    +        elsif ($tokens->[$index] eq '}')
    +            {
    +            if ($self->ClosingScopeSymbol() eq '}')
    +                {  $self->EndScope($lineNumber);  };
    +
    +            $index++;
    +            }
    +
    +        else
    +            {
    +            $self->SkipRestOfStatement(\$index, \$lineNumber);
    +            };
    +        };
    +
    +
    +    # Don't need to keep these around.
    +    $self->ClearTokens();
    +
    +
    +    my $autoTopics = $self->AutoTopics();
    +
    +    my $scopeRecord = $self->ScopeRecord();
    +    if (defined $scopeRecord && !scalar @$scopeRecord)
    +        {  $scopeRecord = undef;  };
    +
    +    return ( $autoTopics, $scopeRecord );
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Statement Parsing Functions
    +# All functions here assume that the current position is at the beginning of a statement.
    +#
    +# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as
    +# often as it should.  We're making use of the fact that Perl will always return undef in these cases to keep the code simpler.
    +
    +
    +#
    +#   Function: TryToGetNamespace
    +#
    +#   Determines whether the position is at a namespace declaration statement, and if so, adjusts the scope, skips it, and returns
    +#   true.
    +#
    +#   Why no topic?:
    +#
    +#       The main reason we don't create a Natural Docs topic for a namespace is because in order to declare class A.B.C in C#,
    +#       you must do this:
    +#
    +#       > namespace A.B
    +#       >    {
    +#       >    class C
    +#       >        { ... }
    +#       >    }
    +#
    +#       That would result in a namespace topic whose only purpose is really to qualify C.  It would take the default page title, and
    +#       thus the default menu title.  So if you have files for A.B.X, A.B.Y, and A.B.Z, they all will appear as A.B on the menu.
    +#
    +#       If something actually appears in the namespace besides a class, it will be handled by
    +#       <NaturalDocs::Parser->AddPackageDelineators()>.  That function will add a package topic to correct the scope.
    +#
    +#       If the user actually documented it, it will still appear because of the manual topic.
    +#
    +sub TryToGetNamespace #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if (lc($tokens->[$$indexRef]) ne 'namespace')
    +        {  return undef;  };
    +
    +    my $index = $$indexRef + 1;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if (!$self->TryToSkipWhitespace(\$index, \$lineNumber))
    +        {  return undef;  };
    +
    +    my $name;
    +
    +    while ($tokens->[$index] =~ /^[a-z_\.\@]/i)
    +        {
    +        $name .= $tokens->[$index];
    +        $index++;
    +        };
    +
    +    if (!defined $name)
    +        {  return undef;  };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    if ($tokens->[$index] ne '{')
    +        {  return undef;  };
    +
    +    $index++;
    +
    +
    +    # We found a valid one if we made it this far.
    +
    +    my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(), $name,
    +                                                                                         $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                         undef,
    +                                                                                         undef, undef, $$lineNumberRef);
    +
    +    # We don't add an auto-topic for namespaces.  See the function documentation above.
    +
    +    NaturalDocs::Parser->OnClass($autoTopic->Package());
    +
    +    $self->StartScope('}', $lineNumber, $autoTopic->Package());
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetClass
    +#
    +#   Determines whether the position is at a class declaration statement, and if so, generates a topic for it, skips it, and
    +#   returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Classes
    +#       - Structs
    +#       - Interfaces
    +#
    +sub TryToGetClass #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +    my $needsPrototype = 0;
    +
    +    if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +        {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              !exists $classKeywords{lc($tokens->[$index])} &&
    +              exists $classModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    if (!exists $classKeywords{lc($tokens->[$index])})
    +        {  return undef;  };
    +
    +    my $lcClassKeyword = lc($tokens->[$index]);
    +
    +    $index++;
    +
    +    if (!$self->TryToSkipWhitespace(\$index, \$lineNumber))
    +        {  return undef;  };
    +
    +    my $name;
    +
    +    while ($tokens->[$index] =~ /^[a-z_\@]/i)
    +        {
    +        $name .= $tokens->[$index];
    +        $index++;
    +        };
    +
    +    if (!defined $name)
    +        {  return undef;  };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +	if ($self->TryToSkipTemplateSpec(\$index, \$lineNumber))
    +		{
    +		$needsPrototype = 1;
    +		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +		if ($self->TryToSkipWhereClauses(\$index, \$lineNumber))
    +			{  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +		}
    +
    +    my @parents;
    +
    +    if ($tokens->[$index] eq ':')
    +        {
    +        my $inheritsTemplates;
    +
    +        do
    +            {
    +            $index++;
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            my $parentName;
    +
    +            while ($tokens->[$index] =~ /^[a-z_\.\@]/i)
    +                {
    +                $parentName .= $tokens->[$index];
    +                $index++;
    +                };
    +
    +            if (!defined $parentName)
    +                {  return undef;  };
    +
    +            push @parents, NaturalDocs::SymbolString->FromText($parentName);
    +
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            if ($self->TryToSkipTemplateSpec(\$index, \$lineNumber))
    +            	{
    +            	$inheritsTemplates = 1;
    +            	$needsPrototype = 1;
    +            	$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            	}
    +            }
    +        while ($tokens->[$index] eq ',');
    +
    +        if ($inheritsTemplates)
    +        	{
    +        	if ($self->TryToSkipWhereClauses(\$index, \$lineNumber))
    +        		{  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +        	}
    +        };
    +
    +    if ($tokens->[$index] ne '{')
    +        {  return undef;  };
    +
    +
    +    # If we made it this far, we have a valid class declaration.
    +
    +    my @scopeIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($self->CurrentScope());
    +    $name = join('.', @scopeIdentifiers, $name);
    +
    +    my $topicType;
    +
    +    if ($lcClassKeyword eq 'interface')
    +        {  $topicType = ::TOPIC_INTERFACE();  }
    +    else
    +        {  $topicType = ::TOPIC_CLASS();  };
    +
    +    my $prototype;
    +
    +    if ($needsPrototype)
    +            {
    +            $prototype = $self->CreateString($startIndex, $index);
    +            }
    +
    +    my $autoTopic = NaturalDocs::Parser::ParsedTopic->New($topicType, $name,
    +                                                                                         undef, $self->CurrentUsing(),
    +                                                                                         $prototype,
    +                                                                                         undef, undef, $$lineNumberRef);
    +
    +    $self->AddAutoTopic($autoTopic);
    +    NaturalDocs::Parser->OnClass($autoTopic->Package());
    +
    +    foreach my $parent (@parents)
    +        {
    +        NaturalDocs::Parser->OnClassParent($autoTopic->Package(), $parent, $self->CurrentScope(), undef,
    +                                                               ::RESOLVE_RELATIVE());
    +        };
    +
    +    $self->StartScope('}', $lineNumber, $autoTopic->Package());
    +
    +    $index++;
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetUsing
    +#
    +#   Determines whether the position is at a using statement, and if so, adds it to the current scope, skips it, and returns
    +#        true.
    +#
    +#        Supported:
    +#
    +#       - Using
    +#
    +#        Unsupported:
    +#
    +#                - Using with alias
    +#
    +sub TryToGetUsing #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if (lc($tokens->[$index]) ne 'using')
    +        {  return undef;  };
    +
    +    $index++;
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $name;
    +
    +    while ($tokens->[$index] =~ /^[a-z_\@\.]/i)
    +        {
    +        $name .= $tokens->[$index];
    +        $index++;
    +        };
    +
    +    if ($tokens->[$index] ne ';' ||
    +                !defined $name)
    +        {  return undef;  };
    +
    +    $index++;
    +
    +
    +    $self->AddUsing( NaturalDocs::SymbolString->FromText($name) );
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +
    +#
    +#   Function: TryToGetFunction
    +#
    +#   Determines if the position is on a function declaration, and if so, generates a topic for it, skips it, and returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Functions
    +#       - Constructors
    +#       - Destructors
    +#       - Properties
    +#       - Indexers
    +#       - Delegates
    +#       - Events
    +#
    +sub TryToGetFunction #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +        {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              exists $functionModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    my $isDelegate;
    +    my $isEvent;
    +
    +    if (lc($tokens->[$index]) eq 'delegate')
    +        {
    +        $isDelegate = 1;
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        }
    +    elsif (lc($tokens->[$index]) eq 'event')
    +        {
    +        $isEvent = 1;
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    my $returnType = $self->TryToGetType(\$index, \$lineNumber);
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $name;
    +    my $lastNameWord;
    +    my $hasTemplates;
    +
    +    while ($tokens->[$index] =~ /^[a-z\_\@\.\~]/i)
    +        {
    +        $name .= $tokens->[$index];
    +        $lastNameWord = $tokens->[$index];
    +        $index++;
    +
    +        # For explicit generic interface definitions, such as
    +        # IDObjectType System.Collections.Generic.IEnumerator<IDObjectType>.Current
    +        # or templated functions.
    +
    +        if ($self->TryToSkipTemplateSpec(\$index, \$lineNumber))
    +        	{
    +        	$hasTemplates = 1;
    +        	$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        	}
    +        };
    +
    +    if (!defined $name)
    +        {
    +        # Constructors and destructors don't have return types.  It's possible their names were mistaken for the return type.
    +        if (defined $returnType)
    +            {
    +            $name = $returnType;
    +            $returnType = undef;
    +
    +            $name =~ /([a-z0-9_]+)$/i;
    +            $lastNameWord = $1;
    +            }
    +        else
    +            {  return undef;  };
    +        };
    +
    +    # If there's no return type, make sure it's a constructor or destructor.
    +    if (!defined $returnType)
    +        {
    +        my @identifiers = NaturalDocs::SymbolString->IdentifiersOf( $self->CurrentScope() );
    +
    +        if ($lastNameWord ne $identifiers[-1])
    +            {  return undef;  };
    +        };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +
    +    # Skip the brackets on indexers.
    +    if ($tokens->[$index] eq '[' && lc($lastNameWord) eq 'this')
    +        {
    +        # This should jump the brackets completely.
    +        $self->GenericSkip(\$index, \$lineNumber);
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        $name .= '[]';
    +        };
    +
    +
    +    # Properties, indexers, events with braces
    +
    +    if ($tokens->[$index] eq '{')
    +        {
    +        my $prototype = $self->CreateString($startIndex, $index);
    +
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        my ($aWord, $bWord, $hasA, $hasB);
    +
    +        if ($isEvent)
    +            {
    +            $aWord = 'add';
    +            $bWord = 'remove';
    +            }
    +        else
    +            {
    +            $aWord = 'get';
    +            $bWord = 'set';
    +            };
    +
    +        while ($index < scalar @$tokens)
    +            {
    +            if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +                {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +
    +            if (lc($tokens->[$index]) eq $aWord)
    +                {  $hasA = 1;  }
    +            elsif (lc($tokens->[$index]) eq $bWord)
    +                {  $hasB = 1;  }
    +            elsif ($tokens->[$index] eq '}')
    +                {
    +                $index++;
    +                last;
    +                };
    +
    +            $self->SkipRestOfStatement(\$index, \$lineNumber);
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            };
    +
    +        if ($hasA && $hasB)
    +            {  $prototype .= ' { ' . $aWord . ', ' . $bWord . ' }';  }
    +        elsif ($hasA)
    +            {  $prototype .= ' { ' . $aWord . ' }';  }
    +        elsif ($hasB)
    +            {  $prototype .= ' { ' . $bWord . ' }';  };
    +
    +        $prototype = $self->NormalizePrototype($prototype);
    +
    +        my $topicType = ( $isEvent ? ::TOPIC_EVENT() : ::TOPIC_PROPERTY() );
    +
    +        $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($topicType, $name,
    +                                                                                                  $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                                  $prototype,
    +                                                                                                  undef, undef, $startLine));
    +        }
    +
    +
    +    # Functions, constructors, destructors, delegates.
    +
    +    elsif ($tokens->[$index] eq '(')
    +        {
    +        # This should jump the parenthesis completely.
    +        $self->GenericSkip(\$index, \$lineNumber);
    +		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if ($hasTemplates && $self->TryToSkipWhereClauses(\$index, \$lineNumber))
    +        	{  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +
    +        my $topicType = ( $isDelegate ? ::TOPIC_DELEGATE() : ::TOPIC_FUNCTION() );
    +        my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) );
    +
    +        $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($topicType, $name,
    +                                                                                                  $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                                  $prototype,
    +                                                                                                  undef, undef, $startLine));
    +
    +        $self->SkipRestOfStatement(\$index, \$lineNumber);
    +        }
    +
    +
    +    # Events without braces
    +
    +    elsif ($isEvent && $tokens->[$index] eq ';')
    +        {
    +        my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) );
    +
    +        $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_EVENT(), $name,
    +                                                                                                  $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                                  $prototype,
    +                                                                                                  undef, undef, $startLine));
    +        $index++;
    +        }
    +
    +    else
    +        {  return undef;  };
    +
    +
    +    # We succeeded if we got this far.
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetOverloadedOperator
    +#
    +#   Determines if the position is on an operator overload declaration, and if so, generates a topic for it, skips it, and returns true.
    +#
    +sub TryToGetOverloadedOperator #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +        {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              exists $functionModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +
    +    my $name;
    +
    +
    +    # Casting operators.
    +
    +    if (lc($tokens->[$index]) eq 'implicit' || lc($tokens->[$index]) eq 'explicit')
    +        {
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if (lc($tokens->[$index]) ne 'operator')
    +            {  return undef;  };
    +
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        $name = $self->TryToGetType(\$index, \$lineNumber);
    +
    +        if (!defined $name)
    +            {  return undef;  };
    +        }
    +
    +
    +    # Symbol operators.
    +
    +    else
    +        {
    +        if (!$self->TryToGetType(\$index, \$lineNumber))
    +            {  return undef;  };
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if (lc($tokens->[$index]) ne 'operator')
    +            {  return undef;  };
    +
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if (lc($tokens->[$index]) eq 'true' || lc($tokens->[$index]) eq 'false')
    +            {
    +            $name = $tokens->[$index];
    +            $index++;
    +            }
    +        else
    +            {
    +            while ($tokens->[$index] =~ /^[\+\-\!\~\*\/\%\&\|\^\<\>\=]$/)
    +                {
    +                $name .= $tokens->[$index];
    +                $index++;
    +                };
    +            };
    +        };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    if ($tokens->[$index] ne '(')
    +        {  return undef;  };
    +
    +    # This should skip the parenthesis completely.
    +    $self->GenericSkip(\$index, \$lineNumber);
    +
    +    my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) );
    +
    +    $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FUNCTION(), 'operator ' . $name,
    +                                                                                              $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                              $prototype,
    +                                                                                              undef, undef, $startLine));
    +
    +    $self->SkipRestOfStatement(\$index, \$lineNumber);
    +
    +
    +    # We succeeded if we got this far.
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetVariable
    +#
    +#   Determines if the position is on a variable declaration statement, and if so, generates a topic for each variable, skips the
    +#   statement, and returns true.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Variables
    +#       - Constants
    +#
    +sub TryToGetVariable #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +        {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              exists $variableModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    my $type;
    +    if (lc($tokens->[$index]) eq 'const')
    +        {
    +        $type = ::TOPIC_CONSTANT();
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        }
    +    else
    +        {
    +        $type = ::TOPIC_VARIABLE();
    +        };
    +
    +    if (!$self->TryToGetType(\$index, \$lineNumber))
    +        {  return undef;  };
    +
    +    my $endTypeIndex = $index;
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my @names;
    +
    +    for (;;)
    +        {
    +        my $name;
    +
    +        while ($tokens->[$index] =~ /^[a-z\@\_]/i)
    +            {
    +            $name .= $tokens->[$index];
    +            $index++;
    +            };
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if ($tokens->[$index] eq '=')
    +            {
    +            do
    +                {
    +                $self->GenericSkip(\$index, \$lineNumber);
    +                }
    +            while ($tokens->[$index] ne ',' && $tokens->[$index] ne ';');
    +            };
    +
    +        push @names, $name;
    +
    +        if ($tokens->[$index] eq ';')
    +            {
    +            $index++;
    +            last;
    +            }
    +        elsif ($tokens->[$index] eq ',')
    +            {
    +            $index++;
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            }
    +        else
    +            {  return undef;  };
    +        };
    +
    +
    +    # We succeeded if we got this far.
    +
    +    my $prototypePrefix = $self->CreateString($startIndex, $endTypeIndex);
    +
    +    foreach my $name (@names)
    +        {
    +        my $prototype = $self->NormalizePrototype( $prototypePrefix . ' ' . $name );
    +
    +        $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $name,
    +                                                                                                  $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                                  $prototype,
    +                                                                                                  undef, undef, $startLine));
    +        };
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetEnum
    +#
    +#   Determines if the position is on an enum declaration statement, and if so, generates a topic for it.
    +#
    +#   Supported Syntaxes:
    +#
    +#       - Enums
    +#       - Enums with declared types
    +#
    +#   Unsupported:
    +#
    +#       - Documenting the members automatically
    +#
    +sub TryToGetEnum #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($self->TryToSkipAttributes(\$index, \$lineNumber))
    +        {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +
    +    my $startIndex = $index;
    +    my $startLine = $lineNumber;
    +
    +    my @modifiers;
    +
    +    while ($tokens->[$index] =~ /^[a-z]/i &&
    +              exists $variableModifiers{lc($tokens->[$index])} )
    +        {
    +        push @modifiers, lc($tokens->[$index]);
    +        $index++;
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        };
    +
    +    if (lc($tokens->[$index]) ne 'enum')
    +        {  return undef;  }
    +
    +    $index++;
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    my $name;
    +
    +    while ($tokens->[$index] =~ /^[a-z\@\_]/i)
    +        {
    +        $name .= $tokens->[$index];
    +        $index++;
    +        };
    +
    +    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +    if ($tokens->[$index] eq ':')
    +        {
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if (!exists $enumTypes{ lc($tokens->[$index]) })
    +            {  return undef;  }
    +
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        }
    +
    +    if ($tokens->[$index] ne '{')
    +        {  return undef;  }
    +
    +    # We succeeded if we got this far.
    +
    +    my $prototype = $self->CreateString($startIndex, $index);
    +    $prototype = $self->NormalizePrototype( $prototype );
    +
    +    $self->SkipRestOfStatement(\$index, \$lineNumber);
    +
    +    $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_ENUMERATION(), $name,
    +                                                                                              $self->CurrentScope(), $self->CurrentUsing(),
    +                                                                                              $prototype,
    +                                                                                              undef, undef, $startLine));
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: TryToGetType
    +#
    +#   Determines if the position is on a type identifier, and if so, skips it and returns it as a string.  This function does _not_ allow
    +#   modifiers.  You must take care of those beforehand.
    +#
    +sub TryToGetType #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $name;
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    while ($tokens->[$index] =~ /^[a-z\@\.\_]/i)
    +        {
    +		# Case sensitive since you can declare a class Lock even though lock is a keyword.
    +        if (exists $impossibleTypeWords{ $tokens->[$index] } && $name !~ /\@$/)
    +            {  return undef;  };
    +
    +        $name .= $tokens->[$index];
    +        $index++;
    +        };
    +
    +    if (!defined $name)
    +        {  return undef;  };
    +
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if ($tokens->[$index] eq '?')
    +                {
    +                $name .= '?';
    +                $index++;
    +
    +                $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +                }
    +
    +    if ($self->TryToSkipTemplateSpec(\$index, \$lineNumber))
    +    	{  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +
    +    while ($tokens->[$index] eq '[')
    +        {
    +        $name .= '[';
    +        $index++;
    +
    +        while ($tokens->[$index] eq ',')
    +            {
    +            $name .= ',';
    +            $index++;
    +            };
    +
    +        if ($tokens->[$index] eq ']')
    +            {
    +            $name .= ']';
    +            $index++;
    +            }
    +        else
    +            {  return undef;  }
    +        };
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return $name;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Low Level Parsing Functions
    +
    +
    +#
    +#   Function: GenericSkip
    +#
    +#   Advances the position one place through general code.
    +#
    +#   - If the position is on a string, it will skip it completely.
    +#   - If the position is on an opening symbol, it will skip until the past the closing symbol.
    +#   - If the position is on whitespace (including comments and preprocessing directives), it will skip it completely.
    +#   - Otherwise it skips one token.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the current index.
    +#       lineNumberRef - A reference to the current line number.
    +#
    +sub GenericSkip #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway.
    +    if ($tokens->[$$indexRef] eq '{')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '(')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '[')
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']');
    +        }
    +
    +    elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipString($indexRef, $lineNumberRef))
    +        {
    +        }
    +
    +    else
    +        {  $$indexRef++;  };
    +    };
    +
    +
    +#
    +#   Function: GenericSkipUntilAfter
    +#
    +#   Advances the position via <GenericSkip()> until a specific token is reached and passed.
    +#
    +sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $token) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token)
    +        {  $self->GenericSkip($indexRef, $lineNumberRef);  };
    +
    +    if ($tokens->[$$indexRef] eq "\n")
    +        {  $$lineNumberRef++;  };
    +    $$indexRef++;
    +    };
    +
    +
    +#
    +#   Function: SkipRestOfStatement
    +#
    +#   Advances the position via <GenericSkip()> until after the end of the current statement, which is defined as a semicolon or
    +#   a brace group.  Of course, either of those appearing inside parenthesis, a nested brace group, etc. don't count.
    +#
    +sub SkipRestOfStatement #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens &&
    +             $tokens->[$$indexRef] ne ';' &&
    +             $tokens->[$$indexRef] ne '{')
    +        {
    +        $self->GenericSkip($indexRef, $lineNumberRef);
    +        };
    +
    +    if ($tokens->[$$indexRef] eq ';')
    +        {  $$indexRef++;  }
    +    elsif ($tokens->[$$indexRef] eq '{')
    +        {  $self->GenericSkip($indexRef, $lineNumberRef);  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipString
    +#   If the current position is on a string delimiter, skip past the string and return true.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the index of the position to start at.
    +#       lineNumberRef - A reference to the line number of the position.
    +#
    +#   Returns:
    +#
    +#       Whether the position was at a string.
    +#
    +#   Syntax Support:
    +#
    +#       - Supports quotes, apostrophes, and at-quotes.
    +#
    +sub TryToSkipString #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # The three string delimiters.  All three are Perl variables when preceded by a dollar sign.
    +    if ($self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'') ||
    +        $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"') )
    +        {
    +        return 1;
    +        }
    +    elsif ($tokens->[$$indexRef] eq '@' && $tokens->[$$indexRef+1] eq '"')
    +        {
    +        $$indexRef += 2;
    +
    +        # We need to do at-strings manually because backslash characters are accepted as regular characters, and two consecutive
    +        # quotes are accepted as well.
    +
    +        while ($$indexRef < scalar @$tokens && !($tokens->[$$indexRef] eq '"' && $tokens->[$$indexRef+1] ne '"') )
    +            {
    +            if ($tokens->[$$indexRef] eq '"')
    +                {
    +                # This is safe because the while condition will only let through quote pairs.
    +                $$indexRef += 2;
    +                }
    +            elsif ($tokens->[$$indexRef] eq "\n")
    +                {
    +                $$indexRef++;
    +                $$lineNumberRef++;
    +                }
    +            else
    +                {
    +                $$indexRef++;
    +                };
    +            };
    +
    +        # Skip the closing quote.
    +        if ($$indexRef < scalar @$tokens)
    +            {  $$indexRef++;  };
    +
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipAttributes
    +#   If the current position is on an attribute section, skip it and return true.  Skips multiple attribute sections if they appear
    +#   consecutively.
    +#
    +sub TryToSkipAttributes #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $success;
    +
    +    while ($tokens->[$$indexRef] eq '[')
    +        {
    +        $success = 1;
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']');
    +        $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +        };
    +
    +    return $success;
    +    };
    +
    +
    +#
    +#	Function: TryToSkipTemplateSpec
    +#	If the current position is on a template spec (the part in angle brackets) skip it and return true.  Can handle nested
    +#	templates.
    +#
    +sub TryToSkipTemplateSpec #(indexRef, lineNumberRef)
    +	{
    +	my ($self, $indexRef, $lineNumberRef) = @_;
    +	my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '<')
    +        {
    +        my $nestingLevel = 1;
    +        $$indexRef++;
    +        $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +
    +        while ($$indexRef < scalar @$tokens && $nestingLevel > 0)
    +        	{
    +        	if ($tokens->[$$indexRef] eq '<')
    +        		{  $nestingLevel++;  }
    +        	elsif ($tokens->[$$indexRef] eq '>')
    +        		{  $nestingLevel--;  }
    +
    +            $$indexRef++;
    +        	$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +			}
    +
    +		return 1;
    +        }
    +    else
    +    	{  return undef;  }
    +	}
    +
    +
    +#
    +#	Function: TryToSkipWhereClauses
    +#	If the current position is on a "where" clause, skips it and returns true.  Can handle multiple wheres in a row.
    +#
    +sub TryToSkipWhereClauses #(indexRef, lineNumberRef)
    +	{
    +	my ($self, $indexRef, $lineNumberRef) = @_;
    +	my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] ne 'where')
    +    	{  return undef;  }
    +
    +	my $index = $$indexRef;
    +	my $lineNumber = $$lineNumberRef;
    +
    +    do
    +        {
    +        $index++;  # Skip the where
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        $index++;  # Skip the variable name
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        if ($tokens->[$index] ne ':')
    +        	{  return undef;  }
    +
    +        $index++;  # Skip the colon
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        for (;;)
    +        	{
    +        	if ($index >= scalar @$tokens)
    +        		{  return undef;  }
    +        	elsif ($tokens->[$index] eq 'new')
    +        		{
    +        		$index++;
    +        		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        		if ($tokens->[$index] ne '(')
    +        			{  return undef;  }
    +
    +                $index++;
    +        		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        		if ($tokens->[$index] ne ')')
    +        			{  return undef;  }
    +
    +        		$index++;
    +        		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        		}
    +        	else
    +        		{
    +        		while ($index < scalar @$tokens && $tokens->[$index] =~ /^[a-z0-9_\.\@]+$/i)
    +        			{  $index++;  }
    +
    +        		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        		if ($self->TryToSkipTemplateSpec(\$index, \$lineNumber))
    +        			{  $self->TryToSkipWhitespace(\$index, \$lineNumber);  }
    +        		}
    +
    +        	if ($tokens->[$index] eq ',')
    +        		{
    +        		$index++;
    +        		$self->TryToSkipWhitespace(\$index, \$lineNumber);
    +        		}
    +        	else
    +        		{  last;  }
    +        	}
    +        }
    +    while ($tokens->[$index] eq 'where');
    +
    +    $$indexRef = $index;
    +    $$lineNumberRef = $lineNumber;
    +
    +    return 1;
    +	}
    +
    +
    +#
    +#   Function: TryToSkipWhitespace
    +#   If the current position is on a whitespace token, a line break token, a comment, or a preprocessing directive, it skips them
    +#   and returns true.  If there are a number of these in a row, it skips them all.
    +#
    +sub TryToSkipWhitespace #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $result;
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        if ($tokens->[$$indexRef] =~ /^[ \t]/)
    +            {
    +            $$indexRef++;
    +            $result = 1;
    +            }
    +        elsif ($tokens->[$$indexRef] eq "\n")
    +            {
    +            $$indexRef++;
    +            $$lineNumberRef++;
    +            $result = 1;
    +            }
    +        elsif ($self->TryToSkipComment($indexRef, $lineNumberRef) ||
    +                $self->TryToSkipPreprocessingDirective($indexRef, $lineNumberRef))
    +            {
    +            $result = 1;
    +            }
    +        else
    +            {  last;  };
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipComment
    +#   If the current position is on a comment, skip past it and return true.
    +#
    +sub TryToSkipComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +
    +    return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) ||
    +                $self->TryToSkipMultilineComment($indexRef, $lineNumberRef) );
    +    };
    +
    +
    +#
    +#   Function: TryToSkipLineComment
    +#   If the current position is on a line comment symbol, skip past it and return true.
    +#
    +sub TryToSkipLineComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '/')
    +        {
    +        $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipMultilineComment
    +#   If the current position is on an opening comment symbol, skip past it and return true.
    +#
    +sub TryToSkipMultilineComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '*')
    +        {
    +        $self->SkipUntilAfter($indexRef, $lineNumberRef, '*', '/');
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipPreprocessingDirective
    +#   If the current position is on a preprocessing directive, skip past it and return true.
    +#
    +sub TryToSkipPreprocessingDirective #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq '#' && $self->IsFirstLineToken($$indexRef))
    +        {
    +        $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/PLSQL.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/PLSQL.pm
    new file mode 100644
    index 000000000..c72db78cb
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/PLSQL.pm
    @@ -0,0 +1,320 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::PLSQL
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of PL/SQL.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::PLSQL;
    +
    +use base 'NaturalDocs::Languages::Simple';
    +
    +
    +#
    +#   Function: OnPrototypeEnd
    +#
    +#   Microsoft's SQL specifies parameters as shown below.
    +#
    +#   > CREATE PROCEDURE Test @as int, @foo int AS ...
    +#
    +#   Having a parameter @is or @as is perfectly valid even though those words are also used to end the prototype.  We need to
    +#   ignore text-based enders preceded by an at sign.  Also note that it does not have parenthesis for parameter lists.  We need to
    +#   skip all commas if the prototype doesn't have parenthesis but does have @ characters.
    +#
    +#	Identifiers such as function names may contain the characters $, #, and _, so if "as" or "is" appears directly after one of them
    +#	we need to ignore the ender there as well.
    +#
    +#	> FUNCTION Something_is_something ...
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the prototype.
    +#       prototypeRef - A reference to the prototype so far, minus the ender in dispute.
    +#       ender - The ender symbol.
    +#
    +#   Returns:
    +#
    +#       ENDER_ACCEPT - The ender is accepted and the prototype is finished.
    +#       ENDER_IGNORE - The ender is rejected and parsing should continue.  Note that the prototype will be rejected as a whole
    +#                                  if all enders are ignored before reaching the end of the code.
    +#       ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is.  However, the prototype might
    +#                                                          also continue on so continue parsing.  If there is no accepted ender between here and
    +#                                                          the end of the code this version will be accepted instead.
    +#       ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed.  Use the last accepted
    +#                                                        version and end parsing.
    +#
    +sub OnPrototypeEnd #(type, prototypeRef, ender)
    +    {
    +    my ($self, $type, $prototypeRef, $ender) = @_;
    +
    +    # _ should be handled already.
    +    if ($ender =~ /^[a-z]+$/i && substr($$prototypeRef, -1) =~ /^[\@\$\#]$/)
    +        {  return ::ENDER_IGNORE();  }
    +
    +    elsif ($type eq ::TOPIC_FUNCTION() && $ender eq ',')
    +        {
    +        if ($$prototypeRef =~ /^[^\(]*\@/)
    +            {  return ::ENDER_IGNORE();  }
    +        else
    +            {  return ::ENDER_ACCEPT();  };
    +        }
    +
    +    else
    +        {  return ::ENDER_ACCEPT();  };
    +    };
    +
    +
    +#
    +#   Function: ParsePrototype
    +#
    +#   Overridden to handle Microsoft's parenthesisless version.  Otherwise just throws to the parent.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType>.
    +#       prototype - The text prototype.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::Languages::Prototype> object.
    +#
    +sub ParsePrototype #(type, prototype)
    +    {
    +    my ($self, $type, $prototype) = @_;
    +
    +    my $noParenthesisParameters = ($type eq ::TOPIC_FUNCTION() && $prototype =~ /^[^\(]*\@/);
    +
    +    if ($prototype !~ /\(.*[^ ].*\)/ && !$noParenthesisParameters)
    +        {  return $self->SUPER::ParsePrototype($type, $prototype);  };
    +
    +
    +
    +    my ($beforeParameters, $afterParameters, $isAfterParameters);
    +
    +    if ($noParenthesisParameters)
    +        {
    +        ($beforeParameters, $prototype) = split(/\@/, $prototype, 2);
    +        $prototype = '@' . $prototype;
    +        };
    +
    +    my @tokens = $prototype =~ /([^\(\)\[\]\{\}\<\>\'\"\,]+|.)/g;
    +
    +    my $parameter;
    +    my @parameterLines;
    +
    +    my @symbolStack;
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($isAfterParameters)
    +            {  $afterParameters .= $token;  }
    +
    +        elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
    +            {
    +            if ($noParenthesisParameters || $symbolStack[0] eq '(')
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +
    +            if ($token eq $symbolStack[-1])
    +                {  pop @symbolStack;  };
    +            }
    +
    +        elsif ($token =~ /^[\(\[\{\<\'\"]$/)
    +            {
    +            if ($noParenthesisParameters || $symbolStack[0] eq '(')
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +
    +            push @symbolStack, $token;
    +            }
    +
    +        elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
    +                 ($token eq ']' && $symbolStack[-1] eq '[') ||
    +                 ($token eq '}' && $symbolStack[-1] eq '{') ||
    +                 ($token eq '>' && $symbolStack[-1] eq '<') )
    +            {
    +            if (!$noParenthesisParameters && $token eq ')' && scalar @symbolStack == 1 && $symbolStack[0] eq '(')
    +                {
    +                $afterParameters .= $token;
    +                $isAfterParameters = 1;
    +                }
    +            else
    +                {  $parameter .= $token;  };
    +
    +            pop @symbolStack;
    +            }
    +
    +        elsif ($token eq ',')
    +            {
    +            if (!scalar @symbolStack)
    +                {
    +                if ($noParenthesisParameters)
    +                    {
    +                    push @parameterLines, $parameter . $token;
    +                    $parameter = undef;
    +                    }
    +                else
    +                    {
    +                    $beforeParameters .= $token;
    +                    };
    +                }
    +            else
    +                {
    +                if (scalar @symbolStack == 1 && $symbolStack[0] eq '(' && !$noParenthesisParameters)
    +                    {
    +                    push @parameterLines, $parameter . $token;
    +                    $parameter = undef;
    +                    }
    +                else
    +                    {
    +                    $parameter .= $token;
    +                    };
    +                };
    +            }
    +
    +        else
    +            {
    +            if ($noParenthesisParameters || $symbolStack[0] eq '(')
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +            };
    +        };
    +
    +    push @parameterLines, $parameter;
    +
    +    foreach my $item (\$beforeParameters, \$afterParameters)
    +        {
    +        $$item =~ s/^ //;
    +        $$item =~ s/ $//;
    +        }
    +
    +    my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters);
    +
    +
    +    # Parse the actual parameters.
    +
    +    foreach my $parameterLine (@parameterLines)
    +        {
    +        $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) );
    +        };
    +
    +    return $prototypeObject;
    +    };
    +
    +
    +#
    +#   Function: ParseParameterLine
    +#
    +#   Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
    +#
    +sub ParseParameterLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    $line =~ s/^ //;
    +    $line =~ s/ $//;
    +
    +    my @tokens = $line =~ /([^\(\)\[\]\{\}\<\>\'\"\:\=\ ]+|\:\=|.)/g;
    +
    +    my ($name, $type, $defaultValue, $defaultValuePrefix, $inType, $inDefaultValue);
    +
    +
    +    my @symbolStack;
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($inDefaultValue)
    +            {  $defaultValue .= $token;  }
    +
    +        elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
    +            {
    +            if ($inType)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +
    +            if ($token eq $symbolStack[-1])
    +                {  pop @symbolStack;  };
    +            }
    +
    +        elsif ($token =~ /^[\(\[\{\<\'\"]$/)
    +            {
    +            if ($inType)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +
    +            push @symbolStack, $token;
    +            }
    +
    +        elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
    +                 ($token eq ']' && $symbolStack[-1] eq '[') ||
    +                 ($token eq '}' && $symbolStack[-1] eq '{') ||
    +                 ($token eq '>' && $symbolStack[-1] eq '<') )
    +            {
    +            if ($inType)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +
    +            pop @symbolStack;
    +            }
    +
    +        elsif ($token eq ' ')
    +            {
    +            if ($inType)
    +                {  $type .= $token;  }
    +            elsif (!scalar @symbolStack)
    +                {  $inType = 1;  }
    +            else
    +                {  $name .= $token;  };
    +            }
    +
    +        elsif ($token eq ':=' || $token eq '=')
    +            {
    +            if (!scalar @symbolStack)
    +                {
    +                $defaultValuePrefix = $token;
    +                $inDefaultValue = 1;
    +                }
    +            elsif ($inType)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +            }
    +
    +        else
    +            {
    +            if ($inType)
    +                {  $type .= $token;  }
    +            else
    +                {  $name .= $token;  };
    +            };
    +        };
    +
    +    foreach my $part (\$type, \$defaultValue)
    +        {
    +        $$part =~ s/ $//;
    +        };
    +
    +    return NaturalDocs::Languages::Prototype::Parameter->New($type, undef, $name, undef, $defaultValue, $defaultValuePrefix);
    +    };
    +
    +
    +sub TypeBeforeParameter
    +    {  return 0;  };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Pascal.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Pascal.pm
    new file mode 100644
    index 000000000..69cf56784
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Pascal.pm
    @@ -0,0 +1,144 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Pascal
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of Pascal and Delphi.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Pascal;
    +
    +use base 'NaturalDocs::Languages::Simple';
    +
    +
    +#
    +#   hash: prototypeDirectives
    +#
    +#   An existence hash of all the directives that can appear after a function prototype and will be included.  The keys are the all
    +#   lowercase keywords.
    +#
    +my %prototypeDirectives = ( 'overload' => 1,
    +                                           'override' => 1,
    +                                           'virtual' => 1,
    +                                           'abstract' => 1,
    +                                           'reintroduce' => 1,
    +                                           'export' => 1,
    +                                           'public' => 1,
    +                                           'interrupt' => 1,
    +                                           'register' => 1,
    +                                           'pascal' => 1,
    +                                           'cdecl' => 1,
    +                                           'stdcall' => 1,
    +                                           'popstack' => 1,
    +                                           'saveregisters' => 1,
    +                                           'inline' => 1,
    +                                           'safecall' => 1 );
    +
    +#
    +#   hash: longPrototypeDirectives
    +#
    +#   An existence hash of all the directives with parameters that can appear after a function prototype and will be included.  The
    +#   keys are the all lowercase keywords.
    +#
    +my %longPrototypeDirectives = ( 'alias' => 1,
    +                                                 'external' => 1 );
    +
    +#
    +#   bool: checkingForDirectives
    +#
    +#   Set after the first function semicolon, which means we're in directives mode.
    +#
    +my $checkingForDirectives;
    +
    +
    +#
    +#   Function: OnCode
    +#
    +#   Just overridden to reset <checkingForDirectives>.
    +#
    +sub OnCode #(...)
    +    {
    +    my ($self, @parameters) = @_;
    +
    +    $checkingForDirectives = 0;
    +
    +    return $self->SUPER::OnCode(@parameters);
    +    };
    +
    +
    +#
    +#   Function: OnPrototypeEnd
    +#
    +#   Pascal's syntax has directives after the prototype that should be included.
    +#
    +#   > function MyFunction ( param1: type ); virtual; abstract;
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the prototype.
    +#       prototypeRef - A reference to the prototype so far, minus the ender in dispute.
    +#       ender - The ender symbol.
    +#
    +#   Returns:
    +#
    +#       ENDER_ACCEPT - The ender is accepted and the prototype is finished.
    +#       ENDER_IGNORE - The ender is rejected and parsing should continue.  Note that the prototype will be rejected as a whole
    +#                                  if all enders are ignored before reaching the end of the code.
    +#       ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is.  However, the prototype might
    +#                                                          also continue on so continue parsing.  If there is no accepted ender between here and
    +#                                                          the end of the code this version will be accepted instead.
    +#       ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed.  Use the last accepted
    +#                                                        version and end parsing.
    +#
    +sub OnPrototypeEnd #(type, prototypeRef, ender)
    +    {
    +    my ($self, $type, $prototypeRef, $ender) = @_;
    +
    +    if ($type eq ::TOPIC_FUNCTION() && $ender eq ';')
    +        {
    +        if (!$checkingForDirectives)
    +            {
    +            $checkingForDirectives = 1;
    +            return ::ENDER_ACCEPT_AND_CONTINUE();
    +            }
    +        elsif ($$prototypeRef =~ /;[ \t]*([a-z]+)([^;]*)$/i)
    +            {
    +            my ($lastDirective, $extra) = (lc($1), $2);
    +
    +            if (exists $prototypeDirectives{$lastDirective} && $extra =~ /^[ \t]*$/)
    +                {  return ::ENDER_ACCEPT_AND_CONTINUE();  }
    +            elsif (exists $longPrototypeDirectives{$lastDirective})
    +                {  return ::ENDER_ACCEPT_AND_CONTINUE();  }
    +            else
    +                {  return ::ENDER_REVERT_TO_ACCEPTED();  };
    +            }
    +        else
    +            {  return ::ENDER_REVERT_TO_ACCEPTED();  };
    +        }
    +    else
    +        {  return ::ENDER_ACCEPT();  };
    +    };
    +
    +
    +sub ParseParameterLine #(...)
    +    {
    +    my ($self, @params) = @_;
    +    return $self->SUPER::ParsePascalParameterLine(@params);
    +    };
    +
    +sub TypeBeforeParameter
    +    {
    +    return 0;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Perl.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Perl.pm
    new file mode 100644
    index 000000000..5d971b8d5
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Perl.pm
    @@ -0,0 +1,1371 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Perl
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of Perl.
    +#
    +#
    +#   Topic: Language Support
    +#
    +#       Supported:
    +#
    +#       - Packages
    +#       - Inheritance via "use base" and "@ISA =".
    +#       - Functions
    +#       - Variables
    +#
    +#       Not supported yet:
    +#
    +#       - Constants
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Perl;
    +
    +use base 'NaturalDocs::Languages::Advanced';
    +
    +
    +#
    +#   array: hereDocTerminators
    +#   An array of active Here Doc terminators, or an empty array if not active.  Each entry is an arrayref of tokens.  The entries
    +#   must appear in the order they must appear in the source.
    +#
    +my @hereDocTerminators;
    +
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: PackageSeparator
    +#   Returns the package separator symbol.
    +#
    +sub PackageSeparator
    +    {  return '::';  };
    +
    +#
    +#   Function: EnumValues
    +#   Returns the <EnumValuesType> that describes how the language handles enums.
    +#
    +sub EnumValues
    +    {  return ::ENUM_GLOBAL();  };
    +
    +
    +#
    +#   Function: ParseFile
    +#
    +#   Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The name of the source file to parse.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#
    +#   Returns:
    +#
    +#       The array ( autoTopics, scopeRecord ).
    +#
    +#       autoTopics - An arrayref of automatically generated topics from the file, or undef if none.
    +#       scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none.
    +#
    +sub ParseFile #(sourceFile, topicsList)
    +    {
    +    my ($self, $sourceFile, $topicsList) = @_;
    +
    +    @hereDocTerminators = ( );
    +
    +    # The regular block comment symbols are undef because they're all potentially JavaDoc comments.  PreprocessFile() will
    +    # handle translating things like =begin naturaldocs and =begin javadoc to =begin nd.
    +    $self->ParseForCommentsAndTokens($sourceFile, [ '#' ], undef, [ '##' ], [ '=begin nd', '=end nd' ]);
    +
    +    my $tokens = $self->Tokens();
    +    my $index = 0;
    +    my $lineNumber = 1;
    +
    +    while ($index < scalar @$tokens)
    +        {
    +        if ($self->TryToSkipWhitespace(\$index, \$lineNumber) ||
    +            $self->TryToGetPackage(\$index, \$lineNumber) ||
    +            $self->TryToGetBase(\$index, \$lineNumber) ||
    +            $self->TryToGetFunction(\$index, \$lineNumber) ||
    +            $self->TryToGetVariable(\$index, \$lineNumber) )
    +            {
    +            # The functions above will handle everything.
    +            }
    +
    +        elsif ($tokens->[$index] eq '{')
    +            {
    +            $self->StartScope('}', $lineNumber, undef);
    +            $index++;
    +            }
    +
    +        elsif ($tokens->[$index] eq '}')
    +            {
    +            if ($self->ClosingScopeSymbol() eq '}')
    +                {  $self->EndScope($lineNumber);  };
    +
    +            $index++;
    +            }
    +
    +        elsif (lc($tokens->[$index]) eq 'eval')
    +            {
    +            # We want to skip the token in this case instead of letting it fall to SkipRestOfStatement.  This allows evals with braces
    +            # to be treated like normal floating braces.
    +            $index++;
    +            }
    +
    +        else
    +            {
    +            $self->SkipRestOfStatement(\$index, \$lineNumber);
    +            };
    +        };
    +
    +
    +    # Don't need to keep these around.
    +    $self->ClearTokens();
    +
    +    return ( $self->AutoTopics(), $self->ScopeRecord() );
    +    };
    +
    +
    +#
    +#   Function: PreprocessFile
    +#
    +#   Overridden to support "=begin nd" and similar.
    +#
    +#   - "=begin [nd|naturaldocs|natural docs|jd|javadoc|java doc]" all translate to "=begin nd".
    +#   - "=[nd|naturaldocs|natural docs]" also translate to "=begin nd".
    +#   - "=end [nd|naturaldocs|natural docs|jd|javadoc]" all translate to "=end nd".
    +#   - "=cut" from a ND block translates into "=end nd", but the next line will be altered to begin with "(NDPODBREAK)".  This is
    +#     so if there is POD leading into ND which ends with a cut, the parser can still end the original POD because the end ND line
    +#     would have been removed.  Remember, <NaturalDocs::Languages::Advanced->ParseForCommentsAndTokens()> removes
    +#     Natural Docs-worthy comments to save parsing time.
    +#   - "=pod begin nd" and "=pod end nd" are supported for compatibility with ND 1.32 and earlier, even though the syntax is a
    +#     mistake.
    +#   - It also supports the wrong plural forms, so naturaldoc/natural doc/javadocs/java docs will work.
    +#
    +sub PreprocessFile #(lines)
    +    {
    +    my ($self, $lines) = @_;
    +
    +    my $inNDPOD = 0;
    +    my $mustBreakPOD = 0;
    +
    +    for (my $i = 0; $i < scalar @$lines; $i++)
    +        {
    +        if ($lines->[$i] =~ /^\=(?:(?:pod[ \t]+)?begin[ \t]+)?(?:nd|natural[ \t]*docs?|jd|java[ \t]*docs?)[ \t]*$/i)
    +            {
    +            $lines->[$i] = '=begin nd';
    +            $inNDPOD = 1;
    +            $mustBreakPOD = 0;
    +            }
    +        elsif ($lines->[$i] =~ /^\=(?:pod[ \t]+)end[ \t]+(?:nd|natural[ \t]*docs?|jd|javadocs?)[ \t]*$/i)
    +            {
    +            $lines->[$i] = '=end nd';
    +            $inNDPOD = 0;
    +            $mustBreakPOD = 0;
    +            }
    +        elsif ($lines->[$i] =~ /^\=cut[ \t]*$/i)
    +            {
    +            if ($inNDPOD)
    +                {
    +                $lines->[$i] = '=end nd';
    +                $inNDPOD = 0;
    +                $mustBreakPOD = 1;
    +                };
    +            }
    +        elsif ($mustBreakPOD)
    +            {
    +            $lines->[$i] = '(NDPODBREAK)' . $lines->[$i];
    +            $mustBreakPOD = 0;
    +            };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Statement Parsing Functions
    +# All functions here assume that the current position is at the beginning of a statement.
    +#
    +# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as
    +# often as it should.  We're making use of the fact that Perl will always return undef in these cases to keep the code simpler.
    +
    +
    +#
    +#   Function: TryToGetPackage
    +#
    +#   Determines whether the position is at a package declaration statement, and if so, generates a topic for it, skips it, and
    +#   returns true.
    +#
    +sub TryToGetPackage #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if (lc($tokens->[$$indexRef]) eq 'package')
    +        {
    +        my $index = $$indexRef + 1;
    +        my $lineNumber = $$lineNumberRef;
    +
    +        if (!$self->TryToSkipWhitespace(\$index, \$lineNumber))
    +            {  return undef;  };
    +
    +        my $name;
    +
    +        while ($tokens->[$index] =~ /^[a-z_\:]/i)
    +            {
    +            $name .= $tokens->[$index];
    +            $index++;
    +            };
    +
    +        if (!defined $name)
    +            {  return undef;  };
    +
    +        my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(), $name,
    +                                                                                             undef, undef,
    +                                                                                             undef,
    +                                                                                             undef, undef, $$lineNumberRef);
    +        $self->AddAutoTopic($autoTopic);
    +
    +        NaturalDocs::Parser->OnClass($autoTopic->Symbol());
    +
    +        $self->SetPackage($autoTopic->Symbol(), $$lineNumberRef);
    +
    +        $$indexRef = $index;
    +        $$lineNumberRef = $lineNumber;
    +        $self->SkipRestOfStatement($indexRef, $lineNumberRef);
    +
    +        return 1;
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: TryToGetBase
    +#
    +#   Determines whether the position is at a package base declaration statement, and if so, calls
    +#   <NaturalDocs::Parser->OnClassParent()>.
    +#
    +#   Supported Syntaxes:
    +#
    +#   > use base [list of strings]
    +#   > @ISA = [list of strings]
    +#   > @[package]::ISA = [list of strings]
    +#   > our @ISA = [list of strings]
    +#
    +sub TryToGetBase #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my ($index, $lineNumber, $class, $parents);
    +
    +    if (lc($tokens->[$$indexRef]) eq 'use')
    +        {
    +        $index = $$indexRef + 1;
    +        $lineNumber = $$lineNumberRef;
    +
    +        if (!$self->TryToSkipWhitespace(\$index, \$lineNumber) ||
    +           lc($tokens->[$index]) ne 'base')
    +            {  return undef;  }
    +
    +        $index++;
    +        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +        $parents = $self->TryToGetListOfStrings(\$index, \$lineNumber);
    +        }
    +
    +    else
    +        {
    +        $index = $$indexRef;
    +        $lineNumber = $$lineNumberRef;
    +
    +        if (lc($tokens->[$index]) eq 'our')
    +            {
    +            $index++;
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +            };
    +
    +        if ($tokens->[$index] eq '@')
    +            {
    +            $index++;
    +
    +            while ($index < scalar @$tokens)
    +                {
    +                if ($tokens->[$index] eq 'ISA')
    +                    {
    +                    $index++;
    +                    $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +                    if ($tokens->[$index] eq '=')
    +                        {
    +                        $index++;
    +                        $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +                        $parents = $self->TryToGetListOfStrings(\$index, \$lineNumber);
    +                        }
    +                    else
    +                        {  last;  };
    +                    }
    +
    +                # If token isn't ISA...
    +                elsif ($tokens->[$index] =~ /^[a-z0-9_:]/i)
    +                    {
    +                    $class .= $tokens->[$index];
    +                    $index++;
    +                    }
    +                else
    +                    {  last;  };
    +                };
    +            };
    +        };
    +
    +    if (defined $parents)
    +        {
    +        if (defined $class)
    +            {
    +            $class =~ s/::$//;
    +            my @classIdentifiers = split(/::/, $class);
    +            $class = NaturalDocs::SymbolString->Join(@classIdentifiers);
    +            }
    +        else
    +            {  $class = $self->CurrentScope();  };
    +
    +        foreach my $parent (@$parents)
    +            {
    +            my @parentIdentifiers = split(/::/, $parent);
    +            my $parentSymbol = NaturalDocs::SymbolString->Join(@parentIdentifiers);
    +
    +            NaturalDocs::Parser->OnClassParent($class, $parentSymbol, undef, undef, ::RESOLVE_ABSOLUTE());
    +            };
    +
    +        $$indexRef = $index;
    +        $$lineNumberRef = $lineNumber;
    +        $self->SkipRestOfStatement($indexRef, $lineNumberRef);
    +
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToGetFunction
    +#
    +#   Determines whether the position is at a function declaration statement, and if so, generates a topic for it, skips it, and
    +#   returns true.
    +#
    +sub TryToGetFunction #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ( lc($tokens->[$$indexRef]) eq 'sub')
    +        {
    +        my $prototypeStart = $$indexRef;
    +        my $prototypeStartLine = $$lineNumberRef;
    +        my $prototypeEnd = $$indexRef + 1;
    +        my $prototypeEndLine = $$lineNumberRef;
    +
    +        if ( !$self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine) ||
    +             $tokens->[$prototypeEnd] !~ /^[a-z_]/i )
    +            {  return undef;  };
    +
    +        my $name = $tokens->[$prototypeEnd];
    +        $prototypeEnd++;
    +
    +        # We parsed 'sub [name]'.  Now keep going until we find a semicolon or a brace.
    +
    +        for (;;)
    +            {
    +            if ($prototypeEnd >= scalar @$tokens)
    +                {  return undef;  }
    +
    +            # End if we find a semicolon, since it means we found a predeclaration rather than an actual function.
    +            elsif ($tokens->[$prototypeEnd] eq ';')
    +                {  return undef;  }
    +
    +            elsif ($tokens->[$prototypeEnd] eq '{')
    +                {
    +                # Found it!
    +
    +                my $prototype = $self->NormalizePrototype( $self->CreateString($prototypeStart, $prototypeEnd) );
    +
    +                $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FUNCTION(), $name,
    +                                                                                                          $self->CurrentScope(), undef,
    +                                                                                                          $prototype,
    +                                                                                                          undef, undef, $prototypeStartLine));
    +
    +                $$indexRef = $prototypeEnd;
    +                $$lineNumberRef = $prototypeEndLine;
    +
    +                $self->SkipRestOfStatement($indexRef, $lineNumberRef);
    +
    +                return 1;
    +                }
    +
    +            else
    +                {  $self->GenericSkip(\$prototypeEnd, \$prototypeEndLine, 0, 1);  };
    +            };
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToGetVariable
    +#
    +#   Determines if the position is at a variable declaration statement, and if so, generates a topic for it, skips it, and returns
    +#   true.
    +#
    +#   Supported Syntaxes:
    +#
    +#   - Supports variables declared with "my", "our", and "local".
    +#   - Supports multiple declarations in one statement, such as "my ($x, $y);".
    +#   - Supports types and attributes.
    +#
    +sub TryToGetVariable #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $firstToken = lc( $tokens->[$$indexRef] );
    +
    +    if ($firstToken eq 'my' || $firstToken eq 'our' || $firstToken eq 'local')
    +        {
    +        my $prototypeStart = $$indexRef;
    +        my $prototypeStartLine = $$lineNumberRef;
    +        my $prototypeEnd = $$indexRef + 1;
    +        my $prototypeEndLine = $$lineNumberRef;
    +
    +        $self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine);
    +
    +
    +        # Get the type if present.
    +
    +        my $type;
    +
    +        if ($tokens->[$prototypeEnd] =~ /^[a-z\:]/i)
    +            {
    +            do
    +                {
    +                $type .= $tokens->[$prototypeEnd];
    +                $prototypeEnd++;
    +                }
    +            while ($tokens->[$prototypeEnd] =~ /^[a-z\:]/i);
    +
    +            if (!$self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine))
    +                {  return undef;  };
    +            };
    +
    +
    +        # Get the name, or possibly names.
    +
    +        if ($tokens->[$prototypeEnd] eq '(')
    +            {
    +            # If there's multiple variables, we'll need to build a custom prototype for each one.  $firstToken already has the
    +            # declaring word.  We're going to store each name in @names, and we're going to use $prototypeStart and
    +            # $prototypeEnd to capture any properties appearing after the list.
    +
    +            my $name;
    +            my @names;
    +            my $hasComma = 0;
    +
    +            $prototypeStart = $prototypeEnd + 1;
    +            $prototypeStartLine = $prototypeEndLine;
    +
    +            for (;;)
    +                {
    +                $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine);
    +
    +                $name = $self->TryToGetVariableName(\$prototypeStart, \$prototypeStartLine);
    +
    +                if (!defined $name)
    +                    {  return undef;  };
    +
    +                push @names, $name;
    +
    +                $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine);
    +
    +                # We can have multiple commas in a row.  We can also have trailing commas.  However, the parenthesis must
    +                # not start with a comma or be empty, hence this logic does not appear earlier.
    +                while ($tokens->[$prototypeStart] eq ',')
    +                    {
    +                    $prototypeStart++;
    +                    $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine);
    +
    +                    $hasComma = 1;
    +                    }
    +
    +                if ($tokens->[$prototypeStart] eq ')')
    +                    {
    +                    $prototypeStart++;
    +                    last;
    +                    }
    +                elsif (!$hasComma)
    +                    {  return undef;  };
    +                };
    +
    +
    +            # Now find the end of the prototype.
    +
    +            $prototypeEnd = $prototypeStart;
    +            $prototypeEndLine = $prototypeStartLine;
    +
    +            while ($prototypeEnd < scalar @$tokens &&
    +                     $tokens->[$prototypeEnd] !~ /^[\;\=]/)
    +                {
    +                $prototypeEnd++;
    +                };
    +
    +
    +            my $prototypePrefix = $firstToken . ' ';
    +            if (defined $type)
    +                {  $prototypePrefix .= $type . ' ';  };
    +
    +            my $prototypeSuffix = ' ' . $self->CreateString($prototypeStart, $prototypeEnd);
    +
    +            foreach $name (@names)
    +                {
    +                my $prototype = $self->NormalizePrototype( $prototypePrefix . $name . $prototypeSuffix );
    +
    +                $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_VARIABLE(), $name,
    +                                                                                                           $self->CurrentScope(), undef,
    +                                                                                                           $prototype,
    +                                                                                                           undef, undef, $prototypeStartLine));
    +                };
    +
    +            $self->SkipRestOfStatement(\$prototypeEnd, \$prototypeEndLine);
    +
    +            $$indexRef = $prototypeEnd;
    +            $$lineNumberRef = $prototypeEndLine;
    +            }
    +
    +        else # no parenthesis
    +            {
    +            my $name = $self->TryToGetVariableName(\$prototypeEnd, \$prototypeEndLine);
    +
    +            if (!defined $name)
    +                {  return undef;  };
    +
    +            while ($prototypeEnd < scalar @$tokens &&
    +                     $tokens->[$prototypeEnd] !~ /^[\;\=]/)
    +                {
    +                $prototypeEnd++;
    +                };
    +
    +            my $prototype = $self->NormalizePrototype( $self->CreateString($prototypeStart, $prototypeEnd) );
    +
    +            $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_VARIABLE(), $name,
    +                                                                                                       $self->CurrentScope(), undef,
    +                                                                                                       $prototype,
    +                                                                                                       undef, undef, $prototypeStartLine));
    +
    +            $self->SkipRestOfStatement(\$prototypeEnd, \$prototypeEndLine);
    +
    +            $$indexRef = $prototypeEnd;
    +            $$lineNumberRef = $prototypeEndLine;
    +            };
    +
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToGetVariableName
    +#
    +#   Determines if the position is at a variable name, and if so, skips it and returns the name.
    +#
    +sub TryToGetVariableName #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $name;
    +
    +    if ($tokens->[$$indexRef] =~ /^[\$\@\%\*]/)
    +        {
    +        $name .= $tokens->[$$indexRef];
    +        $$indexRef++;
    +
    +        $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +
    +        if ($tokens->[$$indexRef] =~ /^[a-z_]/i)
    +            {
    +            $name .= $tokens->[$$indexRef];
    +            $$indexRef++;
    +            }
    +        else
    +            {  return undef;  };
    +        };
    +
    +    return $name;
    +    };
    +
    +
    +#
    +#   Function: TryToGetListOfStrings
    +#
    +#   Attempts to retrieve a list of strings from the current position.  Returns an arrayref of them if any are found, or undef if none.
    +#   It stops the moment it reaches a non-string, so "string1, variable, string2" will only return string1.
    +#
    +#   Supported Syntaxes:
    +#
    +#   - Supports parenthesis.
    +#   - Supports all string forms supported by <TryToSkipString()>.
    +#   - Supports qw() string arrays.
    +#
    +sub TryToGetListOfStrings #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $parenthesis = 0;
    +    my $strings;
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        # We'll tolerate parenthesis.
    +        if ($tokens->[$$indexRef] eq '(')
    +            {
    +            $$indexRef++;
    +            $parenthesis++;
    +            }
    +        elsif ($tokens->[$$indexRef] eq ')')
    +            {
    +            if ($parenthesis == 0)
    +                {  last;  };
    +
    +            $$indexRef++;
    +            $parenthesis--;
    +            }
    +        elsif ($tokens->[$$indexRef] eq ',')
    +            {
    +            $$indexRef++;
    +            }
    +        else
    +            {
    +            my ($startContent, $endContent);
    +            my $symbolIndex = $$indexRef;
    +
    +            if ($self->TryToSkipString($indexRef, $lineNumberRef, \$startContent, \$endContent))
    +                {
    +                my $content = $self->CreateString($startContent, $endContent);
    +
    +                if (!defined $strings)
    +                    {  $strings = [ ];  };
    +
    +                if (lc($tokens->[$symbolIndex]) eq 'qw')
    +                    {
    +                    $content =~ tr/ \t\n/ /s;
    +                    $content =~ s/^ //;
    +
    +                    my @qwStrings = split(/ /, $content);
    +
    +                    push @$strings, @qwStrings;
    +                    }
    +                else
    +                    {
    +                    push @$strings, $content;
    +                    };
    +                }
    +            else
    +                {  last;  };
    +            };
    +
    +        $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +        };
    +
    +    return $strings;
    +    };
    +
    +
    +###############################################################################
    +# Group: Low Level Parsing Functions
    +
    +
    +#
    +#   Function: GenericSkip
    +#
    +#   Advances the position one place through general code.
    +#
    +#   - If the position is on a comment or string, it will skip it completely.
    +#   - If the position is on an opening symbol, it will skip until the past the closing symbol.
    +#   - If the position is on a regexp or quote-like operator, it will skip it completely.
    +#   - If the position is on a backslash, it will skip it and the following token.
    +#   - If the position is on whitespace (including comments), it will skip it completely.
    +#   - Otherwise it skips one token.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the current index.
    +#       lineNumberRef - A reference to the current line number.
    +#       noRegExps - If set, does not test for regular expressions.
    +#
    +sub GenericSkip #(indexRef, lineNumberRef, noRegExps)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $noRegExps, $allowStringedClosingParens) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq "\\" && $$indexRef + 1 < scalar @$tokens && $tokens->[$$indexRef+1] ne "\n")
    +        {  $$indexRef += 2;  }
    +
    +    # Note that we don't want to count backslashed ()[]{} since they could be in regexps.  Also, ()[] are valid variable names
    +    # when preceded by a string.
    +
    +    # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway.
    +    elsif ($tokens->[$$indexRef] eq '{' && !$self->IsBackslashed($$indexRef))
    +        {
    +        $$indexRef++;
    +        $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}', $noRegExps, $allowStringedClosingParens);
    +        }
    +    elsif ($tokens->[$$indexRef] eq '(' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef))
    +        {
    +        # Temporarily allow stringed closing parenthesis if it looks like we're in an anonymous function declaration with Perl's
    +        # cheap version of prototypes, such as "my $_declare = sub($) {}".
    +        my $tempAllowStringedClosingParens = $allowStringedClosingParens;
    +        if (!$allowStringedClosingParens)
    +        	{
    +        	my $tempIndex = $$indexRef - 1;
    +        	if ($tempIndex >= 0 && $tokens->[$tempIndex] =~ /^[ \t]/)
    +        		{  $tempIndex--;  }
    +        	if ($tempIndex >= 0 && $tokens->[$tempIndex] eq 'sub')
    +        		{  $tempAllowStringedClosingParens = 1;  }
    +        	}
    +
    +        $$indexRef++;
    +
    +        do
    +            {  $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')', $noRegExps, $tempAllowStringedClosingParens);  }
    +        while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1) && !$tempAllowStringedClosingParens);
    +        }
    +    elsif ($tokens->[$$indexRef] eq '[' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef))
    +        {
    +        $$indexRef++;
    +
    +        do
    +            {  $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']', $noRegExps, $allowStringedClosingParens);  }
    +        while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1));
    +        }
    +
    +    elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipString($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipHereDocDeclaration($indexRef, $lineNumberRef) ||
    +            (!$noRegExps && $self->TryToSkipRegexp($indexRef, $lineNumberRef) ) )
    +        {
    +        }
    +
    +    else
    +        {  $$indexRef++;  };
    +    };
    +
    +
    +#
    +#   Function: GenericSkipUntilAfter
    +#
    +#   Advances the position via <GenericSkip()> until a specific token is reached and passed.
    +#
    +sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token, noRegExps, allowStringedClosingParens)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $token, $noRegExps, $allowStringedClosingParens) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token)
    +        {  $self->GenericSkip($indexRef, $lineNumberRef, $noRegExps, $allowStringedClosingParens);  };
    +
    +    if ($tokens->[$$indexRef] eq "\n")
    +        {  $$lineNumberRef++;  };
    +    $$indexRef++;
    +    };
    +
    +
    +#
    +#   Function: GenericRegexpSkip
    +#
    +#   Advances the position one place through regexp code.
    +#
    +#   - If the position is on an opening symbol, it will skip until the past the closing symbol.
    +#   - If the position is on a backslash, it will skip it and the following token.
    +#   - If the position is on whitespace (not including comments), it will skip it completely.
    +#   - Otherwise it skips one token.
    +#
    +#   Also differs from <GenericSkip()> in that the parenthesis in $( and $) do count against the scope, where they wouldn't
    +#   normally.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the current index.
    +#       lineNumberRef - A reference to the current line number.
    +#       inBrackets - Whether we're in brackets or not.  If true, we don't care about matching braces and parenthesis.
    +#
    +sub GenericRegexpSkip #(indexRef, lineNumberRef, inBrackets)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $inBrackets) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($tokens->[$$indexRef] eq "\\" && $$indexRef + 1 < scalar @$tokens && $tokens->[$$indexRef+1] ne "\n")
    +        {  $$indexRef += 2;  }
    +
    +    # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway.
    +    elsif ($tokens->[$$indexRef] eq '{' && !$self->IsBackslashed($$indexRef) && !$inBrackets)
    +        {
    +        $$indexRef++;
    +        $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, '}');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '(' && !$self->IsBackslashed($$indexRef) && !$inBrackets)
    +        {
    +        $$indexRef++;
    +        $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, ')');
    +        }
    +    elsif ($tokens->[$$indexRef] eq '[' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef))
    +        {
    +        $$indexRef++;
    +
    +        do
    +            {  $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, ']');  }
    +        while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1));
    +        }
    +
    +    elsif ($tokens->[$$indexRef] eq "\n")
    +        {
    +        $$lineNumberRef++;
    +        $$indexRef++;
    +        }
    +
    +    else
    +        {  $$indexRef++;  };
    +    };
    +
    +
    +#
    +#   Function: GenericRegexpSkipUntilAfter
    +#
    +#   Advances the position via <GenericRegexpSkip()> until a specific token is reached and passed.
    +#
    +sub GenericRegexpSkipUntilAfter #(indexRef, lineNumberRef, token)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $token) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $inBrackets = ( $token eq ']' );
    +
    +    while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token)
    +        {  $self->GenericRegexpSkip($indexRef, $lineNumberRef, $inBrackets);  };
    +
    +    if ($tokens->[$$indexRef] eq "\n")
    +        {  $$lineNumberRef++;  };
    +    $$indexRef++;
    +    };
    +
    +
    +#
    +#   Function: SkipRestOfStatement
    +#
    +#   Advances the position via <GenericSkip()> until after the end of the current statement, which is defined as a semicolon or
    +#   a brace group.  Of course, either of those appearing inside parenthesis, a nested brace group, etc. don't count.
    +#
    +sub SkipRestOfStatement #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    while ($$indexRef < scalar @$tokens &&
    +             $tokens->[$$indexRef] ne ';' &&
    +             !($tokens->[$$indexRef] eq '{' && !$self->IsStringed($$indexRef)) )
    +        {
    +        $self->GenericSkip($indexRef, $lineNumberRef);
    +        };
    +
    +    if ($tokens->[$$indexRef] eq ';')
    +        {  $$indexRef++;  }
    +    elsif ($tokens->[$$indexRef] eq '{')
    +        {  $self->GenericSkip($indexRef, $lineNumberRef);  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipWhitespace
    +#
    +#   If the current position is on whitespace it skips them and returns true.  If there are a number of these in a row, it skips them
    +#   all.
    +#
    +#   Supported Syntax:
    +#
    +#       - Whitespace
    +#       - Line break
    +#       - All comment forms supported by <TryToSkipComment()>
    +#       - Here Doc content
    +#
    +sub TryToSkipWhitespace #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $result;
    +
    +    while ($$indexRef < scalar @$tokens)
    +        {
    +        if ($self->TryToSkipHereDocContent($indexRef, $lineNumberRef) ||
    +            $self->TryToSkipComment($indexRef, $lineNumberRef))
    +            {
    +            $result = 1;
    +            }
    +        elsif ($tokens->[$$indexRef] =~ /^[ \t]/)
    +            {
    +            $$indexRef++;
    +            $result = 1;
    +            }
    +        elsif ($tokens->[$$indexRef] eq "\n")
    +            {
    +            $$indexRef++;
    +            $$lineNumberRef++;
    +            $result = 1;
    +            }
    +        else
    +            {  last;  };
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipComment
    +#   If the current position is on a comment, skip past it and return true.
    +#
    +sub TryToSkipComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +
    +    return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) ||
    +                $self->TryToSkipPODComment($indexRef, $lineNumberRef) );
    +    };
    +
    +
    +#
    +#   Function: TryToSkipLineComment
    +#   If the current position is on a line comment symbol, skip past it and return true.
    +#
    +sub TryToSkipLineComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # Note that $#var is not a comment.
    +    if ($tokens->[$$indexRef] eq '#' && !$self->IsStringed($$indexRef))
    +        {
    +        $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipPODComment
    +#   If the current position is on a POD comment symbol, skip past it and return true.
    +#
    +sub TryToSkipPODComment #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # Note that whitespace is not allowed before the equals sign.  It must directly start a line.
    +    if ($tokens->[$$indexRef] eq '=' &&
    +        ( $$indexRef == 0 || $tokens->[$$indexRef - 1] eq "\n" ) &&
    +        $tokens->[$$indexRef + 1] =~ /^[a-z]/i )
    +        {
    +        # Skip until =cut or (NDPODBREAK).  Note that it's theoretically possible for =cut to appear without a prior POD directive.
    +
    +        do
    +            {
    +            if ($tokens->[$$indexRef] eq '=' && lc( $tokens->[$$indexRef + 1] ) eq 'cut')
    +                {
    +                $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +                last;
    +                }
    +            elsif ($tokens->[$$indexRef] eq '(' && $$indexRef + 2 < scalar @$tokens &&
    +                    $tokens->[$$indexRef+1] eq 'NDPODBREAK' && $tokens->[$$indexRef+2] eq ')')
    +                {
    +                $$indexRef += 3;
    +                last;
    +                }
    +            else
    +                {
    +                $self->SkipRestOfLine($indexRef, $lineNumberRef);
    +                };
    +            }
    +        while ($$indexRef < scalar @$tokens);
    +
    +        return 1;
    +        }
    +
    +    # It's also possible that (NDPODBREAK) will appear without any opening pod statement because "=begin nd" and "=cut" will
    +    # still result in one.  We need to pick off the stray (NDPODBREAK).
    +    elsif ($tokens->[$$indexRef] eq '(' && $$indexRef + 2 < scalar @$tokens &&
    +            $tokens->[$$indexRef+1] eq 'NDPODBREAK' && $tokens->[$$indexRef+2] eq ')')
    +        {
    +        $$indexRef += 3;
    +        return 1;
    +        }
    +
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipString
    +#   If the current position is on a string delimiter, skip past the string and return true.
    +#
    +#   Parameters:
    +#
    +#       indexRef - A reference to the index of the position to start at.
    +#       lineNumberRef - A reference to the line number of the position.
    +#       startContentIndexRef - A reference to the variable in which to store the index of the first content token.  May be undef.
    +#       endContentIndexRef - A reference to the variable in which to store the index of the end of the content, which is one past
    +#                                        the last content token.  may be undef.
    +#
    +#   Returns:
    +#
    +#       Whether the position was at a string.  The index, line number, and content index variabls will only be changed if true.
    +#
    +#   Syntax Support:
    +#
    +#       - Supports quotes, apostrophes, backticks, q(), qq(), qx(), and qw().
    +#       - All symbols are supported for the letter forms.
    +#
    +sub TryToSkipString #(indexRef, lineNumberRef, startContentIndexRef, endContentIndexRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef, $startContentIndexRef, $endContentIndexRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # The three string delimiters.  All three are Perl variables when preceded by a dollar sign.
    +    if (!$self->IsStringed($$indexRef) &&
    +        ( $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'', '\'', $startContentIndexRef, $endContentIndexRef) ||
    +          $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"', '"', $startContentIndexRef, $endContentIndexRef) ||
    +          $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '`', '`', $startContentIndexRef, $endContentIndexRef) ) )
    +        {
    +        return 1;
    +        }
    +    elsif ($tokens->[$$indexRef] =~ /^(?:q|qq|qx|qw)$/i &&
    +            ($$indexRef == 0 || $tokens->[$$indexRef - 1] !~ /^[\$\%\@\*]$/))
    +        {
    +        $$indexRef++;
    +
    +        $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +
    +        my $openingSymbol = $tokens->[$$indexRef];
    +        my $closingSymbol;
    +
    +        if ($openingSymbol eq '{')
    +            {  $closingSymbol = '}';  }
    +        elsif ($openingSymbol eq '(')
    +            {  $closingSymbol = ')';  }
    +        elsif ($openingSymbol eq '[')
    +            {  $closingSymbol = ']';  }
    +        elsif ($openingSymbol eq '<')
    +            {  $closingSymbol = '>';  }
    +        else
    +            {  $closingSymbol = $openingSymbol;  };
    +
    +        $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, $openingSymbol, $closingSymbol,
    +                                                      $startContentIndexRef, $endContentIndexRef);
    +
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipHereDocDeclaration
    +#
    +#   If the current position is on a Here Doc declaration, add its terminators to <hereDocTerminators> and skip it.
    +#
    +#   Syntax Support:
    +#
    +#       - Supports <<EOF
    +#       - Supports << "String" with all string forms supported by <TryToSkipString()>.
    +#
    +sub TryToSkipHereDocDeclaration #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $index = $$indexRef;
    +    my $lineNumber = $$lineNumberRef;
    +
    +    if ($tokens->[$index] eq '<' && $tokens->[$index + 1] eq '<')
    +        {
    +        $index += 2;
    +        my $success;
    +
    +        # No whitespace allowed with the bare word.
    +        if ($tokens->[$index] =~ /^[a-z0-9_]/i)
    +            {
    +            push @hereDocTerminators, [ $tokens->[$index] ];
    +            $index++;
    +            $success = 1;
    +            }
    +        else
    +            {
    +            $self->TryToSkipWhitespace(\$index, \$lineNumber);
    +
    +            my ($contentStart, $contentEnd);
    +            if ($self->TryToSkipString(\$index, \$lineNumber, \$contentStart, \$contentEnd))
    +                {
    +                push @hereDocTerminators, [ @{$tokens}[$contentStart..$contentEnd - 1] ];
    +                $success = 1;
    +                };
    +            };
    +
    +        if ($success)
    +            {
    +            $$indexRef = $index;
    +            $$lineNumberRef = $lineNumber;
    +
    +            return 1;
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +#
    +#   Function: TryToSkipHereDocContent
    +#
    +#   If the current position is at the beginning of a line and there are entries in <hereDocTerminators>, skips lines until all the
    +#   terminators are exhausted or we reach the end of the file.
    +#
    +#   Returns:
    +#
    +#       Whether the position was on Here Doc content.
    +#
    +sub TryToSkipHereDocContent #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    # We don't use IsFirstLineToken() because it really needs to be the first line token.  Whitespace is not allowed.
    +    if ($$indexRef > 0 && $tokens->[$$indexRef - 1] eq "\n")
    +        {
    +        my $success = (scalar @hereDocTerminators > 0);
    +
    +        while (scalar @hereDocTerminators && $$indexRef < scalar @$tokens)
    +            {
    +            my $terminatorIndex = 0;
    +
    +            while ($hereDocTerminators[0]->[$terminatorIndex] eq $tokens->[$$indexRef])
    +                {
    +                $terminatorIndex++;
    +                $$indexRef++;
    +                };
    +
    +            if ($terminatorIndex == scalar @{$hereDocTerminators[0]} &&
    +                ($tokens->[$$indexRef] eq "\n" || ($tokens->[$$indexRef] =~ /^[ \t]/ && $tokens->[$$indexRef + 1] eq "\n")) )
    +                {
    +                shift @hereDocTerminators;
    +                $$indexRef++;
    +                $$lineNumberRef++;
    +                }
    +            else
    +                {  $self->SkipRestOfLine($indexRef, $lineNumberRef);  };
    +            };
    +
    +        return $success;
    +        }
    +
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: TryToSkipRegexp
    +#   If the current position is on a regular expression or a quote-like operator, skip past it and return true.
    +#
    +#   Syntax Support:
    +#
    +#       - Supports //, ??, m//, qr//, s///, tr///, and y///.
    +#       - All symbols are supported for the letter forms.
    +#       - ?? is *not* supported because it could cause problems with ?: statements.  The generic parser has a good chance of
    +#         successfully stumbling through a regex, whereas the regex code will almost certainly see the rest of the file as part of it.
    +#
    +sub TryToSkipRegexp #(indexRef, lineNumberRef)
    +    {
    +    my ($self, $indexRef, $lineNumberRef) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    my $isRegexp;
    +
    +    # If it's a supported character sequence that's not a variable (ex $qr) or package (ex a::tr)...
    +    if ($tokens->[$$indexRef] =~ /^(?:m|qr|s|tr|y)$/i &&
    +         ($$indexRef == 0 || $tokens->[$$indexRef - 1] !~ /^[\$\%\@\*\-\>\:]$/) )
    +        {  $isRegexp = 1;  }
    +
    +    elsif ($tokens->[$$indexRef] eq '/' && !$self->IsStringed($$indexRef))
    +        {
    +        # This is a bit of a hack.  If we find a random slash, it could be a divide operator or a bare regexp.  Find the first previous
    +        # non-whitespace token and if it's text, a closing brace, or a string, assume it's a divide operator.  (Strings don't make
    +        # much pratical sense there but a regexp would be impossible.)  Otherwise assume it's a regexp.
    +
    +        # We make a special consideration for split() appearing without parenthesis.  If the previous token is split and it's not a
    +        # variable, assume it is a regexp even though it fails the above test.
    +
    +        my $index = $$indexRef - 1;
    +
    +        while ($index >= 0 && $tokens->[$index] =~ /^(?: |\t|\n)/)
    +            {  $index--;  };
    +
    +        if ($index < 0 || $tokens->[$index] !~ /^[a-zA-Z0-9_\)\]\}\'\"\`]/ ||
    +            ($tokens->[$index] =~ /^split|grep$/ && $index > 0 && $tokens->[$index-1] !~ /^[\$\%\@\*]$/) )
    +            {  $isRegexp = 1;  };
    +        };
    +
    +    if ($isRegexp)
    +        {
    +        my $operator = lc($tokens->[$$indexRef]);
    +        my $index = $$indexRef;
    +        my $lineNumber = $$lineNumberRef;
    +
    +        if ($operator =~ /^[\?\/]/)
    +            {  $operator = 'm';  }
    +        else
    +            {
    +            $index++;
    +
    +            # Believe it or not, s#...# is allowed.  We can't pass over number signs here.
    +            if ($tokens->[$index] ne '#')
    +                {  $self->TryToSkipWhitespace(\$index, \$lineNumber);  };
    +            };
    +
    +        if ($tokens->[$index] =~ /^\w/)
    +            {  return undef;  };
    +        if ($tokens->[$index] eq '=' && $tokens->[$index+1] eq '>')
    +        	{  return undef;  };
    +
    +        my $openingSymbol = $tokens->[$index];
    +        my $closingSymbol;
    +
    +        if ($openingSymbol eq '{')
    +            {  $closingSymbol = '}';  }
    +        elsif ($openingSymbol eq '(')
    +            {  $closingSymbol = ')';  }
    +        elsif ($openingSymbol eq '[')
    +            {  $closingSymbol = ']';  }
    +        elsif ($openingSymbol eq '<')
    +            {  $closingSymbol = '>';  }
    +        else
    +            {  $closingSymbol = $openingSymbol;  };
    +
    +        $index++;
    +
    +        $self->GenericRegexpSkipUntilAfter(\$index, \$lineNumber, $closingSymbol);
    +
    +        $$indexRef = $index;
    +        $$lineNumberRef = $lineNumber;
    +
    +        if ($operator =~ /^(?:s|tr|y)$/)
    +            {
    +            if ($openingSymbol ne $closingSymbol)
    +                {
    +                $self->TryToSkipWhitespace($indexRef, $lineNumberRef);
    +
    +                $openingSymbol = $tokens->[$index];
    +
    +                if ($openingSymbol eq '{')
    +                    {  $closingSymbol = '}';  }
    +                elsif ($openingSymbol eq '(')
    +                    {  $closingSymbol = ')';  }
    +                elsif ($openingSymbol eq '[')
    +                    {  $closingSymbol = ']';  }
    +                elsif ($openingSymbol eq '<')
    +                    {  $closingSymbol = '>';  }
    +                else
    +                    {  $closingSymbol = $openingSymbol;  };
    +
    +                $$indexRef++;
    +                };
    +
    +            if ($operator eq 's')
    +                {
    +                $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, $closingSymbol, 1);
    +                }
    +            else # ($operator eq 'tr' || $operator eq 'y')
    +                {
    +                while ($$indexRef < scalar @$tokens &&
    +                          ($tokens->[$$indexRef] ne $closingSymbol || $self->IsBackslashed($$indexRef)) )
    +                    {
    +                    if ($tokens->[$$indexRef] eq "\n")
    +                        {  $$lineNumberRef++;  };
    +                    $$indexRef++;
    +                    };
    +
    +                $$indexRef++;
    +                };
    +            };
    +
    +        # We want to skip any letters after the regexp.  Otherwise something like tr/a/b/s; could have the trailing s; interpreted
    +        # as another regexp.  Whitespace is not allowed between the closing symbol and the letters.
    +
    +        if ($tokens->[$$indexRef] =~ /^[a-z]/i)
    +            {  $$indexRef++;  };
    +
    +        return 1;
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: IsStringed
    +#
    +#   Returns whether the position is after a string (dollar sign) character.  Returns false if it's preceded by two dollar signs so
    +#   "if ($x == $$)" doesn't skip the closing parenthesis as stringed.
    +#
    +#   Parameters:
    +#
    +#       index - The index of the postition.
    +#
    +sub IsStringed #(index)
    +    {
    +    my ($self, $index) = @_;
    +    my $tokens = $self->Tokens();
    +
    +    if ($index > 0 && $tokens->[$index - 1] eq '$' && !($index > 1 && $tokens->[$index - 2] eq '$'))
    +        {  return 1;  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype.pm
    new file mode 100644
    index 000000000..b4d5fc86f
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype.pm
    @@ -0,0 +1,93 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Prototype
    +#
    +###############################################################################
    +#
    +#   A data class for storing parsed prototypes.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::Languages::Prototype::Parameter;
    +
    +
    +package NaturalDocs::Languages::Prototype;
    +
    +use NaturalDocs::DefineMembers 'BEFORE_PARAMETERS', 'BeforeParameters()', 'SetBeforeParameters()',
    +                                                 'AFTER_PARAMETERS', 'AfterParameters()', 'SetAfterParameters()',
    +                                                 'PARAMETERS', 'Parameters()';
    +# Dependency: New(), constant order, no parents.
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new prototype object.
    +#
    +#   Parameters:
    +#
    +#       beforeParameters - The part of the prototype before the parameter list.
    +#       afterParameters - The part of the prototype after the parameter list.
    +#
    +#   You cannot set the parameters from here.  Use <AddParameter()>.
    +#
    +sub New #(beforeParameters, afterParameters)
    +    {
    +    my ($package, @params) = @_;
    +
    +    # Dependency: Constant order, no parents.
    +
    +    my $object = [ @params ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Members
    +#
    +#   BeforeParameters - Returns the part of the prototype before the parameter list.  If there is no parameter list, this will be the
    +#                                only thing that returns content.
    +#   SetBeforeParameters - Replaces the part of the prototype before the parameter list.
    +#   AfterParameters - Returns the part of the prototype after the parameter list, if any.
    +#   SetAfterParameters - Replaces the part of the prototype after the parameter list.
    +#   Parameters - Returns the parameter list as an arrayref of <NaturalDocs::Languages::Prototype::Parameters>, or undef if none.
    +#
    +
    +#
    +#   Function: AddParameter
    +#
    +#   Adds a <NaturalDocs::Languages::Prototype::Parameter> to the list.
    +#
    +sub AddParameter #(parameter)
    +    {
    +    my ($self, $parameter) = @_;
    +
    +    if (!defined $self->[PARAMETERS])
    +        {  $self->[PARAMETERS] = [ ];  };
    +
    +    push @{$self->[PARAMETERS]}, $parameter;
    +    };
    +
    +
    +#
    +#   Function: OnlyBeforeParameters
    +#
    +#   Returns whether <BeforeParameters()> is the only thing set.
    +#
    +sub OnlyBeforeParameters
    +    {
    +    my $self = shift;
    +    return (!defined $self->[PARAMETERS] && !defined $self->[AFTER_PARAMETERS]);
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm
    new file mode 100644
    index 000000000..41b5bce54
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm
    @@ -0,0 +1,88 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Prototype::Parameter
    +#
    +###############################################################################
    +#
    +#   A data class for storing parsed prototype parameters.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Prototype::Parameter;
    +
    +use NaturalDocs::DefineMembers 'TYPE', 'Type()', 'SetType()',
    +                                                 'TYPE_PREFIX', 'TypePrefix()', 'SetTypePrefix()',
    +                                                 'NAME', 'Name()', 'SetName()',
    +                                                 'NAME_PREFIX', 'NamePrefix()', 'SetNamePrefix()',
    +                                                 'DEFAULT_VALUE', 'DefaultValue()', 'SetDefaultValue()',
    +                                                 'DEFAULT_VALUE_PREFIX', 'DefaultValuePrefix()', 'SetDefaultValuePrefix()';
    +# Dependency: New() depends on the order of these constants and that they don't inherit from another class.
    +
    +
    +#
    +#   Constants: Members
    +#
    +#   The object is implemented as a blessed arrayref, with the following constants as its indexes.
    +#
    +#       TYPE - The parameter type, if any.
    +#       TYPE_PREFIX - The parameter type prefix which should be aligned separately, if any.
    +#       NAME - The parameter name.
    +#       NAME_PREFIX - The parameter name prefix which should be aligned separately, if any.
    +#       DEFAULT_VALUE - The default value expression, if any.
    +#       DEFAULT_VALUE_PREFIX - The default value prefix which should be aligned separately, if any.
    +#
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new prototype object.
    +#
    +#   Parameters:
    +#
    +#       type - The parameter type, if any.
    +#       typePrefix - The parameter type prefix which should be aligned separately, if any.
    +#       name - The parameter name.
    +#       namePrefix - The parameter name prefix which should be aligned separately, if any.
    +#       defaultValue - The default value expression, if any.
    +#       defaultValuePrefix - The default value prefix which should be aligned separately, if any.
    +#
    +sub New #(type, typePrefix, name, namePrefix, defaultValue, defaultValuePrefix)
    +    {
    +    my ($package, @params) = @_;
    +
    +    # Dependency: This depends on the order of the parameters being the same as the order of the constants, and that the
    +    # constants don't inherit from another class.
    +
    +    my $object = [ @params ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Members
    +#
    +#   Type - The parameter type, if any.
    +#   SetType - Replaces the parameter type.
    +#   TypePrefix - The parameter type prefix, which should be aligned separately, if any.
    +#   SetTypePrefix - Replaces the parameter type prefix.
    +#   Name - The parameter name.
    +#   SetName - Replaces the parameter name.
    +#   NamePrefix - The parameter name prefix, which should be aligned separately, if any.
    +#   SetNamePrefix - Replaces the parameter name prefix.
    +#   DefaultValue - The default value expression, if any.
    +#   SetDefaultValue - Replaces the default value expression.
    +#   DefaultValuePrefix - The default value prefix, which should be aligned separately, if any.
    +#   SetDefaultValuePrefix - Replaces the default value prefix.
    +#
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Simple.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Simple.pm
    new file mode 100644
    index 000000000..6dfacd86a
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Simple.pm
    @@ -0,0 +1,487 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Simple
    +#
    +###############################################################################
    +#
    +#   A class containing the characteristics of a particular programming language for basic support within Natural Docs.
    +#   Also serves as a base class for languages that break from general conventions, such as not having parameter lists use
    +#   parenthesis and commas.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Simple;
    +
    +use base 'NaturalDocs::Languages::Base';
    +use base 'Exporter';
    +
    +our @EXPORT = ( 'ENDER_ACCEPT', 'ENDER_IGNORE', 'ENDER_ACCEPT_AND_CONTINUE', 'ENDER_REVERT_TO_ACCEPTED' );
    +
    +
    +use NaturalDocs::DefineMembers 'LINE_COMMENT_SYMBOLS', 'LineCommentSymbols()', 'SetLineCommentSymbols() duparrayref',
    +                                                 'BLOCK_COMMENT_SYMBOLS', 'BlockCommentSymbols()',
    +                                                                                              'SetBlockCommentSymbols() duparrayref',
    +                                                 'PROTOTYPE_ENDERS',
    +                                                 'LINE_EXTENDER', 'LineExtender()', 'SetLineExtender()',
    +                                                 'PACKAGE_SEPARATOR', 'PackageSeparator()',
    +                                                 'PACKAGE_SEPARATOR_WAS_SET', 'PackageSeparatorWasSet()',
    +                                                 'ENUM_VALUES', 'EnumValues()',
    +                                                 'ENUM_VALUES_WAS_SET', 'EnumValuesWasSet()';
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       name - The name of the language.
    +#
    +sub New #(name)
    +    {
    +    my ($selfPackage, $name) = @_;
    +
    +    my $object = $selfPackage->SUPER::New($name);
    +
    +    $object->[ENUM_VALUES] = ::ENUM_GLOBAL();
    +    $object->[PACKAGE_SEPARATOR] = '.';
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Members
    +#
    +#   LineCommentSymbols - Returns an arrayref of symbols that start a line comment, or undef if none.
    +#   SetLineCommentSymbols - Replaces the arrayref of symbols that start a line comment.
    +#   BlockCommentSymbols - Returns an arrayref of start/end symbol pairs that specify a block comment, or undef if none.  Pairs
    +#                                        are specified with two consecutive array entries.
    +#   SetBlockCommentSymbols - Replaces the arrayref of start/end symbol pairs that specify a block comment.  Pairs are
    +#                                             specified with two consecutive array entries.
    +#   LineExtender - Returns the symbol to ignore a line break in languages where line breaks are significant.
    +#   SetLineExtender - Replaces the symbol to ignore a line break in languages where line breaks are significant.
    +#   PackageSeparator - Returns the package separator symbol.
    +#   PackageSeparatorWasSet - Returns whether the package separator symbol was ever changed from the default.
    +#
    +
    +#
    +#   Function: SetPackageSeparator
    +#   Replaces the language's package separator string.
    +#
    +sub SetPackageSeparator #(separator)
    +    {
    +    my ($self, $separator) = @_;
    +    $self->[PACKAGE_SEPARATOR] = $separator;
    +    $self->[PACKAGE_SEPARATOR_WAS_SET] = 1;
    +    };
    +
    +
    +#
    +#   Functions: Members
    +#
    +#   EnumValues - Returns the <EnumValuesType> that describes how the language handles enums.
    +#   EnumValuesWasSet - Returns whether <EnumValues> was ever changed from the default.
    +
    +
    +#
    +#   Function: SetEnumValues
    +#   Replaces the <EnumValuesType> that describes how the language handles enums.
    +#
    +sub SetEnumValues #(EnumValuesType newBehavior)
    +    {
    +    my ($self, $behavior) = @_;
    +    $self->[ENUM_VALUES] = $behavior;
    +    $self->[ENUM_VALUES_WAS_SET] = 1;
    +    };
    +
    +
    +#
    +#   Function: PrototypeEndersFor
    +#
    +#   Returns an arrayref of prototype ender symbols for the passed <TopicType>, or undef if none.
    +#
    +sub PrototypeEndersFor #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    if (defined $self->[PROTOTYPE_ENDERS])
    +        {  return $self->[PROTOTYPE_ENDERS]->{$type};  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: SetPrototypeEndersFor
    +#
    +#   Replaces the arrayref of prototype ender symbols for the passed <TopicType>.
    +#
    +sub SetPrototypeEndersFor #(type, enders)
    +    {
    +    my ($self, $type, $enders) = @_;
    +
    +    if (!defined $self->[PROTOTYPE_ENDERS])
    +        {  $self->[PROTOTYPE_ENDERS] = { };  };
    +
    +    if (!defined $enders)
    +        {  delete $self->[PROTOTYPE_ENDERS]->{$type};  }
    +    else
    +        {
    +        $self->[PROTOTYPE_ENDERS]->{$type} = [ @$enders ];
    +        };
    +    };
    +
    +
    +
    +
    +###############################################################################
    +# Group: Parsing Functions
    +
    +
    +#
    +#   Function: ParseFile
    +#
    +#   Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>
    +#   and all other sections to <OnCode()>.
    +#
    +#   Parameters:
    +#
    +#       sourceFile - The <FileName> of the source file to parse.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#
    +#   Returns:
    +#
    +#       Since this class cannot automatically document the code or generate a scope record, it always returns ( undef, undef ).
    +#
    +sub ParseFile #(sourceFile, topicsList)
    +    {
    +    my ($self, $sourceFile, $topicsList) = @_;
    +
    +    open(SOURCEFILEHANDLE, '<' . $sourceFile)
    +        or die "Couldn't open input file " . $sourceFile . "\n";
    +
    +    my $lineReader = NaturalDocs::LineReader->New(\*SOURCEFILEHANDLE);
    +
    +    my @commentLines;
    +    my @codeLines;
    +    my $lastCommentTopicCount = 0;
    +
    +    if ($self->Name() eq 'Text File')
    +        {
    +        @commentLines = $lineReader->GetAll();
    +        NaturalDocs::Parser->OnComment(\@commentLines, 1);
    +        }
    +
    +    else
    +        {
    +        my $line = $lineReader->Get();
    +        my $lineNumber = 1;
    +
    +        while (defined $line)
    +            {
    +            my $originalLine = $line;
    +
    +
    +            # Retrieve multiline comments.  This leaves $line at the next line.
    +            # We check for multiline comments before single line comments because in Lua the symbols are --[[ and --.
    +
    +            if (my $closingSymbol = $self->StripOpeningBlockSymbols(\$line, $self->BlockCommentSymbols()))
    +                {
    +                # Note that it is possible for a multiline comment to start correctly but not end so.  We want those comments to stay in
    +                # the code.  For example, look at this prototype with this splint annotation:
    +                #
    +                # int get_array(integer_t id,
    +                #                    /*@out@*/ array_t array);
    +                #
    +                # The annotation starts correctly but doesn't end so because it is followed by code on the same line.
    +
    +                my $lineRemainder;
    +
    +                for (;;)
    +                    {
    +                    $lineRemainder = $self->StripClosingSymbol(\$line, $closingSymbol);
    +
    +                    push @commentLines, $line;
    +
    +                    #  If we found an end comment symbol...
    +                    if (defined $lineRemainder)
    +                        {  last;  };
    +
    +                    $line = $lineReader->Get();
    +
    +                    if (!defined $line)
    +                        {  last;  };
    +                    };
    +
    +                if ($lineRemainder !~ /^[ \t]*$/)
    +                    {
    +                    # If there was something past the closing symbol this wasn't an acceptable comment, so move the lines to code.
    +                    push @codeLines, @commentLines;
    +                    @commentLines = ( );
    +                    };
    +
    +                $line = $lineReader->Get();
    +                }
    +
    +
    +            # Retrieve single line comments.  This leaves $line at the next line.
    +
    +            elsif ($self->StripOpeningSymbols(\$line, $self->LineCommentSymbols()))
    +                {
    +                do
    +                    {
    +                    push @commentLines, $line;
    +                    $line = $lineReader->Get();
    +
    +                    if (!defined $line)
    +                        {  goto EndDo;  };
    +                    }
    +                while ($self->StripOpeningSymbols(\$line, $self->LineCommentSymbols()));
    +
    +                EndDo:  # I hate Perl sometimes.
    +                }
    +
    +
    +            # Otherwise just add it to the code.
    +
    +            else
    +                {
    +                push @codeLines, $line;
    +                $line = $lineReader->Get();
    +                };
    +
    +
    +            # If there were comments, send them to Parser->OnComment().
    +
    +            if (scalar @commentLines)
    +                {
    +                # First process any code lines before the comment.
    +                if (scalar @codeLines)
    +                    {
    +                    $self->OnCode(\@codeLines, $lineNumber, $topicsList, $lastCommentTopicCount);
    +                    $lineNumber += scalar @codeLines;
    +                    @codeLines = ( );
    +                    };
    +
    +                $lastCommentTopicCount = NaturalDocs::Parser->OnComment(\@commentLines, $lineNumber);
    +                $lineNumber += scalar @commentLines;
    +                @commentLines = ( );
    +                };
    +
    +            };  # while (defined $line)
    +
    +
    +        # Clean up any remaining code.
    +        if (scalar @codeLines)
    +            {
    +            $self->OnCode(\@codeLines, $lineNumber, $topicsList, $lastCommentTopicCount);
    +            @codeLines = ( );
    +            };
    +
    +        };
    +
    +    close(SOURCEFILEHANDLE);
    +
    +    return ( undef, undef );
    +    };
    +
    +
    +#
    +#   Function: OnCode
    +#
    +#   Called whenever a section of code is encountered by the parser.  Is used to find the prototype of the last topic created.
    +#
    +#   Parameters:
    +#
    +#       codeLines - The source code as an arrayref of lines.
    +#       codeLineNumber - The line number of the first line of code.
    +#       topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file.
    +#       lastCommentTopicCount - The number of Natural Docs topics that were created by the last comment.
    +#
    +sub OnCode #(codeLines, codeLineNumber, topicList, lastCommentTopicCount)
    +    {
    +    my ($self, $codeLines, $codeLineNumber, $topicList, $lastCommentTopicCount) = @_;
    +
    +    if ($lastCommentTopicCount && defined $self->PrototypeEndersFor($topicList->[-1]->Type()))
    +        {
    +        my $lineIndex = 0;
    +        my $prototype;
    +
    +        # Skip all blank lines before a prototype.
    +        while ($lineIndex < scalar @$codeLines && $codeLines->[$lineIndex] =~ /^[ \t]*$/)
    +            {  $lineIndex++;  };
    +
    +        my @tokens;
    +        my $tokenIndex = 0;
    +
    +        my @brackets;
    +        my $enders = $self->PrototypeEndersFor($topicList->[-1]->Type());
    +
    +        # Add prototype lines until we reach the end of the prototype or the end of the code lines.
    +        while ($lineIndex < scalar @$codeLines)
    +            {
    +            my $line = $self->RemoveLineExtender($codeLines->[$lineIndex] . "\n");
    +
    +            push @tokens, $line =~ /([^\(\)\[\]\{\}\<\>]+|.)/g;
    +
    +            while ($tokenIndex < scalar @tokens)
    +                {
    +                # If we're not inside brackets, check for ender symbols.
    +                if (!scalar @brackets)
    +                    {
    +                    my $startingIndex = 0;
    +                    my $testPrototype;
    +
    +                    for (;;)
    +                        {
    +                        my ($enderIndex, $ender) = ::FindFirstSymbol($tokens[$tokenIndex], $enders, $startingIndex);
    +
    +                        if ($enderIndex == -1)
    +                            {  last;  }
    +                        else
    +                            {
    +                            # We do this here so we don't duplicate prototype for every single token.  Just the first time an ender symbol
    +                            # is found in one.
    +                            if (!defined $testPrototype)
    +                                {  $testPrototype = $prototype;  };
    +
    +                            $testPrototype .= substr($tokens[$tokenIndex], $startingIndex, $enderIndex - $startingIndex);
    +
    +                            my $enderResult;
    +
    +                            # If the ender is all text and the character preceding or following it is as well, ignore it.
    +                            if ($ender =~ /^[a-z0-9]+$/i &&
    +                                ( ($enderIndex > 0 && substr($tokens[$tokenIndex], $enderIndex - 1, 1) =~ /^[a-z0-9_]$/i) ||
    +                                   substr($tokens[$tokenIndex], $enderIndex + length($ender), 1) =~ /^[a-z0-9_]$/i ) )
    +                                {  $enderResult = ENDER_IGNORE();  }
    +                            else
    +                                {  $enderResult = $self->OnPrototypeEnd($topicList->[-1]->Type(), \$testPrototype, $ender);  }
    +
    +                            if ($enderResult == ENDER_IGNORE())
    +                                {
    +                                $testPrototype .= $ender;
    +                                $startingIndex = $enderIndex + length($ender);
    +                                }
    +                            elsif ($enderResult == ENDER_REVERT_TO_ACCEPTED())
    +                                {
    +                                return;
    +                                }
    +                            else # ENDER_ACCEPT || ENDER_ACCEPT_AND_CONTINUE
    +                                {
    +                                my $titleInPrototype = $topicList->[-1]->Title();
    +
    +                                # Strip parenthesis so Function(2) and Function(int, int) will still match Function(anything).
    +                                $titleInPrototype =~ s/[\t ]*\([^\(]*$//;
    +
    +                                if (index($testPrototype, $titleInPrototype) != -1)
    +                                    {
    +                                    $topicList->[-1]->SetPrototype( $self->NormalizePrototype($testPrototype) );
    +                                    };
    +
    +                                if ($enderResult == ENDER_ACCEPT())
    +                                    {  return;  }
    +                                else # ENDER_ACCEPT_AND_CONTINUE
    +                                    {
    +                                    $testPrototype .= $ender;
    +                                    $startingIndex = $enderIndex + length($ender);
    +                                    };
    +                                };
    +                            };
    +                        };
    +                    }
    +
    +                # If we are inside brackets, check for closing symbols.
    +                elsif ( ($tokens[$tokenIndex] eq ')' && $brackets[-1] eq '(') ||
    +                         ($tokens[$tokenIndex] eq ']' && $brackets[-1] eq '[') ||
    +                         ($tokens[$tokenIndex] eq '}' && $brackets[-1] eq '{') ||
    +                         ($tokens[$tokenIndex] eq '>' && $brackets[-1] eq '<') )
    +                    {
    +                    pop @brackets;
    +                    };
    +
    +                # Check for opening brackets.
    +				if ($tokens[$tokenIndex] =~ /^[\(\[\{]$/ ||
    +				    ($tokens[$tokenIndex] eq "<" && $tokens[$tokenIndex-1] !~ /operator[ \t]*$/) )
    +	                {
    +                    push @brackets, $tokens[$tokenIndex];
    +                    };
    +
    +                $prototype .= $tokens[$tokenIndex];
    +                $tokenIndex++;
    +                };
    +
    +            $lineIndex++;
    +            };
    +
    +        # If we got out of that while loop by running out of lines, there was no prototype.
    +        };
    +    };
    +
    +
    +use constant ENDER_ACCEPT => 1;
    +use constant ENDER_IGNORE => 2;
    +use constant ENDER_ACCEPT_AND_CONTINUE => 3;
    +use constant ENDER_REVERT_TO_ACCEPTED => 4;
    +
    +#
    +#   Function: OnPrototypeEnd
    +#
    +#   Called whenever the end of a prototype is found so that there's a chance for derived classes to mark false positives.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the prototype.
    +#       prototypeRef - A reference to the prototype so far, minus the ender in dispute.
    +#       ender - The ender symbol.
    +#
    +#   Returns:
    +#
    +#       ENDER_ACCEPT - The ender is accepted and the prototype is finished.
    +#       ENDER_IGNORE - The ender is rejected and parsing should continue.  Note that the prototype will be rejected as a whole
    +#                                  if all enders are ignored before reaching the end of the code.
    +#       ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is.  However, the prototype might
    +#                                                          also continue on so continue parsing.  If there is no accepted ender between here and
    +#                                                          the end of the code this version will be accepted instead.
    +#       ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed.  Use the last accepted
    +#                                                        version and end parsing.
    +#
    +sub OnPrototypeEnd #(type, prototypeRef, ender)
    +    {
    +    return ENDER_ACCEPT();
    +    };
    +
    +
    +#
    +#   Function: RemoveLineExtender
    +#
    +#   If the passed line has a line extender, returns it without the extender or the line break that follows.  If it doesn't, or there are
    +#   no line extenders defined, returns the passed line unchanged.
    +#
    +sub RemoveLineExtender #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    if (defined $self->LineExtender())
    +        {
    +        my $lineExtenderIndex = rindex($line, $self->LineExtender());
    +
    +        if ($lineExtenderIndex != -1 &&
    +            substr($line, $lineExtenderIndex + length($self->LineExtender())) =~ /^[ \t]*\n$/)
    +            {
    +            $line = substr($line, 0, $lineExtenderIndex) . ' ';
    +            };
    +        };
    +
    +    return $line;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Languages/Tcl.pm b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Tcl.pm
    new file mode 100644
    index 000000000..fde2a190a
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Languages/Tcl.pm
    @@ -0,0 +1,220 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Languages::Tcl
    +#
    +###############################################################################
    +#
    +#   A subclass to handle the language variations of Tcl.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Languages::Tcl;
    +
    +use base 'NaturalDocs::Languages::Simple';
    +
    +
    +#
    +#   bool: pastFirstBrace
    +#
    +#   Whether we've past the first brace in a function prototype or not.
    +#
    +my $pastFirstBrace;
    +
    +
    +#
    +#   Function: OnCode
    +#
    +#   This is just overridden to reset <pastFirstBrace>.
    +#
    +sub OnCode #(...)
    +    {
    +    my ($self, @params) = @_;
    +
    +    $pastFirstBrace = 0;
    +
    +    return $self->SUPER::OnCode(@params);
    +    };
    +
    +
    +#
    +#   Function: OnPrototypeEnd
    +#
    +#   Tcl's function syntax is shown below.
    +#
    +#   > proc [name] { [params] } { [code] }
    +#
    +#   The opening brace is one of the prototype enders.  We need to allow the first opening brace because it contains the
    +#   parameters.
    +#
    +#   Also, the parameters may have braces within them.  I've seen one that used { seconds 20 } as a parameter.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the prototype.
    +#       prototypeRef - A reference to the prototype so far, minus the ender in dispute.
    +#       ender - The ender symbol.
    +#
    +#   Returns:
    +#
    +#       ENDER_ACCEPT - The ender is accepted and the prototype is finished.
    +#       ENDER_IGNORE - The ender is rejected and parsing should continue.  Note that the prototype will be rejected as a whole
    +#                                  if all enders are ignored before reaching the end of the code.
    +#       ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is.  However, the prototype might
    +#                                                          also continue on so continue parsing.  If there is no accepted ender between here and
    +#                                                          the end of the code this version will be accepted instead.
    +#       ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed.  Use the last accepted
    +#                                                        version and end parsing.
    +#
    +sub OnPrototypeEnd #(type, prototypeRef, ender)
    +    {
    +    my ($self, $type, $prototypeRef, $ender) = @_;
    +
    +    if ($type eq ::TOPIC_FUNCTION() && $ender eq '{' && !$pastFirstBrace)
    +        {
    +        $pastFirstBrace = 1;
    +        return ::ENDER_IGNORE();
    +        }
    +    else
    +        {  return ::ENDER_ACCEPT();  };
    +    };
    +
    +
    +#
    +#   Function: ParsePrototype
    +#
    +#   Parses the prototype and returns it as a <NaturalDocs::Languages::Prototype> object.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType>.
    +#       prototype - The text prototype.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::Languages::Prototype> object.
    +#
    +sub ParsePrototype #(type, prototype)
    +    {
    +    my ($self, $type, $prototype) = @_;
    +
    +    if ($type ne ::TOPIC_FUNCTION())
    +        {
    +        my $object = NaturalDocs::Languages::Prototype->New($prototype);
    +        return $object;
    +        };
    +
    +
    +    # Parse the parameters out of the prototype.
    +
    +    my @tokens = $prototype =~ /([^\{\}\ ]+|.)/g;
    +
    +    my $parameter;
    +    my @parameterLines;
    +
    +    my $braceLevel = 0;
    +
    +    my ($beforeParameters, $afterParameters, $finishedParameters);
    +
    +    foreach my $token (@tokens)
    +        {
    +        if ($finishedParameters)
    +            {  $afterParameters .= $token;  }
    +
    +        elsif ($token eq '{')
    +            {
    +            if ($braceLevel == 0)
    +                {  $beforeParameters .= $token;  }
    +
    +            else # braceLevel > 0
    +                {  $parameter .= $token;   };
    +
    +            $braceLevel++;
    +            }
    +
    +        elsif ($token eq '}')
    +            {
    +            if ($braceLevel == 1)
    +                {
    +                if ($parameter && $parameter ne ' ')
    +                    {  push @parameterLines, $parameter;  };
    +
    +                $finishedParameters = 1;
    +                $afterParameters .= $token;
    +
    +                $braceLevel--;
    +                }
    +            elsif ($braceLevel > 1)
    +                {
    +                $parameter .= $token;
    +                $braceLevel--;
    +                };
    +            }
    +
    +        elsif ($token eq ' ')
    +            {
    +            if ($braceLevel == 1)
    +                {
    +                if ($parameter)
    +                    {  push @parameterLines, $parameter;  };
    +
    +                $parameter = undef;
    +                }
    +            elsif ($braceLevel > 1)
    +                {
    +                $parameter .= $token;
    +                }
    +            else
    +                {
    +                $beforeParameters .= $token;
    +                };
    +            }
    +
    +        else
    +            {
    +            if ($braceLevel > 0)
    +                {  $parameter .= $token;  }
    +            else
    +                {  $beforeParameters .= $token;  };
    +            };
    +        };
    +
    +    foreach my $part (\$beforeParameters, \$afterParameters)
    +        {
    +        $$part =~ s/^ //;
    +        $$part =~ s/ $//;
    +        };
    +
    +    my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters);
    +
    +
    +    # Parse the actual parameters.
    +
    +    foreach my $parameterLine (@parameterLines)
    +        {
    +        $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) );
    +        };
    +
    +    return $prototypeObject;
    +    };
    +
    +
    +#
    +#   Function: ParseParameterLine
    +#
    +#   Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
    +#
    +sub ParseParameterLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +    return NaturalDocs::Languages::Prototype::Parameter->New(undef, undef, $line, undef, undef, undef);
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/LineReader.pm b/vendor/naturaldocs/Modules/NaturalDocs/LineReader.pm
    new file mode 100644
    index 000000000..c732afbd2
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/LineReader.pm
    @@ -0,0 +1,166 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::LineReader
    +#
    +###############################################################################
    +#
    +#   An object to handle reading text files line by line in a cross platform manner.  Using this class instead of the standard
    +#	angle brackets approach has the following benefits:
    +#
    +#	- It strips all three types of line breaks automatically: CR/LF (Windows) LF (Unix) and CR (Classic Mac).  You do not need to
    +#	  call chomp().  Perl's chomp() fails when parsing Windows-format line breaks on a Unix platform anyway.  It leaves the /r on,
    +#	  which screws everything up.
    +#	- It reads Classic Mac files line by line correctly, whereas the Perl version returns it all as one line.
    +#	- It abstracts away ignoring the Unicode BOM on the first line, if present.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::LineReader;
    +
    +#
    +#	Constants: Members
    +#
    +#	LINEREADER_FILEHANDLE - The file handle being used to read the file.  Has the LINEREADER_ prefix to make sure it doesn't
    +#											 conflict with any actual filehandles named FILEHANDLE in the program.
    +#	CACHED_LINES - An arrayref of lines already read into memory.
    +#
    +use NaturalDocs::DefineMembers 'LINEREADER_FILEHANDLE',
    +                                                 'CACHED_LINES';
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       filehandle - The file handle being used to read the file.
    +#
    +sub New #(filehandle)
    +    {
    +    my ($selfPackage, $filehandle) = @_;
    +
    +    my $object = [ ];
    +
    +    $object->[LINEREADER_FILEHANDLE] = $filehandle;
    +    $object->[CACHED_LINES] = [ ];
    +
    +    binmode($filehandle, ':raw');
    +
    +    my $possibleBOM = undef;
    +    read($filehandle, $possibleBOM, 2);
    +
    +    if ($possibleBOM eq "\xEF\xBB")
    +        {
    +        read($filehandle, $possibleBOM, 1);
    +        if ($possibleBOM eq "\xBF")
    +            {
    +            seek($filehandle, 3, 0);
    +            binmode($filehandle, ':crlf:encoding(UTF-8)');  # Strict UTF-8, not Perl's lax version.
    +            }
    +        else
    +            {
    +            seek($filehandle, 0, 0);
    +            binmode($filehandle, ':crlf');
    +            }
    +        }
    +    elsif ($possibleBOM eq "\xFE\xFF")
    +        {
    +        seek($filehandle, 2, 0);
    +        binmode($filehandle, ':crlf:encoding(UTF-16BE)');
    +        }
    +    elsif ($possibleBOM eq "\xFF\xFE")
    +        {
    +        seek($filehandle, 2, 0);
    +        binmode($filehandle, ':crlf:encoding(UTF-16LE)');
    +        }
    +    else
    +        {
    +        seek($filehandle, 0, 0);
    +        binmode($filehandle, ':crlf');
    +        }
    +
    +    bless $object, $selfPackage;
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: Chomp
    +#
    +#   Removes any line breaks from the end of a value.  It does not remove any that are in the middle of it.
    +#
    +#   Parameters:
    +#
    +#       lineRef - A *reference* to the line to chomp.
    +#
    +sub Chomp #(lineRef)
    +    {
    +    my ($self, $lineRef) = @_;
    +    $$lineRef =~ s/(?:\r\n|\r|\n)$//;
    +    };
    +
    +
    +#
    +#	Function: Get
    +#
    +#	Returns the next line of text from the file, or undef if there are no more.  The line break will be removed automatically.  If
    +#	the first line contains a Unicode BOM, that will also be removed automatically.
    +#
    +sub Get
    +	{
    +	my $self = shift;
    +	my $line = undef;
    +
    +	if (scalar @{$self->[CACHED_LINES]} == 0)
    +		{
    +		my $filehandle = $self->[LINEREADER_FILEHANDLE];
    +		my $rawLine = <$filehandle>;
    +
    +		if (!defined $rawLine)
    +			{  return undef;  }
    +
    +		$self->Chomp(\$rawLine);
    +
    +        if ($rawLine =~ /\r/)
    +        	{
    +	  		push @{$self->[CACHED_LINES]}, split(/\r/, $rawLine);  # Split for Classic Mac
    +			$line = shift @{$self->[CACHED_LINES]};
    +          	}
    +        else
    +        	{  $line = $rawLine;  }
    +		}
    +	else
    +		{  $line = shift @{$self->[CACHED_LINES]};  }
    +
    +	return $line;
    +	}
    +
    +
    +#
    +#	Function: GetAll
    +#
    +#	Returns an array of all the lines from the file.  The line breaks will be removed automatically.  If the first line contains a
    +#	Unicode BOM, that will also be removed automatically.
    +#
    +sub GetAll
    +	{
    +	my $self = shift;
    +
    +	my $filehandle = $self->[LINEREADER_FILEHANDLE];
    +	my $rawContent;
    +
    +    read($filehandle, $rawContent, -s $filehandle);
    +
    +    return split(/\r\n|\n|\r/, $rawContent);
    +	}
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Menu.pm b/vendor/naturaldocs/Modules/NaturalDocs/Menu.pm
    new file mode 100644
    index 000000000..f0502d9a0
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Menu.pm
    @@ -0,0 +1,3405 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Menu
    +#
    +###############################################################################
    +#
    +#   A package handling the menu's contents and state.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - The <Event Handlers> can be called by <NaturalDocs::Project> immediately.
    +#
    +#       - Prior to initialization, <NaturalDocs::Project> must be initialized, and all files that have been changed must be run
    +#         through <NaturalDocs::Parser->ParseForInformation()>.
    +#
    +#       - To initialize, call <LoadAndUpdate()>.  Afterwards, all other functions are available.  Also, <LoadAndUpdate()> will
    +#         call <NaturalDocs::Settings->GenerateDirectoryNames()>.
    +#
    +#       - To save the changes back to disk, call <Save()>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use Tie::RefHash;
    +
    +use NaturalDocs::Menu::Entry;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Menu;
    +
    +
    +#
    +#   Constants: Constants
    +#
    +#   MAXFILESINGROUP - The maximum number of file entries that can be present in a group before it becomes a candidate for
    +#                                  sub-grouping.
    +#   MINFILESINNEWGROUP - The minimum number of file entries that must be present in a group before it will be automatically
    +#                                        created.  This is *not* the number of files that must be in a group before it's deleted.
    +#
    +use constant MAXFILESINGROUP => 6;
    +use constant MINFILESINNEWGROUP => 3;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   bool: hasChanged
    +#
    +#   Whether the menu changed or not, regardless of why.
    +#
    +my $hasChanged;
    +
    +
    +#
    +#   Object: menu
    +#
    +#   The parsed menu file.  Is stored as a <MENU_GROUP> <NaturalDocs::Menu::Entry> object, with the top-level entries being
    +#   stored as the group's content.  This is done because it makes a number of functions simpler to implement, plus it allows group
    +#   flags to be set on the top-level.  However, it is exposed externally via <Content()> as an arrayref.
    +#
    +#   This structure will only contain objects for <MENU_FILE>, <MENU_GROUP>, <MENU_TEXT>, <MENU_LINK>, and
    +#   <MENU_INDEX> entries.  Other types, such as <MENU_TITLE>, are stored in variables such as <title>.
    +#
    +my $menu;
    +
    +#
    +#   hash: defaultTitlesChanged
    +#
    +#   An existence hash of default titles that have changed, since <OnDefaultTitleChange()> will be called before
    +#   <LoadAndUpdate()>.  Collects them to be applied later.  The keys are the <FileNames>.
    +#
    +my %defaultTitlesChanged;
    +
    +#
    +#   String: title
    +#
    +#   The title of the menu.
    +#
    +my $title;
    +
    +#
    +#   String: subTitle
    +#
    +#   The sub-title of the menu.
    +#
    +my $subTitle;
    +
    +#
    +#   String: footer
    +#
    +#   The footer for the documentation.
    +#
    +my $footer;
    +
    +#
    +#   String: timestampText
    +#
    +#   The timestamp for the documentation, stored as the final output text.
    +#
    +my $timestampText;
    +
    +#
    +#   String: timestampCode
    +#
    +#   The timestamp for the documentation, storted as the symbolic code.
    +#
    +my $timestampCode;
    +
    +#
    +#   hash: indexes
    +#
    +#   An existence hash of all the defined index <TopicTypes> appearing in the menu.
    +#
    +my %indexes;
    +
    +#
    +#   hash: previousIndexes
    +#
    +#   An existence hash of all the index <TopicTypes> that appeared in the menu last time.
    +#
    +my %previousIndexes;
    +
    +#
    +#   hash: bannedIndexes
    +#
    +#   An existence hash of all the index <TopicTypes> that the user has manually deleted, and thus should not be added back to
    +#   the menu automatically.
    +#
    +my %bannedIndexes;
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +#
    +#   File: Menu.txt
    +#
    +#   The file used to generate the menu.
    +#
    +#   Format:
    +#
    +#       The file is plain text.  Blank lines can appear anywhere and are ignored.  Tags and their content must be completely
    +#       contained on one line with the exception of Group's braces.  All values in brackets below are encoded with entity characters.
    +#
    +#       > # [comment]
    +#
    +#       The file supports single-line comments via #.  They can appear alone on a line or after content.
    +#
    +#       > Format: [version]
    +#       > Title: [title]
    +#       > SubTitle: [subtitle]
    +#       > Footer: [footer]
    +#       > Timestamp: [timestamp code]
    +#
    +#       The file format version, menu title, subtitle, footer, and timestamp are specified as above.  Each can only be specified once,
    +#       with subsequent ones being ignored.  Subtitle is ignored if Title is not present.  Format must be the first entry in the file.  If
    +#       it's not present, it's assumed the menu is from version 0.95 or earlier, since it was added with 1.0.
    +#
    +#       The timestamp code is as follows.
    +#
    +#           m - Single digit month, where applicable.  January is "1".
    +#           mm - Always double digit month.  January is "01".
    +#           mon - Short month word.  January is "Jan".
    +#           month - Long month word.  January is "January".
    +#           d - Single digit day, where applicable.  1 is "1".
    +#           dd - Always double digit day.  1 is "01".
    +#           day - Day with text extension.  1 is "1st".
    +#           yy - Double digit year.  2006 is "06".
    +#           yyyy - Four digit year.  2006 is "2006".
    +#           year - Four digit year.  2006 is "2006".
    +#
    +#       Anything else is left literal in the output.
    +#
    +#       > File: [title] ([file name])
    +#       > File: [title] (auto-title, [file name])
    +#       > File: [title] (no auto-title, [file name])
    +#
    +#       Files are specified as above.  If there is only one input directory, file names are relative.  Otherwise they are absolute.
    +#       If "no auto-title" is specified, the title on the line is used.  If not, the title is ignored and the
    +#       default file title is used instead.  Auto-title defaults to on, so specifying "auto-title" is for compatibility only.
    +#
    +#       > Group: [title]
    +#       > Group: [title] { ... }
    +#
    +#       Groups are specified as above.  If no braces are specified, the group's content is everything that follows until the end of the
    +#       file, the next group (braced or unbraced), or the closing brace of a parent group.  Group braces are the only things in this
    +#       file that can span multiple lines.
    +#
    +#       There is no limitations on where the braces can appear.  The opening brace can appear after the group tag, on its own line,
    +#       or preceding another tag on a line.  Similarly, the closing brace can appear after another tag or on its own line.  Being
    +#       bitchy here would just get in the way of quick and dirty editing; the package will clean it up automatically when it writes it
    +#       back to disk.
    +#
    +#       > Text: [text]
    +#
    +#       Arbitrary text is specified as above.  As with other tags, everything must be contained on the same line.
    +#
    +#       > Link: [URL]
    +#       > Link: [title] ([URL])
    +#
    +#       External links can be specified as above.  If the titled form is not used, the URL is used as the title.
    +#
    +#       > Index: [name]
    +#       > [topic type name] Index: [name]
    +#
    +#       Indexes are specified as above.  The topic type names can be either singular or plural.  General is assumed if not specified.
    +#
    +#       > Don't Index: [topic type name]
    +#       > Don't Index: [topic type name], [topic type name], ...
    +#
    +#       The option above prevents indexes that exist but are not on the menu from being automatically added.
    +#
    +#       > Data: [number]([obscured data])
    +#
    +#       Used to store non-user editable data.
    +#
    +#       > Data: 1([obscured: [directory name]///[input directory]])
    +#
    +#       When there is more than one directory, these lines store the input directories used in the last run and their names.  This
    +#       allows menu files to be shared across machines since the names will be consistent and the directories can be used to convert
    +#       filenames to the local machine's paths.  We don't want this user-editable because they may think changing it changes the
    +#       input directories, when it doesn't.  Also, changing it without changing all the paths screws up resolving.
    +#
    +#       > Data: 2([obscured: [directory name])
    +#
    +#       When there is only one directory and its name is not "default", this stores the name.
    +#
    +#
    +#   Entities:
    +#
    +#       &amp; - Ampersand.
    +#       &lparen; - Left parenthesis.
    +#       &rparen; - Right parenthesis.
    +#       &lbrace; - Left brace.
    +#       &rbrace; - Right brace.
    +#
    +#
    +#   Revisions:
    +#
    +#       1.4:
    +#
    +#           - Added Timestamp property.
    +#           - Values are now encoded with entity characters.
    +#
    +#       1.3:
    +#
    +#           - File names are now relative again if there is only one input directory.
    +#           - Data: 2(...) added.
    +#           - Can't use synonyms like "copyright" for "footer" or "sub-title" for "subtitle".
    +#           - "Don't Index" line now requires commas to separate them, whereas it tolerated just spaces before.
    +#
    +#       1.16:
    +#
    +#           - File names are now absolute instead of relative.  Prior to 1.16 only one input directory was allowed, so they could be
    +#             relative.
    +#           - Data keywords introduced to store input directories and their names.
    +#
    +#       1.14:
    +#
    +#           - Renamed this file from NaturalDocs_Menu.txt to Menu.txt.
    +#
    +#       1.1:
    +#
    +#           - Added the "don't index" line.
    +#
    +#           This is also the point where indexes were automatically added and removed, so all index entries from prior revisions
    +#           were manually added and are not guaranteed to contain anything.
    +#
    +#       1.0:
    +#
    +#           - Added the format line.
    +#           - Added the "no auto-title" attribute.
    +#           - Changed the file entry default to auto-title.
    +#
    +#           This is also the point where auto-organization and better auto-titles were introduced.  All groups in prior revisions were
    +#           manually added, with the exception of a top-level Other group where new files were automatically added if there were
    +#           groups defined.
    +#
    +#       Break in support:
    +#
    +#           Releases prior to 1.0 are no longer supported.  Why?
    +#
    +#           - They don't have a Format: line, which is required by <NaturalDocs::ConfigFile>, although I could work around this
    +#             if I needed to.
    +#           - No significant number of downloads for pre-1.0 releases.
    +#           - Code simplification.  I don't have to bridge the conversion from manual-only menu organization to automatic.
    +#
    +#       0.9:
    +#
    +#           - Added index entries.
    +#
    +
    +#
    +#   File: PreviousMenuState.nd
    +#
    +#   The file used to store the previous state of the menu so as to detect changes.
    +#
    +#
    +#   Format:
    +#
    +#   > [BINARY_FORMAT]
    +#   > [VersionInt: app version]
    +#
    +#   First is the standard <BINARY_FORMAT> <VersionInt> header.
    +#
    +#   > [UInt8: 0 (end group)]
    +#   > [UInt8: MENU_FILE] [UInt8: noAutoTitle] [AString16: title] [AString16: target]
    +#   > [UInt8: MENU_GROUP] [AString16: title]
    +#   > [UInt8: MENU_INDEX] [AString16: title] [AString16: topic type]
    +#   > [UInt8: MENU_LINK] [AString16: title] [AString16: url]
    +#   > [UInt8: MENU_TEXT] [AString16: text]
    +#
    +#   The first UInt8 of each following line is either zero or one of the <Menu Entry Types>.  What follows is contextual.
    +#
    +#   There are no entries for title, subtitle, or footer.  Only the entries present in <menu>.
    +#
    +#   See Also:
    +#
    +#       <File Format Conventions>
    +#
    +#   Dependencies:
    +#
    +#       - Because the type is represented by a UInt8, the <Menu Entry Types> must all be <= 255.
    +#
    +#   Revisions:
    +#
    +#       1.3:
    +#
    +#           - The topic type following the <MENU_INDEX> entries were changed from UInt8s to AString16s, since <TopicTypes>
    +#             were switched from integer constants to strings.  You can still convert the old to the new via
    +#             <NaturalDocs::Topics->TypeFromLegacy()>.
    +#
    +#       1.16:
    +#
    +#           - The file targets are now absolute.  Prior to 1.16, they were relative to the input directory since only one was allowed.
    +#
    +#       1.14:
    +#
    +#           - The file was renamed from NaturalDocs.m to PreviousMenuState.nd and moved into the Data subdirectory.
    +#
    +#       1.0:
    +#
    +#           - The file's format was completely redone.  Prior to 1.0, the file was a text file consisting of the app version and a line
    +#             which was a tab-separated list of the indexes present in the menu.  * meant the general index.
    +#
    +#       Break in support:
    +#
    +#           Pre-1.0 files are no longer supported.  There was no significant number of downloads for pre-1.0 releases, and this
    +#           eliminates a separate code path for them.
    +#
    +#       0.95:
    +#
    +#           - Change the file version to match the app version.  Prior to 0.95, the version line was 1.  Test for "1" instead of "1.0" to
    +#             distinguish.
    +#
    +#       0.9:
    +#
    +#           - The file was added to the project.  Prior to 0.9, it didn't exist.
    +#
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +#
    +#   Function: LoadAndUpdate
    +#
    +#   Loads the menu file from disk and updates it.  Will add, remove, rearrange, and remove auto-titling from entries as
    +#   necessary.  Will also call <NaturalDocs::Settings->GenerateDirectoryNames()>.
    +#
    +sub LoadAndUpdate
    +    {
    +    my ($self) = @_;
    +
    +    my ($inputDirectoryNames, $relativeFiles, $onlyDirectoryName) = $self->LoadMenuFile();
    +
    +    my $errorCount = NaturalDocs::ConfigFile->ErrorCount();
    +    if ($errorCount)
    +        {
    +        NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile();
    +        NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors')
    +                                                    . ' in ' . NaturalDocs::Project->UserConfigFile('Menu.txt'));
    +        };
    +
    +    # If the menu has a timestamp and today is a different day than the last time Natural Docs was run, we have to count it as the
    +    # menu changing.
    +    if (defined $timestampCode)
    +        {
    +        my (undef, undef, undef, $currentDay, $currentMonth, $currentYear) = localtime();
    +        my (undef, undef, undef, $lastDay, $lastMonth, $lastYear) =
    +            localtime( (stat( NaturalDocs::Project->DataFile('PreviousMenuState.nd') ))[9] );
    +            # This should be okay if the previous menu state file doesn't exist.
    +
    +        if ($currentDay != $lastDay || $currentMonth != $lastMonth || $currentYear != $lastYear)
    +            {  $hasChanged = 1;  };
    +        };
    +
    +
    +    if ($relativeFiles)
    +        {
    +        my $inputDirectory = $self->ResolveRelativeInputDirectories($onlyDirectoryName);
    +
    +        if ($onlyDirectoryName)
    +            {  $inputDirectoryNames = { $inputDirectory => $onlyDirectoryName };  };
    +        }
    +    else
    +        {  $self->ResolveInputDirectories($inputDirectoryNames);  };
    +
    +    NaturalDocs::Settings->GenerateDirectoryNames($inputDirectoryNames);
    +
    +    my $filesInMenu = $self->FilesInMenu();
    +
    +    my ($previousMenu, $previousIndexes, $previousFiles) = $self->LoadPreviousMenuStateFile();
    +
    +    if (defined $previousIndexes)
    +        {  %previousIndexes = %$previousIndexes;  };
    +
    +    if (defined $previousFiles)
    +        {  $self->LockUserTitleChanges($previousFiles);  };
    +
    +    # Don't need these anymore.  We keep this level of detail because it may be used more in the future.
    +    $previousMenu = undef;
    +    $previousFiles = undef;
    +    $previousIndexes = undef;
    +
    +    # We flag title changes instead of actually performing them at this point for two reasons.  First, contents of groups are still
    +    # subject to change, which would affect the generated titles.  Second, we haven't detected the sort order yet.  Changing titles
    +    # could make groups appear unalphabetized when they were beforehand.
    +
    +    my $updateAllTitles;
    +
    +    # If the menu file changed, we can't be sure which groups changed and which didn't without a comparison, which really isn't
    +    # worth the trouble.  So we regenerate all the titles instead.
    +    if (NaturalDocs::Project->UserConfigFileStatus('Menu.txt') == ::FILE_CHANGED())
    +        {  $updateAllTitles = 1;  }
    +    else
    +        {  $self->FlagAutoTitleChanges();  };
    +
    +    # We add new files before deleting old files so their presence still affects the grouping.  If we deleted old files first, it could
    +    # throw off where to place the new ones.
    +
    +    $self->AutoPlaceNewFiles($filesInMenu);
    +
    +    my $numberRemoved = $self->RemoveDeadFiles();
    +
    +    $self->CheckForTrashedMenu(scalar keys %$filesInMenu, $numberRemoved);
    +
    +    # Don't ban indexes if they deleted Menu.txt.  They may have not deleted PreviousMenuState.nd and we don't want everything
    +    # to be banned because of it.
    +    if (NaturalDocs::Project->UserConfigFileStatus('Menu.txt') != ::FILE_DOESNTEXIST())
    +        {  $self->BanAndUnbanIndexes();  };
    +
    +    # Index groups need to be detected before adding new ones.
    +
    +    $self->DetectIndexGroups();
    +
    +    $self->AddAndRemoveIndexes();
    +
    +   # We wait until after new files are placed to remove dead groups because a new file may save a group.
    +
    +    $self->RemoveDeadGroups();
    +
    +    $self->CreateDirectorySubGroups();
    +
    +    # We detect the sort before regenerating the titles so it doesn't get thrown off by changes.  However, we do it after deleting
    +    # dead entries and moving things into subgroups because their removal may bump it into a stronger sort category (i.e.
    +    # SORTFILESANDGROUPS instead of just SORTFILES.)  New additions don't factor into the sort.
    +
    +    $self->DetectOrder($updateAllTitles);
    +
    +    $self->GenerateAutoFileTitles($updateAllTitles);
    +
    +    $self->ResortGroups($updateAllTitles);
    +
    +
    +    # Don't need this anymore.
    +    %defaultTitlesChanged = ( );
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Writes the changes to the menu files.
    +#
    +sub Save
    +    {
    +    my ($self) = @_;
    +
    +    if ($hasChanged)
    +        {
    +        $self->SaveMenuFile();
    +        $self->SavePreviousMenuStateFile();
    +        };
    +    };
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +#
    +#   Function: HasChanged
    +#
    +#   Returns whether the menu has changed or not.
    +#
    +sub HasChanged
    +    {  return $hasChanged;  };
    +
    +#
    +#   Function: Content
    +#
    +#   Returns the parsed menu as an arrayref of <NaturalDocs::Menu::Entry> objects.  Do not change the arrayref.
    +#
    +#   The arrayref will only contain <MENU_FILE>, <MENU_GROUP>, <MENU_INDEX>, <MENU_TEXT>, and <MENU_LINK>
    +#   entries.  Entries such as <MENU_TITLE> are parsed out and are only accessible via functions such as <Title()>.
    +#
    +sub Content
    +    {  return $menu->GroupContent();  };
    +
    +#
    +#   Function: Title
    +#
    +#   Returns the title of the menu, or undef if none.
    +#
    +sub Title
    +    {  return $title;  };
    +
    +#
    +#   Function: SubTitle
    +#
    +#   Returns the sub-title of the menu, or undef if none.
    +#
    +sub SubTitle
    +    {  return $subTitle;  };
    +
    +#
    +#   Function: Footer
    +#
    +#   Returns the footer of the documentation, or undef if none.
    +#
    +sub Footer
    +    {  return $footer;  };
    +
    +#
    +#   Function: TimeStamp
    +#
    +#   Returns the timestamp text of the documentation, or undef if none.
    +#
    +sub TimeStamp
    +    {  return $timestampText;  };
    +
    +#
    +#   Function: Indexes
    +#
    +#   Returns an existence hashref of all the index <TopicTypes> appearing in the menu.  Do not change the hashref.
    +#
    +sub Indexes
    +    {  return \%indexes;  };
    +
    +#
    +#   Function: PreviousIndexes
    +#
    +#   Returns an existence hashref of all the index <TopicTypes> that previously appeared in the menu.  Do not change the
    +#   hashref.
    +#
    +sub PreviousIndexes
    +    {  return \%previousIndexes;  };
    +
    +
    +#
    +#   Function: FilesInMenu
    +#
    +#   Returns a hashref of all the files present in the menu.  The keys are the <FileNames>, and the values are references to their
    +#   <NaturalDocs::Menu::Entry> objects.
    +#
    +sub FilesInMenu
    +    {
    +    my ($self) = @_;
    +
    +    my @groupStack = ( $menu );
    +    my $filesInMenu = { };
    +
    +    while (scalar @groupStack)
    +        {
    +        my $currentGroup = pop @groupStack;
    +        my $currentGroupContent = $currentGroup->GroupContent();
    +
    +        foreach my $entry (@$currentGroupContent)
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @groupStack, $entry;  }
    +            elsif ($entry->Type() == ::MENU_FILE())
    +                {  $filesInMenu->{ $entry->Target() } = $entry;  };
    +            };
    +        };
    +
    +    return $filesInMenu;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Event Handlers
    +#
    +#   These functions are called by <NaturalDocs::Project> only.  You don't need to worry about calling them.  For example, when
    +#   changing the default menu title of a file, you only need to call <NaturalDocs::Project->SetDefaultMenuTitle()>.  That function
    +#   will handle calling <OnDefaultTitleChange()>.
    +
    +
    +#
    +#   Function: OnDefaultTitleChange
    +#
    +#   Called by <NaturalDocs::Project> if the default menu title of a source file has changed.
    +#
    +#   Parameters:
    +#
    +#       file    - The source <FileName> that had its default menu title changed.
    +#
    +sub OnDefaultTitleChange #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    # Collect them for later.  We'll deal with them in LoadAndUpdate().
    +
    +    $defaultTitlesChanged{$file} = 1;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: LoadMenuFile
    +#
    +#   Loads and parses the menu file <Menu.txt>.  This will fill <menu>, <title>, <subTitle>, <footer>, <timestampText>,
    +#   <timestampCode>, <indexes>, and <bannedIndexes>.  If there are any errors in the file, they will be recorded with
    +#   <NaturalDocs::ConfigFile->AddError()>.
    +#
    +#   Returns:
    +#
    +#       The array ( inputDirectories, relativeFiles, onlyDirectoryName ) or an empty array if the file doesn't exist.
    +#
    +#       inputDirectories - A hashref of all the input directories and their names stored in the menu file.  The keys are the
    +#                                 directories and the values are their names.  Undef if none.
    +#       relativeFiles - Whether the menu uses relative file names.
    +#       onlyDirectoryName - The name of the input directory if there is only one.
    +#
    +sub LoadMenuFile
    +    {
    +    my ($self) = @_;
    +
    +    my $inputDirectories = { };
    +    my $relativeFiles;
    +    my $onlyDirectoryName;
    +
    +    # A stack of Menu::Entry object references as we move through the groups.
    +    my @groupStack;
    +
    +    $menu = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), undef, undef, undef);
    +    my $currentGroup = $menu;
    +
    +    # Whether we're currently in a braceless group, since we'd have to find the implied end rather than an explicit one.
    +    my $inBracelessGroup;
    +
    +    # Whether we're right after a group token, which is the only place there can be an opening brace.
    +    my $afterGroupToken;
    +
    +    my $version;
    +
    +    if ($version = NaturalDocs::ConfigFile->Open(NaturalDocs::Project->UserConfigFile('Menu.txt'), 1))
    +        {
    +        # We don't check if the menu file is from a future version because we can't just throw it out and regenerate it like we can
    +        # with other data files.  So we just keep going regardless.  Any syntactic differences will show up as errors.
    +
    +        while (my ($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine())
    +            {
    +            # Check for an opening brace after a group token.  This has to be separate from the rest of the code because the flag
    +            # needs to be reset after every line.
    +            if ($afterGroupToken)
    +                {
    +                $afterGroupToken = undef;
    +
    +                if ($keyword eq '{')
    +                    {
    +                    $inBracelessGroup = undef;
    +                    next;
    +                    }
    +                else
    +                    {  $inBracelessGroup = 1;  };
    +                };
    +
    +
    +            # Now on to the real code.
    +
    +            if ($keyword eq 'file')
    +                {
    +                my $flags = 0;
    +
    +                if ($value =~ /^(.+)\(([^\(]+)\)$/)
    +                    {
    +                    my ($title, $file) = ($1, $2);
    +
    +                    $title =~ s/ +$//;
    +
    +                    # Check for auto-title modifier.
    +                    if ($file =~ /^((?:no )?auto-title, ?)(.+)$/i)
    +                        {
    +                        my $modifier;
    +                        ($modifier, $file) = ($1, $2);
    +
    +                        if ($modifier =~ /^no/i)
    +                            {  $flags |= ::MENU_FILE_NOAUTOTITLE();  };
    +                        };
    +
    +                    my $entry = NaturalDocs::Menu::Entry->New(::MENU_FILE(), $self->RestoreAmpChars($title),
    +                                                                                       $self->RestoreAmpChars($file), $flags);
    +
    +                    $currentGroup->PushToGroup($entry);
    +                    }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('File lines must be in the format "File: [title] ([location])"');  };
    +                }
    +
    +            elsif ($keyword eq 'group')
    +                {
    +                # End a braceless group, if we were in one.
    +                if ($inBracelessGroup)
    +                    {
    +                    $currentGroup = pop @groupStack;
    +                    $inBracelessGroup = undef;
    +                    };
    +
    +                my $entry = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), $self->RestoreAmpChars($value), undef, undef);
    +
    +                $currentGroup->PushToGroup($entry);
    +
    +                push @groupStack, $currentGroup;
    +                $currentGroup = $entry;
    +
    +                $afterGroupToken = 1;
    +                }
    +
    +
    +            elsif ($keyword eq '{')
    +                {
    +                NaturalDocs::ConfigFile->AddError('Opening braces are only allowed after Group tags.');
    +                }
    +
    +
    +            elsif ($keyword eq '}')
    +                {
    +                # End a braceless group, if we were in one.
    +                if ($inBracelessGroup)
    +                    {
    +                    $currentGroup = pop @groupStack;
    +                    $inBracelessGroup = undef;
    +                    };
    +
    +                # End a braced group too.
    +                if (scalar @groupStack)
    +                    {  $currentGroup = pop @groupStack;  }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Unmatched closing brace.');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'title')
    +                {
    +                if (!defined $title)
    +                    {  $title = $self->RestoreAmpChars($value);  }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Title can only be defined once.');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'subtitle')
    +                {
    +                if (defined $title)
    +                    {
    +                    if (!defined $subTitle)
    +                        {  $subTitle = $self->RestoreAmpChars($value);  }
    +                    else
    +                        {  NaturalDocs::ConfigFile->AddError('SubTitle can only be defined once.');  };
    +                    }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Title must be defined before SubTitle.');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'footer')
    +                {
    +                if (!defined $footer)
    +                    {  $footer = $self->RestoreAmpChars($value);  }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Footer can only be defined once.');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'timestamp')
    +                {
    +                if (!defined $timestampCode)
    +                    {
    +                    $timestampCode = $self->RestoreAmpChars($value);
    +                    $self->GenerateTimestampText();
    +                    }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Timestamp can only be defined once.');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'text')
    +                {
    +                $currentGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_TEXT(), $self->RestoreAmpChars($value),
    +                                                                                                              undef, undef) );
    +                }
    +
    +
    +            elsif ($keyword eq 'link')
    +                {
    +                my ($title, $url);
    +
    +                if ($value =~ /^([^\(\)]+?) ?\(([^\)]+)\)$/)
    +                    {
    +                    ($title, $url) = ($1, $2);
    +                    }
    +                elsif (defined $comment)
    +                    {
    +                    $value .= $comment;
    +
    +                    if ($value =~ /^([^\(\)]+?) ?\(([^\)]+)\) ?(?:#.*)?$/)
    +                        {
    +                        ($title, $url) = ($1, $2);
    +                        };
    +                    };
    +
    +                if ($title)
    +                    {
    +                    $currentGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_LINK(), $self->RestoreAmpChars($title),
    +                                                                 $self->RestoreAmpChars($url), undef) );
    +                    }
    +                else
    +                    {  NaturalDocs::ConfigFile->AddError('Link lines must be in the format "Link: [title] ([url])"');  };
    +                }
    +
    +
    +            elsif ($keyword eq 'data')
    +                {
    +                $value =~ /^(\d)\((.*)\)$/;
    +                my ($number, $data) = ($1, $2);
    +
    +                $data = NaturalDocs::ConfigFile->Unobscure($data);
    +
    +                # The input directory naming convention changed with version 1.32, but NaturalDocs::Settings will handle that
    +                # automatically.
    +
    +                if ($number == 1)
    +                    {
    +                    my ($dirName, $inputDir) = split(/\/\/\//, $data, 2);
    +                    $inputDirectories->{$inputDir} = $dirName;
    +                    }
    +                elsif ($number == 2)
    +                    {  $onlyDirectoryName = $data;  };
    +                # Ignore other numbers because it may be from a future format and we don't want to make the user delete it
    +                # manually.
    +                }
    +
    +            elsif ($keyword eq "don't index")
    +                {
    +                my @indexes = split(/, ?/, $value);
    +
    +                foreach my $index (@indexes)
    +                    {
    +                    my $indexType = NaturalDocs::Topics->TypeFromName( $self->RestoreAmpChars($index) );
    +
    +                    if (defined $indexType)
    +                        {  $bannedIndexes{$indexType} = 1;  };
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'index')
    +                {
    +                my $entry = NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $self->RestoreAmpChars($value),
    +                                                                                   ::TOPIC_GENERAL(), undef);
    +                $currentGroup->PushToGroup($entry);
    +
    +                $indexes{::TOPIC_GENERAL()} = 1;
    +                }
    +
    +            elsif (substr($keyword, -6) eq ' index')
    +                {
    +                my $index = substr($keyword, 0, -6);
    +                my ($indexType, $indexInfo) = NaturalDocs::Topics->NameInfo( $self->RestoreAmpChars($index) );
    +
    +                if (defined $indexType)
    +                    {
    +                    if ($indexInfo->Index())
    +                        {
    +                        $indexes{$indexType} = 1;
    +                        $currentGroup->PushToGroup(
    +                            NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $self->RestoreAmpChars($value), $indexType, undef) );
    +                        }
    +                    else
    +                        {
    +                        # If it's on the menu but isn't indexable, the topic setting may have changed out from under it.
    +                        $hasChanged = 1;
    +                        };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError($index . ' is not a valid index type.');
    +                    };
    +                }
    +
    +            else
    +                {
    +                NaturalDocs::ConfigFile->AddError(ucfirst($keyword) . ' is not a valid keyword.');
    +                };
    +            };
    +
    +
    +        # End a braceless group, if we were in one.
    +        if ($inBracelessGroup)
    +            {
    +            $currentGroup = pop @groupStack;
    +            $inBracelessGroup = undef;
    +            };
    +
    +        # Close up all open groups.
    +        my $openGroups = 0;
    +        while (scalar @groupStack)
    +            {
    +            $currentGroup = pop @groupStack;
    +            $openGroups++;
    +            };
    +
    +        if ($openGroups == 1)
    +            {  NaturalDocs::ConfigFile->AddError('There is an unclosed group.');  }
    +        elsif ($openGroups > 1)
    +            {  NaturalDocs::ConfigFile->AddError('There are ' . $openGroups . ' unclosed groups.');  };
    +
    +
    +        if (!scalar keys %$inputDirectories)
    +            {
    +            $inputDirectories = undef;
    +            $relativeFiles = 1;
    +            };
    +
    +        NaturalDocs::ConfigFile->Close();
    +
    +        return ($inputDirectories, $relativeFiles, $onlyDirectoryName);
    +        }
    +
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +#
    +#   Function: SaveMenuFile
    +#
    +#   Saves the current menu to <Menu.txt>.
    +#
    +sub SaveMenuFile
    +    {
    +    my ($self) = @_;
    +
    +    open(MENUFILEHANDLE, '>' . NaturalDocs::Project->UserConfigFile('Menu.txt'))
    +        or die "Couldn't save menu file " . NaturalDocs::Project->UserConfigFile('Menu.txt') . "\n";
    +
    +
    +    print MENUFILEHANDLE
    +    "Format: " . NaturalDocs::Settings->TextAppVersion() . "\n\n\n";
    +
    +    my $inputDirs = NaturalDocs::Settings->InputDirectories();
    +
    +
    +    if (defined $title)
    +        {
    +        print MENUFILEHANDLE 'Title: ' . $self->ConvertAmpChars($title) . "\n";
    +
    +        if (defined $subTitle)
    +            {
    +            print MENUFILEHANDLE 'SubTitle: ' . $self->ConvertAmpChars($subTitle) . "\n";
    +            }
    +        else
    +            {
    +            print MENUFILEHANDLE
    +            "\n"
    +            . "# You can also add a sub-title to your menu like this:\n"
    +            . "# SubTitle: [subtitle]\n";
    +            };
    +        }
    +    else
    +        {
    +        print MENUFILEHANDLE
    +        "# You can add a title and sub-title to your menu like this:\n"
    +        . "# Title: [project name]\n"
    +        . "# SubTitle: [subtitle]\n";
    +        };
    +
    +    print MENUFILEHANDLE "\n";
    +
    +    if (defined $footer)
    +        {
    +        print MENUFILEHANDLE 'Footer: ' . $self->ConvertAmpChars($footer) . "\n";
    +        }
    +    else
    +        {
    +        print MENUFILEHANDLE
    +        "# You can add a footer to your documentation like this:\n"
    +        . "# Footer: [text]\n"
    +        . "# If you want to add a copyright notice, this would be the place to do it.\n";
    +        };
    +
    +    if (defined $timestampCode)
    +        {
    +        print MENUFILEHANDLE 'Timestamp: ' . $self->ConvertAmpChars($timestampCode) . "\n";
    +        }
    +    else
    +        {
    +        print MENUFILEHANDLE
    +        "\n"
    +        . "# You can add a timestamp to your documentation like one of these:\n"
    +        . "# Timestamp: Generated on month day, year\n"
    +        . "# Timestamp: Updated mm/dd/yyyy\n"
    +        . "# Timestamp: Last updated mon day\n"
    +        . "#\n";
    +        };
    +
    +    print MENUFILEHANDLE
    +        qq{#   m     - One or two digit month.  January is "1"\n}
    +        . qq{#   mm    - Always two digit month.  January is "01"\n}
    +        . qq{#   mon   - Short month word.  January is "Jan"\n}
    +        . qq{#   month - Long month word.  January is "January"\n}
    +        . qq{#   d     - One or two digit day.  1 is "1"\n}
    +        . qq{#   dd    - Always two digit day.  1 is "01"\n}
    +        . qq{#   day   - Day with letter extension.  1 is "1st"\n}
    +        . qq{#   yy    - Two digit year.  2006 is "06"\n}
    +        . qq{#   yyyy  - Four digit year.  2006 is "2006"\n}
    +        . qq{#   year  - Four digit year.  2006 is "2006"\n}
    +
    +        . "\n";
    +
    +    if (scalar keys %bannedIndexes)
    +        {
    +        print MENUFILEHANDLE
    +
    +        "# These are indexes you deleted, so Natural Docs will not add them again\n"
    +        . "# unless you remove them from this line.\n"
    +        . "\n"
    +        . "Don't Index: ";
    +
    +        my $first = 1;
    +
    +        foreach my $index (keys %bannedIndexes)
    +            {
    +            if (!$first)
    +                {  print MENUFILEHANDLE ', ';  }
    +            else
    +                {  $first = undef;  };
    +
    +            print MENUFILEHANDLE $self->ConvertAmpChars( NaturalDocs::Topics->NameOfType($index, 1), CONVERT_COMMAS() );
    +            };
    +
    +        print MENUFILEHANDLE "\n\n";
    +        };
    +
    +
    +    # Remember to keep lines below eighty characters.
    +
    +    print MENUFILEHANDLE
    +    "\n"
    +    . "# --------------------------------------------------------------------------\n"
    +    . "# \n"
    +    . "# Cut and paste the lines below to change the order in which your files\n"
    +    . "# appear on the menu.  Don't worry about adding or removing files, Natural\n"
    +    . "# Docs will take care of that.\n"
    +    . "# \n"
    +    . "# You can further organize the menu by grouping the entries.  Add a\n"
    +    . "# \"Group: [name] {\" line to start a group, and add a \"}\" to end it.\n"
    +    . "# \n"
    +    . "# You can add text and web links to the menu by adding \"Text: [text]\" and\n"
    +    . "# \"Link: [name] ([URL])\" lines, respectively.\n"
    +    . "# \n"
    +    . "# The formatting and comments are auto-generated, so don't worry about\n"
    +    . "# neatness when editing the file.  Natural Docs will clean it up the next\n"
    +    . "# time it is run.  When working with groups, just deal with the braces and\n"
    +    . "# forget about the indentation and comments.\n"
    +    . "# \n";
    +
    +    if (scalar @$inputDirs > 1)
    +        {
    +        print MENUFILEHANDLE
    +        "# You can use this file on other computers even if they use different\n"
    +        . "# directories.  As long as the command line points to the same source files,\n"
    +        . "# Natural Docs will be able to correct the locations automatically.\n"
    +        . "# \n";
    +        };
    +
    +    print MENUFILEHANDLE
    +    "# --------------------------------------------------------------------------\n"
    +
    +    . "\n\n";
    +
    +
    +    $self->WriteMenuEntries($menu->GroupContent(), \*MENUFILEHANDLE, undef, (scalar @$inputDirs == 1));
    +
    +
    +    if (scalar @$inputDirs > 1)
    +        {
    +        print MENUFILEHANDLE
    +        "\n\n##### Do not change or remove these lines. #####\n";
    +
    +        foreach my $inputDir (@$inputDirs)
    +            {
    +            print MENUFILEHANDLE
    +            'Data: 1(' . NaturalDocs::ConfigFile->Obscure( NaturalDocs::Settings->InputDirectoryNameOf($inputDir)
    +                                                                              . '///' . $inputDir ) . ")\n";
    +            };
    +        }
    +    elsif (lc(NaturalDocs::Settings->InputDirectoryNameOf($inputDirs->[0])) != 1)
    +        {
    +        print MENUFILEHANDLE
    +        "\n\n##### Do not change or remove this line. #####\n"
    +        . 'Data: 2(' . NaturalDocs::ConfigFile->Obscure( NaturalDocs::Settings->InputDirectoryNameOf($inputDirs->[0]) ) . ")\n";
    +        }
    +
    +    close(MENUFILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: WriteMenuEntries
    +#
    +#   A recursive function to write the contents of an arrayref of <NaturalDocs::Menu::Entry> objects to disk.
    +#
    +#   Parameters:
    +#
    +#       entries          - The arrayref of menu entries to write.
    +#       fileHandle      - The handle to the output file.
    +#       indentChars   - The indentation _characters_ to add before each line.  It is not the number of characters, it is the characters
    +#                              themselves.  Use undef for none.
    +#       relativeFiles - Whether to use relative file names.
    +#
    +sub WriteMenuEntries #(entries, fileHandle, indentChars, relativeFiles)
    +    {
    +    my ($self, $entries, $fileHandle, $indentChars, $relativeFiles) = @_;
    +    my $lastEntryType;
    +
    +    foreach my $entry (@$entries)
    +        {
    +        if ($entry->Type() == ::MENU_FILE())
    +            {
    +            my $fileName;
    +
    +            if ($relativeFiles)
    +                {  $fileName = (NaturalDocs::Settings->SplitFromInputDirectory($entry->Target()))[1];  }
    +            else
    +                {  $fileName = $entry->Target();  };
    +
    +            print $fileHandle $indentChars . 'File: ' . $self->ConvertAmpChars( $entry->Title(), CONVERT_PARENTHESIS() )
    +                                  . '  (' . ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE() ? 'no auto-title, ' : '')
    +                                  . $self->ConvertAmpChars($fileName) . ")\n";
    +            }
    +        elsif ($entry->Type() == ::MENU_GROUP())
    +            {
    +            if (defined $lastEntryType && $lastEntryType != ::MENU_GROUP())
    +                {  print $fileHandle "\n";  };
    +
    +            print $fileHandle $indentChars . 'Group: ' . $self->ConvertAmpChars( $entry->Title() ) . "  {\n\n";
    +            $self->WriteMenuEntries($entry->GroupContent(), $fileHandle, '   ' . $indentChars, $relativeFiles);
    +            print $fileHandle '   ' . $indentChars . '}  # Group: ' . $self->ConvertAmpChars( $entry->Title() ) . "\n\n";
    +            }
    +        elsif ($entry->Type() == ::MENU_TEXT())
    +            {
    +            print $fileHandle $indentChars . 'Text: ' . $self->ConvertAmpChars( $entry->Title() ) . "\n";
    +            }
    +        elsif ($entry->Type() == ::MENU_LINK())
    +            {
    +            print $fileHandle $indentChars . 'Link: ' . $self->ConvertAmpChars( $entry->Title() ) . '  '
    +                                                        . '(' . $self->ConvertAmpChars( $entry->Target(), CONVERT_PARENTHESIS() ) . ')' . "\n";
    +            }
    +        elsif ($entry->Type() == ::MENU_INDEX())
    +            {
    +            my $type;
    +            if ($entry->Target() ne ::TOPIC_GENERAL())
    +                {
    +                $type = NaturalDocs::Topics->NameOfType($entry->Target()) . ' ';
    +                };
    +
    +            print $fileHandle $indentChars . $self->ConvertAmpChars($type, CONVERT_COLONS()) . 'Index: '
    +                                                        . $self->ConvertAmpChars( $entry->Title() ) . "\n";
    +            };
    +
    +        $lastEntryType = $entry->Type();
    +        };
    +    };
    +
    +
    +#
    +#   Function: LoadPreviousMenuStateFile
    +#
    +#   Loads and parses the previous menu state file.
    +#
    +#   Returns:
    +#
    +#       The array ( previousMenu, previousIndexes, previousFiles ) or an empty array if there was a problem with the file.
    +#
    +#       previousMenu - A <MENU_GROUP> <NaturalDocs::Menu::Entry> object, similar to <menu>, which contains the entire
    +#                              previous menu.
    +#       previousIndexes - An existence hashref of the index <TopicTypes> present in the previous menu.
    +#       previousFiles - A hashref of the files present in the previous menu.  The keys are the <FileNames>, and the entries are
    +#                             references to its object in previousMenu.
    +#
    +sub LoadPreviousMenuStateFile
    +    {
    +    my ($self) = @_;
    +
    +    my $fileIsOkay;
    +    my $version;
    +    my $previousStateFileName = NaturalDocs::Project->DataFile('PreviousMenuState.nd');
    +
    +    if (open(PREVIOUSSTATEFILEHANDLE, '<' . $previousStateFileName))
    +        {
    +        # See if it's binary.
    +        binmode(PREVIOUSSTATEFILEHANDLE);
    +
    +        my $firstChar;
    +        read(PREVIOUSSTATEFILEHANDLE, $firstChar, 1);
    +
    +        if ($firstChar == ::BINARY_FORMAT())
    +            {
    +            $version = NaturalDocs::Version->FromBinaryFile(\*PREVIOUSSTATEFILEHANDLE);
    +
    +            # Only the topic type format has changed since switching to binary, and we support both methods.
    +
    +            if (NaturalDocs::Version->CheckFileFormat($version))
    +                {  $fileIsOkay = 1;  }
    +            else
    +                {  close(PREVIOUSSTATEFILEHANDLE);  };
    +            }
    +
    +        else # it's not in binary
    +            {  close(PREVIOUSSTATEFILEHANDLE);  };
    +        };
    +
    +    if ($fileIsOkay)
    +        {
    +        if (NaturalDocs::Project->UserConfigFileStatus('Menu.txt') == ::FILE_CHANGED())
    +            {  $hasChanged = 1;  };
    +
    +
    +        my $menu = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), undef, undef, undef);
    +        my $indexes = { };
    +        my $files = { };
    +
    +        my @groupStack;
    +        my $currentGroup = $menu;
    +        my $raw;
    +
    +        # [UInt8: type or 0 for end group]
    +
    +        while (read(PREVIOUSSTATEFILEHANDLE, $raw, 1))
    +            {
    +            my ($type, $flags, $title, $titleLength, $target, $targetLength);
    +            $type = unpack('C', $raw);
    +
    +            if ($type == 0)
    +                {  $currentGroup = pop @groupStack;  }
    +
    +            elsif ($type == ::MENU_FILE())
    +                {
    +                # [UInt8: noAutoTitle] [AString16: title] [AString16: target]
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 3);
    +                (my $noAutoTitle, $titleLength) = unpack('Cn', $raw);
    +
    +                if ($noAutoTitle)
    +                    {  $flags = ::MENU_FILE_NOAUTOTITLE();  };
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength);
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +
    +                $targetLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength);
    +                }
    +
    +            elsif ($type == ::MENU_GROUP())
    +                {
    +                # [AString16: title]
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                $titleLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength);
    +                }
    +
    +            elsif ($type == ::MENU_INDEX())
    +                {
    +                # [AString16: title]
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                $titleLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength);
    +
    +                if ($version >= NaturalDocs::Version->FromString('1.3'))
    +                    {
    +                    # [AString16: topic type]
    +                    read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                    $targetLength = unpack('n', $raw);
    +
    +                    read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength);
    +                    }
    +                else
    +                    {
    +                    # [UInt8: topic type (0 for general)]
    +                    read(PREVIOUSSTATEFILEHANDLE, $raw, 1);
    +                    $target = unpack('C', $raw);
    +
    +                    $target = NaturalDocs::Topics->TypeFromLegacy($target);
    +                    };
    +                }
    +
    +            elsif ($type == ::MENU_LINK())
    +                {
    +                # [AString16: title] [AString16: url]
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                $titleLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength);
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                $targetLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength);
    +                }
    +
    +            elsif ($type == ::MENU_TEXT())
    +                {
    +                # [AString16: text]
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $raw, 2);
    +                $titleLength = unpack('n', $raw);
    +
    +                read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength);
    +                };
    +
    +
    +            # The topic type of the index may have been removed.
    +
    +            if ( !($type == ::MENU_INDEX() && !NaturalDocs::Topics->IsValidType($target)) )
    +                {
    +                my $entry = NaturalDocs::Menu::Entry->New($type, $title, $target, ($flags || 0));
    +                $currentGroup->PushToGroup($entry);
    +
    +                if ($type == ::MENU_FILE())
    +                    {
    +                    $files->{$target} = $entry;
    +                    }
    +                elsif ($type == ::MENU_GROUP())
    +                    {
    +                    push @groupStack, $currentGroup;
    +                    $currentGroup = $entry;
    +                    }
    +                elsif ($type == ::MENU_INDEX())
    +                    {
    +                    $indexes->{$target} = 1;
    +                    };
    +                };
    +
    +            };
    +
    +        close(PREVIOUSSTATEFILEHANDLE);
    +
    +        return ($menu, $indexes, $files);
    +        }
    +    else
    +        {
    +        $hasChanged = 1;
    +        return ( );
    +        };
    +    };
    +
    +
    +#
    +#   Function: SavePreviousMenuStateFile
    +#
    +#   Saves changes to <PreviousMenuState.nd>.
    +#
    +sub SavePreviousMenuStateFile
    +    {
    +    my ($self) = @_;
    +
    +    open (PREVIOUSSTATEFILEHANDLE, '>' . NaturalDocs::Project->DataFile('PreviousMenuState.nd'))
    +        or die "Couldn't save " . NaturalDocs::Project->DataFile('PreviousMenuState.nd') . ".\n";
    +
    +    binmode(PREVIOUSSTATEFILEHANDLE);
    +
    +    print PREVIOUSSTATEFILEHANDLE '' . ::BINARY_FORMAT();
    +
    +    NaturalDocs::Version->ToBinaryFile(\*PREVIOUSSTATEFILEHANDLE, NaturalDocs::Settings->AppVersion());
    +
    +    $self->WritePreviousMenuStateEntries($menu->GroupContent(), \*PREVIOUSSTATEFILEHANDLE);
    +
    +    close(PREVIOUSSTATEFILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: WritePreviousMenuStateEntries
    +#
    +#   A recursive function to write the contents of an arrayref of <NaturalDocs::Menu::Entry> objects to disk.
    +#
    +#   Parameters:
    +#
    +#       entries          - The arrayref of menu entries to write.
    +#       fileHandle      - The handle to the output file.
    +#
    +sub WritePreviousMenuStateEntries #(entries, fileHandle)
    +    {
    +    my ($self, $entries, $fileHandle) = @_;
    +
    +    foreach my $entry (@$entries)
    +        {
    +        if ($entry->Type() == ::MENU_FILE())
    +            {
    +            # We need to do length manually instead of using n/A in the template because it's not supported in earlier versions
    +            # of Perl.
    +
    +            # [UInt8: MENU_FILE] [UInt8: noAutoTitle] [AString16: title] [AString16: target]
    +            print $fileHandle pack('CCnA*nA*', ::MENU_FILE(), ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE() ? 1 : 0),
    +                                                                length($entry->Title()), $entry->Title(),
    +                                                                length($entry->Target()), $entry->Target());
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_GROUP())
    +            {
    +            # [UInt8: MENU_GROUP] [AString16: title]
    +            print $fileHandle pack('CnA*', ::MENU_GROUP(), length($entry->Title()), $entry->Title());
    +            $self->WritePreviousMenuStateEntries($entry->GroupContent(), $fileHandle);
    +            print $fileHandle pack('C', 0);
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_INDEX())
    +            {
    +            # [UInt8: MENU_INDEX] [AString16: title] [AString16: topic type]
    +            print $fileHandle pack('CnA*nA*', ::MENU_INDEX(), length($entry->Title()), $entry->Title(),
    +                                                                                       length($entry->Target()), $entry->Target());
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_LINK())
    +            {
    +            # [UInt8: MENU_LINK] [AString16: title] [AString16: url]
    +            print $fileHandle pack('CnA*nA*', ::MENU_LINK(), length($entry->Title()), $entry->Title(),
    +                                                             length($entry->Target()), $entry->Target());
    +            }
    +
    +        elsif ($entry->Type() == ::MENU_TEXT())
    +            {
    +            # [UInt8: MENU_TEXT] [AString16: hext]
    +            print $fileHandle pack('CnA*', ::MENU_TEXT(), length($entry->Title()), $entry->Title());
    +            };
    +        };
    +
    +    };
    +
    +
    +#
    +#   Function: CheckForTrashedMenu
    +#
    +#   Checks the menu to see if a significant number of file entries didn't resolve to actual files, and if so, saves a backup of the
    +#   menu and issues a warning.
    +#
    +#   Parameters:
    +#
    +#       numberOriginallyInMenu - A count of how many file entries were in the menu orignally.
    +#       numberRemoved - A count of how many file entries were removed from the menu.
    +#
    +sub CheckForTrashedMenu #(numberOriginallyInMenu, numberRemoved)
    +    {
    +    my ($self, $numberOriginallyInMenu, $numberRemoved) = @_;
    +
    +    no integer;
    +
    +    if ( ($numberOriginallyInMenu >= 6 && $numberRemoved == $numberOriginallyInMenu) ||
    +         ($numberOriginallyInMenu >= 12 && ($numberRemoved / $numberOriginallyInMenu) >= 0.4) ||
    +         ($numberRemoved >= 15) )
    +        {
    +        my $backupFile = NaturalDocs::Project->UserConfigFile('Menu_Backup.txt');
    +        my $backupFileNumber = 1;
    +
    +        while (-e $backupFile)
    +            {
    +            $backupFileNumber++;
    +            $backupFile = NaturalDocs::Project->UserConfigFile('Menu_Backup_' . $backupFileNumber . '.txt');
    +            };
    +
    +        NaturalDocs::File->Copy( NaturalDocs::Project->UserConfigFile('Menu.txt'), $backupFile );
    +
    +        print STDERR
    +        "\n"
    +        # GNU format.  See http://www.gnu.org/prep/standards_15.html
    +        . "NaturalDocs: warning: possible trashed menu\n"
    +        . "\n"
    +        . "   Natural Docs has detected that a significant number file entries in the\n"
    +        . "   menu did not resolve to actual files.  A backup of your original menu file\n"
    +        . "   has been saved as\n"
    +        . "\n"
    +        . "   " . $backupFile . "\n"
    +        . "\n"
    +        . "   - If you recently deleted a lot of files from your project, you can safely\n"
    +        . "     ignore this message.  They have been deleted from the menu as well.\n"
    +        . "   - If you recently rearranged your source tree, you may want to restore your\n"
    +        . "     menu from the backup and do a search and replace to preserve your layout.\n"
    +        . "     Otherwise the position of any moved files will be reset.\n"
    +        . "   - If neither of these is the case, you may have gotten the -i parameter\n"
    +        . "     wrong in the command line.  You should definitely restore the backup and\n"
    +        . "     try again, because otherwise every file in your menu will be reset.\n"
    +        . "\n";
    +        };
    +
    +    use integer;
    +    };
    +
    +
    +#
    +#   Function: GenerateTimestampText
    +#
    +#   Generates <timestampText> from <timestampCode> with the current date.
    +#
    +sub GenerateTimestampText
    +    {
    +    my $self = shift;
    +
    +    my @longMonths = ( 'January', 'February', 'March', 'April', 'May', 'June',
    +                                   'July', 'August', 'September', 'October', 'November', 'December' );
    +    my @shortMonths = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec' );
    +
    +    my (undef, undef, undef, $day, $month, $year) = localtime();
    +    $year += 1900;
    +
    +    my $longDay;
    +    if ($day % 10 == 1 && $day != 11)
    +        {  $longDay = $day . 'st';  }
    +    elsif ($day % 10 == 2 && $day != 12)
    +        {  $longDay = $day . 'nd';  }
    +    elsif ($day % 10 == 3 && $day != 13)
    +        {  $longDay = $day . 'rd';  }
    +    else
    +        {  $longDay = $day . 'th';  };
    +
    +
    +    $timestampText = $timestampCode;
    +
    +    $timestampText =~ s/(?<![a-z])month(?![a-z])/$longMonths[$month]/i;
    +    $timestampText =~ s/(?<![a-z])mon(?![a-z])/$shortMonths[$month]/i;
    +    $timestampText =~ s/(?<![a-z])mm(?![a-z])/sprintf('%02d', $month + 1)/ie;
    +    $timestampText =~ s/(?<![a-z])m(?![a-z])/$month + 1/ie;
    +
    +    $timestampText =~ s/(?<![a-z])day(?![a-z])/$longDay/i;
    +    $timestampText =~ s/(?<![a-z])dd(?![a-z])/sprintf('%02d', $day)/ie;
    +    $timestampText =~ s/(?<![a-z])d(?![a-z])/$day/i;
    +
    +    $timestampText =~ s/(?<![a-z])(?:year|yyyy)(?![a-z])/$year/i;
    +    $timestampText =~ s/(?<![a-z])yy(?![a-z])/sprintf('%02d', $year % 100)/ie;
    +    };
    +
    +
    +use constant CONVERT_PARENTHESIS => 0x01;
    +use constant CONVERT_COMMAS => 0x02;
    +use constant CONVERT_COLONS => 0x04;
    +
    +#
    +#   Function: ConvertAmpChars
    +#   Replaces certain characters in the string with their entities and returns it.
    +#
    +#   Parameters:
    +#
    +#       text - The text to convert.
    +#       flags - The flags of any additional characters to convert.
    +#
    +#   Flags:
    +#
    +#       - CONVERT_PARENTHESIS
    +#       - CONVERT_COMMAS
    +#       - CONVERT_COLONS
    +#
    +#   Returns:
    +#
    +#       The string with the amp chars converted.
    +#
    +sub ConvertAmpChars #(string text, int flags) => string
    +    {
    +    my ($self, $text, $flags) = @_;
    +
    +    $text =~ s/&/&amp;/g;
    +    $text =~ s/\{/&lbrace;/g;
    +    $text =~ s/\}/&rbrace;/g;
    +
    +    if ($flags & CONVERT_PARENTHESIS())
    +        {
    +        $text =~ s/\(/&lparen;/g;
    +        $text =~ s/\)/&rparen;/g;
    +        };
    +    if ($flags & CONVERT_COMMAS())
    +        {
    +        $text =~ s/\,/&comma;/g;
    +        };
    +    if ($flags & CONVERT_COLONS())
    +        {
    +        $text =~ s/\:/&colon;/g;
    +        };
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: RestoreAmpChars
    +#   Replaces entity characters in the string with their original characters and returns it.  This will restore all amp chars regardless
    +#   of the flags passed to <ConvertAmpChars()>.
    +#
    +sub RestoreAmpChars #(string text) => string
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ s/&lparen;/(/gi;
    +    $text =~ s/&rparen;/)/gi;
    +    $text =~ s/&lbrace;/{/gi;
    +    $text =~ s/&rbrace;/}/gi;
    +    $text =~ s/&comma;/,/gi;
    +    $text =~ s/&amp;/&/gi;
    +    $text =~ s/&colon;/:/gi;
    +
    +    return $text;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Auto-Adjustment Functions
    +
    +
    +#
    +#   Function: ResolveInputDirectories
    +#
    +#   Detects if the input directories in the menu file match those in the command line, and if not, tries to resolve them.  This allows
    +#   menu files to work across machines, since the absolute paths won't be the same but the relative ones should be.
    +#
    +#   Parameters:
    +#
    +#       inputDirectoryNames - A hashref of the input directories appearing in the menu file, or undef if none.  The keys are the
    +#                                        directories, and the values are their names.  May be undef.
    +#
    +sub ResolveInputDirectories #(inputDirectoryNames)
    +    {
    +    my ($self, $menuDirectoryNames) = @_;
    +
    +
    +    # Determine which directories don't match the command line, if any.
    +
    +    my $inputDirectories = NaturalDocs::Settings->InputDirectories();
    +    my @unresolvedMenuDirectories;
    +
    +    foreach my $menuDirectory (keys %$menuDirectoryNames)
    +        {
    +        my $found;
    +
    +        foreach my $inputDirectory (@$inputDirectories)
    +            {
    +            if ($menuDirectory eq $inputDirectory)
    +                {
    +                $found = 1;
    +                last;
    +                };
    +            };
    +
    +        if (!$found)
    +            {  push @unresolvedMenuDirectories, $menuDirectory;  };
    +        };
    +
    +    # Quit if everything matches up, which should be the most common case.
    +    if (!scalar @unresolvedMenuDirectories)
    +        {  return;  };
    +
    +    # Poop.  See which input directories are still available.
    +
    +    my @unresolvedInputDirectories;
    +
    +    foreach my $inputDirectory (@$inputDirectories)
    +        {
    +        if (!exists $menuDirectoryNames->{$inputDirectory})
    +            {  push @unresolvedInputDirectories, $inputDirectory;  };
    +        };
    +
    +    # Quit if there are none.  This means an input directory is in the menu that isn't in the command line.  Natural Docs should
    +    # proceed normally and let the files be deleted.
    +    if (!scalar @unresolvedInputDirectories)
    +        {
    +        $hasChanged = 1;
    +        return;
    +        };
    +
    +    # The index into menuDirectoryScores is the same as in unresolvedMenuDirectories.  The index into each arrayref within it is
    +    # the same as in unresolvedInputDirectories.
    +    my @menuDirectoryScores;
    +    for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++)
    +        {  push @menuDirectoryScores, [ ];  };
    +
    +
    +    # Now plow through the menu, looking for files that have an unresolved base.
    +
    +    my @menuGroups = ( $menu );
    +
    +    while (scalar @menuGroups)
    +        {
    +        my $currentGroup = pop @menuGroups;
    +        my $currentGroupContent = $currentGroup->GroupContent();
    +
    +        foreach my $entry (@$currentGroupContent)
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {
    +                push @menuGroups, $entry;
    +                }
    +            elsif ($entry->Type() == ::MENU_FILE())
    +                {
    +                # Check if it uses an unresolved base.
    +                for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++)
    +                    {
    +                    if (NaturalDocs::File->IsSubPathOf($unresolvedMenuDirectories[$i], $entry->Target()))
    +                        {
    +                        my $relativePath = NaturalDocs::File->MakeRelativePath($unresolvedMenuDirectories[$i], $entry->Target());
    +                        $self->ResolveFile($relativePath, \@unresolvedInputDirectories, $menuDirectoryScores[$i]);
    +                        last;
    +                        };
    +                    };
    +                };
    +            };
    +        };
    +
    +
    +    # Now, create an array of score objects.  Each score object is the three value arrayref [ from, to, score ].  From and To are the
    +    # conversion options and are the indexes into unresolvedInput/MenuDirectories.  We'll sort this array by score to get the best
    +    # possible conversions.  Yes, really.
    +    my @scores;
    +
    +    for (my $menuIndex = 0; $menuIndex < scalar @unresolvedMenuDirectories; $menuIndex++)
    +        {
    +        for (my $inputIndex = 0; $inputIndex < scalar @unresolvedInputDirectories; $inputIndex++)
    +            {
    +            if ($menuDirectoryScores[$menuIndex]->[$inputIndex])
    +                {
    +                push @scores, [ $menuIndex, $inputIndex, $menuDirectoryScores[$menuIndex]->[$inputIndex] ];
    +                };
    +            };
    +        };
    +
    +    @scores = sort { $b->[2] <=> $a->[2] } @scores;
    +
    +
    +    # Now we determine what goes where.
    +    my @menuDirectoryConversions;
    +
    +    foreach my $scoreObject (@scores)
    +        {
    +        if (!defined $menuDirectoryConversions[ $scoreObject->[0] ])
    +            {
    +            $menuDirectoryConversions[ $scoreObject->[0] ] = $unresolvedInputDirectories[ $scoreObject->[1] ];
    +            };
    +        };
    +
    +
    +    # Now, FINALLY, we do the conversion.  Note that not every menu directory may have a conversion defined.
    +
    +    @menuGroups = ( $menu );
    +
    +    while (scalar @menuGroups)
    +        {
    +        my $currentGroup = pop @menuGroups;
    +        my $currentGroupContent = $currentGroup->GroupContent();
    +
    +        foreach my $entry (@$currentGroupContent)
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {
    +                push @menuGroups, $entry;
    +                }
    +            elsif ($entry->Type() == ::MENU_FILE())
    +                {
    +                # Check if it uses an unresolved base.
    +                for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++)
    +                    {
    +                    if (NaturalDocs::File->IsSubPathOf($unresolvedMenuDirectories[$i], $entry->Target()) &&
    +                        defined $menuDirectoryConversions[$i])
    +                        {
    +                        my $relativePath = NaturalDocs::File->MakeRelativePath($unresolvedMenuDirectories[$i], $entry->Target());
    +                        $entry->SetTarget( NaturalDocs::File->JoinPaths($menuDirectoryConversions[$i], $relativePath) );
    +                        last;
    +                        };
    +                    };
    +                };
    +            };
    +        };
    +
    +
    +    # Whew.
    +
    +    $hasChanged = 1;
    +    };
    +
    +
    +#
    +#   Function: ResolveRelativeInputDirectories
    +#
    +#   Resolves relative input directories to the input directories available.
    +#
    +sub ResolveRelativeInputDirectories
    +    {
    +    my ($self) = @_;
    +
    +    my $inputDirectories = NaturalDocs::Settings->InputDirectories();
    +    my $resolvedInputDirectory;
    +
    +    if (scalar @$inputDirectories == 1)
    +        {  $resolvedInputDirectory = $inputDirectories->[0];  }
    +    else
    +        {
    +        my @score;
    +
    +        # Plow through the menu, looking for files and scoring them.
    +
    +        my @menuGroups = ( $menu );
    +
    +        while (scalar @menuGroups)
    +            {
    +            my $currentGroup = pop @menuGroups;
    +            my $currentGroupContent = $currentGroup->GroupContent();
    +
    +            foreach my $entry (@$currentGroupContent)
    +                {
    +                if ($entry->Type() == ::MENU_GROUP())
    +                    {
    +                    push @menuGroups, $entry;
    +                    }
    +                elsif ($entry->Type() == ::MENU_FILE())
    +                    {
    +                    $self->ResolveFile($entry->Target(), $inputDirectories, \@score);
    +                    };
    +                };
    +            };
    +
    +        # Determine the best match.
    +
    +        my $bestScore = 0;
    +        my $bestIndex = 0;
    +
    +        for (my $i = 0; $i < scalar @$inputDirectories; $i++)
    +            {
    +            if ($score[$i] > $bestScore)
    +                {
    +                $bestScore = $score[$i];
    +                $bestIndex = $i;
    +                };
    +            };
    +
    +        $resolvedInputDirectory = $inputDirectories->[$bestIndex];
    +        };
    +
    +
    +    # Okay, now that we have our resolved directory, update everything.
    +
    +    my @menuGroups = ( $menu );
    +
    +    while (scalar @menuGroups)
    +        {
    +        my $currentGroup = pop @menuGroups;
    +        my $currentGroupContent = $currentGroup->GroupContent();
    +
    +        foreach my $entry (@$currentGroupContent)
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @menuGroups, $entry;  }
    +            elsif ($entry->Type() == ::MENU_FILE())
    +                {
    +                $entry->SetTarget( NaturalDocs::File->JoinPaths($resolvedInputDirectory, $entry->Target()) );
    +                };
    +            };
    +        };
    +
    +    if (scalar @$inputDirectories > 1)
    +        {  $hasChanged = 1;  };
    +
    +    return $resolvedInputDirectory;
    +    };
    +
    +
    +#
    +#   Function: ResolveFile
    +#
    +#   Tests a relative path against a list of directories.  Adds one to the score of each base where there is a match.
    +#
    +#   Parameters:
    +#
    +#       relativePath - The relative file name to test.
    +#       possibleBases - An arrayref of bases to test it against.
    +#       possibleBaseScores - An arrayref of scores to adjust.  The score indexes should correspond to the base indexes.
    +#
    +sub ResolveFile #(relativePath, possibleBases, possibleBaseScores)
    +    {
    +    my ($self, $relativePath, $possibleBases, $possibleBaseScores) = @_;
    +
    +    for (my $i = 0; $i < scalar @$possibleBases; $i++)
    +        {
    +        if (-e NaturalDocs::File->JoinPaths($possibleBases->[$i], $relativePath))
    +            {  $possibleBaseScores->[$i]++;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: LockUserTitleChanges
    +#
    +#   Detects if the user manually changed any file titles, and if so, automatically locks them with <MENU_FILE_NOAUTOTITLE>.
    +#
    +#   Parameters:
    +#
    +#       previousMenuFiles - A hashref of the files from the previous menu state.  The keys are the <FileNames>, and the values are
    +#                                    references to their <NaturalDocs::Menu::Entry> objects.
    +#
    +sub LockUserTitleChanges #(previousMenuFiles)
    +    {
    +    my ($self, $previousMenuFiles) = @_;
    +
    +    my @groupStack = ( $menu );
    +    my $groupEntry;
    +
    +    while (scalar @groupStack)
    +        {
    +        $groupEntry = pop @groupStack;
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +
    +            # If it's an unlocked file entry
    +            if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0)
    +                {
    +                my $previousEntry = $previousMenuFiles->{$entry->Target()};
    +
    +                # If the previous entry was also unlocked and the titles are different, the user changed the title.  Automatically lock it.
    +                if (defined $previousEntry && ($previousEntry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 &&
    +                    $entry->Title() ne $previousEntry->Title())
    +                    {
    +                    $entry->SetFlags($entry->Flags() | ::MENU_FILE_NOAUTOTITLE());
    +                    $hasChanged = 1;
    +                    };
    +                }
    +
    +            elsif ($entry->Type() == ::MENU_GROUP())
    +                {
    +                push @groupStack, $entry;
    +                };
    +
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: FlagAutoTitleChanges
    +#
    +#   Finds which files have auto-titles that changed and flags their groups for updating with <MENU_GROUP_UPDATETITLES> and
    +#   <MENU_GROUP_UPDATEORDER>.
    +#
    +sub FlagAutoTitleChanges
    +    {
    +    my ($self) = @_;
    +
    +    my @groupStack = ( $menu );
    +    my $groupEntry;
    +
    +    while (scalar @groupStack)
    +        {
    +        $groupEntry = pop @groupStack;
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 &&
    +                exists $defaultTitlesChanged{$entry->Target()})
    +                {
    +                $groupEntry->SetFlags($groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() | ::MENU_GROUP_UPDATEORDER());
    +                $hasChanged = 1;
    +                }
    +            elsif ($entry->Type() == ::MENU_GROUP())
    +                {
    +                push @groupStack, $entry;
    +                };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: AutoPlaceNewFiles
    +#
    +#   Adds files to the menu that aren't already on it, attempting to guess where they belong.
    +#
    +#   New files are placed after a dummy <MENU_ENDOFORIGINAL> entry so that they don't affect the detected order.  Also, the
    +#   groups they're placed in get <MENU_GROUP_UPDATETITLES>, <MENU_GROUP_UPDATESTRUCTURE>, and
    +#   <MENU_GROUP_UPDATEORDER> flags.
    +#
    +#   Parameters:
    +#
    +#       filesInMenu - An existence hash of all the <FileNames> present in the menu.
    +#
    +sub AutoPlaceNewFiles #(fileInMenu)
    +    {
    +    my ($self, $filesInMenu) = @_;
    +
    +    my $files = NaturalDocs::Project->FilesWithContent();
    +
    +    my $directories;
    +
    +    foreach my $file (keys %$files)
    +        {
    +        if (!exists $filesInMenu->{$file})
    +            {
    +            # This is done on demand because new files shouldn't be added very often, so this will save time.
    +            if (!defined $directories)
    +                {  $directories = $self->MatchDirectoriesAndGroups();  };
    +
    +            my $targetGroup;
    +            my $fileDirectoryString = (NaturalDocs::File->SplitPath($file))[1];
    +
    +            $targetGroup = $directories->{$fileDirectoryString};
    +
    +            if (!defined $targetGroup)
    +                {
    +                # Okay, if there's no exact match, work our way down.
    +
    +                my @fileDirectories = NaturalDocs::File->SplitDirectories($fileDirectoryString);
    +
    +                do
    +                    {
    +                    pop @fileDirectories;
    +                    $targetGroup = $directories->{ NaturalDocs::File->JoinDirectories(@fileDirectories) };
    +                    }
    +                while (!defined $targetGroup && scalar @fileDirectories);
    +
    +                if (!defined $targetGroup)
    +                    {  $targetGroup = $menu;  };
    +                };
    +
    +            $targetGroup->MarkEndOfOriginal();
    +            $targetGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_FILE(), undef, $file, undef) );
    +
    +            $targetGroup->SetFlags( $targetGroup->Flags() | ::MENU_GROUP_UPDATETITLES() |
    +                                                 ::MENU_GROUP_UPDATESTRUCTURE() | ::MENU_GROUP_UPDATEORDER() );
    +
    +            $hasChanged = 1;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: MatchDirectoriesAndGroups
    +#
    +#   Determines which groups files in certain directories should be placed in.
    +#
    +#   Returns:
    +#
    +#       A hashref.  The keys are the directory names, and the values are references to the group objects they should be placed in.
    +#
    +#       This only repreesents directories that currently have files on the menu, so it shouldn't be assumed that every possible
    +#       directory will exist.  To match, you should first try to match the directory, and then strip the deepest directories one by
    +#       one until there's a match or there's none left.  If there's none left, use the root group <menu>.
    +#
    +sub MatchDirectoriesAndGroups
    +    {
    +    my ($self) = @_;
    +
    +    # The keys are the directory names, and the values are hashrefs.  For the hashrefs, the keys are the group objects, and the
    +    # values are the number of files in them from that directory.  In other words,
    +    # $directories{$directory}->{$groupEntry} = $count;
    +    my %directories;
    +    # Note that we need to use Tie::RefHash to use references as keys.  Won't work otherwise.  Also, not every Perl distro comes
    +    # with Tie::RefHash::Nestable, so we can't rely on that.
    +
    +    # We're using an index instead of pushing and popping because we want to save a list of the groups in the order they appear
    +    # to break ties.
    +    my @groups = ( $menu );
    +    my $groupIndex = 0;
    +
    +
    +    # Count the number of files in each group that appear in each directory.
    +
    +    while ($groupIndex < scalar @groups)
    +        {
    +        my $groupEntry = $groups[$groupIndex];
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {
    +                push @groups, $entry;
    +                }
    +            elsif ($entry->Type() == ::MENU_FILE())
    +                {
    +                my $directory = (NaturalDocs::File->SplitPath($entry->Target()))[1];
    +
    +                if (!exists $directories{$directory})
    +                    {
    +                    my $subHash = { };
    +                    tie %$subHash, 'Tie::RefHash';
    +                    $directories{$directory} = $subHash;
    +                    };
    +
    +                if (!exists $directories{$directory}->{$groupEntry})
    +                    {  $directories{$directory}->{$groupEntry} = 1;  }
    +                else
    +                    {  $directories{$directory}->{$groupEntry}++;  };
    +                };
    +            };
    +
    +        $groupIndex++;
    +        };
    +
    +
    +    # Determine which group goes with which directory, breaking ties by using whichever group appears first.
    +
    +    my $finalDirectories = { };
    +
    +    while (my ($directory, $directoryGroups) = each %directories)
    +        {
    +        my $bestGroup;
    +        my $bestCount = 0;
    +        my %tiedGroups;  # Existence hash
    +
    +        while (my ($group, $count) = each %$directoryGroups)
    +            {
    +            if ($count > $bestCount)
    +                {
    +                $bestGroup = $group;
    +                $bestCount = $count;
    +                %tiedGroups = ( );
    +                }
    +            elsif ($count == $bestCount)
    +                {
    +                $tiedGroups{$group} = 1;
    +                };
    +            };
    +
    +        # Break ties.
    +        if (scalar keys %tiedGroups)
    +            {
    +            $tiedGroups{$bestGroup} = 1;
    +
    +            foreach my $group (@groups)
    +                {
    +                if (exists $tiedGroups{$group})
    +                    {
    +                    $bestGroup = $group;
    +                    last;
    +                    };
    +                };
    +            };
    +
    +
    +        $finalDirectories->{$directory} = $bestGroup;
    +        };
    +
    +
    +    return $finalDirectories;
    +    };
    +
    +
    +#
    +#   Function: RemoveDeadFiles
    +#
    +#   Removes files from the menu that no longer exist or no longer have Natural Docs content.
    +#
    +#   Returns:
    +#
    +#       The number of file entries removed.
    +#
    +sub RemoveDeadFiles
    +    {
    +    my ($self) = @_;
    +
    +    my @groupStack = ( $menu );
    +    my $numberRemoved = 0;
    +
    +    my $filesWithContent = NaturalDocs::Project->FilesWithContent();
    +
    +    while (scalar @groupStack)
    +        {
    +        my $groupEntry = pop @groupStack;
    +        my $groupContent = $groupEntry->GroupContent();
    +
    +        my $index = 0;
    +        while ($index < scalar @$groupContent)
    +            {
    +            if ($groupContent->[$index]->Type() == ::MENU_FILE() &&
    +                !exists $filesWithContent->{ $groupContent->[$index]->Target() } )
    +                {
    +                $groupEntry->DeleteFromGroup($index);
    +
    +                $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() |
    +                                                   ::MENU_GROUP_UPDATESTRUCTURE() );
    +                $numberRemoved++;
    +                $hasChanged = 1;
    +                }
    +
    +            elsif ($groupContent->[$index]->Type() == ::MENU_GROUP())
    +                {
    +                push @groupStack, $groupContent->[$index];
    +                $index++;
    +                }
    +
    +            else
    +                {  $index++;  };
    +            };
    +        };
    +
    +    return $numberRemoved;
    +    };
    +
    +
    +#
    +#   Function: BanAndUnbanIndexes
    +#
    +#   Adjusts the indexes that are banned depending on if the user added or deleted any.
    +#
    +sub BanAndUnbanIndexes
    +    {
    +    my ($self) = @_;
    +
    +    # Unban any indexes that are present, meaning the user added them back manually without deleting the ban.
    +    foreach my $index (keys %indexes)
    +        {  delete $bannedIndexes{$index};  };
    +
    +    # Ban any indexes that were in the previous menu but not the current, meaning the user manually deleted them.  However,
    +    # don't do this if the topic isn't indexable, meaning they changed the topic type rather than the menu.
    +    foreach my $index (keys %previousIndexes)
    +        {
    +        if (!exists $indexes{$index} && NaturalDocs::Topics->TypeInfo($index)->Index())
    +            {  $bannedIndexes{$index} = 1;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: AddAndRemoveIndexes
    +#
    +#   Automatically adds and removes index entries on the menu as necessary.  <DetectIndexGroups()> should be called
    +#   beforehand.
    +#
    +sub AddAndRemoveIndexes
    +    {
    +    my ($self) = @_;
    +
    +    my %validIndexes;
    +    my @allIndexes = NaturalDocs::Topics->AllIndexableTypes();
    +
    +    foreach my $index (@allIndexes)
    +        {
    +        # Strip the banned indexes first so it's potentially less work for SymbolTable.
    +        if (!exists $bannedIndexes{$index})
    +            {  $validIndexes{$index} = 1;  };
    +        };
    +
    +    %validIndexes = %{NaturalDocs::SymbolTable->HasIndexes(\%validIndexes)};
    +
    +
    +    # Delete dead indexes and find the best index group.
    +
    +    my @groupStack = ( $menu );
    +
    +    my $bestIndexGroup;
    +    my $bestIndexCount = 0;
    +
    +    while (scalar @groupStack)
    +        {
    +        my $currentGroup = pop @groupStack;
    +        my $index = 0;
    +
    +        my $currentIndexCount = 0;
    +
    +        while ($index < scalar @{$currentGroup->GroupContent()})
    +            {
    +            my $entry = $currentGroup->GroupContent()->[$index];
    +
    +            if ($entry->Type() == ::MENU_INDEX())
    +                {
    +                $currentIndexCount++;
    +
    +                if ($currentIndexCount > $bestIndexCount)
    +                    {
    +                    $bestIndexCount = $currentIndexCount;
    +                    $bestIndexGroup = $currentGroup;
    +                    };
    +
    +                # Remove it if it's dead.
    +
    +                if (!exists $validIndexes{ $entry->Target() })
    +                    {
    +                    $currentGroup->DeleteFromGroup($index);
    +                    delete $indexes{ $entry->Target() };
    +                    $hasChanged = 1;
    +                    }
    +                else
    +                    {  $index++;  };
    +                }
    +
    +            else
    +                {
    +                if ($entry->Type() == ::MENU_GROUP())
    +                    {  push @groupStack, $entry;  };
    +
    +                $index++;
    +                };
    +            };
    +        };
    +
    +
    +    # Now add the new indexes.
    +
    +    foreach my $index (keys %indexes)
    +        {  delete $validIndexes{$index};  };
    +
    +    if (scalar keys %validIndexes)
    +        {
    +        # Add a group if there are no indexes at all.
    +
    +        if ($bestIndexCount == 0)
    +            {
    +            $menu->MarkEndOfOriginal();
    +
    +            my $newIndexGroup = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), 'Index', undef,
    +                                                                                              ::MENU_GROUP_ISINDEXGROUP());
    +            $menu->PushToGroup($newIndexGroup);
    +
    +            $bestIndexGroup = $newIndexGroup;
    +            $menu->SetFlags( $menu->Flags() | ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() );
    +            };
    +
    +        # Add the new indexes.
    +
    +        $bestIndexGroup->MarkEndOfOriginal();
    +        my $isIndexGroup = $bestIndexGroup->Flags() & ::MENU_GROUP_ISINDEXGROUP();
    +
    +        foreach my $index (keys %validIndexes)
    +            {
    +            my $title;
    +
    +            if ($isIndexGroup)
    +                {
    +                if ($index eq ::TOPIC_GENERAL())
    +                    {  $title = 'Everything';  }
    +                else
    +                    {  $title = NaturalDocs::Topics->NameOfType($index, 1);  };
    +                }
    +            else
    +                {
    +                $title = NaturalDocs::Topics->NameOfType($index) . ' Index';
    +                };
    +
    +            my $newEntry = NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $title, $index, undef);
    +            $bestIndexGroup->PushToGroup($newEntry);
    +
    +            $indexes{$index} = 1;
    +            };
    +
    +        $bestIndexGroup->SetFlags( $bestIndexGroup->Flags() |
    +                                                   ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() );
    +        $hasChanged = 1;
    +        };
    +    };
    +
    +
    +#
    +#   Function: RemoveDeadGroups
    +#
    +#   Removes groups with less than two entries.  It will always remove empty groups, and it will remove groups with one entry if it
    +#   has the <MENU_GROUP_UPDATESTRUCTURE> flag.
    +#
    +sub RemoveDeadGroups
    +    {
    +    my ($self) = @_;
    +
    +    my $index = 0;
    +
    +    while ($index < scalar @{$menu->GroupContent()})
    +        {
    +        my $entry = $menu->GroupContent()->[$index];
    +
    +        if ($entry->Type() == ::MENU_GROUP())
    +            {
    +            my $removed = $self->RemoveIfDead($entry, $menu, $index);
    +
    +            if (!$removed)
    +                {  $index++;  };
    +            }
    +        else
    +            {  $index++;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: RemoveIfDead
    +#
    +#   Checks a group and all its sub-groups for life and remove any that are dead.  Empty groups are removed, and groups with one
    +#   entry and the <MENU_GROUP_UPDATESTRUCTURE> flag have their entry moved to the parent group.
    +#
    +#   Parameters:
    +#
    +#       groupEntry - The group to check for possible deletion.
    +#       parentGroupEntry - The parent group to move the single entry to if necessary.
    +#       parentGroupIndex - The index of the group in its parent.
    +#
    +#   Returns:
    +#
    +#       Whether the group was removed or not.
    +#
    +sub RemoveIfDead #(groupEntry, parentGroupEntry, parentGroupIndex)
    +    {
    +    my ($self, $groupEntry, $parentGroupEntry, $parentGroupIndex) = @_;
    +
    +
    +    # Do all sub-groups first, since their deletions will affect our UPDATESTRUCTURE flag and content count.
    +
    +    my $index = 0;
    +    while ($index < scalar @{$groupEntry->GroupContent()})
    +        {
    +        my $entry = $groupEntry->GroupContent()->[$index];
    +
    +        if ($entry->Type() == ::MENU_GROUP())
    +            {
    +            my $removed = $self->RemoveIfDead($entry, $groupEntry, $index);
    +
    +            if (!$removed)
    +                {  $index++;  };
    +            }
    +        else
    +            {  $index++;  };
    +        };
    +
    +
    +    # Now check ourself.
    +
    +    my $count = scalar @{$groupEntry->GroupContent()};
    +    if ($groupEntry->Flags() & ::MENU_GROUP_HASENDOFORIGINAL())
    +        {  $count--;  };
    +
    +    if ($count == 0)
    +        {
    +        $parentGroupEntry->DeleteFromGroup($parentGroupIndex);
    +
    +        $parentGroupEntry->SetFlags( $parentGroupEntry->Flags() | ::MENU_GROUP_UPDATESTRUCTURE() );
    +
    +        $hasChanged = 1;
    +        return 1;
    +        }
    +    elsif ($count == 1 && ($groupEntry->Flags() & ::MENU_GROUP_UPDATESTRUCTURE()) )
    +        {
    +        my $onlyEntry = $groupEntry->GroupContent()->[0];
    +        if ($onlyEntry->Type() == ::MENU_ENDOFORIGINAL())
    +            {  $onlyEntry = $groupEntry->GroupContent()->[1];  };
    +
    +        $parentGroupEntry->DeleteFromGroup($parentGroupIndex);
    +
    +        $parentGroupEntry->MarkEndOfOriginal();
    +        $parentGroupEntry->PushToGroup($onlyEntry);
    +
    +        $parentGroupEntry->SetFlags( $parentGroupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() |
    +                                                     ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() );
    +
    +        $hasChanged = 1;
    +        return 1;
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: DetectIndexGroups
    +#
    +#   Finds groups that are primarily used for indexes and gives them the <MENU_GROUP_ISINDEXGROUP> flag.
    +#
    +sub DetectIndexGroups
    +    {
    +    my ($self) = @_;
    +
    +    my @groupStack = ( $menu );
    +
    +    while (scalar @groupStack)
    +        {
    +        my $groupEntry = pop @groupStack;
    +
    +        my $isIndexGroup = -1;  # -1: Can't tell yet.  0: Can't be an index group.  1: Is an index group so far.
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_INDEX())
    +                {
    +                if ($isIndexGroup == -1)
    +                    {  $isIndexGroup = 1;  };
    +                }
    +
    +            # Text is tolerated, but it still needs at least one index entry.
    +            elsif ($entry->Type() != ::MENU_TEXT())
    +                {
    +                $isIndexGroup = 0;
    +
    +                if ($entry->Type() == ::MENU_GROUP())
    +                    {  push @groupStack, $entry;  };
    +                };
    +            };
    +
    +        if ($isIndexGroup == 1)
    +            {
    +            $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_ISINDEXGROUP() );
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: CreateDirectorySubGroups
    +#
    +#   Where possible, creates sub-groups based on directories for any long groups that have <MENU_GROUP_UPDATESTRUCTURE>
    +#   set.  Clears the flag afterwards on groups that are short enough to not need any more sub-groups, but leaves it for the rest.
    +#
    +sub CreateDirectorySubGroups
    +    {
    +    my ($self) = @_;
    +
    +    my @groupStack = ( $menu );
    +
    +    foreach my $groupEntry (@groupStack)
    +        {
    +        if ($groupEntry->Flags() & ::MENU_GROUP_UPDATESTRUCTURE())
    +            {
    +            # Count the number of files.
    +
    +            my $fileCount = 0;
    +
    +            foreach my $entry (@{$groupEntry->GroupContent()})
    +                {
    +                if ($entry->Type() == ::MENU_FILE())
    +                    {  $fileCount++;  };
    +                };
    +
    +
    +            if ($fileCount > MAXFILESINGROUP)
    +                {
    +                my @sharedDirectories = $self->SharedDirectoriesOf($groupEntry);
    +                my $unsharedIndex = scalar @sharedDirectories;
    +
    +                # The keys are the first directory entries after the shared ones, and the values are the number of files that are in
    +                # that directory.  Files that don't have subdirectories after the shared directories aren't included because they shouldn't
    +                # be put in a subgroup.
    +                my %directoryCounts;
    +
    +                foreach my $entry (@{$groupEntry->GroupContent()})
    +                    {
    +                    if ($entry->Type() == ::MENU_FILE())
    +                        {
    +                        my @entryDirectories = NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] );
    +
    +                        if (scalar @entryDirectories > $unsharedIndex)
    +                            {
    +                            my $unsharedDirectory = $entryDirectories[$unsharedIndex];
    +
    +                            if (!exists $directoryCounts{$unsharedDirectory})
    +                                {  $directoryCounts{$unsharedDirectory} = 1;  }
    +                            else
    +                                {  $directoryCounts{$unsharedDirectory}++;  };
    +                            };
    +                        };
    +                    };
    +
    +
    +                # Now create the subgroups.
    +
    +                # The keys are the first directory entries after the shared ones, and the values are the groups for those files to be
    +                # put in.  There will only be entries for the groups with at least MINFILESINNEWGROUP files.
    +                my %directoryGroups;
    +
    +                while (my ($directory, $count) = each %directoryCounts)
    +                    {
    +                    if ($count >= MINFILESINNEWGROUP)
    +                        {
    +                        my $newGroup = NaturalDocs::Menu::Entry->New( ::MENU_GROUP(), ucfirst($directory), undef,
    +                                                                                                   ::MENU_GROUP_UPDATETITLES() |
    +                                                                                                   ::MENU_GROUP_UPDATEORDER() );
    +
    +                        if ($count > MAXFILESINGROUP)
    +                            {  $newGroup->SetFlags( $newGroup->Flags() | ::MENU_GROUP_UPDATESTRUCTURE());  };
    +
    +                        $groupEntry->MarkEndOfOriginal();
    +                        push @{$groupEntry->GroupContent()}, $newGroup;
    +
    +                        $directoryGroups{$directory} = $newGroup;
    +                        $fileCount -= $count;
    +                        };
    +                    };
    +
    +
    +                # Now fill the subgroups.
    +
    +                if (scalar keys %directoryGroups)
    +                    {
    +                    my $afterOriginal;
    +                    my $index = 0;
    +
    +                    while ($index < scalar @{$groupEntry->GroupContent()})
    +                        {
    +                        my $entry = $groupEntry->GroupContent()->[$index];
    +
    +                        if ($entry->Type() == ::MENU_FILE())
    +                            {
    +                            my @entryDirectories =
    +                                NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] );
    +
    +                            my $unsharedDirectory = $entryDirectories[$unsharedIndex];
    +
    +                            if (exists $directoryGroups{$unsharedDirectory})
    +                                {
    +                                my $targetGroup = $directoryGroups{$unsharedDirectory};
    +
    +                                if ($afterOriginal)
    +                                    {  $targetGroup->MarkEndOfOriginal();  };
    +                                $targetGroup->PushToGroup($entry);
    +
    +                                $groupEntry->DeleteFromGroup($index);
    +                                }
    +                            else
    +                                {  $index++;  };
    +                            }
    +
    +                        elsif ($entry->Type() == ::MENU_ENDOFORIGINAL())
    +                            {
    +                            $afterOriginal = 1;
    +                            $index++;
    +                            }
    +
    +                        elsif ($entry->Type() == ::MENU_GROUP())
    +                            {
    +                            # See if we need to relocate this group.
    +
    +                            my @groupDirectories = $self->SharedDirectoriesOf($entry);
    +
    +                            # The group's shared directories must be at least two levels deeper than the current.  If the first level deeper
    +                            # is a new group, move it there because it's a subdirectory of that one.
    +                            if (scalar @groupDirectories - scalar @sharedDirectories >= 2)
    +                                {
    +                                my $unsharedDirectory = $groupDirectories[$unsharedIndex];
    +
    +                                if (exists $directoryGroups{$unsharedDirectory} &&
    +                                    $directoryGroups{$unsharedDirectory} != $entry)
    +                                    {
    +                                    my $targetGroup = $directoryGroups{$unsharedDirectory};
    +
    +                                    if ($afterOriginal)
    +                                        {  $targetGroup->MarkEndOfOriginal();  };
    +                                    $targetGroup->PushToGroup($entry);
    +
    +                                    $groupEntry->DeleteFromGroup($index);
    +
    +                                    # We need to retitle the group if it has the name of the unshared directory.
    +
    +                                    my $oldTitle = $entry->Title();
    +                                    $oldTitle =~ s/ +//g;
    +                                    $unsharedDirectory =~ s/ +//g;
    +
    +                                    if (lc($oldTitle) eq lc($unsharedDirectory))
    +                                        {
    +                                        $entry->SetTitle($groupDirectories[$unsharedIndex + 1]);
    +                                        };
    +                                    }
    +                                else
    +                                    {  $index++;  };
    +                                }
    +                            else
    +                                {  $index++;  };
    +                            }
    +
    +                        else
    +                            {  $index++;  };
    +                        };
    +
    +                    $hasChanged = 1;
    +
    +                    if ($fileCount <= MAXFILESINGROUP)
    +                        {  $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_UPDATESTRUCTURE() );  };
    +
    +                    $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() |
    +                                                                                         ::MENU_GROUP_UPDATEORDER() );
    +                    };
    +
    +                };  # If group has >MAXFILESINGROUP files
    +            };  # If group has UPDATESTRUCTURE
    +
    +
    +        # Okay, now go through all the subgroups.  We do this after the above so that newly created groups can get subgrouped
    +        # further.
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @groupStack, $entry;  };
    +            };
    +
    +        };  # For each group entry
    +    };
    +
    +
    +#
    +#   Function: DetectOrder
    +#
    +#   Detects the order of the entries in all groups that have the <MENU_GROUP_UPDATEORDER> flag set.  Will set one of the
    +#   <MENU_GROUP_FILESSORTED>, <MENU_GROUP_FILESANDGROUPSSORTED>, <MENU_GROUP_EVERYTHINGSORTED>, or
    +#   <MENU_GROUP_UNSORTED> flags.  It will always go for the most comprehensive sort possible, so if a group only has one
    +#   entry, it will be flagged as <MENU_GROUP_EVERYTHINGSORTED>.
    +#
    +#   <DetectIndexGroups()> should be called beforehand, as the <MENU_GROUP_ISINDEXGROUP> flag affects how the order is
    +#   detected.
    +#
    +#   The sort detection stops if it reaches a <MENU_ENDOFORIGINAL> entry, so new entries can be added to the end while still
    +#   allowing the original sort to be detected.
    +#
    +#   Parameters:
    +#
    +#       forceAll - If set, the order will be detected for all groups regardless of whether <MENU_GROUP_UPDATEORDER> is set.
    +#
    +sub DetectOrder #(forceAll)
    +    {
    +    my ($self, $forceAll) = @_;
    +    my @groupStack = ( $menu );
    +
    +    while (scalar @groupStack)
    +        {
    +        my $groupEntry = pop @groupStack;
    +        my $index = 0;
    +
    +
    +        # First detect the sort.
    +
    +        if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATEORDER()) )
    +            {
    +            my $order = ::MENU_GROUP_EVERYTHINGSORTED();
    +
    +            my $lastFile;
    +            my $lastFileOrGroup;
    +
    +            while ($index < scalar @{$groupEntry->GroupContent()} &&
    +                     $groupEntry->GroupContent()->[$index]->Type() != ::MENU_ENDOFORIGINAL() &&
    +                     $order != ::MENU_GROUP_UNSORTED())
    +                {
    +                my $entry = $groupEntry->GroupContent()->[$index];
    +
    +
    +                # Ignore the last entry if it's an index group.  We don't want it to affect the sort.
    +
    +                if ($index + 1 == scalar @{$groupEntry->GroupContent()} &&
    +                    $entry->Type() == ::MENU_GROUP() && ($entry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) )
    +                    {
    +                    # Ignore.
    +
    +                    # This is an awkward code construct, basically working towards an else instead of using an if, but the code just gets
    +                    # too hard to read otherwise.  The compiled code should work out to roughly the same thing anyway.
    +                    }
    +
    +
    +                # Ignore the first entry if it's the general index in an index group.  We don't want it to affect the sort.
    +
    +                elsif ($index == 0 && ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) &&
    +                        $entry->Type() == ::MENU_INDEX() && $entry->Target() eq ::TOPIC_GENERAL() )
    +                    {
    +                    # Ignore.
    +                    }
    +
    +
    +                # Degenerate the sort.
    +
    +                else
    +                    {
    +
    +                    if ($order == ::MENU_GROUP_EVERYTHINGSORTED() && $index > 0 &&
    +                        ::StringCompare($entry->Title(), $groupEntry->GroupContent()->[$index - 1]->Title()) < 0)
    +                        {  $order = ::MENU_GROUP_FILESANDGROUPSSORTED();  };
    +
    +                    if ($order == ::MENU_GROUP_FILESANDGROUPSSORTED() &&
    +                        ($entry->Type() == ::MENU_FILE() || $entry->Type() == ::MENU_GROUP()) &&
    +                        defined $lastFileOrGroup && ::StringCompare($entry->Title(), $lastFileOrGroup->Title()) < 0)
    +                        {  $order = ::MENU_GROUP_FILESSORTED();  };
    +
    +                    if ($order == ::MENU_GROUP_FILESSORTED() &&
    +                        $entry->Type() == ::MENU_FILE() && defined $lastFile &&
    +                        ::StringCompare($entry->Title(), $lastFile->Title()) < 0)
    +                        {  $order = ::MENU_GROUP_UNSORTED();  };
    +
    +                    };
    +
    +
    +                # Set the lastX parameters for comparison and add sub-groups to the stack.
    +
    +                if ($entry->Type() == ::MENU_FILE())
    +                    {
    +                    $lastFile = $entry;
    +                    $lastFileOrGroup = $entry;
    +                    }
    +                elsif ($entry->Type() == ::MENU_GROUP())
    +                    {
    +                    $lastFileOrGroup = $entry;
    +                    push @groupStack, $entry;
    +                    };
    +
    +                $index++;
    +                };
    +
    +            $groupEntry->SetFlags($groupEntry->Flags() | $order);
    +            };
    +
    +
    +        # Find any subgroups in the remaining entries.
    +
    +        while ($index < scalar @{$groupEntry->GroupContent()})
    +            {
    +            my $entry = $groupEntry->GroupContent()->[$index];
    +
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @groupStack, $entry;  };
    +
    +            $index++;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: GenerateAutoFileTitles
    +#
    +#   Creates titles for the unlocked file entries in all groups that have the <MENU_GROUP_UPDATETITLES> flag set.  It clears the
    +#   flag afterwards so it can be used efficiently for multiple sweeps.
    +#
    +#   Parameters:
    +#
    +#       forceAll - If set, forces all the unlocked file titles to update regardless of whether the group has the
    +#                     <MENU_GROUP_UPDATETITLES> flag set.
    +#
    +sub GenerateAutoFileTitles #(forceAll)
    +    {
    +    my ($self, $forceAll) = @_;
    +
    +    my @groupStack = ( $menu );
    +
    +    while (scalar @groupStack)
    +        {
    +        my $groupEntry = pop @groupStack;
    +
    +        if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATETITLES()) )
    +            {
    +            # Find common prefixes and paths to strip from the default menu titles.
    +
    +            my @sharedDirectories = $self->SharedDirectoriesOf($groupEntry);
    +            my $noSharedDirectories = (scalar @sharedDirectories == 0);
    +
    +            my @sharedPrefixes;
    +            my $noSharedPrefixes;
    +
    +            foreach my $entry (@{$groupEntry->GroupContent()})
    +                {
    +                if ($entry->Type() == ::MENU_FILE())
    +                    {
    +                    # Find the common prefixes among all file entries that are unlocked and don't use the file name as their default title.
    +
    +                    my $defaultTitle = NaturalDocs::Project->DefaultMenuTitleOf($entry->Target());
    +
    +                    if (!$noSharedPrefixes && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 &&
    +                        $defaultTitle ne $entry->Target())
    +                        {
    +                        # If the filename is part of the title, separate it off so no part of it gets included as a common prefix.  This would
    +                        # happen if there's a group with only one file in it (Project.h => h) or only files that differ by extension
    +                        # (Project.h, Project.cpp => h, cpp) and people labeled them manually (// File: Project.h).
    +                        my $filename = (NaturalDocs::File->SplitPath($entry->Target()))[2];
    +                        my $filenamePart;
    +
    +                        if ( length $defaultTitle >= length $filename &&
    +                             lc(substr($defaultTitle, 0 - length($filename))) eq lc($filename) )
    +                            {
    +                            $filenamePart = substr($defaultTitle, 0 - length($filename));
    +                            $defaultTitle = substr($defaultTitle, 0, 0 - length($filename));
    +                            };
    +
    +
    +                        my @entryPrefixes = split(/(\.|::|->)/, $defaultTitle);
    +
    +                        # Remove potential leading undef/empty string.
    +                        if (!length $entryPrefixes[0])
    +                            {  shift @entryPrefixes;  };
    +
    +                        # Remove last entry.  Something has to exist for the title.  If we already separated off the filename, that will be
    +                        # it instead.
    +                        if (!$filenamePart)
    +                            {  pop @entryPrefixes;  };
    +
    +                        if (!scalar @entryPrefixes)
    +                            {  $noSharedPrefixes = 1;  }
    +                        elsif (!scalar @sharedPrefixes)
    +                            {  @sharedPrefixes = @entryPrefixes;  }
    +                        elsif ($entryPrefixes[0] ne $sharedPrefixes[0])
    +                            {  $noSharedPrefixes = 1;  }
    +
    +                        # If both arrays have entries, and the first is shared...
    +                        else
    +                            {
    +                            my $index = 1;
    +
    +                            while ($index < scalar @sharedPrefixes && $entryPrefixes[$index] eq $sharedPrefixes[$index])
    +                                {  $index++;  };
    +
    +                            if ($index < scalar @sharedPrefixes)
    +                                {  splice(@sharedPrefixes, $index);  };
    +                            };
    +                        };
    +
    +                    };  # if entry is MENU_FILE
    +                };  # foreach entry in group content.
    +
    +
    +            if (!scalar @sharedPrefixes)
    +                {  $noSharedPrefixes = 1;  };
    +
    +
    +            # Update all the menu titles of unlocked file entries.
    +
    +            foreach my $entry (@{$groupEntry->GroupContent()})
    +                {
    +                if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0)
    +                    {
    +                    my $title = NaturalDocs::Project->DefaultMenuTitleOf($entry->Target());
    +
    +                    if ($title eq $entry->Target())
    +                        {
    +                        my ($volume, $directoryString, $file) = NaturalDocs::File->SplitPath($entry->Target());
    +                        my @directories = NaturalDocs::File->SplitDirectories($directoryString);
    +
    +                        if (!$noSharedDirectories)
    +                            {  splice(@directories, 0, scalar @sharedDirectories);  };
    +
    +                        # directory\...\directory\file.ext
    +
    +                        if (scalar @directories > 2)
    +                            {  @directories = ( $directories[0], '...', $directories[-1] );  };
    +
    +                        $directoryString = NaturalDocs::File->JoinDirectories(@directories);
    +                        $title = NaturalDocs::File->JoinPaths($directoryString, $file);
    +                        }
    +
    +                    else
    +                        {
    +                        my $filename = (NaturalDocs::File->SplitPath($entry->Target()))[2];
    +                        my $filenamePart;
    +
    +                        if ( length $title >= length $filename &&
    +                             lc(substr($title, 0 - length($filename))) eq lc($filename) )
    +                            {
    +                            $filenamePart = substr($title, 0 - length($filename));
    +                            $title = substr($title, 0, 0 - length($filename));
    +                            };
    +
    +                        my @segments = split(/(::|\.|->)/, $title);
    +                        if (!length $segments[0])
    +                            {  shift @segments;  };
    +
    +                        if ($filenamePart)
    +                            {  push @segments, $filenamePart;  };
    +
    +                        if (!$noSharedPrefixes)
    +                            {  splice(@segments, 0, scalar @sharedPrefixes);  };
    +
    +                        # package...package::target
    +
    +                        if (scalar @segments > 5)
    +                            {  splice(@segments, 1, scalar @segments - 4, '...');  };
    +
    +                        $title = join('', @segments);
    +                        };
    +
    +                    $entry->SetTitle($title);
    +                    };  # If entry is an unlocked file
    +                };  # Foreach entry
    +
    +            $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_UPDATETITLES() );
    +
    +            };  # If updating group titles
    +
    +        # Now find any subgroups.
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @groupStack, $entry;  };
    +            };
    +        };
    +
    +    };
    +
    +
    +#
    +#   Function: ResortGroups
    +#
    +#   Resorts all groups that have <MENU_GROUP_UPDATEORDER> set.  Assumes <DetectOrder()> and <GenerateAutoFileTitles()>
    +#   have already been called.  Will clear the flag and any <MENU_ENDOFORIGINAL> entries on reordered groups.
    +#
    +#   Parameters:
    +#
    +#       forceAll - If set, resorts all groups regardless of whether <MENU_GROUP_UPDATEORDER> is set.
    +#
    +sub ResortGroups #(forceAll)
    +    {
    +    my ($self, $forceAll) = @_;
    +    my @groupStack = ( $menu );
    +
    +    while (scalar @groupStack)
    +        {
    +        my $groupEntry = pop @groupStack;
    +
    +        if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATEORDER()) )
    +            {
    +            my $newEntriesIndex;
    +
    +
    +            # Strip the ENDOFORIGINAL.
    +
    +            if ($groupEntry->Flags() & ::MENU_GROUP_HASENDOFORIGINAL())
    +                {
    +                $newEntriesIndex = 0;
    +
    +                while ($newEntriesIndex < scalar @{$groupEntry->GroupContent()} &&
    +                         $groupEntry->GroupContent()->[$newEntriesIndex]->Type() != ::MENU_ENDOFORIGINAL() )
    +                    {  $newEntriesIndex++;  };
    +
    +                $groupEntry->DeleteFromGroup($newEntriesIndex);
    +
    +                $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_HASENDOFORIGINAL() );
    +                }
    +            else
    +                {  $newEntriesIndex = -1;  };
    +
    +
    +            # Strip the exceptions.
    +
    +            my $trailingIndexGroup;
    +            my $leadingGeneralIndex;
    +
    +            if ( ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) &&
    +                 $groupEntry->GroupContent()->[0]->Type() == ::MENU_INDEX() &&
    +                 $groupEntry->GroupContent()->[0]->Target() eq ::TOPIC_GENERAL() )
    +                {
    +                $leadingGeneralIndex = shift @{$groupEntry->GroupContent()};
    +                if ($newEntriesIndex != -1)
    +                    {  $newEntriesIndex--;  };
    +                }
    +
    +            elsif (scalar @{$groupEntry->GroupContent()} && $newEntriesIndex != 0)
    +                {
    +                my $lastIndex;
    +
    +                if ($newEntriesIndex != -1)
    +                    {  $lastIndex = $newEntriesIndex - 1;  }
    +                else
    +                    {  $lastIndex = scalar @{$groupEntry->GroupContent()} - 1;  };
    +
    +                if ($groupEntry->GroupContent()->[$lastIndex]->Type() == ::MENU_GROUP() &&
    +                    ( $groupEntry->GroupContent()->[$lastIndex]->Flags() & ::MENU_GROUP_ISINDEXGROUP() ) )
    +                    {
    +                    $trailingIndexGroup = $groupEntry->GroupContent()->[$lastIndex];
    +                    $groupEntry->DeleteFromGroup($lastIndex);
    +
    +                    if ($newEntriesIndex != -1)
    +                        {  $newEntriesIndex++;  };
    +                    };
    +                };
    +
    +
    +            # If there weren't already exceptions, strip them from the new entries.
    +
    +            if ( (!defined $trailingIndexGroup || !defined $leadingGeneralIndex) && $newEntriesIndex != -1)
    +                {
    +                my $index = $newEntriesIndex;
    +
    +                while ($index < scalar @{$groupEntry->GroupContent()})
    +                    {
    +                    my $entry = $groupEntry->GroupContent()->[$index];
    +
    +                    if (!defined $trailingIndexGroup &&
    +                        $entry->Type() == ::MENU_GROUP() && ($entry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) )
    +                        {
    +                        $trailingIndexGroup = $entry;
    +                        $groupEntry->DeleteFromGroup($index);
    +                        }
    +                    elsif (!defined $leadingGeneralIndex && ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) &&
    +                            $entry->Type() == ::MENU_INDEX() && !defined $entry->Target())
    +                        {
    +                        $leadingGeneralIndex = $entry;
    +                        $groupEntry->DeleteFromGroup($index);
    +                        }
    +                    else
    +                        {  $index++;  };
    +                    };
    +                };
    +
    +
    +            # If there's no order, we still want to sort the new additions.
    +
    +            if ($groupEntry->Flags() & ::MENU_GROUP_UNSORTED())
    +                {
    +                if ($newEntriesIndex != -1)
    +                    {
    +                    my @newEntries =
    +                        @{$groupEntry->GroupContent()}[$newEntriesIndex..scalar @{$groupEntry->GroupContent()} - 1];
    +
    +                    @newEntries = sort { $self->CompareEntries($a, $b) } @newEntries;
    +
    +                    foreach my $newEntry (@newEntries)
    +                        {
    +                        $groupEntry->GroupContent()->[$newEntriesIndex] = $newEntry;
    +                        $newEntriesIndex++;
    +                        };
    +                    };
    +                }
    +
    +            elsif ($groupEntry->Flags() & ::MENU_GROUP_EVERYTHINGSORTED())
    +                {
    +                @{$groupEntry->GroupContent()} = sort { $self->CompareEntries($a, $b) } @{$groupEntry->GroupContent()};
    +                }
    +
    +            elsif ( ($groupEntry->Flags() & ::MENU_GROUP_FILESSORTED()) ||
    +                     ($groupEntry->Flags() & ::MENU_GROUP_FILESANDGROUPSSORTED()) )
    +                {
    +                my $groupContent = $groupEntry->GroupContent();
    +                my @newEntries;
    +
    +                if ($newEntriesIndex != -1)
    +                    {  @newEntries = splice( @$groupContent, $newEntriesIndex );  };
    +
    +
    +                # First resort the existing entries.
    +
    +                # A couple of support functions.  They're defined here instead of spun off into their own functions because they're only
    +                # used here and to make them general we would need to add support for the other sort options.
    +
    +                sub IsIncludedInSort #(groupEntry, entry)
    +                    {
    +                    my ($self, $groupEntry, $entry) = @_;
    +
    +                    return ($entry->Type() == ::MENU_FILE() ||
    +                                ( $entry->Type() == ::MENU_GROUP() &&
    +                                    ($groupEntry->Flags() & ::MENU_GROUP_FILESANDGROUPSSORTED()) ) );
    +                    };
    +
    +                sub IsSorted #(groupEntry)
    +                    {
    +                    my ($self, $groupEntry) = @_;
    +                    my $lastApplicable;
    +
    +                    foreach my $entry (@{$groupEntry->GroupContent()})
    +                        {
    +                        # If the entry is applicable to the sort order...
    +                        if ($self->IsIncludedInSort($groupEntry, $entry))
    +                            {
    +                            if (defined $lastApplicable)
    +                                {
    +                                if ($self->CompareEntries($entry, $lastApplicable) < 0)
    +                                    {  return undef;  };
    +                                };
    +
    +                            $lastApplicable = $entry;
    +                            };
    +                        };
    +
    +                    return 1;
    +                    };
    +
    +
    +                # There's a good chance it's still sorted.  They should only become unsorted if an auto-title changes.
    +                if (!$self->IsSorted($groupEntry))
    +                    {
    +                    # Crap.  Okay, method one is to sort each group of continuous sortable elements.  There's a possibility that doing
    +                    # this will cause the whole to become sorted again.  We try this first, even though it isn't guaranteed to succeed,
    +                    # because it will restore the sort without moving any unsortable entries.
    +
    +                    # Copy it because we'll need the original if this fails.
    +                    my @originalGroupContent = @$groupContent;
    +
    +                    my $index = 0;
    +                    my $startSortable = 0;
    +
    +                    while (1)
    +                        {
    +                        # If index is on an unsortable entry or the end of the array...
    +                        if ($index == scalar @$groupContent || !$self->IsIncludedInSort($groupEntry, $groupContent->[$index]))
    +                            {
    +                            # If we have at least two sortable entries...
    +                            if ($index - $startSortable >= 2)
    +                                {
    +                                # Sort them.
    +                                my @sortableEntries = @{$groupContent}[$startSortable .. $index - 1];
    +                                @sortableEntries = sort { $self->CompareEntries($a, $b) } @sortableEntries;
    +                                foreach my $sortableEntry (@sortableEntries)
    +                                    {
    +                                    $groupContent->[$startSortable] = $sortableEntry;
    +                                    $startSortable++;
    +                                    };
    +                                };
    +
    +                            if ($index == scalar @$groupContent)
    +                                {  last;  };
    +
    +                            $startSortable = $index + 1;
    +                            };
    +
    +                        $index++;
    +                        };
    +
    +                    if (!$self->IsSorted($groupEntry))
    +                        {
    +                        # Crap crap.  Okay, now we do a full sort but with potential damage to the original structure.  Each unsortable
    +                        # element is locked to the next sortable element.  We sort the sortable elements, bringing all the unsortable
    +                        # pieces with them.
    +
    +                        my @pieces = ( [ ] );
    +                        my $currentPiece = $pieces[0];
    +
    +                        foreach my $entry (@originalGroupContent)
    +                            {
    +                            push @$currentPiece, $entry;
    +
    +                            # If the entry is sortable...
    +                            if ($self->IsIncludedInSort($groupEntry, $entry))
    +                                {
    +                                $currentPiece = [ ];
    +                                push @pieces, $currentPiece;
    +                                };
    +                            };
    +
    +                        my $lastUnsortablePiece;
    +
    +                        # If the last entry was sortable, we'll have an empty piece at the end.  Drop it.
    +                        if (scalar @{$pieces[-1]} == 0)
    +                            {  pop @pieces;  }
    +
    +                        # If the last entry wasn't sortable, the last piece won't end with a sortable element.  Save it, but remove it
    +                        # from the list.
    +                        else
    +                            {  $lastUnsortablePiece = pop @pieces;  };
    +
    +                        # Sort the list.
    +                        @pieces = sort { $self->CompareEntries( $a->[-1], $b->[-1] ) } @pieces;
    +
    +                        # Copy it back to the original.
    +                        if (defined $lastUnsortablePiece)
    +                            {  push @pieces, $lastUnsortablePiece;  };
    +
    +                        my $index = 0;
    +
    +                        foreach my $piece (@pieces)
    +                            {
    +                            foreach my $entry (@{$piece})
    +                                {
    +                                $groupEntry->GroupContent()->[$index] = $entry;
    +                                $index++;
    +                                };
    +                            };
    +                        };
    +                    };
    +
    +
    +                # Okay, the orginal entries are sorted now.  Sort the new entries and apply.
    +
    +                if (scalar @newEntries)
    +                    {
    +                    @newEntries = sort { $self->CompareEntries($a, $b) } @newEntries;
    +                    my @originalEntries = @$groupContent;
    +                    @$groupContent = ( );
    +
    +                    while (1)
    +                        {
    +                        while (scalar @originalEntries && !$self->IsIncludedInSort($groupEntry, $originalEntries[0]))
    +                            {  push @$groupContent, (shift @originalEntries);  };
    +
    +                        if (!scalar @originalEntries || !scalar @newEntries)
    +                            {  last;  };
    +
    +                        while (scalar @newEntries && $self->CompareEntries($newEntries[0], $originalEntries[0]) < 0)
    +                            {  push @$groupContent, (shift @newEntries);  };
    +
    +                        push @$groupContent, (shift @originalEntries);
    +
    +                        if (!scalar @originalEntries || !scalar @newEntries)
    +                            {  last;  };
    +                        };
    +
    +                    if (scalar @originalEntries)
    +                        {  push @$groupContent, @originalEntries;  }
    +                    elsif (scalar @newEntries)
    +                        {  push @$groupContent, @newEntries;  };
    +                    };
    +                };
    +
    +
    +            # Now re-add the exceptions.
    +
    +            if (defined $leadingGeneralIndex)
    +                {
    +                unshift @{$groupEntry->GroupContent()}, $leadingGeneralIndex;
    +                };
    +
    +            if (defined $trailingIndexGroup)
    +                {
    +                $groupEntry->PushToGroup($trailingIndexGroup);
    +                };
    +
    +            };
    +
    +        foreach my $entry (@{$groupEntry->GroupContent()})
    +            {
    +            if ($entry->Type() == ::MENU_GROUP())
    +                {  push @groupStack, $entry;  };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: CompareEntries
    +#
    +#   A comparison function for use in sorting.  Compares the two entries by their titles with <StringCompare()>, but in the case
    +#   of a tie, puts <MENU_FILE> entries above <MENU_GROUP> entries.
    +#
    +sub CompareEntries #(a, b)
    +    {
    +    my ($self, $a, $b) = @_;
    +
    +    my $result = ::StringCompare($a->Title(), $b->Title());
    +
    +    if ($result == 0)
    +        {
    +        if ($a->Type() == ::MENU_FILE() && $b->Type() == ::MENU_GROUP())
    +            {  $result = -1;  }
    +        elsif ($a->Type() == ::MENU_GROUP() && $b->Type() == ::MENU_FILE())
    +            {  $result = 1;  };
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: SharedDirectoriesOf
    +#
    +#   Returns an array of all the directories shared by the files in the group.  If none, returns an empty array.
    +#
    +sub SharedDirectoriesOf #(group)
    +    {
    +    my ($self, $groupEntry) = @_;
    +    my @sharedDirectories;
    +
    +    foreach my $entry (@{$groupEntry->GroupContent()})
    +        {
    +        if ($entry->Type() == ::MENU_FILE())
    +            {
    +            my @entryDirectories = NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] );
    +
    +            if (!scalar @sharedDirectories)
    +                {  @sharedDirectories = @entryDirectories;  }
    +            else
    +                {  ::ShortenToMatchStrings(\@sharedDirectories, \@entryDirectories);  };
    +
    +            if (!scalar @sharedDirectories)
    +                {  last;  };
    +            };
    +        };
    +
    +    return @sharedDirectories;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Menu/Entry.pm b/vendor/naturaldocs/Modules/NaturalDocs/Menu/Entry.pm
    new file mode 100644
    index 000000000..00bdd4e29
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Menu/Entry.pm
    @@ -0,0 +1,202 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Menu::Entry
    +#
    +###############################################################################
    +#
    +#   A class representing an entry in the menu.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Menu::Entry;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The object is implemented as a blessed arrayref with the indexes below.
    +#
    +#       TYPE      - The <MenuEntryType>
    +#       TITLE     - The title of the entry.
    +#       TARGET  - The target of the entry.  If the type is <MENU_FILE>, it will be the source <FileName>.  If the type is
    +#                       <MENU_LINK>, it will be the URL.  If the type is <MENU_GROUP>, it will be an arrayref of
    +#                       <NaturalDocs::Menu::Entry> objects representing the group's content.  If the type is <MENU_INDEX>, it will be
    +#                       a <TopicType>.
    +#       FLAGS    - Any <Menu Entry Flags> that apply.
    +#
    +use constant TYPE => 0;
    +use constant TITLE => 1;
    +use constant TARGET => 2;
    +use constant FLAGS => 3;
    +# DEPENDENCY: New() depends on the order of these constants.
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       type     - The <MenuEntryType>.
    +#       title      - The title of the entry.
    +#       target   - The target of the entry, if applicable.  If the type is <MENU_FILE>, use the source <FileName>.  If the type is
    +#                     <MENU_LINK>, use the URL.  If the type is <MENU_INDEX>, use the <TopicType>.  Otherwise set it to undef.
    +#       flags     - Any <Menu Entry Flags> that apply.
    +#
    +sub New #(type, title, target, flags)
    +    {
    +    # DEPENDENCY: This gode depends on the order of the constants.
    +
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    if ($object->[TYPE] == ::MENU_GROUP())
    +        {  $object->[TARGET] = [ ];  };
    +    if (!defined $object->[FLAGS])
    +        {  $object->[FLAGS] = 0;  };
    +
    +    return $object;
    +    };
    +
    +
    +#   Function: Type
    +#   Returns the <MenuEntryType>.
    +sub Type
    +    {  return $_[0]->[TYPE];  };
    +
    +#   Function: Title
    +#   Returns the title of the entry.
    +sub Title
    +    {  return $_[0]->[TITLE];  };
    +
    +# Function: SetTitle
    +# Replaces the entry's title.
    +sub SetTitle #(title)
    +    {  $_[0]->[TITLE] = $_[1];  };
    +
    +#
    +#   Function: Target
    +#
    +#   Returns the target of the entry, if applicable.  If the type is <MENU_FILE>, it returns the source <FileName>.  If the type is
    +#   <MENU_LINK>, it returns the URL.  If the type is <MENU_INDEX>, it returns the <TopicType>.  Otherwise it returns undef.
    +#
    +sub Target
    +    {
    +    my $self = shift;
    +
    +    # Group entries are the only time when target won't be undef when it should be.
    +    if ($self->Type() == ::MENU_GROUP())
    +        {  return undef;  }
    +    else
    +        {  return $self->[TARGET];  };
    +    };
    +
    +# Function: SetTarget
    +# Replaces the entry's target.
    +sub SetTarget #(target)
    +    {  $_[0]->[TARGET] = $_[1];  };
    +
    +#   Function: Flags
    +#   Returns the <Menu Entry Flags>.
    +sub Flags
    +    {  return $_[0]->[FLAGS];  };
    +
    +# Function: SetFlags
    +# Replaces the <Menu Entry Flags>.
    +sub SetFlags #(flags)
    +    {  $_[0]->[FLAGS] = $_[1];  };
    +
    +
    +
    +###############################################################################
    +# Group: Group Functions
    +#
    +#   All of these functions assume the type is <MENU_GROUP>.  Do *not* call any of these without checking <Type()> first.
    +
    +
    +#
    +#   Function: GroupContent
    +#
    +#   Returns an arrayref of <NaturalDocs::Menu::Entry> objects representing the contents of the
    +#   group, or undef otherwise.  This arrayref will always exist for <MENU_GROUP>'s and can be changed.
    +#
    +sub GroupContent
    +    {
    +    return $_[0]->[TARGET];
    +    };
    +
    +
    +#
    +#   Function: GroupIsEmpty
    +#
    +#   If the type is <MENU_GROUP>, returns whether the group is empty.
    +#
    +sub GroupIsEmpty
    +    {
    +    my $self = shift;
    +    return (scalar @{$self->GroupContent()} > 0);
    +    };
    +
    +
    +#
    +#   Function: PushToGroup
    +#
    +#   Pushes the entry to the end of the group content.
    +#
    +sub PushToGroup #(entry)
    +    {
    +    my ($self, $entry) = @_;
    +    push @{$self->GroupContent()}, $entry;
    +    };
    +
    +
    +#
    +#   Function: DeleteFromGroup
    +#
    +#   Deletes an entry from the group content by index.
    +#
    +sub DeleteFromGroup #(index)
    +    {
    +    my ($self, $index) = @_;
    +
    +    my $groupContent = $self->GroupContent();
    +
    +    splice( @$groupContent, $index, 1 );
    +    };
    +
    +
    +#
    +#   Function: MarkEndOfOriginal
    +#
    +#   If the group doesn't already have one, adds a <MENU_ENDOFORIGINAL> entry to the end and sets the
    +#   <MENU_GROUP_HASENDOFORIGINAL> flag.
    +#
    +sub MarkEndOfOriginal
    +    {
    +    my $self = shift;
    +
    +    if (($self->Flags() & ::MENU_GROUP_HASENDOFORIGINAL()) == 0)
    +        {
    +        $self->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_ENDOFORIGINAL(), undef, undef, undef) );
    +        $self->SetFlags( $self->Flags() | ::MENU_GROUP_HASENDOFORIGINAL() );
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/NDMarkup.pm b/vendor/naturaldocs/Modules/NaturalDocs/NDMarkup.pm
    new file mode 100644
    index 000000000..46adc0211
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/NDMarkup.pm
    @@ -0,0 +1,77 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::NDMarkup
    +#
    +###############################################################################
    +#
    +#   A package of support functions for dealing with <NDMarkup>.
    +#
    +#   Usage and Dependencies:
    +#
    +#       The package doesn't depend on any Natural Docs packages and is ready to use right away.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::NDMarkup;
    +
    +#
    +#   Function: ConvertAmpChars
    +#
    +#   Substitutes certain characters with their <NDMarkup> amp chars.
    +#
    +#   Parameters:
    +#
    +#       text - The block of text to convert.
    +#
    +#   Returns:
    +#
    +#       The converted text block.
    +#
    +sub ConvertAmpChars #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ s/&/&amp;/g;
    +    $text =~ s/</&lt;/g;
    +    $text =~ s/>/&gt;/g;
    +    $text =~ s/\"/&quot;/g;
    +
    +    return $text;
    +    };
    +
    +
    +#
    +#   Function: RestoreAmpChars
    +#
    +#   Replaces <NDMarkup> amp chars with their original symbols.
    +#
    +#   Parameters:
    +#
    +#       text - The text to restore.
    +#
    +#   Returns:
    +#
    +#       The restored text.
    +#
    +sub RestoreAmpChars #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ s/&quot;/\"/g;
    +    $text =~ s/&gt;/>/g;
    +    $text =~ s/&lt;/</g;
    +    $text =~ s/&amp;/&/g;
    +
    +    return $text;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Parser.pm b/vendor/naturaldocs/Modules/NaturalDocs/Parser.pm
    new file mode 100644
    index 000000000..24c415feb
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Parser.pm
    @@ -0,0 +1,1335 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Parser
    +#
    +###############################################################################
    +#
    +#   A package that coordinates source file parsing between the <NaturalDocs::Languages::Base>-derived objects and its own
    +#   sub-packages such as <NaturalDocs::Parser::Native>.  Also handles sending symbols to <NaturalDocs::SymbolTable> and
    +#   other generic topic processing.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - Prior to use, <NaturalDocs::Settings>, <NaturalDocs::Languages>, <NaturalDocs::Project>, <NaturalDocs::SymbolTable>,
    +#         and <NaturalDocs::ClassHierarchy> must be initialized.  <NaturalDocs::SymbolTable> and <NaturalDocs::ClassHierarchy>
    +#         do not have to be fully resolved.
    +#
    +#       - Aside from that, the package is ready to use right away.  It does not have its own initialization function.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use NaturalDocs::Parser::ParsedTopic;
    +use NaturalDocs::Parser::Native;
    +use NaturalDocs::Parser::JavaDoc;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Parser;
    +
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   var: sourceFile
    +#
    +#   The source <FileName> currently being parsed.
    +#
    +my $sourceFile;
    +
    +#
    +#   var: language
    +#
    +#   The language object for the file, derived from <NaturalDocs::Languages::Base>.
    +#
    +my $language;
    +
    +#
    +#   Array: parsedFile
    +#
    +#   An array of <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +my @parsedFile;
    +
    +
    +#
    +#   bool: parsingForInformation
    +#   Whether <ParseForInformation()> was called.  If false, then <ParseForBuild()> was called.
    +#
    +my $parsingForInformation;
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: ParseForInformation
    +#
    +#   Parses the input file for information.  Will update the information about the file in <NaturalDocs::SymbolTable> and
    +#   <NaturalDocs::Project>.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to parse.
    +#
    +sub ParseForInformation #(file)
    +    {
    +    my ($self, $file) = @_;
    +    $sourceFile = $file;
    +
    +    $parsingForInformation = 1;
    +
    +    # Watch this parse so we detect any changes.
    +    NaturalDocs::SymbolTable->WatchFileForChanges($sourceFile);
    +    NaturalDocs::ClassHierarchy->WatchFileForChanges($sourceFile);
    +    NaturalDocs::SourceDB->WatchFileForChanges($sourceFile);
    +
    +    my $defaultMenuTitle = $self->Parse();
    +
    +    foreach my $topic (@parsedFile)
    +        {
    +        # Add a symbol for the topic.
    +
    +        my $type = $topic->Type();
    +        if ($type eq ::TOPIC_ENUMERATION())
    +            {  $type = ::TOPIC_TYPE();  };
    +
    +        NaturalDocs::SymbolTable->AddSymbol($topic->Symbol(), $sourceFile, $type,
    +                                                                   $topic->Prototype(), $topic->Summary());
    +
    +
    +        # You can't put the function call directly in a while with a regex.  It has to sit in a variable to work.
    +        my $body = $topic->Body();
    +
    +
    +        # If it's a list or enum topic, add a symbol for each description list entry.
    +
    +        if ($topic->IsList() || $topic->Type() eq ::TOPIC_ENUMERATION())
    +            {
    +            # We'll hijack the enum constants to apply to non-enum behavior too.
    +            my $behavior;
    +
    +            if ($topic->Type() eq ::TOPIC_ENUMERATION())
    +                {
    +                $type = ::TOPIC_CONSTANT();
    +                $behavior = $language->EnumValues();
    +                }
    +            elsif (NaturalDocs::Topics->TypeInfo($topic->Type())->Scope() == ::SCOPE_ALWAYS_GLOBAL())
    +                {
    +                $behavior = ::ENUM_GLOBAL();
    +                }
    +            else
    +                {
    +                $behavior = ::ENUM_UNDER_PARENT();
    +                };
    +
    +            while ($body =~ /<ds>([^<]+)<\/ds><dd>(.*?)<\/dd>/g)
    +                {
    +                my ($listTextSymbol, $listSummary) = ($1, $2);
    +
    +                $listTextSymbol = NaturalDocs::NDMarkup->RestoreAmpChars($listTextSymbol);
    +                my $listSymbol = NaturalDocs::SymbolString->FromText($listTextSymbol);
    +
    +                if ($behavior == ::ENUM_UNDER_PARENT())
    +                    {  $listSymbol = NaturalDocs::SymbolString->Join($topic->Package(), $listSymbol);  }
    +                elsif ($behavior == ::ENUM_UNDER_TYPE())
    +                    {  $listSymbol = NaturalDocs::SymbolString->Join($topic->Symbol(), $listSymbol);  };
    +
    +                NaturalDocs::SymbolTable->AddSymbol($listSymbol, $sourceFile, $type, undef,
    +                                                                           $self->GetSummaryFromDescriptionList($listSummary));
    +                };
    +            };
    +
    +
    +        # Add references in the topic.
    +
    +        while ($body =~ /<link target=\"([^\"]*)\" name=\"[^\"]*\" original=\"[^\"]*\">/g)
    +            {
    +            my $linkText = NaturalDocs::NDMarkup->RestoreAmpChars($1);
    +            my $linkSymbol = NaturalDocs::SymbolString->FromText($linkText);
    +
    +            NaturalDocs::SymbolTable->AddReference(::REFERENCE_TEXT(), $linkSymbol,
    +                                                                           $topic->Package(), $topic->Using(), $sourceFile);
    +            };
    +
    +
    +        # Add images in the topic.
    +
    +        while ($body =~ /<img mode=\"[^\"]*\" target=\"([^\"]+)\" original=\"[^\"]*\">/g)
    +            {
    +            my $target = NaturalDocs::NDMarkup->RestoreAmpChars($1);
    +            NaturalDocs::ImageReferenceTable->AddReference($sourceFile, $target);
    +            };
    +        };
    +
    +    # Handle any changes to the file.
    +    NaturalDocs::ClassHierarchy->AnalyzeChanges();
    +    NaturalDocs::SymbolTable->AnalyzeChanges();
    +    NaturalDocs::SourceDB->AnalyzeWatchedFileChanges();
    +
    +    # Update project on the file's characteristics.
    +    my $hasContent = (scalar @parsedFile > 0);
    +
    +    NaturalDocs::Project->SetHasContent($sourceFile, $hasContent);
    +    if ($hasContent)
    +        {  NaturalDocs::Project->SetDefaultMenuTitle($sourceFile, $defaultMenuTitle);  };
    +
    +    # We don't need to keep this around.
    +    @parsedFile = ( );
    +    };
    +
    +
    +#
    +#   Function: ParseForBuild
    +#
    +#   Parses the input file for building, returning it as a <NaturalDocs::Parser::ParsedTopic> arrayref.
    +#
    +#   Note that all new and changed files should be parsed for symbols via <ParseForInformation()> before calling this function on
    +#   *any* file.  The reason is that <NaturalDocs::SymbolTable> needs to know about all the symbol definitions and references to
    +#   resolve them properly.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to parse for building.
    +#
    +#   Returns:
    +#
    +#       An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects.
    +#
    +sub ParseForBuild #(file)
    +    {
    +    my ($self, $file) = @_;
    +    $sourceFile = $file;
    +
    +    $parsingForInformation = undef;
    +
    +    $self->Parse();
    +
    +    return \@parsedFile;
    +    };
    +
    +
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: OnComment
    +#
    +#   The function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a comment
    +#   suitable for documentation.
    +#
    +#   Parameters:
    +#
    +#       commentLines - An arrayref of the comment's lines.  The language's comment symbols should be converted to spaces,
    +#                               and there should be no line break characters at the end of each line.  *The original memory will be
    +#                               changed.*
    +#       lineNumber - The line number of the first of the comment lines.
    +#       isJavaDoc - Whether the comment is in JavaDoc format.
    +#
    +#   Returns:
    +#
    +#       The number of topics created by this comment, or zero if none.
    +#
    +sub OnComment #(string[] commentLines, int lineNumber, bool isJavaDoc)
    +    {
    +    my ($self, $commentLines, $lineNumber, $isJavaDoc) = @_;
    +
    +    $self->CleanComment($commentLines);
    +
    +    # We check if it's definitely Natural Docs content first.  This overrides all else, since it's possible that a comment could start
    +    # with a topic line yet have something that looks like a JavaDoc tag.  Natural Docs wins in this case.
    +    if (NaturalDocs::Parser::Native->IsMine($commentLines, $isJavaDoc))
    +        {  return NaturalDocs::Parser::Native->ParseComment($commentLines, $isJavaDoc, $lineNumber, \@parsedFile);  }
    +
    +    elsif (NaturalDocs::Parser::JavaDoc->IsMine($commentLines, $isJavaDoc))
    +        {  return NaturalDocs::Parser::JavaDoc->ParseComment($commentLines, $isJavaDoc, $lineNumber, \@parsedFile);  }
    +
    +    # If the content is ambiguous and it's a JavaDoc-styled comment, treat it as Natural Docs content.
    +    elsif ($isJavaDoc)
    +        {  return NaturalDocs::Parser::Native->ParseComment($commentLines, $isJavaDoc, $lineNumber, \@parsedFile);  }
    +    };
    +
    +
    +#
    +#   Function: OnClass
    +#
    +#   A function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a class declaration.
    +#
    +#   Parameters:
    +#
    +#       class - The <SymbolString> of the class encountered.
    +#
    +sub OnClass #(class)
    +    {
    +    my ($self, $class) = @_;
    +
    +    if ($parsingForInformation)
    +        {  NaturalDocs::ClassHierarchy->AddClass($sourceFile, $class);  };
    +    };
    +
    +
    +#
    +#   Function: OnClassParent
    +#
    +#   A function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a declaration of
    +#   inheritance.
    +#
    +#   Parameters:
    +#
    +#       class - The <SymbolString> of the class we're in.
    +#       parent - The <SymbolString> of the class it inherits.
    +#       scope - The package <SymbolString> that the reference appeared in.
    +#       using - An arrayref of package <SymbolStrings> that the reference has access to via "using" statements.
    +#       resolvingFlags - Any <Resolving Flags> to be used when resolving the reference.  <RESOLVE_NOPLURAL> is added
    +#                              automatically since that would never apply to source code.
    +#
    +sub OnClassParent #(class, parent, scope, using, resolvingFlags)
    +    {
    +    my ($self, $class, $parent, $scope, $using, $resolvingFlags) = @_;
    +
    +    if ($parsingForInformation)
    +        {
    +        NaturalDocs::ClassHierarchy->AddParentReference($sourceFile, $class, $parent, $scope, $using,
    +                                                                                   $resolvingFlags | ::RESOLVE_NOPLURAL());
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#   Function: Parse
    +#
    +#   Opens the source file and parses process.  Most of the actual parsing is done in <NaturalDocs::Languages::Base->ParseFile()>
    +#   and <OnComment()>, though.
    +#
    +#   *Do not call externally.*  Rather, call <ParseForInformation()> or <ParseForBuild()>.
    +#
    +#   Returns:
    +#
    +#       The default menu title of the file.  Will be the <FileName> if nothing better is found.
    +#
    +sub Parse
    +    {
    +    my ($self) = @_;
    +
    +    NaturalDocs::Error->OnStartParsing($sourceFile);
    +
    +    $language = NaturalDocs::Languages->LanguageOf($sourceFile);
    +    NaturalDocs::Parser::Native->Start();
    +    @parsedFile = ( );
    +
    +    my ($autoTopics, $scopeRecord) = $language->ParseFile($sourceFile, \@parsedFile);
    +
    +
    +    $self->AddToClassHierarchy();
    +
    +    $self->BreakLists();
    +
    +    if (defined $autoTopics)
    +        {
    +        if (defined $scopeRecord)
    +            {  $self->RepairPackages($autoTopics, $scopeRecord);  };
    +
    +        $self->MergeAutoTopics($language, $autoTopics);
    +        };
    +
    +    $self->RemoveRemainingHeaderlessTopics();
    +
    +
    +    # We don't need to do this if there aren't any auto-topics because the only package changes would be implied by the comments.
    +    if (defined $autoTopics)
    +        {  $self->AddPackageDelineators();  };
    +
    +    if (!NaturalDocs::Settings->NoAutoGroup())
    +        {  $self->MakeAutoGroups($autoTopics);  };
    +
    +
    +    # Set the menu title.
    +
    +    my $defaultMenuTitle = $sourceFile;
    +
    +    if (scalar @parsedFile)
    +        {
    +        my $addFileTitle;
    +
    +        if (NaturalDocs::Settings->OnlyFileTitles())
    +            {
    +            # We still want to use the title from the topics if the first one is a file.
    +            if ($parsedFile[0]->Type() eq ::TOPIC_FILE())
    +                {  $addFileTitle = 0;  }
    +            else
    +                {  $addFileTitle = 1;  };
    +            }
    +        elsif (scalar @parsedFile == 1 || NaturalDocs::Topics->TypeInfo( $parsedFile[0]->Type() )->PageTitleIfFirst())
    +            {  $addFileTitle = 0;  }
    +        else
    +            {  $addFileTitle = 1;  };
    +
    +        if (!$addFileTitle)
    +            {
    +            $defaultMenuTitle = $parsedFile[0]->Title();
    +            }
    +        else
    +            {
    +            # If the title ended up being the file name, add a leading section for it.
    +
    +            unshift @parsedFile,
    +                       NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FILE(), (NaturalDocs::File->SplitPath($sourceFile))[2],
    +                                                                                  undef, undef, undef, undef, undef, 1, undef);
    +            };
    +        };
    +
    +    NaturalDocs::Error->OnEndParsing($sourceFile);
    +
    +    return $defaultMenuTitle;
    +    };
    +
    +
    +#
    +#   Function: CleanComment
    +#
    +#   Removes any extraneous formatting and whitespace from the comment.  Eliminates comment boxes, horizontal lines, trailing
    +#   whitespace from lines, and expands all tab characters.  It keeps leading whitespace, though, since it may be needed for
    +#   example code, and blank lines, since the original line numbers are needed.
    +#
    +#   Parameters:
    +#
    +#       commentLines  - An arrayref of the comment lines to clean.  *The original memory will be changed.*  Lines should have the
    +#                                language's comment symbols replaced by spaces and not have a trailing line break.
    +#
    +sub CleanComment #(commentLines)
    +    {
    +    my ($self, $commentLines) = @_;
    +
    +    use constant DONT_KNOW => 0;
    +    use constant IS_UNIFORM => 1;
    +    use constant IS_UNIFORM_IF_AT_END => 2;
    +    use constant IS_NOT_UNIFORM => 3;
    +
    +    my $leftSide = DONT_KNOW;
    +    my $rightSide = DONT_KNOW;
    +    my $leftSideChar;
    +    my $rightSideChar;
    +
    +    my $index = 0;
    +    my $tabLength = NaturalDocs::Settings->TabLength();
    +
    +    while ($index < scalar @$commentLines)
    +        {
    +        # Strip trailing whitespace from the original.
    +
    +        $commentLines->[$index] =~ s/[ \t]+$//;
    +
    +
    +        # Expand tabs in the original.  This method is almost six times faster than Text::Tabs' method.
    +
    +        my $tabIndex = index($commentLines->[$index], "\t");
    +
    +        while ($tabIndex != -1)
    +            {
    +            substr( $commentLines->[$index], $tabIndex, 1, ' ' x ($tabLength - ($tabIndex % $tabLength)) );
    +            $tabIndex = index($commentLines->[$index], "\t", $tabIndex);
    +            };
    +
    +
    +        # Make a working copy and strip leading whitespace as well.  This has to be done after tabs are expanded because
    +        # stripping indentation could change how far tabs are expanded.
    +
    +        my $line = $commentLines->[$index];
    +        $line =~ s/^ +//;
    +
    +        # If the line is blank...
    +        if (!length $line)
    +            {
    +            # If we have a potential vertical line, this only acceptable if it's at the end of the comment.
    +            if ($leftSide == IS_UNIFORM)
    +                {  $leftSide = IS_UNIFORM_IF_AT_END;  };
    +            if ($rightSide == IS_UNIFORM)
    +                {  $rightSide = IS_UNIFORM_IF_AT_END;  };
    +            }
    +
    +        # If there's at least four symbols in a row, it's a horizontal line.  The second regex supports differing edge characters.  It
    +        # doesn't matter if any of this matches the left and right side symbols.  The length < 256 is a sanity check, because that
    +        # regexp has caused the perl regexp engine to choke on an insane line someone sent me from an automatically generated
    +        # file.  It had over 10k characters on the first line, and most of them were 0x00.
    +        elsif ($line =~ /^([^a-zA-Z0-9 ])\1{3,}$/ ||
    +                (length $line < 256 && $line =~ /^([^a-zA-Z0-9 ])\1*([^a-zA-Z0-9 ])\2{3,}([^a-zA-Z0-9 ])\3*$/) )
    +            {
    +            # Ignore it.  This has no effect on the vertical line detection.  We want to keep it in the output though in case it was
    +            # in a code section.
    +            }
    +
    +        # If the line is not blank or a horizontal line...
    +        else
    +            {
    +            # More content means any previous blank lines are no longer tolerated in vertical line detection.  They are only
    +            # acceptable at the end of the comment.
    +
    +            if ($leftSide == IS_UNIFORM_IF_AT_END)
    +                {  $leftSide = IS_NOT_UNIFORM;  };
    +            if ($rightSide == IS_UNIFORM_IF_AT_END)
    +                {  $rightSide = IS_NOT_UNIFORM;  };
    +
    +
    +            # Detect vertical lines.  Lines are only lines if they are followed by whitespace or a connected horizontal line.
    +            # Otherwise we may accidentally detect lines from short comments that just happen to have every first or last
    +            # character the same.
    +
    +            if ($leftSide != IS_NOT_UNIFORM)
    +                {
    +                if ($line =~ /^([^a-zA-Z0-9])\1*(?: |$)/)
    +                    {
    +                    if ($leftSide == DONT_KNOW)
    +                        {
    +                        $leftSide = IS_UNIFORM;
    +                        $leftSideChar = $1;
    +                        }
    +                    else # ($leftSide == IS_UNIFORM)  Other choices already ruled out.
    +                        {
    +                        if ($leftSideChar ne $1)
    +                            {  $leftSide = IS_NOT_UNIFORM;  };
    +                        };
    +                    }
    +                # We'll tolerate the lack of symbols on the left on the first line, because it may be a
    +                # /* Function: Whatever
    +                #  * Description.
    +                #  */
    +                # comment which would have the leading /* blanked out.
    +                elsif ($index != 0)
    +                    {
    +                    $leftSide = IS_NOT_UNIFORM;
    +                    };
    +                };
    +
    +            if ($rightSide != IS_NOT_UNIFORM)
    +                {
    +                if ($line =~ / ([^a-zA-Z0-9])\1*$/)
    +                    {
    +                    if ($rightSide == DONT_KNOW)
    +                        {
    +                        $rightSide = IS_UNIFORM;
    +                        $rightSideChar = $1;
    +                        }
    +                    else # ($rightSide == IS_UNIFORM)  Other choices already ruled out.
    +                        {
    +                        if ($rightSideChar ne $1)
    +                            {  $rightSide = IS_NOT_UNIFORM;  };
    +                        };
    +                    }
    +                else
    +                    {
    +                    $rightSide = IS_NOT_UNIFORM;
    +                    };
    +                };
    +
    +            # We'll remove vertical lines later if they're uniform throughout the entire comment.
    +            };
    +
    +        $index++;
    +        };
    +
    +
    +    if ($leftSide == IS_UNIFORM_IF_AT_END)
    +        {  $leftSide = IS_UNIFORM;  };
    +    if ($rightSide == IS_UNIFORM_IF_AT_END)
    +        {  $rightSide = IS_UNIFORM;  };
    +
    +
    +    $index = 0;
    +    my $inCodeSection = 0;
    +
    +    while ($index < scalar @$commentLines)
    +        {
    +        # Clear horizontal lines only if we're not in a code section.
    +        if ($commentLines->[$index] =~ /^ *([^a-zA-Z0-9 ])\1{3,}$/ ||
    +            ( length $commentLines->[$index] < 256 &&
    +              $commentLines->[$index] =~ /^ *([^a-zA-Z0-9 ])\1*([^a-zA-Z0-9 ])\2{3,}([^a-zA-Z0-9 ])\3*$/ ) )
    +        	{
    +        	if (!$inCodeSection)
    +        		{  $commentLines->[$index] = '';  }
    +        	}
    +
    +        else
    +        	{
    +	        # Clear vertical lines.
    +
    +	        if ($leftSide == IS_UNIFORM)
    +	            {
    +	            # This works because every line should either start this way, be blank, or be the first line that doesn't start with a
    +	            # symbol.
    +	            $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1*//;
    +	            };
    +
    +	        if ($rightSide == IS_UNIFORM)
    +	            {
    +	            $commentLines->[$index] =~ s/ *([^a-zA-Z0-9 ])\1*$//;
    +	            };
    +
    +
    +	        # Clear horizontal lines again if there were vertical lines.  This catches lines that were separated from the verticals by
    +	        # whitespace.
    +
    +	        if (($leftSide == IS_UNIFORM || $rightSide == IS_UNIFORM) && !$inCodeSection)
    +	            {
    +	            $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1{3,}$//;
    +	            $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1*([^a-zA-Z0-9 ])\2{3,}([^a-zA-Z0-9 ])\3*$//;
    +	            };
    +
    +
    +	        # Check for the start and end of code sections.  Note that this doesn't affect vertical line removal.
    +
    +	        if (!$inCodeSection &&
    +	        	$commentLines->[$index] =~ /^ *\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i )
    +	        	{
    +	        	$inCodeSection = 1;
    +	        	}
    +	        elsif ($inCodeSection &&
    +	        	    $commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
    +	        	 {
    +	        	 $inCodeSection = 0;
    +	        	 }
    +	        }
    +
    +
    +        $index++;
    +        };
    +
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Processing Functions
    +
    +
    +#
    +#   Function: RepairPackages
    +#
    +#   Recalculates the packages for all comment topics using the auto-topics and the scope record.  Call this *before* calling
    +#   <MergeAutoTopics()>.
    +#
    +#   Parameters:
    +#
    +#       autoTopics - A reference to the list of automatically generated <NaturalDocs::Parser::ParsedTopics>.
    +#       scopeRecord - A reference to an array of <NaturalDocs::Languages::Advanced::ScopeChanges>.
    +#
    +sub RepairPackages #(autoTopics, scopeRecord)
    +    {
    +    my ($self, $autoTopics, $scopeRecord) = @_;
    +
    +    my $topicIndex = 0;
    +    my $autoTopicIndex = 0;
    +    my $scopeIndex = 0;
    +
    +    my $topic = $parsedFile[0];
    +    my $autoTopic = $autoTopics->[0];
    +    my $scopeChange = $scopeRecord->[0];
    +
    +    my $currentPackage;
    +    my $inFakePackage;
    +
    +    while (defined $topic)
    +        {
    +        # First update the scope via the record if its defined and has the lowest line number.
    +        if (defined $scopeChange &&
    +            $scopeChange->LineNumber() <= $topic->LineNumber() &&
    +            (!defined $autoTopic || $scopeChange->LineNumber() <= $autoTopic->LineNumber()) )
    +            {
    +            $currentPackage = $scopeChange->Scope();
    +            $scopeIndex++;
    +            $scopeChange = $scopeRecord->[$scopeIndex];  # Will be undef when past end.
    +            $inFakePackage = undef;
    +            }
    +
    +        # Next try to end a fake scope with an auto topic if its defined and has the lowest line number.
    +        elsif (defined $autoTopic &&
    +                $autoTopic->LineNumber() <= $topic->LineNumber())
    +            {
    +            if ($inFakePackage)
    +                {
    +                $currentPackage = $autoTopic->Package();
    +                $inFakePackage = undef;
    +                };
    +
    +            $autoTopicIndex++;
    +            $autoTopic = $autoTopics->[$autoTopicIndex];  # Will be undef when past end.
    +            }
    +
    +
    +        # Finally try to handle the topic, since it has the lowest line number.  Check for Type() because headerless topics won't have
    +        # one.
    +        else
    +            {
    +            my $scope;
    +            if ($topic->Type())
    +                {  $scope = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope();  }
    +            else
    +                {  $scope = ::SCOPE_NORMAL();  };
    +
    +            if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +                {
    +                # They should already have the correct class and scope.
    +                $currentPackage = $topic->Package();
    +                $inFakePackage = 1;
    +                }
    +           else
    +                {
    +                # Fix the package of everything else.
    +
    +                # Note that the first function or variable topic to appear in a fake package will assume that package even if it turns out
    +                # to be incorrect in the actual code, since the topic will come before the auto-topic.  This will be corrected in
    +                # MergeAutoTopics().
    +
    +                $topic->SetPackage($currentPackage);
    +                };
    +
    +            $topicIndex++;
    +            $topic = $parsedFile[$topicIndex];  # Will be undef when past end.
    +            };
    +        };
    +
    +    };
    +
    +
    +#
    +#   Function: MergeAutoTopics
    +#
    +#   Merges the automatically generated topics into the file.  If an auto-topic matches an existing topic, it will have it's prototype
    +#   and package transferred.  If it doesn't, the auto-topic will be inserted into the list unless
    +#   <NaturalDocs::Settings->DocumentedOnly()> is set.  If an existing topic doesn't have a title, it's assumed to be a headerless
    +#   comment and will be merged with the next auto-topic or discarded.
    +#
    +#   Parameters:
    +#
    +#       language - The <NaturalDocs::Languages::Base>-derived class for the file.
    +#       autoTopics - A reference to the list of automatically generated topics.
    +#
    +sub MergeAutoTopics #(language, autoTopics)
    +    {
    +    my ($self, $language, $autoTopics) = @_;
    +
    +    my $topicIndex = 0;
    +    my $autoTopicIndex = 0;
    +
    +    # Keys are topic types, values are existence hashrefs of titles.
    +    my %topicsInLists;
    +
    +    while ($topicIndex < scalar @parsedFile && $autoTopicIndex < scalar @$autoTopics)
    +        {
    +        my $topic = $parsedFile[$topicIndex];
    +        my $autoTopic = $autoTopics->[$autoTopicIndex];
    +
    +        my $cleanTitle = $topic->Title();
    +        $cleanTitle =~ s/[\t ]*\([^\(]*$//;
    +
    +        # Add the auto-topic if it's higher in the file than the current topic.
    +        if ($autoTopic->LineNumber() < $topic->LineNumber())
    +            {
    +            if (exists $topicsInLists{$autoTopic->Type()} &&
    +                exists $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()})
    +                {
    +                # Remove it from the list so a second one with the same name will be added.
    +                delete $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()};
    +                }
    +            elsif (!NaturalDocs::Settings->DocumentedOnly())
    +                {
    +                splice(@parsedFile, $topicIndex, 0, $autoTopic);
    +                $topicIndex++;
    +                };
    +
    +            $autoTopicIndex++;
    +            }
    +
    +        # Remove a headerless topic if there's another topic between it and the next auto-topic.
    +        elsif (!$topic->Title() && $topicIndex + 1 < scalar @parsedFile &&
    +                $parsedFile[$topicIndex+1]->LineNumber() < $autoTopic->LineNumber())
    +            {
    +            splice(@parsedFile, $topicIndex, 1);
    +            }
    +
    +        # Transfer information if we have a match or a headerless topic.
    +        elsif ( !$topic->Title() ||
    +        		  $topic->Symbol() eq $autoTopic->Symbol() ||
    +        		  ( $topic->Type() == $autoTopic->Type() &&
    +        			( index($autoTopic->Title(), $cleanTitle) != -1 || index($cleanTitle, $autoTopic->Title()) != -1 ) ) )
    +            {
    +            $topic->SetType($autoTopic->Type());
    +            $topic->SetPrototype($autoTopic->Prototype());
    +            $topic->SetUsing($autoTopic->Using());
    +
    +            if (!$topic->Title())
    +                {  $topic->SetTitle($autoTopic->Title());  };
    +
    +            if (NaturalDocs::Topics->TypeInfo($topic->Type())->Scope() != ::SCOPE_START())
    +                {  $topic->SetPackage($autoTopic->Package());  }
    +            elsif ($autoTopic->Package() ne $topic->Package())
    +                {
    +                my @autoPackageIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($autoTopic->Package());
    +                my @packageIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($topic->Package());
    +
    +                while (scalar @autoPackageIdentifiers && $autoPackageIdentifiers[-1] eq $packageIdentifiers[-1])
    +                    {
    +                    pop @autoPackageIdentifiers;
    +                    pop @packageIdentifiers;
    +                    };
    +
    +                if (scalar @autoPackageIdentifiers)
    +                    {  $topic->SetPackage( NaturalDocs::SymbolString->Join(@autoPackageIdentifiers) );  };
    +                };
    +
    +            $topicIndex++;
    +            $autoTopicIndex++;
    +            }
    +
    +        # Extract topics in lists.
    +        elsif ($topic->IsList())
    +            {
    +            if (!exists $topicsInLists{$topic->Type()})
    +                {  $topicsInLists{$topic->Type()} = { };  };
    +
    +            my $body = $topic->Body();
    +
    +            while ($body =~ /<ds>([^<]+)<\/ds>/g)
    +                {  $topicsInLists{$topic->Type()}->{NaturalDocs::NDMarkup->RestoreAmpChars($1)} = 1;  };
    +
    +            $topicIndex++;
    +            }
    +
    +        # Otherwise there's no match.  Skip the topic.  The auto-topic will be added later.
    +        else
    +            {
    +            $topicIndex++;
    +            }
    +        };
    +
    +    # Add any auto-topics remaining.
    +    if (!NaturalDocs::Settings->DocumentedOnly())
    +    	{
    +	    while ($autoTopicIndex < scalar @$autoTopics)
    +	        {
    +	        my $autoTopic = $autoTopics->[$autoTopicIndex];
    +
    +	        if (exists $topicsInLists{$autoTopic->Type()} &&
    +	            exists $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()})
    +	            {
    +	            # Remove it from the list so a second one with the same name will be added.
    +	            delete $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()};
    +	            }
    +	        else
    +	            {
    +	            push(@parsedFile, $autoTopic);
    +	            };
    +
    +	        $autoTopicIndex++;
    +	        };
    +        };
    +   };
    +
    +
    +#
    +#   Function: RemoveRemainingHeaderlessTopics
    +#
    +#   After <MergeAutoTopics()> is done, this function removes any remaining headerless topics from the file.  If they don't merge
    +#   into anything, they're not valid topics.
    +#
    +sub RemoveRemainingHeaderlessTopics
    +    {
    +    my ($self) = @_;
    +
    +    my $index = 0;
    +    while ($index < scalar @parsedFile)
    +        {
    +        if ($parsedFile[$index]->Title())
    +            {  $index++;  }
    +        else
    +            {  splice(@parsedFile, $index, 1);  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: MakeAutoGroups
    +#
    +#   Creates group topics for files that do not have them.
    +#
    +sub MakeAutoGroups
    +    {
    +    my ($self) = @_;
    +
    +    # No groups only one topic.
    +    if (scalar @parsedFile < 2)
    +        {  return;  };
    +
    +    my $index = 0;
    +    my $startStretch = 0;
    +
    +    # Skip the first entry if its the page title.
    +    if (NaturalDocs::Topics->TypeInfo( $parsedFile[0]->Type() )->PageTitleIfFirst())
    +        {
    +        $index = 1;
    +        $startStretch = 1;
    +        };
    +
    +    # Make auto-groups for each stretch between scope-altering topics.
    +    while ($index < scalar @parsedFile)
    +        {
    +        my $scope = NaturalDocs::Topics->TypeInfo($parsedFile[$index]->Type())->Scope();
    +
    +        if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +            {
    +            if ($index > $startStretch)
    +                {  $index += $self->MakeAutoGroupsFor($startStretch, $index);  };
    +
    +            $startStretch = $index + 1;
    +            };
    +
    +        $index++;
    +        };
    +
    +    if ($index > $startStretch)
    +        {  $self->MakeAutoGroupsFor($startStretch, $index);  };
    +    };
    +
    +
    +#
    +#   Function: MakeAutoGroupsFor
    +#
    +#   Creates group topics for sections of files that do not have them.  A support function for <MakeAutoGroups()>.
    +#
    +#   Parameters:
    +#
    +#       startIndex - The index to start at.
    +#       endIndex - The index to end at.  Not inclusive.
    +#
    +#   Returns:
    +#
    +#       The number of group topics added.
    +#
    +sub MakeAutoGroupsFor #(startIndex, endIndex)
    +    {
    +    my ($self, $startIndex, $endIndex) = @_;
    +
    +    # No groups if any are defined already.
    +    for (my $i = $startIndex; $i < $endIndex; $i++)
    +        {
    +        if ($parsedFile[$i]->Type() eq ::TOPIC_GROUP())
    +            {  return 0;  };
    +        };
    +
    +
    +    use constant COUNT => 0;
    +    use constant TYPE => 1;
    +    use constant SECOND_TYPE => 2;
    +    use constant SIZE => 3;
    +
    +    # This is an array of ( count, type, secondType ) triples.  Count and Type will always be filled in; count is the number of
    +    # consecutive topics of type.  On the second pass, if small groups are combined secondType will be filled in.  There will not be
    +    # more than two types per group.
    +    my @groups;
    +    my $groupIndex = 0;
    +
    +
    +    # First pass: Determine all the groups.
    +
    +    my $i = $startIndex;
    +    my $currentType;
    +
    +    while ($i < $endIndex)
    +        {
    +        if (!defined $currentType || ($parsedFile[$i]->Type() ne $currentType && $parsedFile[$i]->Type() ne ::TOPIC_GENERIC()) )
    +            {
    +            if (defined $currentType)
    +                {  $groupIndex += SIZE;  };
    +
    +            $currentType = $parsedFile[$i]->Type();
    +
    +            $groups[$groupIndex + COUNT] = 1;
    +            $groups[$groupIndex + TYPE] = $currentType;
    +            }
    +        else
    +            {  $groups[$groupIndex + COUNT]++;  };
    +
    +        $i++;
    +        };
    +
    +
    +    # Second pass: Combine groups based on "noise".  Noise means types go from A to B to A at least once, and there are at least
    +    # two groups in a row with three or less, and at least one of those groups is two or less.  So 3, 3, 3 doesn't count as noise, but
    +    # 3, 2, 3 does.
    +
    +    $groupIndex = 0;
    +
    +    # While there are at least three groups left...
    +    while ($groupIndex < scalar @groups - (2 * SIZE))
    +        {
    +        # If the group two places in front of this one has the same type...
    +        if ($groups[$groupIndex + (2 * SIZE) + TYPE] eq $groups[$groupIndex + TYPE])
    +            {
    +            # It means we went from A to B to A, which partially qualifies as noise.
    +
    +            my $firstType = $groups[$groupIndex + TYPE];
    +            my $secondType = $groups[$groupIndex + SIZE + TYPE];
    +
    +            if (NaturalDocs::Topics->TypeInfo($firstType)->CanGroupWith($secondType) ||
    +                NaturalDocs::Topics->TypeInfo($secondType)->CanGroupWith($firstType))
    +                {
    +                my $hasNoise;
    +
    +                my $hasThrees;
    +                my $hasTwosOrOnes;
    +
    +                my $endIndex = $groupIndex;
    +
    +                while ($endIndex < scalar @groups &&
    +                         ($groups[$endIndex + TYPE] eq $firstType || $groups[$endIndex + TYPE] eq $secondType))
    +                    {
    +                    if ($groups[$endIndex + COUNT] > 3)
    +                        {
    +                        # They must be consecutive to count.
    +                        $hasThrees = 0;
    +                        $hasTwosOrOnes = 0;
    +                        }
    +                    elsif ($groups[$endIndex + COUNT] == 3)
    +                        {
    +                        $hasThrees = 1;
    +
    +                        if ($hasTwosOrOnes)
    +                            {  $hasNoise = 1;  };
    +                        }
    +                    else # < 3
    +                        {
    +                        if ($hasThrees || $hasTwosOrOnes)
    +                            {  $hasNoise = 1;  };
    +
    +                        $hasTwosOrOnes = 1;
    +                        };
    +
    +                    $endIndex += SIZE;
    +                    };
    +
    +                if (!$hasNoise)
    +                    {
    +                    $groupIndex = $endIndex - SIZE;
    +                    }
    +                else # hasNoise
    +                    {
    +                    $groups[$groupIndex + SECOND_TYPE] = $secondType;
    +
    +                    for (my $noiseIndex = $groupIndex + SIZE; $noiseIndex < $endIndex; $noiseIndex += SIZE)
    +                        {
    +                        $groups[$groupIndex + COUNT] += $groups[$noiseIndex + COUNT];
    +                        };
    +
    +                    splice(@groups, $groupIndex + SIZE, $endIndex - $groupIndex - SIZE);
    +
    +                    $groupIndex += SIZE;
    +                    };
    +                }
    +
    +            else # They can't group together
    +                {
    +                $groupIndex += SIZE;
    +                };
    +            }
    +
    +        else
    +            {  $groupIndex += SIZE;  };
    +        };
    +
    +
    +    # Finally, create group topics for the parsed file.
    +
    +    $groupIndex = 0;
    +    $i = $startIndex;
    +
    +    while ($groupIndex < scalar @groups)
    +        {
    +        if ($groups[$groupIndex + TYPE] ne ::TOPIC_GENERIC())
    +            {
    +            my $topic = $parsedFile[$i];
    +            my $title = NaturalDocs::Topics->NameOfType($groups[$groupIndex + TYPE], 1);
    +
    +            if (defined $groups[$groupIndex + SECOND_TYPE])
    +                {  $title .= ' and ' . NaturalDocs::Topics->NameOfType($groups[$groupIndex + SECOND_TYPE], 1);  };
    +
    +            splice(@parsedFile, $i, 0, NaturalDocs::Parser::ParsedTopic->New(::TOPIC_GROUP(),
    +                                                                                                            $title,
    +                                                                                                            $topic->Package(), $topic->Using(),
    +                                                                                                            undef, undef, undef,
    +                                                                                                            $topic->LineNumber()) );
    +            $i++;
    +            };
    +
    +        $i += $groups[$groupIndex + COUNT];
    +        $groupIndex += SIZE;
    +        };
    +
    +    return (scalar @groups / SIZE);
    +    };
    +
    +
    +#
    +#   Function: AddToClassHierarchy
    +#
    +#   Adds any class topics to the class hierarchy, since they may not have been called with <OnClass()> if they didn't match up to
    +#   an auto-topic.
    +#
    +sub AddToClassHierarchy
    +    {
    +    my ($self) = @_;
    +
    +    foreach my $topic (@parsedFile)
    +        {
    +        if ($topic->Type() && NaturalDocs::Topics->TypeInfo( $topic->Type() )->ClassHierarchy())
    +            {
    +            if ($topic->IsList())
    +                {
    +                my $body = $topic->Body();
    +
    +                while ($body =~ /<ds>([^<]+)<\/ds>/g)
    +                    {
    +                    $self->OnClass( NaturalDocs::SymbolString->FromText( NaturalDocs::NDMarkup->RestoreAmpChars($1) ) );
    +                    };
    +                }
    +            else
    +                {
    +                $self->OnClass($topic->Package());
    +                };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: AddPackageDelineators
    +#
    +#   Adds section and class topics to make sure the package is correctly represented in the documentation.  Should be called last in
    +#   this process.
    +#
    +sub AddPackageDelineators
    +    {
    +    my ($self) = @_;
    +
    +    my $index = 0;
    +    my $currentPackage;
    +
    +    # Values are the arrayref [ title, type ];
    +    my %usedPackages;
    +
    +    while ($index < scalar @parsedFile)
    +        {
    +        my $topic = $parsedFile[$index];
    +
    +        if ($topic->Package() ne $currentPackage)
    +            {
    +            $currentPackage = $topic->Package();
    +            my $scopeType = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope();
    +
    +            if ($scopeType == ::SCOPE_START())
    +                {
    +                $usedPackages{$currentPackage} = [ $topic->Title(), $topic->Type() ];
    +                }
    +            elsif ($scopeType == ::SCOPE_END())
    +                {
    +                my $newTopic;
    +
    +                if (!defined $currentPackage)
    +                    {
    +                    $newTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_SECTION(), 'Global',
    +                                                                                                   undef, undef,
    +                                                                                                   undef, undef, undef,
    +                                                                                                   $topic->LineNumber(), undef);
    +                    }
    +                else
    +                    {
    +                    my ($title, $body, $summary, $type);
    +                    my @packageIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($currentPackage);
    +
    +                    if (exists $usedPackages{$currentPackage})
    +                        {
    +                        $title = $usedPackages{$currentPackage}->[0];
    +                        $type = $usedPackages{$currentPackage}->[1];
    +                        $body = '<p>(continued)</p>';
    +                        $summary = '(continued)';
    +                        }
    +                    else
    +                        {
    +                        $title = join($language->PackageSeparator(), @packageIdentifiers);
    +                        $type = ::TOPIC_CLASS();
    +
    +                        # Body and summary stay undef.
    +
    +                        $usedPackages{$currentPackage} = $title;
    +                        };
    +
    +                    my @titleIdentifiers = NaturalDocs::SymbolString->IdentifiersOf( NaturalDocs::SymbolString->FromText($title) );
    +                    for (my $i = 0; $i < scalar @titleIdentifiers; $i++)
    +                        {  pop @packageIdentifiers;  };
    +
    +                    $newTopic = NaturalDocs::Parser::ParsedTopic->New($type, $title,
    +                                                                                                   NaturalDocs::SymbolString->Join(@packageIdentifiers), undef,
    +                                                                                                   undef, $summary, $body,
    +                                                                                                   $topic->LineNumber(), undef);
    +                    }
    +
    +                splice(@parsedFile, $index, 0, $newTopic);
    +                $index++;
    +                }
    +            };
    +
    +        $index++;
    +        };
    +    };
    +
    +
    +#
    +#   Function: BreakLists
    +#
    +#   Breaks list topics into individual topics.
    +#
    +sub BreakLists
    +    {
    +    my $self = shift;
    +
    +    my $index = 0;
    +
    +    while ($index < scalar @parsedFile)
    +        {
    +        my $topic = $parsedFile[$index];
    +
    +        if ($topic->IsList() && NaturalDocs::Topics->TypeInfo( $topic->Type() )->BreakLists())
    +            {
    +            my $body = $topic->Body();
    +
    +            my @newTopics;
    +            my $newBody;
    +
    +            my $bodyIndex = 0;
    +
    +            for (;;)
    +                {
    +                my $startList = index($body, '<dl>', $bodyIndex);
    +
    +                if ($startList == -1)
    +                    {  last;  };
    +
    +                $newBody .= substr($body, $bodyIndex, $startList - $bodyIndex);
    +
    +                my $endList = index($body, '</dl>', $startList);
    +                my $listBody = substr($body, $startList, $endList - $startList);
    +
    +                while ($listBody =~ /<ds>([^<]+)<\/ds><dd>(.*?)<\/dd>/g)
    +                    {
    +                    my ($symbol, $description) = ($1, $2);
    +
    +                    push @newTopics, NaturalDocs::Parser::ParsedTopic->New( $topic->Type(), $symbol, $topic->Package(),
    +                                                                                                            $topic->Using(), undef,
    +                                                                                                            $self->GetSummaryFromDescriptionList($description),
    +                                                                                                            '<p>' . $description .  '</p>', $topic->LineNumber(),
    +                                                                                                            undef );
    +                    };
    +
    +                $bodyIndex = $endList + 5;
    +                };
    +
    +            $newBody .= substr($body, $bodyIndex);
    +
    +            # Remove trailing headings.
    +            $newBody =~ s/(?:<h>[^<]+<\/h>)+$//;
    +
    +            # Remove empty headings.
    +            $newBody =~ s/(?:<h>[^<]+<\/h>)+(<h>[^<]+<\/h>)/$1/g;
    +
    +            if ($newBody)
    +                {
    +                unshift @newTopics, NaturalDocs::Parser::ParsedTopic->New( ::TOPIC_GROUP(), $topic->Title(), $topic->Package(),
    +                                                                                                          $topic->Using(), undef,
    +                                                                                                          $self->GetSummaryFromBody($newBody), $newBody,
    +                                                                                                          $topic->LineNumber(), undef );
    +                };
    +
    +            splice(@parsedFile, $index, 1, @newTopics);
    +
    +            $index += scalar @newTopics;
    +            }
    +
    +        else # not a list
    +            {  $index++;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: GetSummaryFromBody
    +#
    +#   Returns the summary text from the topic body.
    +#
    +#   Parameters:
    +#
    +#       body - The complete topic body, in <NDMarkup>.
    +#
    +#   Returns:
    +#
    +#       The topic summary, or undef if none.
    +#
    +sub GetSummaryFromBody #(body)
    +    {
    +    my ($self, $body) = @_;
    +
    +    my $summary;
    +
    +    # Extract the first sentence from the leading paragraph, if any.  We'll tolerate a single header beforehand, but nothing else.
    +
    +    if ($body =~ /^(?:<h>[^<]*<\/h>)?<p>(.*?)(<\/p>|[\.\!\?](?:[\)\}\'\ ]|&quot;|&gt;))/x)
    +        {
    +        $summary = $1;
    +
    +        if ($2 ne '</p>')
    +            {  $summary .= $2;  };
    +        };
    +
    +    return $summary;
    +    };
    +
    +
    +#
    +#   Function: GetSummaryFromDescriptionList
    +#
    +#   Returns the summary text from a description list entry.
    +#
    +#   Parameters:
    +#
    +#       description - The description in <NDMarkup>.  Should be the content between the <dd></dd> tags only.
    +#
    +#   Returns:
    +#
    +#       The description summary, or undef if none.
    +#
    +sub GetSummaryFromDescriptionList #(description)
    +    {
    +    my ($self, $description) = @_;
    +
    +    my $summary;
    +
    +    if ($description =~ /^(.*?)($|[\.\!\?](?:[\)\}\'\ ]|&quot;|&gt;))/)
    +        {  $summary = $1 . $2;  };
    +
    +    return $summary;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Parser/JavaDoc.pm b/vendor/naturaldocs/Modules/NaturalDocs/Parser/JavaDoc.pm
    new file mode 100644
    index 000000000..0e8bd4bd0
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Parser/JavaDoc.pm
    @@ -0,0 +1,465 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Parser::JavaDoc
    +#
    +###############################################################################
    +#
    +#   A package for translating JavaDoc topics into Natural Docs.
    +#
    +#   Supported tags:
    +#
    +#       - @param
    +#       - @author
    +#       - @deprecated
    +#       - @code, @literal (doesn't change font)
    +#       - @exception, @throws (doesn't link to class)
    +#       - @link, @linkplain (doesn't change font)
    +#       - @return, @returns
    +#       - @see
    +#       - @since
    +#       - @value (shown as link instead of replacement)
    +#       - @version
    +#
    +#   Stripped tags:
    +#
    +#       - @inheritDoc
    +#       - @serial, @serialField, @serialData
    +#       - All other block level tags.
    +#
    +#   Unsupported tags:
    +#
    +#       These will appear literally in the output because I cannot handle them easily.
    +#
    +#       - @docRoot
    +#       - Any other tags not mentioned
    +#
    +#   Supported HTML:
    +#
    +#       - p
    +#       - b, i, u
    +#       - pre
    +#       - a href
    +#       - ol, ul, li (ol gets converted to ul)
    +#       - gt, lt, amp, quot, nbsp entities
    +#
    +#   Stripped HTML:
    +#
    +#       - code
    +#       - HTML comments
    +#
    +#   Unsupported HTML:
    +#
    +#       These will appear literally in the output because I cannot handle them easily.
    +#
    +#       - Any tags with additional properties other than a href.  (ex. <p class=Something>)
    +#       - Any other tags not mentioned
    +#
    +#   Reference:
    +#
    +#       http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Parser::JavaDoc;
    +
    +
    +#
    +#   hash: blockTags
    +#   An existence hash of the all-lowercase JavaDoc block tags, not including the @.
    +#
    +my %blockTags = ( 'param' => 1, 'author' => 1, 'deprecated' => 1, 'exception' => 1, 'return' => 1, 'see' => 1,
    +                             'serial' => 1, 'serialfield' => 1, 'serialdata' => 1, 'since' => 1, 'throws' => 1, 'version' => 1,
    +                             'returns' => 1 );
    +
    +#
    +#   hash: inlineTags
    +#   An existence hash of the all-lowercase JavaDoc inline tags, not including the @.
    +#
    +my %inlineTags = ( 'inheritdoc' => 1, 'docroot' => 1, 'code' => 1, 'literal' => 1, 'link' => 1, 'linkplain' => 1, 'value' => 1 );
    +
    +
    +##
    +#   Examines the comment and returns whether it is *definitely* JavaDoc content, i.e. is owned by this package.
    +#
    +#   Parameters:
    +#
    +#       commentLines - An arrayref of the comment lines.  Must have been run through <NaturalDocs::Parser->CleanComment()>.
    +#       isJavaDoc - Whether the comment is JavaDoc styled.  This doesn't necessarily mean it has JavaDoc content.
    +#
    +#   Returns:
    +#
    +#       Whether the comment is *definitely* JavaDoc content.
    +#
    +sub IsMine #(string[] commentLines, bool isJavaDoc)
    +    {
    +    my ($self, $commentLines, $isJavaDoc) = @_;
    +
    +    if (!$isJavaDoc)
    +        {  return undef;  };
    +
    +    for (my $line = 0; $line < scalar @$commentLines; $line++)
    +        {
    +        if ($commentLines->[$line] =~ /^ *@([a-z]+) /i && exists $blockTags{$1} ||
    +            $commentLines->[$line] =~ /\{@([a-z]+) /i && exists $inlineTags{$1})
    +            {
    +            return 1;
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +##
    +#   Parses the JavaDoc-syntax comment and adds it to the parsed topic list.
    +#
    +#   Parameters:
    +#
    +#       commentLines - An arrayref of the comment lines.  Must have been run through <NaturalDocs::Parser->CleanComment()>.
    +#                               *The original memory will be changed.*
    +#       isJavaDoc - Whether the comment is JavaDoc styled.  This doesn't necessarily mean it has JavaDoc content.
    +#       lineNumber - The line number of the first of the comment lines.
    +#       parsedTopics - A reference to the array where any new <NaturalDocs::Parser::ParsedTopics> should be placed.
    +#
    +#   Returns:
    +#
    +#       The number of parsed topics added to the array, which in this case will always be one.
    +#
    +sub ParseComment #(string[] commentLines, bool isJavaDoc, int lineNumber, ParsedTopics[]* parsedTopics)
    +    {
    +    my ($self, $commentLines, $isJavaDoc, $lineNumber, $parsedTopics) = @_;
    +
    +
    +    # Stage one: Before block level tags.
    +
    +    my $i = 0;
    +    my $output;
    +    my $unformattedText;
    +    my $inCode;
    +    my $sharedCodeIndent;
    +
    +    while ($i < scalar @$commentLines &&
    +              !($commentLines->[$i] =~ /^ *@([a-z]+) /i && exists $blockTags{$1}) )
    +        {
    +        my $line = $self->ConvertAmpChars($commentLines->[$i]);
    +        my @tokens = split(/(&lt;\/?pre&gt;)/, $line);
    +
    +        foreach my $token (@tokens)
    +            {
    +            if ($token =~ /^&lt;pre&gt;$/i)
    +                {
    +                if (!$inCode && $unformattedText)
    +                    {
    +                    $output .= '<p>' . $self->FormatText($unformattedText, 1) . '</p>';
    +                    };
    +
    +                $inCode = 1;
    +                $unformattedText = undef;
    +                }
    +            elsif ($token =~ /^&lt;\/pre&gt;$/i)
    +                {
    +                if ($inCode && $unformattedText)
    +                    {
    +                    $unformattedText =~ s/^ {$sharedCodeIndent}//mg;
    +                    $unformattedText =~ s/\n{3,}/\n\n/g;
    +                    $unformattedText =~ s/\n+$//;
    +                    $output .= '<code type="anonymous">' . $unformattedText . '</code>';
    +
    +                    $sharedCodeIndent = undef;
    +                    };
    +
    +                $inCode = 0;
    +                $unformattedText = undef;
    +                }
    +            elsif (length($token))
    +                {
    +                if (!$inCode)
    +                    {
    +                    $token =~ s/^ +//;
    +                    if ($unformattedText)
    +                        {  $unformattedText .= ' ';  };
    +                    }
    +                else
    +                    {
    +                    $token =~ /^( *)/;
    +                    my $indent = length($1);
    +
    +                    if (!defined $sharedCodeIndent || $indent < $sharedCodeIndent)
    +                        {  $sharedCodeIndent = $indent;  };
    +                    };
    +
    +                $unformattedText .= $token;
    +                };
    +            };
    +
    +        if ($inCode && $unformattedText)
    +            {  $unformattedText .= "\n";  };
    +
    +        $i++;
    +        };
    +
    +    if ($unformattedText)
    +        {
    +        if ($inCode)
    +            {
    +            $unformattedText =~ s/^ {$sharedCodeIndent}//mg;
    +            $unformattedText =~ s/\n{3,}/\n\n/g;
    +            $unformattedText =~ s/\n+$//;
    +            $output .= '<code type="anonymous">' . $unformattedText . '</code>';
    +            }
    +        else
    +            {  $output .= '<p>' . $self->FormatText($unformattedText, 1) . '</p>';  };
    +
    +        $unformattedText = undef;
    +        };
    +
    +
    +    # Stage two: Block level tags.
    +
    +    my ($keyword, $value, $unformattedTextPtr, $unformattedTextCloser);
    +    my ($params, $authors, $deprecation, $throws, $returns, $seeAlso, $since, $version);
    +
    +
    +    while ($i < scalar @$commentLines)
    +        {
    +        my $line = $self->ConvertAmpChars($commentLines->[$i]);
    +        $line =~ s/^ +//;
    +
    +        if ($line =~ /^@([a-z]+) ?(.*)$/i)
    +            {
    +            ($keyword, $value) = (lc($1), $2);
    +
    +            # Process the previous one, if any.
    +            if ($unformattedText)
    +                {
    +                $$unformattedTextPtr .= $self->FormatText($unformattedText) . $unformattedTextCloser;
    +                $unformattedText = undef;
    +                };
    +
    +            if ($keyword eq 'param')
    +                {
    +                $value =~ /^([a-z0-9_]+) *(.*)$/i;
    +
    +                $params .= '<de>' . $1 . '</de><dd>';
    +                $unformattedText = $2;
    +
    +                $unformattedTextPtr = \$params;
    +                $unformattedTextCloser = '</dd>';
    +                }
    +            elsif ($keyword eq 'exception' || $keyword eq 'throws')
    +                {
    +                $value =~ /^([a-z0-9_]+) *(.*)$/i;
    +
    +                $throws .= '<de>' . $1 . '</de><dd>';
    +                $unformattedText = $2;
    +
    +                $unformattedTextPtr = \$throws;
    +                $unformattedTextCloser = '</dd>';
    +                }
    +            elsif ($keyword eq 'return' || $keyword eq 'returns')
    +                {
    +                if ($returns)
    +                    {  $returns .= ' ';  };
    +
    +                $unformattedText = $value;
    +                $unformattedTextPtr = \$returns;
    +                $unformattedTextCloser = undef;
    +                }
    +            elsif ($keyword eq 'author')
    +                {
    +                if ($authors)
    +                    {  $authors .= ', ';  };
    +
    +                $unformattedText = $value;
    +                $unformattedTextPtr = \$authors;
    +                $unformattedTextCloser = undef;
    +                }
    +            elsif ($keyword eq 'deprecated')
    +                {
    +                if ($deprecation)
    +                    {  $deprecation .= ' ';  };
    +
    +                $unformattedText = $value;
    +                $unformattedTextPtr = \$deprecation;
    +                $unformattedTextCloser = undef;
    +                }
    +            elsif ($keyword eq 'since')
    +                {
    +                if ($since)
    +                    {  $since .= ', ';  };
    +
    +                $unformattedText = $value;
    +                $unformattedTextPtr = \$since;
    +                $unformattedTextCloser = undef;
    +                }
    +            elsif ($keyword eq 'version')
    +                {
    +                if ($version)
    +                    {  $version .= ', ';  };
    +
    +                $unformattedText = $value;
    +                $unformattedTextPtr = \$version;
    +                $unformattedTextCloser = undef;
    +                }
    +            elsif ($keyword eq 'see')
    +                {
    +                if ($seeAlso)
    +                    {  $seeAlso .= ', ';  };
    +
    +                $unformattedText = undef;
    +
    +                if ($value =~ /^&(?:quot|lt);/i)
    +                    {  $seeAlso .= $self->FormatText($value);  }
    +                else
    +                    {  $seeAlso .= $self->ConvertLink($value);  };
    +                };
    +
    +            # Everything else will be skipped.
    +            }
    +        elsif ($unformattedText)
    +            {
    +            $unformattedText .= ' ' . $line;
    +            };
    +
    +        $i++;
    +        };
    +
    +    if ($unformattedText)
    +        {
    +        $$unformattedTextPtr .= $self->FormatText($unformattedText) . $unformattedTextCloser;
    +        $unformattedText = undef;
    +        };
    +
    +    if ($params)
    +        {  $output .= '<h>Parameters</h><dl>' . $params . '</dl>';  };
    +    if ($returns)
    +        {  $output .= '<h>Returns</h><p>' . $returns . '</p>';  };
    +    if ($throws)
    +        {  $output .= '<h>Throws</h><dl>' . $throws . '</dl>';  };
    +    if ($since)
    +        {  $output .= '<h>Since</h><p>' . $since . '</p>';  };
    +    if ($version)
    +        {  $output .= '<h>Version</h><p>' . $version . '</p>';  };
    +    if ($deprecation)
    +        {  $output .= '<h>Deprecated</h><p>' . $deprecation . '</p>';  };
    +    if ($authors)
    +        {  $output .= '<h>Author</h><p>' . $authors . '</p>';  };
    +    if ($seeAlso)
    +        {  $output .= '<h>See Also</h><p>' . $seeAlso . '</p>';  };
    +
    +
    +    # Stage three: Build the parsed topic.
    +
    +    my $summary = NaturalDocs::Parser->GetSummaryFromBody($output);
    +
    +    push @$parsedTopics, NaturalDocs::Parser::ParsedTopic->New(undef, undef, undef, undef, undef, $summary,
    +                                                                                                $output, $lineNumber, undef);
    +    return 1;
    +    };
    +
    +
    +##
    +#   Translates any inline tags or HTML codes to <NDMarkup> and returns it.
    +#
    +sub FormatText #(string text, bool inParagraph)
    +    {
    +    my ($self, $text, $inParagraph) = @_;
    +
    +    # JavaDoc Literal
    +
    +    $text =~ s/\{\@(?:code|literal) ([^\}]*)\}/$self->ConvertAmpChars($1)/gie;
    +
    +
    +    # HTML
    +
    +    $text =~ s/&lt;b&gt;(.*?)&lt;\/b&gt;/<b>$1<\/b>/gi;
    +    $text =~ s/&lt;i&gt;(.*?)&lt;\/i&gt;/<i>$1<\/i>/gi;
    +    $text =~ s/&lt;u&gt;(.*?)&lt;\/u&gt;/<u>$1<\/u>/gi;
    +
    +    $text =~ s/&lt;code&gt;(.*?)&lt;\/code&gt;/$1/gi;
    +
    +    $text =~ s/&lt;ul.*?&gt;(.*?)&lt;\/ul&gt;/<ul>$1<\/ul>/gi;
    +    $text =~ s/&lt;ol.*?&gt;(.*?)&lt;\/ol&gt;/<ul>$1<\/ul>/gi;
    +    $text =~ s/&lt;li.*?&gt;(.*?)&lt;\/li&gt;/<li>$1<\/li>/gi;
    +
    +    $text =~ s/&lt;!--.*?--&gt;//gi;
    +
    +    $text =~ s/&lt;\/p&gt;//gi;
    +    $text =~ s/^&lt;p&gt;//i;
    +    if ($inParagraph)
    +        {  $text =~ s/&lt;p&gt;/<\/p><p>/gi;  }
    +    else
    +        {  $text =~ s/&lt;p&gt;//gi;  };
    +
    +    $text =~ s/&lt;a href=&quot;mailto:(.*?)&quot;.*?&gt;(.*?)&lt;\/a&gt;/$self->MakeEMailLink($1, $2)/gie;
    +    $text =~ s/&lt;a href=&quot;(.*?)&quot;.*?&gt;(.*?)&lt;\/a&gt;/$self->MakeURLLink($1, $2)/gie;
    +
    +    $text =~ s/&amp;nbsp;/ /gi;
    +    $text =~ s/&amp;amp;/&amp;/gi;
    +    $text =~ s/&amp;gt;/&gt;/gi;
    +    $text =~ s/&amp;lt;/&lt;/gi;
    +    $text =~ s/&amp;quot;/&quot;/gi;
    +
    +
    +
    +    # JavaDoc
    +
    +    $text =~ s/\{\@inheritdoc\}//gi;
    +    $text =~ s/\{\@(?:linkplain|link|value) ([^\}]*)\}/$self->ConvertLink($1)/gie;
    +
    +    return $text;
    +    };
    +
    +
    +sub ConvertAmpChars #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ s/&/&amp;/g;
    +    $text =~ s/</&lt;/g;
    +    $text =~ s/>/&gt;/g;
    +    $text =~ s/"/&quot;/g;
    +
    +    return $text;
    +    };
    +
    +sub ConvertLink #(text)
    +    {
    +    my ($self, $text) = @_;
    +
    +    $text =~ /^ *([a-z0-9\_\.\:\#]+(?:\([^\)]*\))?) *(.*)$/i;
    +    my ($target, $label) = ($1, $2);
    +
    +    # Convert the anchor to part of the link, but remove it altogether if it's the beginning of the link.
    +    $target =~ s/^\#//;
    +    $target =~ s/\#/\./;
    +
    +    $label =~ s/ +$//;
    +
    +    if (!length $label)
    +        {  return '<link target="' . $target . '" name="' . $target . '" original="' . $target . '">';  }
    +    else
    +        {  return '<link target="' . $target . '" name="' . $label . '" original="' . $label . ' (' . $target . ')">';  };
    +    };
    +
    +sub MakeURLLink #(target, text)
    +    {
    +    my ($self, $target, $text) = @_;
    +    return '<url target="' . $target . '" name="' . $text . '">';
    +    };
    +
    +sub MakeEMailLink #(target, text)
    +    {
    +    my ($self, $target, $text) = @_;
    +    return '<email target="' . $target . '" name="' . $text . '">';
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Parser/Native.pm b/vendor/naturaldocs/Modules/NaturalDocs/Parser/Native.pm
    new file mode 100644
    index 000000000..39d8663b0
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Parser/Native.pm
    @@ -0,0 +1,1073 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Parser::Native
    +#
    +###############################################################################
    +#
    +#   A package that converts comments from Natural Docs' native format into <NaturalDocs::Parser::ParsedTopic> objects.
    +#   Unlike most second-level packages, these are packages and not object classes.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Parser::Native;
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +# Return values of TagType().  Not documented here.
    +use constant POSSIBLE_OPENING_TAG => 1;
    +use constant POSSIBLE_CLOSING_TAG => 2;
    +use constant NOT_A_TAG => 3;
    +
    +
    +#
    +#   var: package
    +#
    +#   A <SymbolString> representing the package normal topics will be a part of at the current point in the file.  This is a package variable
    +#   because it needs to be reserved between function calls.
    +#
    +my $package;
    +
    +#
    +#   hash: functionListIgnoredHeadings
    +#
    +#   An existence hash of all the headings that prevent the parser from creating function list symbols.  Whenever one of
    +#   these headings are used in a function list topic, symbols are not created from definition lists until the next heading.  The keys
    +#   are in all lowercase.
    +#
    +my %functionListIgnoredHeadings = ( 'parameters' => 1,
    +                                                       'parameter' => 1,
    +                                                       'params' => 1,
    +                                                       'param' => 1,
    +                                                       'arguments' => 1,
    +                                                       'argument' => 1,
    +                                                       'args' => 1,
    +                                                       'arg' => 1 );
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +
    +
    +#
    +#   Function: Start
    +#
    +#   This will be called whenever a file is about to be parsed.  It allows the package to reset its internal state.
    +#
    +sub Start
    +    {
    +    my ($self) = @_;
    +    $package = undef;
    +    };
    +
    +
    +#
    +#   Function: IsMine
    +#
    +#   Examines the comment and returns whether it is *definitely* Natural Docs content, i.e. it is owned by this package.  Note
    +#   that a comment can fail this function and still be interpreted as a Natural Docs content, for example a JavaDoc-styled comment
    +#   that doesn't have header lines but no JavaDoc tags either.
    +#
    +#   Parameters:
    +#
    +#       commentLines - An arrayref of the comment lines.  Must have been run through <NaturalDocs::Parser->CleanComment()>.
    +#       isJavaDoc - Whether the comment was JavaDoc-styled.
    +#
    +#   Returns:
    +#
    +#       Whether the comment is *definitely* Natural Docs content.
    +#
    +sub IsMine #(string[] commentLines, bool isJavaDoc)
    +    {
    +    my ($self, $commentLines, $isJavaDoc) = @_;
    +
    +    # Skip to the first line with content.
    +    my $line = 0;
    +
    +    while ($line < scalar @$commentLines && !length $commentLines->[$line])
    +        {  $line++;  };
    +
    +    return $self->ParseHeaderLine($commentLines->[$line]);
    +    };
    +
    +
    +
    +#
    +#   Function: ParseComment
    +#
    +#   This will be called whenever a comment capable of containing Natural Docs content is found.
    +#
    +#   Parameters:
    +#
    +#       commentLines - An arrayref of the comment lines.  Must have been run through <NaturalDocs::Parser->CleanComment()>.
    +#                               *The original memory will be changed.*
    +#       isJavaDoc - Whether the comment is JavaDoc styled.
    +#       lineNumber - The line number of the first of the comment lines.
    +#       parsedTopics - A reference to the array where any new <NaturalDocs::Parser::ParsedTopics> should be placed.
    +#
    +#   Returns:
    +#
    +#       The number of parsed topics added to the array, or zero if none.
    +#
    +sub ParseComment #(commentLines, isJavaDoc, lineNumber, parsedTopics)
    +    {
    +    my ($self, $commentLines, $isJavaDoc, $lineNumber, $parsedTopics) = @_;
    +
    +    my $topicCount = 0;
    +    my $prevLineBlank = 1;
    +    my $inCodeSection = 0;
    +
    +    my ($type, $scope, $isPlural, $title, $symbol);
    +    #my $package;  # package variable.
    +    my ($newKeyword, $newTitle);
    +
    +    my $index = 0;
    +
    +    my $bodyStart = 0;
    +    my $bodyEnd = 0;  # Not inclusive.
    +
    +    while ($index < scalar @$commentLines)
    +        {
    +        # Everything but leading whitespace was removed beforehand.
    +
    +        # If we're in a code section...
    +        if ($inCodeSection)
    +            {
    +            if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
    +                {  $inCodeSection = undef;  };
    +
    +            $prevLineBlank = 0;
    +            $bodyEnd++;
    +            }
    +
    +        # If the line is empty...
    +        elsif (!length($commentLines->[$index]))
    +            {
    +            $prevLineBlank = 1;
    +
    +            if ($topicCount)
    +                {  $bodyEnd++;  };
    +            }
    +
    +        # If the line has a recognized header and the previous line is blank...
    +        elsif ($prevLineBlank && (($newKeyword, $newTitle) = $self->ParseHeaderLine($commentLines->[$index])) )
    +            {
    +            # Process the previous one, if any.
    +
    +            if ($topicCount)
    +                {
    +                if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +                    {  $package = undef;  };
    +
    +                my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
    +                my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
    +                push @$parsedTopics, $newTopic;
    +
    +                $package = $newTopic->Package();
    +                };
    +
    +            $title = $newTitle;
    +
    +            my $typeInfo;
    +            ($type, $typeInfo, $isPlural) = NaturalDocs::Topics->KeywordInfo($newKeyword);
    +            $scope = $typeInfo->Scope();
    +
    +            $bodyStart = $index + 1;
    +            $bodyEnd = $index + 1;
    +
    +            $topicCount++;
    +
    +            $prevLineBlank = 0;
    +            }
    +
    +        # If we're on a non-empty, non-header line of a JavaDoc-styled comment and we haven't started a topic yet...
    +        elsif ($isJavaDoc && !$topicCount)
    +            {
    +            $type = undef;
    +            $scope = ::SCOPE_NORMAL();  # The scope repair and topic merging processes will handle if this is a class topic.
    +            $isPlural = undef;
    +            $title = undef;
    +            $symbol = undef;
    +
    +            $bodyStart = $index;
    +            $bodyEnd = $index + 1;
    +
    +            $topicCount++;
    +
    +            $prevLineBlank = undef;
    +            }
    +
    +        # If we're on a normal content line within a topic
    +        elsif ($topicCount)
    +            {
    +            $prevLineBlank = 0;
    +            $bodyEnd++;
    +
    +            if ($commentLines->[$index] =~ /^ *\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i)
    +                {  $inCodeSection = 1;  };
    +            };
    +
    +
    +        $index++;
    +        };
    +
    +
    +    # Last one, if any.  This is the only one that gets the prototypes.
    +    if ($topicCount)
    +        {
    +        if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
    +            {  $package = undef;  };
    +
    +        my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
    +        my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
    +        push @$parsedTopics, $newTopic;
    +        $topicCount++;
    +
    +        $package = $newTopic->Package();
    +        };
    +
    +    return $topicCount;
    +    };
    +
    +
    +#
    +#   Function: ParseHeaderLine
    +#
    +#   If the passed line is a topic header, returns the array ( keyword, title ).  Otherwise returns an empty array.
    +#
    +sub ParseHeaderLine #(line)
    +    {
    +    my ($self, $line) = @_;
    +
    +    if ($line =~ /^ *([a-z0-9 ]*[a-z0-9]): +(.*)$/i)
    +        {
    +        my ($keyword, $title) = ($1, $2);
    +
    +        # We need to do it this way because if you do "if (ND:T->KeywordInfo($keyword)" and the last element of the array it
    +        # returns is false, the statement is false.  That is really retarded, but there it is.
    +        my ($type, undef, undef) = NaturalDocs::Topics->KeywordInfo($keyword);
    +
    +        if ($type)
    +            {  return ($keyword, $title);  }
    +        else
    +            {  return ( );  };
    +        }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: MakeParsedTopic
    +#
    +#   Creates a <NaturalDocs::Parser::ParsedTopic> object for the passed parameters.  Scope is gotten from
    +#   the package variable <package> instead of from the parameters.  The summary is generated from the body.
    +#
    +#   Parameters:
    +#
    +#       type         - The <TopicType>.  May be undef for headerless topics.
    +#       title          - The title of the topic.  May be undef for headerless topics.
    +#       package    - The package <SymbolString> the topic appears in.
    +#       body        - The topic's body in <NDMarkup>.
    +#       lineNumber - The topic's line number.
    +#       isList         - Whether the topic is a list.
    +#
    +#   Returns:
    +#
    +#       The <NaturalDocs::Parser::ParsedTopic> object.
    +#
    +sub MakeParsedTopic #(type, title, package, body, lineNumber, isList)
    +    {
    +    my ($self, $type, $title, $package, $body, $lineNumber, $isList) = @_;
    +
    +    my $summary;
    +
    +    if (defined $body)
    +        {  $summary = NaturalDocs::Parser->GetSummaryFromBody($body);  };
    +
    +    return NaturalDocs::Parser::ParsedTopic->New($type, $title, $package, undef, undef, $summary,
    +                                                                         $body, $lineNumber, $isList);
    +    };
    +
    +
    +#
    +#    Function: FormatBody
    +#
    +#    Converts the section body to <NDMarkup>.
    +#
    +#    Parameters:
    +#
    +#       commentLines - The arrayref of comment lines.
    +#       startingIndex  - The starting index of the body to format.
    +#       endingIndex   - The ending index of the body to format, *not* inclusive.
    +#       type               - The type of the section.  May be undef for headerless comments.
    +#       isList              - Whether it's a list topic.
    +#
    +#    Returns:
    +#
    +#        The body formatted in <NDMarkup>.
    +#
    +sub FormatBody #(commentLines, startingIndex, endingIndex, type, isList)
    +    {
    +    my ($self, $commentLines, $startingIndex, $endingIndex, $type, $isList) = @_;
    +
    +    use constant TAG_NONE => 1;
    +    use constant TAG_PARAGRAPH => 2;
    +    use constant TAG_BULLETLIST => 3;
    +    use constant TAG_DESCRIPTIONLIST => 4;
    +    use constant TAG_HEADING => 5;
    +    use constant TAG_PREFIXCODE => 6;
    +    use constant TAG_TAGCODE => 7;
    +
    +    my %tagEnders = ( TAG_NONE() => '',
    +                                 TAG_PARAGRAPH() => '</p>',
    +                                 TAG_BULLETLIST() => '</li></ul>',
    +                                 TAG_DESCRIPTIONLIST() => '</dd></dl>',
    +                                 TAG_HEADING() => '</h>',
    +                                 TAG_PREFIXCODE() => '</code>',
    +                                 TAG_TAGCODE() => '</code>' );
    +
    +    my $topLevelTag = TAG_NONE;
    +
    +    my $output;
    +    my $textBlock;
    +    my $prevLineBlank = 1;
    +
    +    my $codeBlock;
    +    my $removedCodeSpaces;
    +
    +    my $ignoreListSymbols;
    +
    +    my $index = $startingIndex;
    +
    +    while ($index < $endingIndex)
    +        {
    +        # If we're in a tagged code section...
    +        if ($topLevelTag == TAG_TAGCODE)
    +            {
    +            if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
    +                {
    +                $codeBlock =~ s/\n+$//;
    +                $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
    +                $codeBlock = undef;
    +                $topLevelTag = TAG_NONE;
    +                $prevLineBlank = undef;
    +                }
    +            else
    +                {
    +                $self->AddToCodeBlock($commentLines->[$index], \$codeBlock, \$removedCodeSpaces);
    +                };
    +            }
    +
    +        # If the line starts with a code designator...
    +        elsif ($commentLines->[$index] =~ /^ *[>:|](.*)$/)
    +            {
    +            my $code = $1;
    +
    +            if ($topLevelTag == TAG_PREFIXCODE)
    +                {
    +                $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
    +                }
    +            else # $topLevelTag != TAG_PREFIXCODE
    +                {
    +                if (defined $textBlock)
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag};
    +                    $textBlock = undef;
    +                    };
    +
    +                $topLevelTag = TAG_PREFIXCODE;
    +                $output .= '<code type="anonymous">';
    +                $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
    +                };
    +            }
    +
    +        # If we're not in either code style...
    +        else
    +            {
    +            # Strip any leading whitespace.
    +            $commentLines->[$index] =~ s/^ +//;
    +
    +            # If we were in a prefixed code section...
    +            if ($topLevelTag == TAG_PREFIXCODE)
    +                {
    +                $codeBlock =~ s/\n+$//;
    +                $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
    +                $codeBlock = undef;
    +                $topLevelTag = TAG_NONE;
    +                $prevLineBlank = undef;
    +                };
    +
    +
    +            # If the line is blank...
    +            if (!length($commentLines->[$index]))
    +                {
    +                # End a paragraph.  Everything else ignores it for now.
    +                if ($topLevelTag == TAG_PARAGRAPH)
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock) . '</p>';
    +                    $textBlock = undef;
    +                    $topLevelTag = TAG_NONE;
    +                    };
    +
    +                $prevLineBlank = 1;
    +                }
    +
    +            # If the line starts with a bullet...
    +            elsif ($commentLines->[$index] =~ /^[-\*o+] +([^ ].*)$/ &&
    +                    substr($1, 0, 2) ne '- ')  # Make sure "o - Something" is a definition, not a bullet.
    +                {
    +                my $bulletedText = $1;
    +
    +                if (defined $textBlock)
    +                    {  $output .= $self->RichFormatTextBlock($textBlock);  };
    +
    +                if ($topLevelTag == TAG_BULLETLIST)
    +                    {
    +                    $output .= '</li><li>';
    +                    }
    +                else #($topLevelTag != TAG_BULLETLIST)
    +                    {
    +                    $output .= $tagEnders{$topLevelTag} . '<ul><li>';
    +                    $topLevelTag = TAG_BULLETLIST;
    +                    };
    +
    +                $textBlock = $bulletedText;
    +
    +                $prevLineBlank = undef;
    +                }
    +
    +            # If the line looks like a description list entry...
    +            elsif ($commentLines->[$index] =~ /^(.+?) +- +([^ ].*)$/ && $topLevelTag != TAG_PARAGRAPH)
    +                {
    +                my $entry = $1;
    +                my $description = $2;
    +
    +                if (defined $textBlock)
    +                    {  $output .= $self->RichFormatTextBlock($textBlock);  };
    +
    +                if ($topLevelTag == TAG_DESCRIPTIONLIST)
    +                    {
    +                    $output .= '</dd>';
    +                    }
    +                else #($topLevelTag != TAG_DESCRIPTIONLIST)
    +                    {
    +                    $output .= $tagEnders{$topLevelTag} . '<dl>';
    +                    $topLevelTag = TAG_DESCRIPTIONLIST;
    +                    };
    +
    +                if (($isList && !$ignoreListSymbols) || $type eq ::TOPIC_ENUMERATION())
    +                    {
    +                    $output .= '<ds>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</ds><dd>';
    +                    }
    +                else
    +                    {
    +                    $output .= '<de>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</de><dd>';
    +                    };
    +
    +                $textBlock = $description;
    +
    +                $prevLineBlank = undef;
    +                }
    +
    +            # If the line could be a header...
    +            elsif ($prevLineBlank && $commentLines->[$index] =~ /^(.*)([^ ]):$/)
    +                {
    +                my $headerText = $1 . $2;
    +
    +                if (defined $textBlock)
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock);
    +                    $textBlock = undef;
    +                    }
    +
    +                $output .= $tagEnders{$topLevelTag};
    +                $topLevelTag = TAG_NONE;
    +
    +                $output .= '<h>' . $self->RichFormatTextBlock($headerText) . '</h>';
    +
    +                if ($type eq ::TOPIC_FUNCTION() && $isList)
    +                    {
    +                    $ignoreListSymbols = exists $functionListIgnoredHeadings{lc($headerText)};
    +                    };
    +
    +                $prevLineBlank = undef;
    +                }
    +
    +            # If the line looks like a code tag...
    +            elsif ($commentLines->[$index] =~ /^\( *(?:(?:start|begin)? +)?(table|code|example|diagram) *\)$/i)
    +                {
    +				my $codeType = lc($1);
    +
    +                if (defined $textBlock)
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock);
    +                    $textBlock = undef;
    +                    };
    +
    +                if ($codeType eq 'example')
    +                	{  $codeType = 'anonymous';  }
    +                elsif ($codeType eq 'table' || $codeType eq 'diagram')
    +                	{  $codeType = 'text';  }
    +                # else leave it 'code'
    +
    +                $output .= $tagEnders{$topLevelTag} . '<code type="' . $codeType . '">';
    +                $topLevelTag = TAG_TAGCODE;
    +                }
    +
    +            # If the line looks like an inline image...
    +            elsif ($commentLines->[$index] =~ /^(\( *see +)([^\)]+?)( *\))$/i)
    +                {
    +                if (defined $textBlock)
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock);
    +                    $textBlock = undef;
    +                    };
    +
    +                $output .= $tagEnders{$topLevelTag};
    +                $topLevelTag = TAG_NONE;
    +
    +                $output .= '<img mode="inline" target="' . NaturalDocs::NDMarkup->ConvertAmpChars($2) . '" '
    +                                . 'original="' . NaturalDocs::NDMarkup->ConvertAmpChars($1 . $2 . $3) . '">';
    +
    +                $prevLineBlank = undef;
    +                }
    +
    +            # If the line isn't any of those, we consider it normal text.
    +            else
    +                {
    +                # A blank line followed by normal text ends lists.  We don't handle this when we detect if the line's blank because
    +                # we don't want blank lines between list items to break the list.
    +                if ($prevLineBlank && ($topLevelTag == TAG_BULLETLIST || $topLevelTag == TAG_DESCRIPTIONLIST))
    +                    {
    +                    $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag} . '<p>';
    +
    +                    $topLevelTag = TAG_PARAGRAPH;
    +                    $textBlock = undef;
    +                    }
    +
    +                elsif ($topLevelTag == TAG_NONE)
    +                    {
    +                    $output .= '<p>';
    +                    $topLevelTag = TAG_PARAGRAPH;
    +                    # textBlock will already be undef.
    +                    };
    +
    +                if (defined $textBlock)
    +                    {  $textBlock .= ' ';  };
    +
    +                $textBlock .= $commentLines->[$index];
    +
    +                $prevLineBlank = undef;
    +                };
    +            };
    +
    +        $index++;
    +        };
    +
    +    # Clean up anything left dangling.
    +    if (defined $textBlock)
    +        {
    +        $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag};
    +        }
    +    elsif (defined $codeBlock)
    +        {
    +        $codeBlock =~ s/\n+$//;
    +        $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
    +        };
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: AddToCodeBlock
    +#
    +#   Adds a line of text to a code block, handling all the indentation processing required.
    +#
    +#   Parameters:
    +#
    +#       line - The line of text to add.
    +#       codeBlockRef - A reference to the code block to add it to.
    +#       removedSpacesRef - A reference to a variable to hold the number of spaces removed.  It needs to be stored between calls.
    +#                                      It will reset itself automatically when the code block codeBlockRef points to is undef.
    +#
    +sub AddToCodeBlock #(line, codeBlockRef, removedSpacesRef)
    +    {
    +    my ($self, $line, $codeBlockRef, $removedSpacesRef) = @_;
    +
    +    $line =~ /^( *)(.*)$/;
    +    my ($spaces, $code) = ($1, $2);
    +
    +    if (!defined $$codeBlockRef)
    +        {
    +        if (length($code))
    +            {
    +            $$codeBlockRef = $code . "\n";
    +            $$removedSpacesRef = length($spaces);
    +            };
    +        # else ignore leading line breaks.
    +        }
    +
    +    elsif (length $code)
    +        {
    +        # Make sure we have the minimum amount of spaces to the left possible.
    +        if (length($spaces) != $$removedSpacesRef)
    +            {
    +            my $spaceDifference = abs( length($spaces) - $$removedSpacesRef );
    +            my $spacesToAdd = ' ' x $spaceDifference;
    +
    +            if (length($spaces) > $$removedSpacesRef)
    +                {
    +                $$codeBlockRef .= $spacesToAdd;
    +                }
    +            else
    +                {
    +                $$codeBlockRef =~ s/^(.)/$spacesToAdd . $1/gme;
    +                $$removedSpacesRef = length($spaces);
    +                };
    +            };
    +
    +        $$codeBlockRef .= $code . "\n";
    +        }
    +
    +    else # (!length $code)
    +        {
    +        $$codeBlockRef .= "\n";
    +        };
    +    };
    +
    +
    +#
    +#   Function: RichFormatTextBlock
    +#
    +#   Applies rich <NDMarkup> formatting to a chunk of text.  This includes both amp chars, formatting tags, and link tags.
    +#
    +#   Parameters:
    +#
    +#       text - The block of text to format.
    +#
    +#   Returns:
    +#
    +#       The formatted text block.
    +#
    +sub RichFormatTextBlock #(text)
    +    {
    +    my ($self, $text) = @_;
    +    my $output;
    +
    +
    +    # First find bare urls, e-mail addresses, and images.  We have to do this before the split because they may contain underscores
    +    # or asterisks.  We have to mark the tags with \x1E and \x1F so they don't get confused with angle brackets from the comment.
    +    # We can't convert the amp chars beforehand because we need lookbehinds in the regexps below and they need to be
    +    # constant length.  Sucks, huh?
    +
    +    $text =~ s{
    +                       # The previous character can't be an alphanumeric or an opening angle bracket.
    +                       (?<!  [a-z0-9<]  )
    +
    +                       # Optional mailto:.  Ignored in output.
    +                       (?:mailto\:)?
    +
    +                       # Begin capture
    +                       (
    +
    +                       # The user portion.  Alphanumeric and - _.  Dots can appear between, but not at the edges or more than
    +                       # one in a row.
    +                       (?:  [a-z0-9\-_]+  \.  )*   [a-z0-9\-_]+
    +
    +                       @
    +
    +                       # The domain.  Alphanumeric and -.  Dots same as above, however, there must be at least two sections
    +                       # and the last one must be two to four alphanumeric characters (.com, .uk, .info, .203 for IP addresses)
    +                       (?:  [a-z0-9\-]+  \.  )+  [a-z]{2,4}
    +
    +                       # End capture.
    +                       )
    +
    +                       # The next character can't be an alphanumeric, which should prevent .abcde from matching the two to
    +                       # four character requirement, or a closing angle bracket.
    +                       (?!  [a-z0-9>]  )
    +
    +                       }
    +
    +                       {"\x1E" . 'email target="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '" '
    +                       . 'name="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '"' . "\x1F"}igxe;
    +
    +    $text =~ s{
    +                       # The previous character can't be an alphanumeric or an opening angle bracket.
    +                       (?<!  [a-z0-9<]  )
    +
    +                       # Begin capture.
    +                       (
    +
    +                       # URL must start with one of the acceptable protocols.
    +                       (?:http|https|ftp|news|file)\:
    +
    +                       # The acceptable URL characters as far as I know.
    +                       [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*\.\,]*
    +
    +                       # The URL characters minus period and comma.  If it ends on them, they're probably intended as
    +                       # punctuation.
    +                       [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*]
    +
    +                       # End capture.
    +                       )
    +
    +                       # The next character must not be an acceptable character or a closing angle bracket.  It must also not be a
    +					   # dot and then an acceptable character.  These will prevent the URL from ending early just to get a match.
    +                       (?!  \.?[a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*\>]  )
    +
    +                       }
    +
    +                       {"\x1E" . 'url target="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '" '
    +                       . 'name="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '"' . "\x1F"}igxe;
    +
    +
    +    # Find image links.  Inline images should already be pulled out by now.
    +
    +    $text =~ s{(\( *see +)([^\)\<\>]+?)( *\))}
    +                      {"\x1E" . 'img mode="link" target="' . NaturalDocs::NDMarkup->ConvertAmpChars($2) . '" '
    +                        . 'original="' . NaturalDocs::NDMarkup->ConvertAmpChars($1 . $2 . $3) . '"' . "\x1F"}gie;
    +
    +
    +
    +    # Split the text from the potential tags.
    +
    +    my @tempTextBlocks = split(/([\*_<>\x1E\x1F])/, $text);
    +
    +    # Since the symbols are considered dividers, empty strings could appear between two in a row or at the beginning/end of the
    +    # array.  This could seriously screw up TagType(), so we need to get rid of them.
    +    my @textBlocks;
    +
    +    while (scalar @tempTextBlocks)
    +        {
    +        my $tempTextBlock = shift @tempTextBlocks;
    +
    +        if (length $tempTextBlock)
    +            {  push @textBlocks, $tempTextBlock;  };
    +        };
    +
    +
    +    my $bold;
    +    my $underline;
    +    my $underlineHasWhitespace;
    +
    +    my $index = 0;
    +
    +    while ($index < scalar @textBlocks)
    +        {
    +        if ($textBlocks[$index] eq "\x1E")
    +            {
    +            $output .= '<';
    +            $index++;
    +
    +            while ($textBlocks[$index] ne "\x1F")
    +                {
    +                $output .= $textBlocks[$index];
    +                $index++;
    +                };
    +
    +            $output .= '>';
    +            }
    +
    +        elsif ($textBlocks[$index] eq '<' && $self->TagType(\@textBlocks, $index) == POSSIBLE_OPENING_TAG)
    +            {
    +            my $endingIndex = $self->ClosingTag(\@textBlocks, $index, undef);
    +
    +            if ($endingIndex != -1)
    +                {
    +                my $linkText;
    +                $index++;
    +
    +                while ($index < $endingIndex)
    +                    {
    +                    $linkText .= $textBlocks[$index];
    +                    $index++;
    +                    };
    +                # Index will be incremented again at the end of the loop.
    +
    +                $linkText = NaturalDocs::NDMarkup->ConvertAmpChars($linkText);
    +
    +                if ($linkText =~ /^(?:mailto\:)?((?:[a-z0-9\-_]+\.)*[a-z0-9\-_]+@(?:[a-z0-9\-]+\.)+[a-z]{2,4})$/i)
    +                    {  $output .= '<email target="' . $1 . '" name="' . $1 . '">';  }
    +                elsif ($linkText =~ /^(.+?) at (?:mailto\:)?((?:[a-z0-9\-_]+\.)*[a-z0-9\-_]+@(?:[a-z0-9\-]+\.)+[a-z]{2,4})$/i)
    +                    {  $output .= '<email target="' . $2 . '" name="' . $1 . '">';  }
    +                elsif ($linkText =~ /^(?:http|https|ftp|news|file)\:/i)
    +                    {  $output .= '<url target="' . $linkText . '" name="' . $linkText . '">';  }
    +                elsif ($linkText =~ /^(.+?) at ((?:http|https|ftp|news|file)\:.+)/i)
    +                    {  $output .= '<url target="' . $2 . '" name="' . $1 . '">';  }
    +                else
    +                    {  $output .= '<link target="' . $linkText . '" name="' . $linkText . '" original="&lt;' . $linkText . '&gt;">';  };
    +                }
    +
    +            else # it's not a link.
    +                {
    +                $output .= '&lt;';
    +                };
    +            }
    +
    +        elsif ($textBlocks[$index] eq '*')
    +            {
    +            my $tagType = $self->TagType(\@textBlocks, $index);
    +
    +            if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, undef) != -1)
    +                {
    +                # ClosingTag() makes sure tags aren't opened multiple times in a row.
    +                $bold = 1;
    +                $output .= '<b>';
    +                }
    +            elsif ($bold && $tagType == POSSIBLE_CLOSING_TAG)
    +                {
    +                $bold = undef;
    +                $output .= '</b>';
    +                }
    +            else
    +                {
    +                $output .= '*';
    +                };
    +            }
    +
    +        elsif ($textBlocks[$index] eq '_')
    +            {
    +            my $tagType = $self->TagType(\@textBlocks, $index);
    +
    +             if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, \$underlineHasWhitespace) != -1)
    +                {
    +                # ClosingTag() makes sure tags aren't opened multiple times in a row.
    +                $underline = 1;
    +                #underlineHasWhitespace is set by ClosingTag().
    +                $output .= '<u>';
    +                }
    +            elsif ($underline && $tagType == POSSIBLE_CLOSING_TAG)
    +                {
    +                $underline = undef;
    +                #underlineHasWhitespace will be reset by the next opening underline.
    +                $output .= '</u>';
    +                }
    +            elsif ($underline && !$underlineHasWhitespace)
    +                {
    +                # If there's no whitespace between underline tags, all underscores are replaced by spaces so
    +                # _some_underlined_text_ becomes <u>some underlined text</u>.  The standard _some underlined text_
    +                # will work too.
    +                $output .= ' ';
    +                }
    +            else
    +                {
    +                $output .= '_';
    +                };
    +            }
    +
    +        else # plain text or a > that isn't part of a link
    +            {
    +            $output .= NaturalDocs::NDMarkup->ConvertAmpChars($textBlocks[$index]);
    +           };
    +
    +        $index++;
    +        };
    +
    +    return $output;
    +    };
    +
    +
    +#
    +#   Function: TagType
    +#
    +#   Returns whether the tag is a possible opening or closing tag, or neither.  "Possible" because it doesn't check if an opening tag is
    +#   closed or a closing tag is opened, just whether the surrounding characters allow it to be a candidate for a tag.  For example, in
    +#   "A _B" the underscore is a possible opening underline tag, but in "A_B" it is not.  Support function for <RichFormatTextBlock()>.
    +#
    +#   Parameters:
    +#
    +#       textBlocks  - A reference to an array of text blocks.
    +#       index         - The index of the tag.
    +#
    +#   Returns:
    +#
    +#       POSSIBLE_OPENING_TAG, POSSIBLE_CLOSING_TAG, or NOT_A_TAG.
    +#
    +sub TagType #(textBlocks, index)
    +    {
    +    my ($self, $textBlocks, $index) = @_;
    +
    +
    +    # Possible opening tags
    +
    +    if ( ( $textBlocks->[$index] =~ /^[\*_<]$/ ) &&
    +
    +        # Before it must be whitespace, the beginning of the text, or ({["'-/*_.
    +        ( $index == 0 || $textBlocks->[$index-1] =~ /[\ \t\n\(\{\[\"\'\-\/\*\_]$/ ) &&
    +
    +        # Notes for 2.0: Include Spanish upside down ! and ? as well as opening quotes (66) and apostrophes (6).  Look into
    +        # Unicode character classes as well.
    +
    +        # After it must be non-whitespace.
    +        ( $index + 1 < scalar @$textBlocks && $textBlocks->[$index+1] !~ /^[\ \t\n]/) &&
    +
    +        # Make sure we don't accept <<, <=, <-, or *= as opening tags.
    +        ( $textBlocks->[$index] ne '<' || $textBlocks->[$index+1] !~ /^[<=-]/ ) &&
    +        ( $textBlocks->[$index] ne '*' || $textBlocks->[$index+1] !~ /^[\=\*]/ ) &&
    +
    +        # Make sure we don't accept * or _ before it unless it's <.
    +        ( $textBlocks->[$index] eq '<' || $index == 0 || $textBlocks->[$index-1] !~ /[\*\_]$/) )
    +        {
    +        return POSSIBLE_OPENING_TAG;
    +        }
    +
    +
    +    # Possible closing tags
    +
    +    elsif ( ( $textBlocks->[$index] =~ /^[\*_>]$/) &&
    +
    +            # After it must be whitespace, the end of the text, or )}].,!?"';:-/*_.
    +            ( $index + 1 == scalar @$textBlocks || $textBlocks->[$index+1] =~ /^[ \t\n\)\]\}\.\,\!\?\"\'\;\:\-\/\*\_]/ ||
    +              # Links also get plurals, like <link>s, <linx>es, <link>'s, and <links>'.
    +              ( $textBlocks->[$index] eq '>' && $textBlocks->[$index+1] =~ /^(?:es|s|\')/ ) ) &&
    +
    +            # Notes for 2.0: Include closing quotes (99) and apostrophes (9).  Look into Unicode character classes as well.
    +
    +            # Before it must be non-whitespace.
    +            ( $index != 0 && $textBlocks->[$index-1] !~ /[ \t\n]$/ ) &&
    +
    +            # Make sure we don't accept >>, ->, or => as closing tags.  >= is already taken care of.
    +            ( $textBlocks->[$index] ne '>' || $textBlocks->[$index-1] !~ /[>=-]$/ ) &&
    +
    +            # Make sure we don't accept * or _ after it unless it's >.
    +            ( $textBlocks->[$index] eq '>' || $textBlocks->[$index+1] !~ /[\*\_]$/) )
    +        {
    +        return POSSIBLE_CLOSING_TAG;
    +        }
    +
    +    else
    +        {
    +        return NOT_A_TAG;
    +        };
    +
    +    };
    +
    +
    +#
    +#   Function: ClosingTag
    +#
    +#   Returns whether a tag is closed or not, where it's closed if it is, and optionally whether there is any whitespace between the
    +#   tags.  Support function for <RichFormatTextBlock()>.
    +#
    +#   The results of this function are in full context, meaning that if it says a tag is closed, it can be interpreted as that tag in the
    +#   final output.  It takes into account any spoiling factors, like there being two opening tags in a row.
    +#
    +#   Parameters:
    +#
    +#       textBlocks             - A reference to an array of text blocks.
    +#       index                    - The index of the opening tag.
    +#       hasWhitespaceRef  - A reference to the variable that will hold whether there is whitespace between the tags or not.  If
    +#                                     undef, the function will not check.  If the tag is not closed, the variable will not be changed.
    +#
    +#   Returns:
    +#
    +#       If the tag is closed, it returns the index of the closing tag and puts whether there was whitespace between the tags in
    +#       hasWhitespaceRef if it was specified.  If the tag is not closed, it returns -1 and doesn't touch the variable pointed to by
    +#       hasWhitespaceRef.
    +#
    +sub ClosingTag #(textBlocks, index, hasWhitespace)
    +    {
    +    my ($self, $textBlocks, $index, $hasWhitespaceRef) = @_;
    +
    +    my $hasWhitespace;
    +    my $closingTag;
    +
    +    if ($textBlocks->[$index] eq '*' || $textBlocks->[$index] eq '_')
    +        {  $closingTag = $textBlocks->[$index];  }
    +    elsif ($textBlocks->[$index] eq '<')
    +        {  $closingTag = '>';  }
    +    else
    +        {  return -1;  };
    +
    +    my $beginningIndex = $index;
    +    $index++;
    +
    +    while ($index < scalar @$textBlocks)
    +        {
    +        if ($textBlocks->[$index] eq '<' && $self->TagType($textBlocks, $index) == POSSIBLE_OPENING_TAG)
    +            {
    +            # If we hit a < and we're checking whether a link is closed, it's not.  The first < becomes literal and the second one
    +            # becomes the new link opening.
    +            if ($closingTag eq '>')
    +                {
    +                return -1;
    +                }
    +
    +            # If we're not searching for the end of a link, we have to skip the link because formatting tags cannot appear within
    +            # them.  That's of course provided it's closed.
    +            else
    +                {
    +                my $linkHasWhitespace;
    +
    +                my $endIndex = $self->ClosingTag($textBlocks, $index,
    +                                                                    ($hasWhitespaceRef && !$hasWhitespace ? \$linkHasWhitespace : undef) );
    +
    +                if ($endIndex != -1)
    +                    {
    +                    if ($linkHasWhitespace)
    +                        {  $hasWhitespace = 1;  };
    +
    +                    # index will be incremented again at the end of the loop, which will bring us past the link's >.
    +                    $index = $endIndex;
    +                    };
    +                };
    +            }
    +
    +        elsif ($textBlocks->[$index] eq $closingTag)
    +            {
    +            my $tagType = $self->TagType($textBlocks, $index);
    +
    +            if ($tagType == POSSIBLE_CLOSING_TAG)
    +                {
    +                # There needs to be something between the tags for them to count.
    +                if ($index == $beginningIndex + 1)
    +                    {  return -1;  }
    +                else
    +                    {
    +                    # Success!
    +
    +                    if ($hasWhitespaceRef)
    +                        {  $$hasWhitespaceRef = $hasWhitespace;  };
    +
    +                    return $index;
    +                    };
    +                }
    +
    +            # If there are two opening tags of the same type, the first becomes literal and the next becomes part of a tag.
    +            elsif ($tagType == POSSIBLE_OPENING_TAG)
    +                {  return -1;  }
    +            }
    +
    +        elsif ($hasWhitespaceRef && !$hasWhitespace)
    +            {
    +            if ($textBlocks->[$index] =~ /[ \t\n]/)
    +                {  $hasWhitespace = 1;  };
    +            };
    +
    +        $index++;
    +        };
    +
    +    # Hit the end of the text blocks if we're here.
    +    return -1;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Parser/ParsedTopic.pm b/vendor/naturaldocs/Modules/NaturalDocs/Parser/ParsedTopic.pm
    new file mode 100644
    index 000000000..c4c2afd80
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Parser/ParsedTopic.pm
    @@ -0,0 +1,254 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Parser::ParsedTopic
    +#
    +###############################################################################
    +#
    +#   A class for parsed topics of source files.  Also encompasses some of the <TopicType>-specific behavior.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Parser::ParsedTopic;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The object is a blessed arrayref with the following indexes.
    +#
    +#       TYPE           - The <TopicType>.
    +#       TITLE          - The title of the topic.
    +#       PACKAGE    - The package <SymbolString> the topic appears in, or undef if none.
    +#       USING         - An arrayref of additional package <SymbolStrings> available to the topic via "using" statements, or undef if
    +#                           none.
    +#       PROTOTYPE - The prototype, if it exists and is applicable.
    +#       SUMMARY    - The summary, if it exists.
    +#       BODY          - The body of the topic, formatted in <NDMarkup>.  Some topics may not have bodies, and if not, this
    +#                           will be undef.
    +#       LINE_NUMBER  - The line number the topic appears at in the file.
    +#       IS_LIST - Whether the topic is a list.
    +#
    +use NaturalDocs::DefineMembers 'TYPE', 'TITLE', 'PACKAGE', 'USING', 'PROTOTYPE', 'SUMMARY', 'BODY',
    +                                                 'LINE_NUMBER', 'IS_LIST';
    +# DEPENDENCY: New() depends on the order of these constants, and that this class is not inheriting any members.
    +
    +
    +#
    +#   Architecture: Title, Package, and Symbol Behavior
    +#
    +#   Title, package, and symbol behavior is a little awkward so it deserves some explanation.  Basically you set them according to
    +#   certain rules, but you get computed values that try to hide all the different scoping situations.
    +#
    +#   Normal Topics:
    +#
    +#       Set them to the title and package as they appear.  "Function" and "PkgA.PkgB" will return "Function" for the title,
    +#       "PkgA.PkgB" for the package, and "PkgA.PkgB.Function" for the symbol.
    +#
    +#       In the rare case that a title has a separator symbol it's treated as inadvertant, so "A vs. B" in "PkgA.PkgB" still returns just
    +#       "PkgA.PkgB" for the package even though if you got it from the symbol it can be seen as "PkgA.PkgB.A vs".
    +#
    +#   Scope Topics:
    +#
    +#       Set the title normally and leave the package undef.  So "PkgA.PkgB" and undef will return "PkgA.PkgB" for the title as well
    +#       as for the package and symbol.
    +#
    +#       The only time you should set the package is when you have full language support and they only documented the class with
    +#       a partial title.  So if you documented "PkgA.PkgB" with just "PkgB", you want to set the package to "PkgA".  This
    +#       will return "PkgB" as the title for presentation and will return "PkgA.PkgB" for the package and symbol, which is correct.
    +#
    +#   Always Global Topics:
    +#
    +#       Set the title and package normally, do not set the package to undef.  So "Global" and "PkgA.PkgB" will return "Global" as
    +#       the title, "PkgA.PkgB" as the package, and "Global" as the symbol.
    +#
    +#   Um, yeah...:
    +#
    +#       So does this suck?  Yes, yes it does.  But the suckiness is centralized here instead of having to be handled everywhere these
    +#       issues come into play.  Just realize there are a certain set of rules to follow when you *set* these variables, and the results
    +#       you see when you *get* them are computed rather than literal.
    +#
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates a new object.
    +#
    +#   Parameters:
    +#
    +#       type          - The <TopicType>.
    +#       title           - The title of the topic.
    +#       package    - The package <SymbolString> the topic appears in, or undef if none.
    +#       using         - An arrayref of additional package <SymbolStrings> available to the topic via "using" statements, or undef if
    +#                          none.
    +#       prototype   - The prototype, if it exists and is applicable.  Otherwise set to undef.
    +#       summary   - The summary of the topic, if any.
    +#       body          - The body of the topic, formatted in <NDMarkup>.  May be undef, as some topics may not have bodies.
    +#       lineNumber - The line number the topic appears at in the file.
    +#       isList          - Whether the topic is a list topic or not.
    +#
    +#   Returns:
    +#
    +#       The new object.
    +#
    +sub New #(type, title, package, using, prototype, summary, body, lineNumber, isList)
    +    {
    +    # DEPENDENCY: This depends on the order of the parameter list being the same as the constants, and that there are no
    +    # members inherited from a base class.
    +
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    if (defined $object->[USING])
    +        {  $object->[USING] = [ @{$object->[USING]} ];  };
    +
    +    return $object;
    +    };
    +
    +
    +# Function: Type
    +# Returns the <TopicType>.
    +sub Type
    +    {  return $_[0]->[TYPE];  };
    +
    +# Function: SetType
    +# Replaces the <TopicType>.
    +sub SetType #(type)
    +    {  $_[0]->[TYPE] = $_[1];  };
    +
    +# Function: IsList
    +# Returns whether the topic is a list.
    +sub IsList
    +    {  return $_[0]->[IS_LIST];  };
    +
    +# Function: SetIsList
    +# Sets whether the topic is a list.
    +sub SetIsList
    +    {  $_[0]->[IS_LIST] = $_[1];  };
    +
    +# Function: Title
    +# Returns the title of the topic.
    +sub Title
    +    {  return $_[0]->[TITLE];  };
    +
    +# Function: SetTitle
    +# Replaces the topic title.
    +sub SetTitle #(title)
    +    {  $_[0]->[TITLE] = $_[1];  };
    +
    +#
    +#   Function: Symbol
    +#
    +#   Returns the <SymbolString> defined by the topic.  It is fully resolved and does _not_ need to be joined with <Package()>.
    +#
    +#   Type-Specific Behavior:
    +#
    +#       - If the <TopicType> is always global, the symbol will be generated from the title only.
    +#       - Everything else's symbols will be generated from the title and the package passed to <New()>.
    +#
    +sub Symbol
    +    {
    +    my ($self) = @_;
    +
    +    my $titleSymbol = NaturalDocs::SymbolString->FromText($self->[TITLE]);
    +
    +    if (NaturalDocs::Topics->TypeInfo($self->Type())->Scope() == ::SCOPE_ALWAYS_GLOBAL())
    +        {  return $titleSymbol;  }
    +    else
    +        {
    +        return NaturalDocs::SymbolString->Join( $self->[PACKAGE], $titleSymbol );
    +        };
    +    };
    +
    +
    +#
    +#   Function: Package
    +#
    +#   Returns the package <SymbolString> that the topic appears in.
    +#
    +#   Type-Specific Behavior:
    +#
    +#       - If the <TopicType> has scope, the package will be generated from both the title and the package passed to <New()>, not
    +#         just the package.
    +#       - If the <TopicType> is always global, the package will be the one passed to <New()>, even though it isn't part of it's
    +#         <Symbol()>.
    +#       - Everything else's package will be what was passed to <New()>, even if the title has separator symbols in it.
    +#
    +sub Package
    +    {
    +    my ($self) = @_;
    +
    +    # Headerless topics may not have a type yet.
    +    if ($self->Type() && NaturalDocs::Topics->TypeInfo($self->Type())->Scope() == ::SCOPE_START())
    +        {  return $self->Symbol();  }
    +    else
    +        {  return $self->[PACKAGE];  };
    +    };
    +
    +
    +# Function: SetPackage
    +# Replaces the package the topic appears in.  This will behave the same way as the package parameter in <New()>.  Later calls
    +# to <Package()> will still be generated according to its type-specific behavior.
    +sub SetPackage #(package)
    +    {  $_[0]->[PACKAGE] = $_[1];  };
    +
    +# Function: Using
    +# Returns an arrayref of additional scope <SymbolStrings> available to the topic via "using" statements, or undef if none.
    +sub Using
    +    {  return $_[0]->[USING];  };
    +
    +# Function: SetUsing
    +# Replaces the using arrayref of sope <SymbolStrings>.
    +sub SetUsing #(using)
    +    {  $_[0]->[USING] = $_[1];  };
    +
    +# Function: Prototype
    +# Returns the prototype if one is defined.  Will be undef otherwise.
    +sub Prototype
    +    {  return $_[0]->[PROTOTYPE];  };
    +
    +# Function: SetPrototype
    +# Replaces the function or variable prototype.
    +sub SetPrototype #(prototype)
    +    {  $_[0]->[PROTOTYPE] = $_[1];  };
    +
    +# Function: Summary
    +# Returns the topic summary, if it exists, formatted in <NDMarkup>.
    +sub Summary
    +    {  return $_[0]->[SUMMARY];  };
    +
    +# Function: Body
    +# Returns the topic's body, formatted in <NDMarkup>.  May be undef.
    +sub Body
    +    {  return $_[0]->[BODY];  };
    +
    +# Function: SetBody
    +# Replaces the topic's body, formatted in <NDMarkup>.  May be undef.
    +sub SetBody #(body)
    +    {
    +    my ($self, $body) = @_;
    +    $self->[BODY] = $body;
    +    };
    +
    +# Function: LineNumber
    +# Returns the line the topic appears at in the file.
    +sub LineNumber
    +    {  return $_[0]->[LINE_NUMBER];  };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Project.pm b/vendor/naturaldocs/Modules/NaturalDocs/Project.pm
    new file mode 100644
    index 000000000..b62495fe1
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Project.pm
    @@ -0,0 +1,1404 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Project
    +#
    +###############################################################################
    +#
    +#   A package that manages information about the files in the source tree, as well as the list of files that have to be parsed
    +#   and built.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - All the <Config and Data File Functions> are available immediately, except for the status functions.
    +#
    +#       - <ReparseEverything()> and <RebuildEverything()> are available immediately, because they may need to be called
    +#         after <LoadConfigFileInfo()> but before <LoadSourceFileInfo()>.
    +#
    +#       - Prior to <LoadConfigFileInfo()>, <NaturalDocs::Settings> must be initialized.
    +#
    +#       - After <LoadConfigFileInfo()>, the status <Config and Data File Functions> are available as well.
    +#
    +#       - Prior to <LoadSourceFileInfo()>, <NaturalDocs::Settings> and <NaturalDocs::Languages> must be initialized.
    +#
    +#       - After <LoadSourceFileInfo()>, the rest of the <Source File Functions> are available.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use NaturalDocs::Project::SourceFile;
    +use NaturalDocs::Project::ImageFile;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Project;
    +
    +
    +###############################################################################
    +# Group: File Handles
    +
    +#
    +#   handle: FH_FILEINFO
    +#
    +#   The file handle for the file information file, <FileInfo.nd>.
    +#
    +
    +#
    +#   handle: FH_CONFIGFILEINFO
    +#
    +#   The file handle for the config file information file, <ConfigFileInfo.nd>.
    +#
    +
    +#
    +#   handle: FH_IMAGEFILE
    +#
    +#   The file handle for determining the dimensions of image files.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: Source File Variables
    +
    +
    +#
    +#   hash: supportedFiles
    +#
    +#   A hash of all the supported files in the input directory.  The keys are the <FileNames>, and the values are
    +#   <NaturalDocs::Project::SourceFile> objects.
    +#
    +my %supportedFiles;
    +
    +#
    +#   hash: filesToParse
    +#
    +#   An existence hash of all the <FileNames> that need to be parsed.
    +#
    +my %filesToParse;
    +
    +#
    +#   hash: filesToBuild
    +#
    +#   An existence hash of all the <FileNames> that need to be built.
    +#
    +my %filesToBuild;
    +
    +#
    +#   hash: filesToPurge
    +#
    +#   An existence hash of the <FileNames> that had Natural Docs content last time, but now either don't exist or no longer have
    +#   content.
    +#
    +my %filesToPurge;
    +
    +#
    +#   hash: unbuiltFilesWithContent
    +#
    +#   An existence hash of all the <FileNames> that have Natural Docs content but are not part of <filesToBuild>.
    +#
    +my %unbuiltFilesWithContent;
    +
    +
    +# bool: reparseEverything
    +# Whether all the source files need to be reparsed.
    +my $reparseEverything;
    +
    +# bool: rebuildEverything
    +# Whether all the source files need to be rebuilt.
    +my $rebuildEverything;
    +
    +# hash: mostUsedLanguage
    +# The name of the most used language.  Doesn't include text files.
    +my $mostUsedLanguage;
    +
    +
    +
    +###############################################################################
    +# Group: Configuration File Variables
    +
    +
    +#
    +#   hash: mainConfigFile
    +#
    +#   A hash mapping all the main configuration file names without paths to their <FileStatus>.  Prior to <LoadConfigFileInfo()>,
    +#   it serves as an existence hashref of the file names.
    +#
    +my %mainConfigFiles = ( 'Topics.txt' => 1, 'Languages.txt' => 1 );
    +
    +#
    +#   hash: userConfigFiles
    +#
    +#   A hash mapping all the user configuration file names without paths to their <FileStatus>.  Prior to <LoadConfigFileInfo()>,
    +#   it serves as an existence hashref of the file names.
    +#
    +my %userConfigFiles = ( 'Topics.txt' => 1, 'Languages.txt' => 1, 'Menu.txt' => 1 );
    +
    +
    +
    +
    +###############################################################################
    +# Group: Image File Variables
    +
    +
    +#
    +#   hash: imageFileExtensions
    +#
    +#   An existence hash of all the file extensions for images.  Extensions are in all lowercase.
    +#
    +my %imageFileExtensions = ( 'jpg' => 1, 'jpeg' => 1, 'gif' => 1, 'png' => 1, 'bmp' => 1 );
    +
    +
    +#
    +#   hash: imageFiles
    +#
    +#   A hash of all the image files in the project.  The keys are the <FileNames> and the values are
    +#   <NaturalDocs::Project::ImageFiles>.
    +#
    +my %imageFiles;
    +
    +
    +#
    +#   hash: imageFilesToUpdate
    +#
    +#   An existence hash of all the image <FileNames> that need to be updated, either because they changed or they're new to the
    +#   project.
    +#
    +my %imageFilesToUpdate;
    +
    +
    +#
    +#   hash: imageFilesToPurge
    +#
    +#   An existence hash of all the image <FileNames> that need to be purged, either because the files no longer exist or because
    +#   they are no longer used.
    +#
    +my %imageFilesToPurge;
    +
    +
    +#
    +#   hash: insensitiveImageFiles
    +#
    +#   A hash that maps all lowercase image <FileNames> to their proper case as it would appear in <imageFiles>.  Used for
    +#   case insensitivity, obviously.
    +#
    +#   You can't just use all lowercase in <imageFiles> because both Linux and HTTP are case sensitive, so the original case must
    +#   be preserved.  We also want to allow separate entries for files that differ based only on case, so it goes to <imageFiles> first
    +#   where they can be distinguished and here only if there's no match.  Ties are broken by whichever is lower with cmp, because
    +#   it has to resolve consistently on all runs of the program.
    +#
    +my %insensitiveImageFiles;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: FileInfo.nd
    +#
    +#   An index of the state of the files as of the last parse.  Used to determine if files were added, deleted, or changed.
    +#
    +#   Format:
    +#
    +#       The format is a text file.
    +#
    +#       > [VersionInt: app version]
    +#
    +#       The beginning of the file is the <VersionInt> it was generated with.
    +#
    +#       > [most used language name]
    +#
    +#       Next is the name of the most used language in the source tree.  Does not include text files.
    +#
    +#       Each following line is
    +#
    +#       > [file name] tab [last modification time] tab [has ND content (0 or 1)] tab [default menu title] \n
    +#
    +#   Revisions:
    +#
    +#       1.3:
    +#
    +#           - The line following the <VersionInt>, which was previously the last modification time of <Menu.txt>, was changed to
    +#             the name of the most used language.
    +#
    +#       1.16:
    +#
    +#           - File names are now absolute.  Prior to 1.16, they were relative to the input directory since only one was allowed.
    +#
    +#       1.14:
    +#
    +#           - The file was renamed from NaturalDocs.files to FileInfo.nd and moved into the Data subdirectory.
    +#
    +#       0.95:
    +#
    +#           - The file version was changed to match the program version.  Prior to 0.95, the version line was 1.  Test for "1" instead
    +#             of "1.0" to distinguish.
    +#
    +
    +
    +#
    +#   File: ConfigFileInfo.nd
    +#
    +#   An index of the state of the config files as of the last parse.
    +#
    +#   Format:
    +#
    +#       > [BINARY_FORMAT]
    +#       > [VersionInt: app version]
    +#
    +#       First is the standard <BINARY_FORMAT> <VersionInt> header.
    +#
    +#       > [UInt32: last modification time of menu]
    +#       > [UInt32: last modification of main topics file]
    +#       > [UInt32: last modification of user topics file]
    +#       > [UInt32: last modification of main languages file]
    +#       > [UInt32: last modification of user languages file]
    +#
    +#       Next are the last modification times of various configuration files as UInt32s in the standard Unix format.
    +#
    +#
    +#   Revisions:
    +#
    +#       1.3:
    +#
    +#           - The file was added to Natural Docs.  Previously the last modification of <Menu.txt> was stored in <FileInfo.nd>, and
    +#             <Topics.txt> and <Languages.txt> didn't exist.
    +#
    +
    +
    +#
    +#   File: ImageFileInfo.nd
    +#
    +#   An index of the state of the image files as of the last parse.
    +#
    +#   Format:
    +#
    +#       > [Standard Binary Header]
    +#
    +#       First is the standard binary file header as defined by <NaturalDocs::BinaryFile>.
    +#
    +#       > [AString16: file name or undef]
    +#       > [UInt32: last modification time]
    +#       > [UInt8: was used]
    +#
    +#       This section is repeated until the file name is null.  The last modification times are UInt32s in the standard Unix format.
    +#
    +#
    +#   Revisions:
    +#
    +#       1.4:
    +#
    +#           - The file was added to Natural Docs.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +#
    +#   Function: LoadSourceFileInfo
    +#
    +#   Loads the project file from disk and compares it against the files in the input directory.  Project is loaded from
    +#   <FileInfo.nd>.  New and changed files will be added to <FilesToParse()>, and if they have content,
    +#   <FilesToBuild()>.
    +#
    +#   Will call <NaturalDocs::Languages->OnMostUsedLanguageKnown()> if <MostUsedLanguage()> changes.
    +#
    +#   Returns:
    +#
    +#       Returns whether the project was changed in any way.
    +#
    +sub LoadSourceFileInfo
    +    {
    +    my ($self) = @_;
    +
    +    $self->GetAllSupportedFiles();
    +    NaturalDocs::Languages->OnMostUsedLanguageKnown();
    +
    +    my $fileIsOkay;
    +    my $version;
    +    my $hasChanged;
    +    my $lineReader;
    +
    +    if (open(FH_FILEINFO, '<' . $self->DataFile('FileInfo.nd')))
    +        {
    +        $lineReader = NaturalDocs::LineReader->New(\*FH_FILEINFO);
    +
    +        # Check if the file is in the right format.
    +        $version = NaturalDocs::Version->FromString($lineReader->Get());
    +
    +        # The project file need to be rebuilt for 1.16.  The source files need to be reparsed and the output files rebuilt for 1.51.
    +        # We'll tolerate the difference between 1.16 and 1.3 in the loader.
    +
    +        if (NaturalDocs::Version->CheckFileFormat( $version, NaturalDocs::Version->FromString('1.16') ))
    +            {
    +            $fileIsOkay = 1;
    +
    +            if (!NaturalDocs::Version->CheckFileFormat( $version, NaturalDocs::Version->FromString('1.51') ))
    +                {
    +                $reparseEverything = 1;
    +                $rebuildEverything = 1;
    +                $hasChanged = 1;
    +                };
    +            }
    +        else
    +            {
    +            close(FH_FILEINFO);
    +            $hasChanged = 1;
    +            };
    +        };
    +
    +
    +    if ($fileIsOkay)
    +        {
    +        my %indexedFiles;
    +
    +
    +        my $line = $lineReader->Get();
    +
    +        # Prior to 1.3 it was the last modification time of Menu.txt, which we ignore and treat as though the most used language
    +        # changed.  Prior to 1.32 the settings didn't transfer over correctly to Menu.txt so we need to behave that way again.
    +        if ($version < NaturalDocs::Version->FromString('1.32') || lc($mostUsedLanguage) ne lc($line))
    +            {
    +            $reparseEverything = 1;
    +            NaturalDocs::SymbolTable->RebuildAllIndexes();
    +            };
    +
    +
    +        # Parse the rest of the file.
    +
    +        while ($line = $lineReader->Get())
    +            {
    +            my ($file, $modification, $hasContent, $menuTitle) = split(/\t/, $line, 4);
    +
    +            # If the file no longer exists...
    +            if (!exists $supportedFiles{$file})
    +                {
    +                if ($hasContent)
    +                    {  $filesToPurge{$file} = 1;  };
    +
    +                $hasChanged = 1;
    +                }
    +
    +            # If the file still exists...
    +            else
    +                {
    +                $indexedFiles{$file} = 1;
    +
    +                # If the file changed...
    +                if ($supportedFiles{$file}->LastModified() != $modification)
    +                    {
    +                    $supportedFiles{$file}->SetStatus(::FILE_CHANGED());
    +                    $filesToParse{$file} = 1;
    +
    +                    # If the file loses its content, this will be removed by SetHasContent().
    +                    if ($hasContent)
    +                        {  $filesToBuild{$file} = 1;  };
    +
    +                    $hasChanged = 1;
    +                    }
    +
    +                # If the file has not changed...
    +                else
    +                    {
    +                    my $status;
    +
    +                    if ($rebuildEverything && $hasContent)
    +                        {
    +                        $status = ::FILE_CHANGED();
    +
    +                        # If the file loses its content, this will be removed by SetHasContent().
    +                        $filesToBuild{$file} = 1;
    +                        $hasChanged = 1;
    +                        }
    +                    else
    +                        {
    +                        $status = ::FILE_SAME();
    +
    +                        if ($hasContent)
    +                            {  $unbuiltFilesWithContent{$file} = 1;  };
    +                        };
    +
    +                    if ($reparseEverything)
    +                        {
    +                        $status = ::FILE_CHANGED();
    +
    +                        $filesToParse{$file} = 1;
    +                        $hasChanged = 1;
    +                        };
    +
    +                    $supportedFiles{$file}->SetStatus($status);
    +                    };
    +
    +                $supportedFiles{$file}->SetHasContent($hasContent);
    +                $supportedFiles{$file}->SetDefaultMenuTitle($menuTitle);
    +                };
    +            };
    +
    +        close(FH_FILEINFO);
    +
    +
    +        # Check for added files.
    +
    +        if (scalar keys %supportedFiles > scalar keys %indexedFiles)
    +            {
    +            foreach my $file (keys %supportedFiles)
    +                {
    +                if (!exists $indexedFiles{$file})
    +                    {
    +                    $supportedFiles{$file}->SetStatus(::FILE_NEW());
    +                    $supportedFiles{$file}->SetDefaultMenuTitle($file);
    +                    $supportedFiles{$file}->SetHasContent(undef);
    +                    $filesToParse{$file} = 1;
    +                    # It will be added to filesToBuild if HasContent gets set to true when it's parsed.
    +                    $hasChanged = 1;
    +                    };
    +                };
    +            };
    +        }
    +
    +    # If something's wrong with FileInfo.nd, everything is new.
    +    else
    +        {
    +        foreach my $file (keys %supportedFiles)
    +            {
    +            $supportedFiles{$file}->SetStatus(::FILE_NEW());
    +            $supportedFiles{$file}->SetDefaultMenuTitle($file);
    +            $supportedFiles{$file}->SetHasContent(undef);
    +            $filesToParse{$file} = 1;
    +            # It will be added to filesToBuild if HasContent gets set to true when it's parsed.
    +            };
    +
    +        $hasChanged = 1;
    +        };
    +
    +
    +    # There are other side effects, so we need to call this.
    +    if ($rebuildEverything)
    +        {  $self->RebuildEverything();  };
    +
    +
    +    return $hasChanged;
    +    };
    +
    +
    +#
    +#   Function: SaveSourceFileInfo
    +#
    +#   Saves the source file info to disk.  Everything is saved in <FileInfo.nd>.
    +#
    +sub SaveSourceFileInfo
    +    {
    +    my ($self) = @_;
    +
    +    open(FH_FILEINFO, '>' . $self->DataFile('FileInfo.nd'))
    +        or die "Couldn't save project file " . $self->DataFile('FileInfo.nd') . "\n";
    +
    +    NaturalDocs::Version->ToTextFile(\*FH_FILEINFO, NaturalDocs::Settings->AppVersion());
    +
    +    print FH_FILEINFO $mostUsedLanguage . "\n";
    +
    +    while (my ($fileName, $file) = each %supportedFiles)
    +        {
    +        print FH_FILEINFO $fileName . "\t"
    +                              . $file->LastModified() . "\t"
    +                              . ($file->HasContent() || '0') . "\t"
    +                              . $file->DefaultMenuTitle() . "\n";
    +        };
    +
    +    close(FH_FILEINFO);
    +    };
    +
    +
    +#
    +#   Function: LoadConfigFileInfo
    +#
    +#   Loads the config file info from disk.
    +#
    +sub LoadConfigFileInfo
    +    {
    +    my ($self) = @_;
    +
    +    my $fileIsOkay;
    +    my $version;
    +    my $fileName = NaturalDocs::Project->DataFile('ConfigFileInfo.nd');
    +
    +    if (open(FH_CONFIGFILEINFO, '<' . $fileName))
    +        {
    +        # See if it's binary.
    +        binmode(FH_CONFIGFILEINFO);
    +
    +        my $firstChar;
    +        read(FH_CONFIGFILEINFO, $firstChar, 1);
    +
    +        if ($firstChar == ::BINARY_FORMAT())
    +            {
    +            $version = NaturalDocs::Version->FromBinaryFile(\*FH_CONFIGFILEINFO);
    +
    +            # It hasn't changed since being introduced.
    +
    +            if (NaturalDocs::Version->CheckFileFormat($version))
    +                {  $fileIsOkay = 1;  }
    +            else
    +                {  close(FH_CONFIGFILEINFO);  };
    +            }
    +
    +        else # it's not in binary
    +            {  close(FH_CONFIGFILEINFO);  };
    +        };
    +
    +    my @configFiles = ( $self->UserConfigFile('Menu.txt'), \$userConfigFiles{'Menu.txt'},
    +                                 $self->MainConfigFile('Topics.txt'), \$mainConfigFiles{'Topics.txt'},
    +                                 $self->UserConfigFile('Topics.txt'), \$userConfigFiles{'Topics.txt'},
    +                                 $self->MainConfigFile('Languages.txt'), \$mainConfigFiles{'Languages.txt'},
    +                                 $self->UserConfigFile('Languages.txt'), \$userConfigFiles{'Languages.txt'} );
    +
    +    if ($fileIsOkay)
    +        {
    +        my $raw;
    +
    +        read(FH_CONFIGFILEINFO, $raw, 20);
    +        my @configFileDates = unpack('NNNNN', $raw);
    +
    +        while (scalar @configFiles)
    +            {
    +            my $file = shift @configFiles;
    +            my $fileStatus = shift @configFiles;
    +            my $fileDate = shift @configFileDates;
    +
    +            if (-e $file)
    +                {
    +                if ($fileDate == (stat($file))[9])
    +                    {  $$fileStatus = ::FILE_SAME();  }
    +                else
    +                    {  $$fileStatus = ::FILE_CHANGED();  };
    +                }
    +            else
    +                {  $$fileStatus = ::FILE_DOESNTEXIST();  };
    +            };
    +
    +        close(FH_CONFIGFILEINFO);
    +        }
    +    else # !$fileIsOkay
    +        {
    +        while (scalar @configFiles)
    +            {
    +            my $file = shift @configFiles;
    +            my $fileStatus = shift @configFiles;
    +
    +            if (-e $file)
    +                {  $$fileStatus = ::FILE_CHANGED();  }
    +            else
    +                {  $$fileStatus = ::FILE_DOESNTEXIST();  };
    +            };
    +        };
    +
    +    if ($userConfigFiles{'Menu.txt'} == ::FILE_SAME() && $rebuildEverything)
    +        {  $userConfigFiles{'Menu.txt'} = ::FILE_CHANGED();  };
    +    };
    +
    +
    +#
    +#   Function: SaveConfigFileInfo
    +#
    +#   Saves the config file info to disk.  You *must* save all other config files first, such as <Menu.txt> and <Topics.txt>.
    +#
    +sub SaveConfigFileInfo
    +    {
    +    my ($self) = @_;
    +
    +    open (FH_CONFIGFILEINFO, '>' . NaturalDocs::Project->DataFile('ConfigFileInfo.nd'))
    +        or die "Couldn't save " . NaturalDocs::Project->DataFile('ConfigFileInfo.nd') . ".\n";
    +
    +    binmode(FH_CONFIGFILEINFO);
    +
    +    print FH_CONFIGFILEINFO '' . ::BINARY_FORMAT();
    +
    +    NaturalDocs::Version->ToBinaryFile(\*FH_CONFIGFILEINFO, NaturalDocs::Settings->AppVersion());
    +
    +    print FH_CONFIGFILEINFO pack('NNNNN', (stat($self->UserConfigFile('Menu.txt')))[9],
    +                                                                (stat($self->MainConfigFile('Topics.txt')))[9],
    +                                                                (stat($self->UserConfigFile('Topics.txt')))[9],
    +                                                                (stat($self->MainConfigFile('Languages.txt')))[9],
    +                                                                (stat($self->UserConfigFile('Languages.txt')))[9] );
    +
    +    close(FH_CONFIGFILEINFO);
    +    };
    +
    +
    +#
    +#   Function: LoadImageFileInfo
    +#
    +#   Loads the image file info from disk.
    +#
    +sub LoadImageFileInfo
    +    {
    +    my ($self) = @_;
    +
    +    my $version = NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('ImageFileInfo.nd') );
    +    my $fileIsOkay;
    +
    +    if (defined $version)
    +        {
    +        # It hasn't changed since being introduced.
    +
    +        if (NaturalDocs::Version->CheckFileFormat($version))
    +            {  $fileIsOkay = 1;  }
    +        else
    +            {  NaturalDocs::BinaryFile->Close();  };
    +        };
    +
    +    if ($fileIsOkay)
    +        {
    +        # [AString16: file name or undef]
    +
    +        while (my $imageFile = NaturalDocs::BinaryFile->GetAString16())
    +            {
    +            # [UInt32: last modified]
    +            # [UInt8: was used]
    +
    +            my $lastModified = NaturalDocs::BinaryFile->GetUInt32();
    +            my $wasUsed = NaturalDocs::BinaryFile->GetUInt8();
    +
    +            my $imageFileObject = $imageFiles{$imageFile};
    +
    +            # If there's an image file in ImageFileInfo.nd that no longer exists...
    +            if (!$imageFileObject)
    +                {
    +                $imageFileObject = NaturalDocs::Project::ImageFile->New($lastModified, ::FILE_DOESNTEXIST(), $wasUsed);
    +                $imageFiles{$imageFile} = $imageFileObject;
    +
    +                if ($wasUsed)
    +                    {  $imageFilesToPurge{$imageFile} = 1;  };
    +                }
    +            else
    +                {
    +                $imageFileObject->SetWasUsed($wasUsed);
    +
    +                # This will be removed if it gets any references.
    +                if ($wasUsed)
    +                    {  $imageFilesToPurge{$imageFile} = 1;  };
    +
    +                if ($imageFileObject->LastModified() == $lastModified && !$rebuildEverything)
    +                    {  $imageFileObject->SetStatus(::FILE_SAME());  }
    +                else
    +                    {  $imageFileObject->SetStatus(::FILE_CHANGED());  };
    +                };
    +            };
    +
    +        NaturalDocs::BinaryFile->Close();
    +        }
    +
    +    else # !$fileIsOkay
    +        {
    +        $self->RebuildEverything();
    +        };
    +    };
    +
    +
    +#
    +#   Function: SaveImageFileInfo
    +#
    +#   Saves the image file info to disk.
    +#
    +sub SaveImageFileInfo
    +    {
    +    my $self = shift;
    +
    +    NaturalDocs::BinaryFile->OpenForWriting( NaturalDocs::Project->DataFile('ImageFileInfo.nd') );
    +
    +    while (my ($imageFile, $imageFileInfo) = each %imageFiles)
    +        {
    +        if ($imageFileInfo->Status() != ::FILE_DOESNTEXIST())
    +            {
    +            # [AString16: file name or undef]
    +            # [UInt32: last modification time]
    +            # [UInt8: was used]
    +
    +            NaturalDocs::BinaryFile->WriteAString16($imageFile);
    +            NaturalDocs::BinaryFile->WriteUInt32($imageFileInfo->LastModified());
    +            NaturalDocs::BinaryFile->WriteUInt8( ($imageFileInfo->ReferenceCount() > 0 ? 1 : 0) );
    +            };
    +        };
    +
    +    NaturalDocs::BinaryFile->WriteAString16(undef);
    +    NaturalDocs::BinaryFile->Close();
    +    };
    +
    +
    +#
    +#   Function: MigrateOldFiles
    +#
    +#   If the project uses the old file names used prior to 1.14, it converts them to the new file names.
    +#
    +sub MigrateOldFiles
    +    {
    +    my ($self) = @_;
    +
    +    my $projectDirectory = NaturalDocs::Settings->ProjectDirectory();
    +
    +    # We use the menu file as a test to see if we're using the new format.
    +    if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs_Menu.txt'))
    +        {
    +        # The Data subdirectory would have been created by NaturalDocs::Settings.
    +
    +        rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs_Menu.txt'), $self->UserConfigFile('Menu.txt') );
    +
    +        if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.sym'))
    +            {  rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.sym'), $self->DataFile('SymbolTable.nd') );  };
    +
    +        if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.files'))
    +            {  rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.files'), $self->DataFile('FileInfo.nd') );  };
    +
    +        if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.m'))
    +            {  rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.m'), $self->DataFile('PreviousMenuState.nd') );  };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Config and Data File Functions
    +
    +
    +#
    +#   Function: MainConfigFile
    +#
    +#   Returns the full path to the passed main configuration file.  Pass the file name only.
    +#
    +sub MainConfigFile #(string file)
    +    {
    +    my ($self, $file) = @_;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ConfigDirectory(), $file );
    +    };
    +
    +#
    +#   Function: MainConfigFileStatus
    +#
    +#   Returns the <FileStatus> of the passed main configuration file.  Pass the file name only.
    +#
    +sub MainConfigFileStatus #(string file)
    +    {
    +    my ($self, $file) = @_;
    +    return $mainConfigFiles{$file};
    +    };
    +
    +#
    +#   Function: UserConfigFile
    +#
    +#   Returns the full path to the passed user configuration file.  Pass the file name only.
    +#
    +sub UserConfigFile #(string file)
    +    {
    +    my ($self, $file) = @_;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), $file );
    +    };
    +
    +#
    +#   Function: UserConfigFileStatus
    +#
    +#   Returns the <FileStatus> of the passed user configuration file.  Pass the file name only.
    +#
    +sub UserConfigFileStatus #(string file)
    +    {
    +    my ($self, $file) = @_;
    +    return $userConfigFiles{$file};
    +    };
    +
    +#
    +#   Function: DataFile
    +#
    +#   Returns the full path to the passed data file.  Pass the file name only.
    +#
    +sub DataFile #(string file)
    +    {
    +    my ($self, $file) = @_;
    +    return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), $file );
    +    };
    +
    +
    +
    +
    +###############################################################################
    +# Group: Source File Functions
    +
    +
    +# Function: FilesToParse
    +# Returns an existence hashref of the <FileNames> to parse.  This is not a copy of the data, so don't change it.
    +sub FilesToParse
    +    {  return \%filesToParse;  };
    +
    +# Function: FilesToBuild
    +# Returns an existence hashref of the <FileNames> to build.  This is not a copy of the data, so don't change it.
    +sub FilesToBuild
    +    {  return \%filesToBuild;  };
    +
    +# Function: FilesToPurge
    +# Returns an existence hashref of the <FileNames> that had content last time, but now either don't anymore or were deleted.
    +# This is not a copy of the data, so don't change it.
    +sub FilesToPurge
    +    {  return \%filesToPurge;  };
    +
    +#
    +#   Function: RebuildFile
    +#
    +#   Adds the file to the list of files to build.  This function will automatically filter out files that don't have Natural Docs content and
    +#   files that are part of <FilesToPurge()>.  If this gets called on a file and that file later gets Natural Docs content, it will be added.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to build or rebuild.
    +#
    +sub RebuildFile #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    # We don't want to add it to the build list if it doesn't exist, doesn't have Natural Docs content, or it's going to be purged.
    +    # If it wasn't parsed yet and will later be found to have ND content, it will be added by SetHasContent().
    +    if (exists $supportedFiles{$file} && !exists $filesToPurge{$file} && $supportedFiles{$file}->HasContent())
    +        {
    +        $filesToBuild{$file} = 1;
    +
    +        if (exists $unbuiltFilesWithContent{$file})
    +            {  delete $unbuiltFilesWithContent{$file};  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: ReparseEverything
    +#
    +#   Adds all supported files to the list of files to parse.  This does not necessarily mean these files are going to be rebuilt.
    +#
    +sub ReparseEverything
    +    {
    +    my ($self) = @_;
    +
    +    if (!$reparseEverything)
    +        {
    +        foreach my $file (keys %supportedFiles)
    +            {
    +            $filesToParse{$file} = 1;
    +            };
    +
    +        $reparseEverything = 1;
    +        };
    +    };
    +
    +
    +#
    +#   Function: RebuildEverything
    +#
    +#   Adds all supported files to the list of files to build.  This does not necessarily mean these files are going to be reparsed.
    +#
    +sub RebuildEverything
    +    {
    +    my ($self) = @_;
    +
    +    foreach my $file (keys %unbuiltFilesWithContent)
    +        {
    +        $filesToBuild{$file} = 1;
    +        };
    +
    +    %unbuiltFilesWithContent = ( );
    +    $rebuildEverything = 1;
    +
    +    NaturalDocs::SymbolTable->RebuildAllIndexes();
    +
    +    if ($userConfigFiles{'Menu.txt'} == ::FILE_SAME())
    +        {  $userConfigFiles{'Menu.txt'} = ::FILE_CHANGED();  };
    +
    +    while (my ($imageFile, $imageObject) = each %imageFiles)
    +        {
    +        if ($imageObject->ReferenceCount())
    +            {  $imageFilesToUpdate{$imageFile} = 1;  };
    +        };
    +    };
    +
    +
    +# Function: UnbuiltFilesWithContent
    +# Returns an existence hashref of the <FileNames> that have Natural Docs content but are not part of <FilesToBuild()>.  This is
    +# not a copy of the data so don't change it.
    +sub UnbuiltFilesWithContent
    +    {  return \%unbuiltFilesWithContent;  };
    +
    +# Function: FilesWithContent
    +# Returns and existence hashref of the <FileNames> that have Natural Docs content.
    +sub FilesWithContent
    +    {
    +    # Don't keep this one internally, but there's an easy way to make it.
    +    return { %filesToBuild, %unbuiltFilesWithContent };
    +    };
    +
    +
    +#
    +#   Function: HasContent
    +#
    +#   Returns whether the <FileName> contains Natural Docs content.
    +#
    +sub HasContent #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (exists $supportedFiles{$file})
    +        {  return $supportedFiles{$file}->HasContent();  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: SetHasContent
    +#
    +#   Sets whether the <FileName> has Natural Docs content or not.
    +#
    +sub SetHasContent #(file, hasContent)
    +    {
    +    my ($self, $file, $hasContent) = @_;
    +
    +    if (exists $supportedFiles{$file} && $supportedFiles{$file}->HasContent() != $hasContent)
    +        {
    +        # If the file now has content...
    +        if ($hasContent)
    +            {
    +            $filesToBuild{$file} = 1;
    +            }
    +
    +        # If the file's content has been removed...
    +        else
    +            {
    +            delete $filesToBuild{$file};  # may not be there
    +            $filesToPurge{$file} = 1;
    +            };
    +
    +        $supportedFiles{$file}->SetHasContent($hasContent);
    +        };
    +    };
    +
    +
    +#
    +#   Function: StatusOf
    +#
    +#   Returns the <FileStatus> of the passed <FileName>.
    +#
    +sub StatusOf #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (exists $supportedFiles{$file})
    +        {  return $supportedFiles{$file}->Status();  }
    +    else
    +        {  return ::FILE_DOESNTEXIST();  };
    +    };
    +
    +
    +#
    +#   Function: DefaultMenuTitleOf
    +#
    +#   Returns the default menu title of the <FileName>.  If one isn't specified, it returns the <FileName>.
    +#
    +sub DefaultMenuTitleOf #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (exists $supportedFiles{$file})
    +        {  return $supportedFiles{$file}->DefaultMenuTitle();  }
    +    else
    +        {  return $file;  };
    +    };
    +
    +
    +#
    +#   Function: SetDefaultMenuTitle
    +#
    +#   Sets the <FileName's> default menu title.
    +#
    +sub SetDefaultMenuTitle #(file, menuTitle)
    +    {
    +    my ($self, $file, $menuTitle) = @_;
    +
    +    if (exists $supportedFiles{$file} && $supportedFiles{$file}->DefaultMenuTitle() ne $menuTitle)
    +        {
    +        $supportedFiles{$file}->SetDefaultMenuTitle($menuTitle);
    +        NaturalDocs::Menu->OnDefaultTitleChange($file);
    +        };
    +    };
    +
    +
    +#
    +#   Function: MostUsedLanguage
    +#
    +#   Returns the name of the most used language in the source trees.  Does not include text files.
    +#
    +sub MostUsedLanguage
    +    {  return $mostUsedLanguage;  };
    +
    +
    +
    +
    +###############################################################################
    +# Group: Image File Functions
    +
    +
    +#
    +#   Function: ImageFileExists
    +#   Returns whether the passed image file exists.
    +#
    +sub ImageFileExists #(FileName file) => bool
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (!exists $imageFiles{$file})
    +        {  $file = $insensitiveImageFiles{lc($file)};  };
    +
    +    return (exists $imageFiles{$file} && $imageFiles{$file}->Status() != ::FILE_DOESNTEXIST());
    +    };
    +
    +
    +#
    +#   Function: ImageFileDimensions
    +#   Returns the dimensions of the passed image file as the array ( width, height ).  Returns them both as undef if it cannot be
    +#   determined.
    +#
    +sub ImageFileDimensions #(FileName file) => (int, int)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (!exists $imageFiles{$file})
    +        {  $file = $insensitiveImageFiles{lc($file)};  };
    +
    +    my $object = $imageFiles{$file};
    +    if (!$object)
    +        {  die "Tried to get the dimensions of an image that doesn't exist.";  };
    +
    +    if ($object->Width() == -1)
    +        {  $self->DetermineImageDimensions($file);  };
    +
    +    return ($object->Width(), $object->Height());
    +    };
    +
    +
    +#
    +#   Function: ImageFileCapitalization
    +#   Returns the properly capitalized version of the passed image <FileName>.  Image file paths are treated as case insensitive
    +#   regardless of whether the underlying operating system is or not, so we have to make sure the final version matches the
    +#   capitalization of the actual file.
    +#
    +sub ImageFileCapitalization #(FileName file) => FileName
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (exists $imageFiles{$file})
    +        {  return $file;  }
    +    elsif (exists $insensitiveImageFiles{lc($file)})
    +        {  return $insensitiveImageFiles{lc($file)};  }
    +    else
    +        {  die "Tried to get the capitalization of an image file that doesn't exist.";  };
    +    };
    +
    +
    +#
    +#   Function: AddImageFileReference
    +#   Adds a reference to the passed image <FileName>.
    +#
    +sub AddImageFileReference #(FileName imageFile)
    +    {
    +    my ($self, $imageFile) = @_;
    +
    +    if (!exists $imageFiles{$imageFile})
    +        {  $imageFile = $insensitiveImageFiles{lc($imageFile)};  };
    +
    +    my $imageFileInfo = $imageFiles{$imageFile};
    +
    +    if ($imageFileInfo == undef || $imageFileInfo->Status() == ::FILE_DOESNTEXIST())
    +        {  die "Tried to add a reference to a non-existant image file.";  };
    +
    +    if ($imageFileInfo->AddReference() == 1)
    +        {
    +        delete $imageFilesToPurge{$imageFile};
    +
    +        if (!$imageFileInfo->WasUsed() ||
    +            $imageFileInfo->Status() == ::FILE_NEW() ||
    +            $imageFileInfo->Status() == ::FILE_CHANGED())
    +            {  $imageFilesToUpdate{$imageFile} = 1;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: DeleteImageFileReference
    +#   Deletes a reference from the passed image <FileName>.
    +#
    +sub DeleteImageFileReference #(FileName imageFile)
    +    {
    +    my ($self, $imageFile) = @_;
    +
    +    if (!exists $imageFiles{$imageFile})
    +        {  $imageFile = $insensitiveImageFiles{lc($imageFile)};  };
    +
    +    if (!exists $imageFiles{$imageFile})
    +        {  die "Tried to delete a reference to a non-existant image file.";  };
    +
    +    if ($imageFiles{$imageFile}->DeleteReference() == 0)
    +        {
    +        delete $imageFilesToUpdate{$imageFile};
    +
    +        if ($imageFiles{$imageFile}->WasUsed())
    +            {  $imageFilesToPurge{$imageFile} = 1;  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: ImageFilesToUpdate
    +#   Returns an existence hashref of image <FileNames> that need to be updated.  *Do not change.*
    +#
    +sub ImageFilesToUpdate
    +    {  return \%imageFilesToUpdate;  };
    +
    +
    +#
    +#   Function: ImageFilesToPurge
    +#   Returns an existence hashref of image <FileNames> that need to be updated.  *Do not change.*
    +#
    +sub ImageFilesToPurge
    +    {  return \%imageFilesToPurge;  };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +#
    +#   Function: GetAllSupportedFiles
    +#
    +#   Gets all the supported files in the passed directory and its subdirectories and puts them into <supportedFiles>.  The only
    +#   attribute that will be set is <NaturalDocs::Project::SourceFile->LastModified()>.  Also sets <mostUsedLanguage>.
    +#
    +sub GetAllSupportedFiles
    +    {
    +    my ($self) = @_;
    +
    +    my @directories = @{NaturalDocs::Settings->InputDirectories()};
    +    my $isCaseSensitive = NaturalDocs::File->IsCaseSensitive();
    +
    +    # Keys are language names, values are counts.
    +    my %languageCounts;
    +
    +
    +    # Make an existence hash of excluded directories.
    +
    +    my %excludedDirectories;
    +    my $excludedDirectoryArrayRef = NaturalDocs::Settings->ExcludedInputDirectories();
    +
    +    foreach my $excludedDirectory (@$excludedDirectoryArrayRef)
    +        {
    +        if ($isCaseSensitive)
    +            {  $excludedDirectories{$excludedDirectory} = 1;  }
    +        else
    +            {  $excludedDirectories{lc($excludedDirectory)} = 1;  };
    +        };
    +
    +
    +    my $imagesOnly;
    +    my $language;
    +
    +    while (scalar @directories)
    +        {
    +        my $directory = pop @directories;
    +
    +        opendir DIRECTORYHANDLE, $directory;
    +        my @entries = readdir DIRECTORYHANDLE;
    +        closedir DIRECTORYHANDLE;
    +
    +        @entries = NaturalDocs::File->NoUpwards(@entries);
    +
    +        foreach my $entry (@entries)
    +            {
    +            my $fullEntry = NaturalDocs::File->JoinPaths($directory, $entry);
    +
    +            # If an entry is a directory, recurse.
    +            if (-d $fullEntry)
    +                {
    +                # Join again with the noFile flag set in case the platform handles them differently.
    +                $fullEntry = NaturalDocs::File->JoinPaths($directory, $entry, 1);
    +
    +                if ($isCaseSensitive)
    +                    {
    +                    if (!exists $excludedDirectories{$fullEntry})
    +                        {  push @directories, $fullEntry;  };
    +                    }
    +                else
    +                    {
    +                    if (!exists $excludedDirectories{lc($fullEntry)})
    +                        {  push @directories, $fullEntry;  };
    +                    };
    +                }
    +
    +            # Otherwise add it if it's a supported extension.
    +            else
    +                {
    +                my $extension = NaturalDocs::File->ExtensionOf($entry);
    +
    +                if (exists $imageFileExtensions{lc($extension)})
    +                    {
    +                    my $fileObject = NaturalDocs::Project::ImageFile->New( (stat($fullEntry))[9], ::FILE_NEW(), 0 );
    +                    $imageFiles{$fullEntry} = $fileObject;
    +
    +                    my $lcFullEntry = lc($fullEntry);
    +
    +                    if (!exists $insensitiveImageFiles{$lcFullEntry} ||
    +                        ($fullEntry cmp $insensitiveImageFiles{$lcFullEntry}) < 0)
    +                        {
    +                        $insensitiveImageFiles{$lcFullEntry} = $fullEntry;
    +                        };
    +                    }
    +                elsif (!$imagesOnly && ($language = NaturalDocs::Languages->LanguageOf($fullEntry)) )
    +                    {
    +                    my $fileObject = NaturalDocs::Project::SourceFile->New();
    +                    $fileObject->SetLastModified(( stat($fullEntry))[9] );
    +                    $supportedFiles{$fullEntry} = $fileObject;
    +
    +                    $languageCounts{$language->Name()}++;
    +                    };
    +                };
    +            };
    +
    +
    +        # After we run out of source directories, add the image directories.
    +
    +        if (scalar @directories == 0 && !$imagesOnly)
    +            {
    +            $imagesOnly = 1;
    +            @directories = @{NaturalDocs::Settings->ImageDirectories()};
    +            };
    +        };
    +
    +
    +    my $topCount = 0;
    +
    +    while (my ($language, $count) = each %languageCounts)
    +        {
    +        if ($count > $topCount && $language ne 'Text File')
    +            {
    +            $topCount = $count;
    +            $mostUsedLanguage = $language;
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: DetermineImageDimensions
    +#
    +#   Attempts to determine the dimensions of the passed image and apply them to their object in <imageFiles>.  Will set them to
    +#   undef if they can't be determined.
    +#
    +sub DetermineImageDimensions #(FileName imageFile)
    +    {
    +    my ($self, $imageFile) = @_;
    +
    +    my $imageFileObject = $imageFiles{$imageFile};
    +    if (!defined $imageFileObject)
    +        {  die "Tried to determine image dimensions of a file with no object.";  };
    +
    +    my $extension = lc( NaturalDocs::File->ExtensionOf($imageFile) );
    +    my ($width, $height);
    +
    +    if ($imageFileExtensions{$extension})
    +        {
    +        open(FH_IMAGEFILE, '<' . $imageFile)
    +            or die 'Could not open ' . $imageFile . "\n";
    +        binmode(FH_IMAGEFILE);
    +
    +        my $raw;
    +
    +        if ($extension eq 'gif')
    +            {
    +            read(FH_IMAGEFILE, $raw, 6);
    +
    +            if ($raw eq 'GIF87a' || $raw eq 'GIF89a')
    +                {
    +                read(FH_IMAGEFILE, $raw, 4);
    +                ($width, $height) = unpack('vv', $raw);
    +                };
    +            }
    +
    +        elsif ($extension eq 'png')
    +            {
    +            read(FH_IMAGEFILE, $raw, 8);
    +
    +            if ($raw eq "\x89PNG\x0D\x0A\x1A\x0A")
    +                {
    +                seek(FH_IMAGEFILE, 4, 1);
    +                read(FH_IMAGEFILE, $raw, 4);
    +
    +                if ($raw eq 'IHDR')
    +                    {
    +                    read(FH_IMAGEFILE, $raw, 8);
    +                    ($width, $height) = unpack('NN', $raw);
    +                    };
    +                };
    +            }
    +
    +        elsif ($extension eq 'bmp')
    +            {
    +            read(FH_IMAGEFILE, $raw, 2);
    +
    +            if ($raw eq 'BM')
    +                {
    +                seek(FH_IMAGEFILE, 16, 1);
    +                read(FH_IMAGEFILE, $raw, 8);
    +
    +                ($width, $height) = unpack('VV', $raw);
    +                };
    +            }
    +
    +        elsif ($extension eq 'jpg' || $extension eq 'jpeg')
    +            {
    +            read(FH_IMAGEFILE, $raw, 2);
    +            my $isOkay = ($raw eq "\xFF\xD8");
    +
    +            while ($isOkay)
    +                {
    +                read(FH_IMAGEFILE, $raw, 4);
    +                my ($marker, $code, $length) = unpack('CCn', $raw);
    +
    +                $isOkay = ($marker eq 0xFF);
    +
    +                if ($isOkay)
    +                    {
    +                    if ($code >= 0xC0 && $code <= 0xC3)
    +                        {
    +                        read(FH_IMAGEFILE, $raw, 5);
    +                        ($height, $width) = unpack('xnn', $raw);
    +                        last;
    +                        }
    +
    +                    else
    +                        {
    +                        $isOkay = seek(FH_IMAGEFILE, $length - 2, 1);
    +                        };
    +                    };
    +                };
    +            };
    +
    +        close(FH_IMAGEFILE);
    +        };
    +
    +
    +    # Sanity check the values.  Although images can theoretically be bigger than 5000, most won't.  The worst that happens in this
    +    # case is just that they don't get length and width values in the output anyway.
    +    if ($width > 0 && $width < 5000 && $height > 0 && $height < 5000)
    +        {  $imageFileObject->SetDimensions($width, $height);  }
    +    else
    +        {  $imageFileObject->SetDimensions(undef, undef);  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Project/ImageFile.pm b/vendor/naturaldocs/Modules/NaturalDocs/Project/ImageFile.pm
    new file mode 100644
    index 000000000..f72adc44e
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Project/ImageFile.pm
    @@ -0,0 +1,161 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Project::ImageFile
    +#
    +###############################################################################
    +#
    +#   A simple information class about project image files.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Project::ImageFile;
    +
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are used as indexes.
    +#
    +#       LAST_MODIFIED - The integer timestamp of when the file was last modified.
    +#       STATUS - <FileStatus> since the last build.
    +#       REFERENCE_COUNT - The number of references to the image from the source files.
    +#       WAS_USED - Whether the image was used the last time Natural Docs was run.
    +#       WIDTH - The image width.  Undef if can't be determined, -1 if haven't attempted to determine yet.
    +#       HEIGHT - The image height.  Undef if can't be determined, -1 if haven't attempted to determine yet.
    +#
    +
    +use NaturalDocs::DefineMembers 'LAST_MODIFIED', 'LastModified()', 'SetLastModified()',
    +                                                 'STATUS', 'Status()', 'SetStatus()',
    +                                                 'REFERENCE_COUNT', 'ReferenceCount()',
    +                                                 'WAS_USED', 'WasUsed()', 'SetWasUsed()',
    +                                                 'WIDTH', 'Width()',
    +                                                 'HEIGHT', 'Height()';
    +
    +
    +#
    +#   Topic: WasUsed versus References
    +#
    +#   <WasUsed()> is a simple true/false that notes whether this image file was used the last time Natural Docs was run.
    +#   <ReferenceCount()> is a counter for the number of times it's used *this* run.  As such, it starts at zero regardless of whether
    +#   <WasUsed()> is set or not.
    +#
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new file object.
    +#
    +#   Parameters:
    +#
    +#       lastModified - The image file's last modification timestamp
    +#       status - The <FileStatus>.
    +#       wasUsed - Whether this image file was used the *last* time Natural Docs was run.
    +#
    +sub New #(timestamp lastModified, FileStatus status, bool wasUsed)
    +    {
    +    my ($package, $lastModified, $status, $width, $height, $wasUsed) = @_;
    +
    +    my $object = [ ];
    +    $object->[LAST_MODIFIED] = $lastModified;
    +    $object->[STATUS] = $status;
    +    $object->[REFERENCE_COUNT] = 0;
    +    $object->[WAS_USED] = $wasUsed;
    +    $object->[WIDTH] = -1;
    +    $object->[HEIGHT] = -1;
    +
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Member Functions
    +#
    +#   LastModified - Returns the integer timestamp of when the file was last modified.
    +#   SetLastModified - Sets the file's last modification timestamp.
    +#   Status - Returns the <FileStatus> since the last build.
    +#   SetStatus - Sets the <FileStatus> since the last build.
    +#
    +
    +#
    +#   Function: ReferenceCount
    +#   Returns the current number of references to this image file during *this* Natural Docs execution.
    +#
    +
    +#
    +#   Function: AddReference
    +#   Increases the number of references to this image file by one.  Returns the new reference count.
    +#
    +sub AddReference
    +    {
    +    my $self = shift;
    +
    +    $self->[REFERENCE_COUNT]++;
    +    return $self->[REFERENCE_COUNT];
    +    };
    +
    +#
    +#   Function: DeleteReference
    +#   Decreases the number of references to this image file by one.  Returns the new reference count.
    +#
    +sub DeleteReference
    +    {
    +    my $self = shift;
    +    $self->[REFERENCE_COUNT]--;
    +
    +    if ($self->[REFERENCE_COUNT] < 0)
    +        {  die "Deleted more references to an image file than existed.";  };
    +
    +    return $self->[REFERENCE_COUNT];
    +    };
    +
    +
    +#
    +#   Functions: Member Functions
    +#
    +#   WasUsed - Returns whether this image file was used during the *last* Natural Docs execution.
    +#   SetWasUsed - Sets whether this image file was used during the *last* Natural Docs execution.
    +#   Width - Returns the width in pixels, undef if it can't be determined, and -1 if determination hasn't been attempted yet.
    +#   Height - Returns the width in pixels, undef if it can't be determined, and -1 if determination hasn't been attempted yet.
    +#
    +
    +
    +#
    +#   Function: SetDimensions
    +#   Sets the width and height of the image.  Set to undef if they can't be determined.
    +#
    +sub SetDimensions #(int width, int height)
    +    {
    +    my ($self, $width, $height) = @_;
    +
    +    # If either are undef, both should be undef.  This will also convert zeroes to undef.
    +    if (!$width || !$height)
    +        {
    +        $self->[WIDTH] = undef;
    +        $self->[HEIGHT] = undef;
    +        }
    +    else
    +        {
    +        $self->[WIDTH] = $width;
    +        $self->[HEIGHT] = $height;
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Project/SourceFile.pm b/vendor/naturaldocs/Modules/NaturalDocs/Project/SourceFile.pm
    new file mode 100644
    index 000000000..aff05c7a5
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Project/SourceFile.pm
    @@ -0,0 +1,114 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Project::SourceFile
    +#
    +###############################################################################
    +#
    +#   A simple information class about project files.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Project::SourceFile;
    +
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are used as indexes.
    +#
    +#       HAS_CONTENT             - Whether the file contains Natural Docs content or not.
    +#       LAST_MODIFIED           - The integer timestamp of when the file was last modified.
    +#       STATUS                       - <FileStatus> since the last build.
    +#       DEFAULT_MENU_TITLE  - The file's default title in the menu.
    +#
    +
    +# DEPENDENCY: New() depends on its parameter list being in the same order as these constants.  If the order changes, New()
    +# needs to be changed.
    +use NaturalDocs::DefineMembers 'HAS_CONTENT', 'LAST_MODIFIED', 'STATUS', 'DEFAULT_MENU_TITLE';
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new file object.
    +#
    +#   Parameters:
    +#
    +#       hasContent         - Whether the file contains Natural Docs content or not.
    +#       lastModified         - The integer timestamp of when the file was last modified.
    +#       status                 - The <FileStatus> since the last build.
    +#       defaultMenuTitle  - The file's title in the menu.
    +#
    +#   Returns:
    +#
    +#       A reference to the new object.
    +#
    +sub New #(hasContent, lastModified, status, defaultMenuTitle)
    +    {
    +    # DEPENDENCY: This function depends on its parameter list being in the same order as the member constants.  If either order
    +    # changes, this function needs to be changed.
    +
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +# Function: HasContent
    +# Returns whether the file contains Natural Docs content or not.
    +sub HasContent
    +    {  return $_[0]->[HAS_CONTENT];  };
    +
    +# Function: SetHasContent
    +# Sets whether the file contains Natural Docs content or not.
    +sub SetHasContent #(hasContent)
    +    {  $_[0]->[HAS_CONTENT] = $_[1];  };
    +
    +# Function: LastModified
    +# Returns the integer timestamp of when the file was last modified.
    +sub LastModified
    +    {  return $_[0]->[LAST_MODIFIED];  };
    +
    +# Function: SetLastModified
    +# Sets the file's last modification timestamp.
    +sub SetLastModified #(lastModified)
    +    {  $_[0]->[LAST_MODIFIED] = $_[1];  };
    +
    +# Function: Status
    +# Returns the <FileStatus> since the last build.
    +sub Status
    +    {  return $_[0]->[STATUS];  };
    +
    +# Function: SetStatus
    +# Sets the <FileStatus> since the last build.
    +sub SetStatus #(status)
    +    {  $_[0]->[STATUS] = $_[1];  };
    +
    +# Function: DefaultMenuTitle
    +# Returns the file's default title on the menu.
    +sub DefaultMenuTitle
    +    {  return $_[0]->[DEFAULT_MENU_TITLE];  };
    +
    +# Function: SetDefaultMenuTitle
    +# Sets the file's default title on the menu.
    +sub SetDefaultMenuTitle #(menuTitle)
    +    {  $_[0]->[DEFAULT_MENU_TITLE] = $_[1];  };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/ReferenceString.pm b/vendor/naturaldocs/Modules/NaturalDocs/ReferenceString.pm
    new file mode 100644
    index 000000000..31fef757b
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/ReferenceString.pm
    @@ -0,0 +1,335 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::ReferenceString
    +#
    +###############################################################################
    +#
    +#   A package to manage <ReferenceString> handling throughout the program.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::ReferenceString;
    +
    +use vars '@ISA', '@EXPORT';
    +@ISA = 'Exporter';
    +@EXPORT = ( 'BINARYREF_NOTYPE', 'BINARYREF_NORESOLVINGFLAGS',
    +
    +                     'REFERENCE_TEXT', 'REFERENCE_CH_CLASS', 'REFERENCE_CH_PARENT',
    +
    +                     'RESOLVE_RELATIVE', 'RESOLVE_ABSOLUTE', 'RESOLVE_NOPLURAL', 'RESOLVE_NOUSING' );
    +
    +
    +#
    +#   Constants: Binary Format Flags
    +#
    +#   These flags can be combined to specify the format when using <ToBinaryFile()> and <FromBinaryFile()>.  All are exported
    +#   by default.
    +#
    +#   BINARYREF_NOTYPE - Do not include the <ReferenceType>.
    +#   BINARYREF_NORESOLVEFLAGS - Do not include the <Resolving Flags>.
    +#
    +use constant BINARYREF_NOTYPE => 0x01;
    +use constant BINARYREF_NORESOLVINGFLAGS => 0x02;
    +
    +
    +#
    +#   Constants: ReferenceType
    +#
    +#   The type of a reference.
    +#
    +#       REFERENCE_TEXT - The reference appears in the text of the documentation.
    +#       REFERENCE_CH_CLASS - A class reference handled by <NaturalDocs::ClassHierarchy>.
    +#       REFERENCE_CH_PARENT - A parent class reference handled by <NaturalDocs::ClassHierarchy>.
    +#
    +#   Dependencies:
    +#
    +#       - <ToBinaryFile()> and <FromBinaryFile()> require that these values fit into a UInt8, i.e. are <= 255.
    +#
    +use constant REFERENCE_TEXT => 1;
    +use constant REFERENCE_CH_CLASS => 2;
    +use constant REFERENCE_CH_PARENT => 3;
    +
    +
    +#
    +#   Constants: Resolving Flags
    +#
    +#   Used to influence the method of resolving references in <NaturalDocs::SymbolTable>.
    +#
    +#       RESOLVE_RELATIVE - The reference text is truly relative, rather than Natural Docs' semi-relative.
    +#       RESOLVE_ABSOLUTE - The reference text is always absolute.  No local or relative references.
    +#       RESOLVE_NOPLURAL - The reference text may not be interpreted as a plural, and thus match singular forms as well.
    +#       RESOLVE_NOUSING - The reference text may not include "using" statements when being resolved.
    +#
    +#       If neither <RESOLVE_RELATIVE> or <RESOLVE_ABSOLUTE> is specified, Natural Docs' semi-relative kicks in instead,
    +#       which is where links are interpreted as local, then global, then relative.  <RESOLVE_RELATIVE> states that links are
    +#       local, then relative, then global.
    +#
    +#   Dependencies:
    +#
    +#       - <ToBinaryFile()> and <FromBinaryFile()> require that these values fit into a UInt8, i.e. are <= 255.
    +#
    +use constant RESOLVE_RELATIVE => 0x01;
    +use constant RESOLVE_ABSOLUTE => 0x02;
    +use constant RESOLVE_NOPLURAL => 0x04;
    +use constant RESOLVE_NOUSING => 0x08;
    +
    +
    +#
    +#
    +#   Function: MakeFrom
    +#
    +#   Encodes the passed information as a <ReferenceString>.  The format of the string should be treated as opaque.  However, the
    +#   characteristic you can rely on is that the same string will always be made from the same parameters, and thus it's suitable
    +#   for comparison and use as hash keys.
    +#
    +#   Parameters:
    +#
    +#       type - The <ReferenceType>.
    +#       symbol - The <SymbolString> of the reference.
    +#       language - The name of the language that defines the file this reference appears in.
    +#       scope - The scope <SymbolString> the reference appears in, or undef if none.
    +#       using - An arrayref of scope <SymbolStrings> that are also available for checking due to the equivalent a "using" statement,
    +#                  or undef if none.
    +#       resolvingFlags - The <Resolving Flags> to use with this reference.  They are ignored if the type is <REFERENCE_TEXT>.
    +#
    +#   Returns:
    +#
    +#       The encoded <ReferenceString>.
    +#
    +sub MakeFrom #(ReferenceType type, SymbolString symbol, string language, SymbolString scope, SymbolString[]* using, flags resolvingFlags)
    +    {
    +    my ($self, $type, $symbol, $language, $scope, $using, $resolvingFlags) = @_;
    +
    +    if ($type == ::REFERENCE_TEXT() || $resolvingFlags == 0)
    +       {  $resolvingFlags = undef;  };
    +
    +    # The format is [type] 0x1E [resolving flags] 0x1E [symbol] 0x1E [scope] ( 0x1E [using] )*
    +    # If there is no scope and/or using, the separator characters still remain.
    +
    +    # DEPENDENCY: SymbolString->FromText() removed all 0x1E characters.
    +    # DEPENDENCY: SymbolString->FromText() doesn't use 0x1E characters in its encoding.
    +
    +    my $string = $type . "\x1E" . $symbol . "\x1E" . $language . "\x1E" . $resolvingFlags . "\x1E";
    +
    +    if (defined $scope)
    +        {
    +        $string .= $scope;
    +        };
    +
    +    $string .= "\x1E";
    +
    +    if (defined $using)
    +        {
    +        $string .= join("\x1E", @$using);
    +        };
    +
    +    return $string;
    +    };
    +
    +
    +#
    +#   Function: ToBinaryFile
    +#
    +#   Writes a <ReferenceString> to the passed filehandle.  Can also encode an undef.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The filehandle to write to.
    +#       referenceString - The <ReferenceString> to write, or undef.
    +#       binaryFormatFlags - Any <Binary Format Flags> you want to use to influence encoding.
    +#
    +#   Format:
    +#
    +#       > [SymbolString: Symbol or undef for an undef reference]
    +#       > [AString16: language]
    +#       > [SymbolString: Scope or undef for none]
    +#       >
    +#       > [SymbolString: Using or undef for none]
    +#       > [SymbolString: Using or undef for no more]
    +#       > ...
    +#       >
    +#       > [UInt8: Type unless BINARYREF_NOTYPE is set]
    +#       > [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set]
    +#
    +#   Dependencies:
    +#
    +#       - <ReferenceTypes> must fit into a UInt8.  All values must be <= 255.
    +#       - All <Resolving Flags> must fit into a UInt8.  All values must be <= 255.
    +#
    +sub ToBinaryFile #(FileHandle fileHandle, ReferenceString referenceString, flags binaryFormatFlags)
    +    {
    +    my ($self, $fileHandle, $referenceString, $binaryFormatFlags) = @_;
    +
    +    my ($type, $symbol, $language, $scope, $using, $resolvingFlags) = $self->InformationOf($referenceString);
    +
    +    # [SymbolString: Symbol or undef for an undef reference]
    +
    +    NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $symbol);
    +
    +    # [AString16: language]
    +
    +    print $fileHandle pack('nA*', length $language, $language);
    +
    +    # [SymbolString: scope or undef if none]
    +
    +    NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $scope);
    +
    +    # [SymbolString: using or undef if none/no more] ...
    +
    +    if (defined $using)
    +        {
    +        foreach my $usingScope (@$using)
    +            {  NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $usingScope);  };
    +        };
    +
    +    NaturalDocs::SymbolString->ToBinaryFile($fileHandle, undef);
    +
    +    # [UInt8: Type unless BINARYREF_NOTYPE is set]
    +
    +    if (!($binaryFormatFlags & BINARYREF_NOTYPE))
    +        {  print $fileHandle pack('C', $type);  };
    +
    +    # [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set]
    +
    +    if (!($binaryFormatFlags & BINARYREF_NORESOLVINGFLAGS))
    +        {  print $fileHandle pack('C', $type);  };
    +    };
    +
    +
    +#
    +#   Function: FromBinaryFile
    +#
    +#   Reads a <ReferenceString> or undef from the passed filehandle.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The filehandle to read from.
    +#       binaryFormatFlags - Any <Binary Format Flags> you want to use to influence decoding.
    +#       type - The <ReferenceType> to use if <BINARYREF_NOTYPE> is set.
    +#       resolvingFlags - The <Resolving Flags> to use if <BINARYREF_NORESOLVINGFLAGS> is set.
    +#
    +#   Returns:
    +#
    +#       The <ReferenceString> or undef.
    +#
    +#   See Also:
    +#
    +#       See <ToBinaryFile()> for format and dependencies.
    +#
    +sub FromBinaryFile #(FileHandle fileHandle, flags binaryFormatFlags, ReferenceType type, flags resolvingFlags)
    +    {
    +    my ($self, $fileHandle, $binaryFormatFlags, $type, $resolvingFlags) = @_;
    +    my $raw;
    +
    +    # [SymbolString: Symbol or undef for an undef reference]
    +
    +    my $symbol = NaturalDocs::SymbolString->FromBinaryFile($fileHandle);
    +
    +    if (!defined $symbol)
    +        {  return undef;  };
    +
    +
    +    # [AString16: language]
    +
    +    read($fileHandle, $raw, 2);
    +    my $languageLength = unpack('n', $raw);
    +
    +    my $language;
    +    read($fileHandle, $language, $languageLength);
    +
    +
    +    # [SymbolString: scope or undef if none]
    +
    +    my $scope = NaturalDocs::SymbolString->FromBinaryFile($fileHandle);
    +
    +    # [SymbolString: using or undef if none/no more] ...
    +
    +    my $usingSymbol;
    +    my @using;
    +
    +    while ($usingSymbol = NaturalDocs::SymbolString->FromBinaryFile($fileHandle))
    +        {  push @using, $usingSymbol;  };
    +
    +    if (scalar @using)
    +        {  $usingSymbol = \@using;  }
    +    else
    +        {  $usingSymbol = undef;  };
    +
    +    # [UInt8: Type unless BINARYREF_NOTYPE is set]
    +
    +    if (!($binaryFormatFlags & BINARYREF_NOTYPE))
    +        {
    +        my $raw;
    +        read($fileHandle, $raw, 1);
    +        $type = unpack('C', $raw);
    +        };
    +
    +    # [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set]
    +
    +    if (!($binaryFormatFlags & BINARYREF_NORESOLVINGFLAGS))
    +        {
    +        my $raw;
    +        read($fileHandle, $raw, 1);
    +        $resolvingFlags = unpack('C', $raw);
    +        };
    +
    +    return $self->MakeFrom($type, $symbol, $language, $scope, $usingSymbol, $resolvingFlags);
    +    };
    +
    +
    +#
    +#   Function: InformationOf
    +#
    +#   Returns the information encoded in a <ReferenceString>.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to decode.
    +#
    +#   Returns:
    +#
    +#       The array ( type, symbol, language, scope, using, resolvingFlags ).
    +#
    +#       type - The <ReferenceType>.
    +#       symbol - The <SymbolString>.
    +#       language - The name of the language that defined the file the reference was defined in.
    +#       scope - The scope <SymbolString>, or undef if none.
    +#       using - An arrayref of scope <SymbolStrings> that the reference also has access to via "using" statements, or undef if none.
    +#       resolvingFlags - The <Resolving Flags> of the reference.
    +#
    +sub InformationOf #(ReferenceString referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    my ($type, $symbolString, $language, $resolvingFlags, $scopeString, @usingStrings) = split(/\x1E/, $referenceString);
    +
    +    if (!length $resolvingFlags)
    +        {  $resolvingFlags = undef;  };
    +
    +    return ( $type, $symbolString, $language, $scopeString, [ @usingStrings ], $resolvingFlags );
    +    };
    +
    +
    +#
    +#   Function: TypeOf
    +#
    +#   Returns the <ReferenceType> encoded in the reference string.  This is faster than <InformationOf()> if this is
    +#   the only information you need.
    +#
    +sub TypeOf #(ReferenceString referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    $referenceString =~ /^([^\x1E]+)/;
    +    return $1;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Settings.pm b/vendor/naturaldocs/Modules/NaturalDocs/Settings.pm
    new file mode 100644
    index 000000000..a9ed60c66
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Settings.pm
    @@ -0,0 +1,1480 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Settings
    +#
    +###############################################################################
    +#
    +#   A package to handle the command line and various other program settings.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use Cwd ();
    +
    +use NaturalDocs::Settings::BuildTarget;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Settings;
    +
    +
    +###############################################################################
    +# Group: Information
    +
    +=pod begin nd
    +
    +    Topic: Usage and Dependencies
    +
    +        - The <Constant Functions> can be called immediately.
    +
    +        - Prior to initialization, <NaturalDocs::Builder> must have all its output packages registered.
    +
    +        - To initialize, call <Load()>.  All functions except <InputDirectoryNameOf()> will then be available.
    +
    +        - <GenerateDirectoryNames()> must be called before <InputDirectoryNameOf()> will work.  Currently it is called by
    +          <NaturalDocs::Menu->LoadAndUpdate()>.
    +
    +
    +    Architecture: Internal Overview
    +
    +        - <Load()> first parses the command line, gathering all the settings and checking for errors.  All <NaturalDocs::Builder>
    +          packages must be registered before this is called because it needs their command line options.
    +          <NaturalDocs::Project->ReparseEverything()> and <NaturalDocs::Project->RebuildEverything()> are called right away if -r
    +          or -ro are used.
    +
    +        - Output directories are *not* named at this point.  See <Named Directories>.
    +
    +        - The previous settings from the last time Natural Docs was run are loaded and compared to the current settings.
    +          <NaturalDocs::Project->ReparseEverything()> and <NaturalDocs::Project->RebuildEverything()> are called if there are
    +          any differences that warrant it.
    +
    +        - It then waits for <GenerateDirectoryNames()> to be called by <NaturalDocs::Menu>.  The reason for this is that the
    +          previous directory names are stored as hints in the menu file, for reasons explained in <Named Directories>.  Once that
    +          happens all the unnamed directories have names generated for them so everything is named.  The package is completely
    +          set up.
    +
    +        - The input directories are stored in an array instead of a hash because the order they were declared in matters.  If two
    +          people use multiple input directories on separate computers without sharing a menu file, they should at least get consistent
    +          directory names by declaring them in the same order.
    +
    +
    +    Architecture: Named Directories
    +
    +        Ever since Natural Docs introduced multiple input directories in 1.16, they've had to be named.  Since they don't necessarily
    +        extend from the same root anymore, they can't share an output directory without the risk of file name conflicts.  There was
    +        an early attempt at giving them actual names, but now they're just numbered from 1.
    +
    +        Directory names aren't generated right away.  It waits for <Menu.txt> to load because that holds the obfuscated names from
    +        the last run.  <NaturalDocs::Menu> then calls <GenerateDirectoryNames()> and passes those along as hints.
    +        <GenerateDirectoryNames()> then applies them to any matches and generates new ones for any remaining.  This is done so
    +        that output page locations can remain consistent when built on multiple computers, so long as the menu file is shared.  I tend
    +        to think the menu file is the most likely configuration file to be shared.
    +
    +
    +    Architecture: Removed Directories
    +
    +        Directories that were part of the previous run but aren't anymore are still stored in the package.  The primary reason, though
    +        there may be others, is file purging.  If an input directory is removed, all the output files that were generated from anything
    +        in it need to be removed.  To find out what the output file name was for a removed source file, it needs to be able to split it
    +        from it's original input directory and know what that directory was named.  If this didn't happen those output files would be
    +        orphaned, as was the case prior to 1.32.
    +
    +=cut
    +
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +# handle: PREVIOUS_SETTINGS_FILEHANDLE
    +# The file handle used with <PreviousSettings.nd>.
    +
    +# array: inputDirectories
    +# An array of input directories.
    +my @inputDirectories;
    +
    +# array: inputDirectoryNames
    +# An array of the input directory names.  Each name corresponds to the directory of the same index in <inputDirectories>.
    +my @inputDirectoryNames;
    +
    +# array: imageDirectories
    +# An array of image directories.
    +my @imageDirectories;
    +
    +# array: imageDirectoryNames
    +# An array of the image directory names.  Each name corresponds to the directory of the same index in <imageDirectories>.
    +my @imageDirectoryNames;
    +
    +# array: relativeImageDirectories
    +# An array of the relative paths for images.  The asterisks found in the command line are not present.
    +my @relativeImageDirectories;
    +
    +# array: excludedInputDirectories
    +# An array of input directories to exclude.
    +my @excludedInputDirectories;
    +
    +# array: removedInputDirectories
    +# An array of input directories that were once in the command line but are no longer.
    +my @removedInputDirectories;
    +
    +# array: removedInputDirectoryNames
    +# An array of the removed input directories' names.  Each name corresponds to the directory of the same index in
    +# <removedInputDirectories>.
    +my @removedInputDirectoryNames;
    +
    +# array: removedImageDirectories
    +# An array of image directories that were once in the command line but are no longer.
    +my @removedImageDirectories;
    +
    +# array: removedImageDirectoryNames
    +# An array of the removed image directories' names.  Each name corresponds to the directory of the same index in
    +# <removedImageDirectories>.
    +my @removedImageDirectoryNames;
    +
    +# var: projectDirectory
    +# The project directory.
    +my $projectDirectory;
    +
    +# array: buildTargets
    +# An array of <NaturalDocs::Settings::BuildTarget>s.
    +my @buildTargets;
    +
    +# var: documentedOnly
    +# Whether undocumented code aspects should be included in the output.
    +my $documentedOnly;
    +
    +# int: tabLength
    +# The number of spaces in tabs.
    +my $tabLength;
    +
    +# bool: noAutoGroup
    +# Whether auto-grouping is turned off.
    +my $noAutoGroup;
    +
    +# bool: onlyFileTitles
    +# Whether source files should always use the file name as the title.
    +my $onlyFileTitles;
    +
    +# bool: isQuiet
    +# Whether the script should be run in quiet mode or not.
    +my $isQuiet;
    +
    +# bool: rebuildData
    +# WHether most data files should be ignored and rebuilt.
    +my $rebuildData;
    +
    +# array: styles
    +# An array of style names to use, most important first.
    +my @styles;
    +
    +# var: highlightCode
    +# Whether syntax highlighting should be applied to code tags.
    +my $highlightCode;
    +
    +# var: highlightAnonymous
    +# Whether syntax highlighting should be applied to anonymous code tags.
    +my $highlightAnonymous;
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: PreviousSettings.nd
    +#
    +#   Stores the previous command line settings.
    +#
    +#   Format:
    +#
    +#       > [BINARY_FORMAT]
    +#       > [VersionInt: app version]
    +#
    +#       The file starts with the standard <BINARY_FORMAT> <VersionInt> header.
    +#
    +#       > [UInt8: tab length]
    +#       > [UInt8: documented only (0 or 1)]
    +#       > [UInt8: no auto-group (0 or 1)]
    +#       > [UInt8: only file titles (0 or 1)]
    +#		> [UInt8: highlight code (0 or 1)]
    +#		> [UInt8: highlight anonymous (0 or 1)]
    +#       >
    +#       > [UInt8: number of input directories]
    +#       > [AString16: input directory] [AString16: input directory name] ...
    +#
    +#       A count of input directories, then that number of directory/name pairs.
    +#
    +#       > [UInt8: number of output targets]
    +#       > [AString16: output directory] [AString16: output format command line option] ...
    +#
    +#       A count of output targets, then that number of directory/format pairs.
    +#
    +#
    +#   Revisions:
    +#
    +#		1.51:
    +#
    +#			- Removed charset.
    +#
    +#		1.5:
    +#
    +#			- Added highlight code and highlight anonymous.
    +#
    +#       1.4:
    +#
    +#           - Added only file titles.
    +#
    +#       1.33:
    +#
    +#           - Added charset.
    +#
    +#       1.3:
    +#
    +#           - Removed headers-only, which was a 0/1 UInt8 after tab length.
    +#           - Change auto-group level (1 = no, 2 = yes, 3 = full only) to no auto-group (0 or 1).
    +#
    +#       1.22:
    +#
    +#           - Added auto-group level.
    +#
    +#       1.2:
    +#
    +#           - File was added to the project.  Prior to 1.2, it didn't exist.
    +#
    +
    +
    +###############################################################################
    +# Group: Action Functions
    +
    +#
    +#   Function: Load
    +#
    +#   Loads and parses all settings from the command line and configuration files.  Will exit if the options are invalid or the syntax
    +#   reference was requested.
    +#
    +sub Load
    +    {
    +    my ($self) = @_;
    +
    +    $self->ParseCommandLine();
    +    $self->LoadAndComparePreviousSettings();
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves all settings in configuration files to disk.
    +#
    +sub Save
    +    {
    +    my ($self) = @_;
    +
    +    $self->SavePreviousSettings();
    +    };
    +
    +
    +#
    +#   Function: GenerateDirectoryNames
    +#
    +#   Generates names for each of the input and image directories, which can later be retrieved with <InputDirectoryNameOf()>
    +#   and <ImageDirectoryNameOf()>.
    +#
    +#   Parameters:
    +#
    +#       inputHints - A hashref of suggested input directory names, where the keys are the directories and the values are the names.
    +#                        These take precedence over anything generated.  You should include names for directories that are no longer in
    +#                        the command line.  This parameter may be undef.
    +#       imageHints - Same as inputHints, only for the image directories.
    +#
    +sub GenerateDirectoryNames #(hashref inputHints, hashref imageHints)
    +    {
    +    my ($self, $inputHints, $imageHints) = @_;
    +
    +    my %usedInputNames;
    +    my %usedImageNames;
    +
    +
    +    if (defined $inputHints)
    +        {
    +        # First, we have to convert all non-numeric names to numbers, since they may come from a pre-1.32 menu file.  We do it
    +        # here instead of in NaturalDocs::Menu to keep the naming scheme centralized.
    +
    +        my @names = values %$inputHints;
    +        my $hasNonNumeric;
    +
    +        foreach my $name (@names)
    +            {
    +            if ($name !~ /^[0-9]+$/)
    +                {
    +                $hasNonNumeric = 1;
    +                last;
    +                };
    +            };
    +
    +
    +        if ($hasNonNumeric)
    +            {
    +            # Hash mapping old names to new names.
    +            my %conversion;
    +
    +            # The sequential number to use.  Starts at two because we want 'default' to be one.
    +            my $currentNumber = 2;
    +
    +            # If there's only one name, we set it to one no matter what it was set to before.
    +            if (scalar @names == 1)
    +                {  $conversion{$names[0]} = 1;  }
    +            else
    +                {
    +                # We sort the list first because we want the end result to be predictable.  This conversion could be happening on many
    +                # machines, and they may not all specify the input directories in the same order.  They need to all come up with the
    +                # same result.
    +                @names = sort @names;
    +
    +                foreach my $name (@names)
    +                    {
    +                    if ($name eq 'default')
    +                        {  $conversion{$name} = 1;  }
    +                    else
    +                        {
    +                        $conversion{$name} = $currentNumber;
    +                        $currentNumber++;
    +                        };
    +                    };
    +                };
    +
    +            # Convert them to the new names.
    +            foreach my $directory (keys %$inputHints)
    +                {
    +                $inputHints->{$directory} = $conversion{ $inputHints->{$directory} };
    +                };
    +            };
    +
    +
    +        # Now we apply all the names from the hints, and save any unused ones as removed directories.
    +
    +        for (my $i = 0; $i < scalar @inputDirectories; $i++)
    +            {
    +            if (exists $inputHints->{$inputDirectories[$i]})
    +                {
    +                $inputDirectoryNames[$i] = $inputHints->{$inputDirectories[$i]};
    +                $usedInputNames{ $inputDirectoryNames[$i] } = 1;
    +                delete $inputHints->{$inputDirectories[$i]};
    +                };
    +            };
    +
    +
    +        # Any remaining hints are saved as removed directories.
    +
    +        while (my ($directory, $name) = each %$inputHints)
    +            {
    +            push @removedInputDirectories, $directory;
    +            push @removedInputDirectoryNames, $name;
    +            };
    +        };
    +
    +
    +    if (defined $imageHints)
    +        {
    +        # Image directory names were never non-numeric, so there is no conversion.  Apply all the names from the hints.
    +
    +        for (my $i = 0; $i < scalar @imageDirectories; $i++)
    +            {
    +            if (exists $imageHints->{$imageDirectories[$i]})
    +                {
    +                $imageDirectoryNames[$i] = $imageHints->{$imageDirectories[$i]};
    +                $usedImageNames{ $imageDirectoryNames[$i] } = 1;
    +                delete $imageHints->{$imageDirectories[$i]};
    +                };
    +            };
    +
    +
    +        # Any remaining hints are saved as removed directories.
    +
    +        while (my ($directory, $name) = each %$imageHints)
    +            {
    +            push @removedImageDirectories, $directory;
    +            push @removedImageDirectoryNames, $name;
    +            };
    +        };
    +
    +
    +    # Now we generate names for anything remaining.
    +
    +    my $inputCounter = 1;
    +
    +    for (my $i = 0; $i < scalar @inputDirectories; $i++)
    +        {
    +        if (!defined $inputDirectoryNames[$i])
    +            {
    +            while (exists $usedInputNames{$inputCounter})
    +                {  $inputCounter++;  };
    +
    +            $inputDirectoryNames[$i] = $inputCounter;
    +            $usedInputNames{$inputCounter} = 1;
    +
    +            $inputCounter++;
    +            };
    +        };
    +
    +
    +    my $imageCounter = 1;
    +
    +    for (my $i = 0; $i < scalar @imageDirectories; $i++)
    +        {
    +        if (!defined $imageDirectoryNames[$i])
    +            {
    +            while (exists $usedImageNames{$imageCounter})
    +                {  $imageCounter++;  };
    +
    +            $imageDirectoryNames[$i] = $imageCounter;
    +            $usedImageNames{$imageCounter} = 1;
    +
    +            $imageCounter++;
    +            };
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +
    +#
    +#   Function: InputDirectories
    +#
    +#   Returns an arrayref of input directories.  Do not change.
    +#
    +#   This will not return any removed input directories.
    +#
    +sub InputDirectories
    +    {  return \@inputDirectories;  };
    +
    +#
    +#   Function: InputDirectoryNameOf
    +#
    +#   Returns the generated name of the passed input directory.  <GenerateDirectoryNames()> must be called once before this
    +#   function is available.
    +#
    +#   If a name for a removed input directory is available, it will be returned as well.
    +#
    +sub InputDirectoryNameOf #(directory)
    +    {
    +    my ($self, $directory) = @_;
    +
    +    for (my $i = 0; $i < scalar @inputDirectories; $i++)
    +        {
    +        if ($directory eq $inputDirectories[$i])
    +            {  return $inputDirectoryNames[$i];  };
    +        };
    +
    +    for (my $i = 0; $i < scalar @removedInputDirectories; $i++)
    +        {
    +        if ($directory eq $removedInputDirectories[$i])
    +            {  return $removedInputDirectoryNames[$i];  };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: SplitFromInputDirectory
    +#
    +#   Takes an input file name and returns the array ( inputDirectory, relativePath ).
    +#
    +#   If the file cannot be split from an input directory, it will try to do it with the removed input directories.
    +#
    +sub SplitFromInputDirectory #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    foreach my $directory (@inputDirectories, @removedInputDirectories)
    +        {
    +        if (NaturalDocs::File->IsSubPathOf($directory, $file))
    +            {  return ( $directory, NaturalDocs::File->MakeRelativePath($directory, $file) );  };
    +        };
    +
    +    return ( );
    +    };
    +
    +
    +#
    +#   Function: ImageDirectories
    +#
    +#   Returns an arrayref of image directories.  Do not change.
    +#
    +#   This will not return any removed image directories.
    +#
    +sub ImageDirectories
    +    {  return \@imageDirectories;  };
    +
    +
    +#
    +#   Function: ImageDirectoryNameOf
    +#
    +#   Returns the generated name of the passed image or input directory.  <GenerateDirectoryNames()> must be called once before
    +#   this function is available.
    +#
    +#   If a name for a removed input or image directory is available, it will be returned as well.
    +#
    +sub ImageDirectoryNameOf #(directory)
    +    {
    +    my ($self, $directory) = @_;
    +
    +    for (my $i = 0; $i < scalar @imageDirectories; $i++)
    +        {
    +        if ($directory eq $imageDirectories[$i])
    +            {  return $imageDirectoryNames[$i];  };
    +        };
    +
    +    for (my $i = 0; $i < scalar @removedImageDirectories; $i++)
    +        {
    +        if ($directory eq $removedImageDirectories[$i])
    +            {  return $removedImageDirectoryNames[$i];  };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +#
    +#   Function: SplitFromImageDirectory
    +#
    +#   Takes an input image file name and returns the array ( imageDirectory, relativePath ).
    +#
    +#   If the file cannot be split from an image directory, it will try to do it with the removed image directories.
    +#
    +sub SplitFromImageDirectory #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    foreach my $directory (@imageDirectories, @removedImageDirectories)
    +        {
    +        if (NaturalDocs::File->IsSubPathOf($directory, $file))
    +            {  return ( $directory, NaturalDocs::File->MakeRelativePath($directory, $file) );  };
    +        };
    +
    +    return ( );
    +    };
    +
    +
    +#
    +#   Function: RelativeImageDirectories
    +#
    +#   Returns an arrayref of relative image directories.  Do not change.
    +#
    +sub RelativeImageDirectories
    +    {  return \@relativeImageDirectories;  };
    +
    +
    +# Function: ExcludedInputDirectories
    +# Returns an arrayref of input directories to exclude.  Do not change.
    +sub ExcludedInputDirectories
    +    {  return \@excludedInputDirectories;  };
    +
    +
    +# Function: BuildTargets
    +# Returns an arrayref of <NaturalDocs::Settings::BuildTarget>s.  Do not change.
    +sub BuildTargets
    +    {  return \@buildTargets;  };
    +
    +
    +#
    +#   Function: OutputDirectoryOf
    +#
    +#   Returns the output directory of a builder object.
    +#
    +#   Parameters:
    +#
    +#       object - The builder object, whose class is derived from <NaturalDocs::Builder::Base>.
    +#
    +#   Returns:
    +#
    +#       The builder directory, or undef if the object wasn't found..
    +#
    +sub OutputDirectoryOf #(object)
    +    {
    +    my ($self, $object) = @_;
    +
    +    foreach my $buildTarget (@buildTargets)
    +        {
    +        if ($buildTarget->Builder() == $object)
    +            {  return $buildTarget->Directory();  };
    +        };
    +
    +    return undef;
    +    };
    +
    +
    +# Function: Styles
    +# Returns an arrayref of the styles associated with the output.
    +sub Styles
    +    {  return \@styles;  };
    +
    +# Function: ProjectDirectory
    +# Returns the project directory.
    +sub ProjectDirectory
    +    {  return $projectDirectory;  };
    +
    +# Function: ProjectDataDirectory
    +# Returns the project data directory.
    +sub ProjectDataDirectory
    +    {  return NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1);  };
    +
    +# Function: StyleDirectory
    +# Returns the main style directory.
    +sub StyleDirectory
    +    {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Styles', 1);  };
    +
    +# Function: JavaScriptDirectory
    +# Returns the main JavaScript directory.
    +sub JavaScriptDirectory
    +    {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'JavaScript', 1);  };
    +
    +# Function: ConfigDirectory
    +# Returns the main configuration directory.
    +sub ConfigDirectory
    +    {  return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Config', 1);  };
    +
    +# Function: DocumentedOnly
    +# Returns whether undocumented code aspects should be included in the output.
    +sub DocumentedOnly
    +    {  return $documentedOnly;  };
    +
    +# Function: TabLength
    +# Returns the number of spaces tabs should be expanded to.
    +sub TabLength
    +    {  return $tabLength;  };
    +
    +# Function: NoAutoGroup
    +# Returns whether auto-grouping is turned off.
    +sub NoAutoGroup
    +    {  return $noAutoGroup;  };
    +
    +# Function: OnlyFileTitles
    +# Returns whether source files should always use the file name as the title.
    +sub OnlyFileTitles
    +    {  return $onlyFileTitles;  };
    +
    +# Function: IsQuiet
    +# Returns whether the script should be run in quiet mode or not.
    +sub IsQuiet
    +    {  return $isQuiet;  };
    +
    +# Function: RebuildData
    +# Returns whether all data files should be ignored and rebuilt.
    +sub RebuildData
    +    {  return $rebuildData;  };
    +
    +# Function: HighlightCode
    +# Returns whether to apply syntax highlighting (start code) sections.
    +sub HighlightCode
    +	{  return $highlightCode;  }
    +
    +# Function: HighlightAnonymous
    +# Returns whether to apply syntax highlighting to anonymous code sections designated with :, >, or |.
    +sub HighlightAnonymous
    +	{  return $highlightAnonymous;  }
    +
    +
    +###############################################################################
    +# Group: Constant Functions
    +
    +#
    +#   Function: AppVersion
    +#
    +#   Returns Natural Docs' version number as an integer.  Use <TextAppVersion()> to get a printable version.
    +#
    +sub AppVersion
    +    {
    +    my ($self) = @_;
    +    return NaturalDocs::Version->FromString($self->TextAppVersion());
    +    };
    +
    +#
    +#   Function: TextAppVersion
    +#
    +#   Returns Natural Docs' version number as plain text.
    +#
    +sub TextAppVersion
    +    {
    +    return '1.51';
    +    };
    +
    +#
    +#   Function: AppURL
    +#
    +#   Returns a string of the project's current web address.
    +#
    +sub AppURL
    +    {  return 'http://www.naturaldocs.org';  };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: ParseCommandLine
    +#
    +#   Parses and validates the command line.  Will cause the script to exit if the options ask for the syntax reference or
    +#   are invalid.
    +#
    +sub ParseCommandLine
    +    {
    +    my ($self) = @_;
    +
    +    my %synonyms = ( 'input'    => '-i',
    +                                  'source' => '-i',
    +                                  'excludeinput' => '-xi',
    +                                  'excludesource' => '-xi',
    +                                  'images' => '-img',
    +                                  'output'  => '-o',
    +                                  'project' => '-p',
    +                                  'documentedonly' => '-do',
    +                                  'style'    => '-s',
    +                                  'rebuild' => '-r',
    +                                  'rebuildoutput' => '-ro',
    +                                  'tablength' => '-t',
    +                                  'quiet'    => '-q',
    +                                  'headersonly' => '-ho',
    +                                  'help'     => '-h',
    +                                  'autogroup' => '-ag',
    +                                  'noautogroup' => '-nag',
    +                                  'onlyfiletitles' => '-oft',
    +                                  'onlyfiletitle' => '-oft',
    +                                  'highlight' => '-hl',
    +                                  'highlighting' => '-hl' );
    +
    +
    +    my @errorMessages;
    +
    +    my $valueRef;
    +    my $option;
    +
    +    my @outputStrings;
    +    my @imageStrings;
    +    my $highlightString;
    +
    +
    +    # Sometimes $valueRef is set to $ignored instead of undef because we don't want certain errors to cause other,
    +    # unnecessary errors.  For example, if they set the input directory twice, we want to show that error and swallow the
    +    # specified directory without complaint.  Otherwise it would complain about the directory too as if it were random crap
    +    # inserted into the command line.
    +    my $ignored;
    +
    +    my $index = 0;
    +
    +    while ($index < scalar @ARGV)
    +        {
    +        my $arg = $ARGV[$index];
    +
    +        if (substr($arg, 0, 1) eq '-')
    +            {
    +            $option = lc($arg);
    +
    +            # Support options like -t2 as well as -t 2.
    +            if ($option =~ /^([^0-9]+)([0-9]+)$/)
    +                {
    +                $option = $1;
    +                splice(@ARGV, $index + 1, 0, $2);
    +                };
    +
    +            # Convert long forms to short.
    +            if (substr($option, 1, 1) eq '-')
    +                {
    +                # Strip all dashes.
    +                my $newOption = $option;
    +                $newOption =~ tr/-//d;
    +
    +                if (exists $synonyms{$newOption})
    +                    {  $option = $synonyms{$newOption};  }
    +                }
    +
    +            if ($option eq '-i')
    +                {
    +                push @inputDirectories, undef;
    +                $valueRef = \$inputDirectories[-1];
    +                }
    +            elsif ($option eq '-xi')
    +                {
    +                push @excludedInputDirectories, undef;
    +                $valueRef = \$excludedInputDirectories[-1];
    +                }
    +            elsif ($option eq '-img')
    +                {
    +                push @imageStrings, undef;
    +                $valueRef = \$imageStrings[-1];
    +                }
    +            elsif ($option eq '-p')
    +                {
    +                if (defined $projectDirectory)
    +                    {
    +                    push @errorMessages, 'You cannot have more than one project directory.';
    +                    $valueRef = \$ignored;
    +                    }
    +                else
    +                    {  $valueRef = \$projectDirectory;  };
    +                }
    +            elsif ($option eq '-o')
    +                {
    +                push @outputStrings, undef;
    +                $valueRef = \$outputStrings[-1];
    +                }
    +            elsif ($option eq '-s')
    +                {
    +                $valueRef = \$styles[0];
    +                }
    +            elsif ($option eq '-t')
    +                {
    +                $valueRef = \$tabLength;
    +                }
    +            elsif ($option eq '-hl')
    +            	{
    +            	$valueRef = \$highlightString;
    +            	}
    +            elsif ($option eq '-ag')
    +                {
    +                push @errorMessages, 'The -ag setting is no longer supported.  You can use -nag (--no-auto-group) to turn off '
    +                                               . "auto-grouping, but there aren't multiple levels anymore.";
    +                $valueRef = \$ignored;
    +                }
    +
    +            # Options that aren't followed by content.
    +            else
    +                {
    +                $valueRef = undef;
    +
    +                if ($option eq '-r')
    +                    {
    +                    NaturalDocs::Project->ReparseEverything();
    +                    NaturalDocs::Project->RebuildEverything();
    +                    $rebuildData = 1;
    +                    }
    +                elsif ($option eq '-ro')
    +                    {
    +                    NaturalDocs::Project->RebuildEverything();
    +                    }
    +                elsif ($option eq '-do')
    +                    {  $documentedOnly = 1;  }
    +                elsif ($option eq '-oft')
    +                    {  $onlyFileTitles = 1;  }
    +                elsif ($option eq '-q')
    +                    {  $isQuiet = 1;  }
    +                elsif ($option eq '-ho')
    +                    {
    +                    push @errorMessages, 'The -ho setting is no longer supported.  You can have Natural Docs skip over the source file '
    +                                                   . 'extensions by editing Languages.txt in your project directory.';
    +                    }
    +                elsif ($option eq '-nag')
    +                    {  $noAutoGroup = 1;  }
    +                elsif ($option eq '-?' || $option eq '-h')
    +                    {
    +                    $self->PrintSyntax();
    +                    exit;
    +                    }
    +                else
    +                    {  push @errorMessages, 'Unrecognized option ' . $option;  };
    +
    +                };
    +
    +            }
    +
    +        # Is a segment of text, not an option...
    +        else
    +            {
    +            if (defined $valueRef)
    +                {
    +                # We want to preserve spaces in paths.
    +                if (defined $$valueRef)
    +                    {  $$valueRef .= ' ';  };
    +
    +                $$valueRef .= $arg;
    +                }
    +
    +            else
    +                {
    +                push @errorMessages, 'Unrecognized element ' . $arg;
    +                };
    +            };
    +
    +        $index++;
    +        };
    +
    +
    +    # Validate the style, if specified.
    +
    +    if ($styles[0])
    +        {
    +        my @stylePieces = split(/ +/, $styles[0]);
    +        @styles = ( );
    +
    +        while (scalar @stylePieces)
    +            {
    +            if (lc($stylePieces[0]) eq 'custom')
    +                {
    +                push @errorMessages, 'The "Custom" style setting is no longer supported.  Copy your custom style sheet to your '
    +                                               . 'project directory and you can refer to it with -s.';
    +                shift @stylePieces;
    +                }
    +            else
    +                {
    +                # People may use styles with spaces in them.  If a style doesn't exist, we need to join the pieces until we find one that
    +                # does or we run out of pieces.
    +
    +                my $extras = 0;
    +                my $success;
    +
    +                while ($extras < scalar @stylePieces)
    +                    {
    +                    my $style;
    +
    +                    if (!$extras)
    +                        {  $style = $stylePieces[0];  }
    +                    else
    +                        {  $style = join(' ', @stylePieces[0..$extras]);  };
    +
    +                    my $cssFile = NaturalDocs::File->JoinPaths( $self->StyleDirectory(), $style . '.css' );
    +                    if (-e $cssFile)
    +                        {
    +                        push @styles, $style;
    +                        splice(@stylePieces, 0, 1 + $extras);
    +                        $success = 1;
    +                        last;
    +                        }
    +                    else
    +                        {
    +                        $cssFile = NaturalDocs::File->JoinPaths( $self->ProjectDirectory(), $style . '.css' );
    +
    +                        if (-e $cssFile)
    +                            {
    +                            push @styles, $style;
    +                            splice(@stylePieces, 0, 1 + $extras);
    +                            $success = 1;
    +                            last;
    +                            }
    +                        else
    +                            {  $extras++;  };
    +                        };
    +                    };
    +
    +                if (!$success)
    +                    {
    +                    push @errorMessages, 'The style "' . $stylePieces[0] . '" does not exist.';
    +                    shift @stylePieces;
    +                    };
    +                };
    +            };
    +        }
    +    else
    +        {  @styles = ( 'Default' );  };
    +
    +
    +    # Decode and validate the output strings.
    +
    +    my %outputDirectories;
    +
    +    foreach my $outputString (@outputStrings)
    +        {
    +        my ($format, $directory) = split(/ /, $outputString, 2);
    +
    +        if (!defined $directory)
    +            {  push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]';  }
    +        else
    +            {
    +            if (!NaturalDocs::File->PathIsAbsolute($directory))
    +                {  $directory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $directory, 1);  };
    +
    +            $directory = NaturalDocs::File->CanonizePath($directory);
    +
    +            if (! -e $directory || ! -d $directory)
    +                {
    +                # They may have forgotten the format portion and the directory name had a space in it.
    +                if (-e ($format . ' ' . $directory) && -d ($format . ' ' . $directory))
    +                    {
    +                    push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]';
    +                    $format = undef;
    +                    }
    +                else
    +                    {  push @errorMessages, 'The output directory ' . $directory . ' does not exist.';  }
    +                }
    +            elsif (exists $outputDirectories{$directory})
    +                {  push @errorMessages, 'You cannot specify the output directory ' . $directory . ' more than once.';  }
    +            else
    +                {  $outputDirectories{$directory} = 1;  };
    +
    +            if (defined $format)
    +                {
    +                my $builderPackage = NaturalDocs::Builder->OutputPackageOf($format);
    +
    +                if (defined $builderPackage)
    +                    {
    +                    push @buildTargets,
    +                            NaturalDocs::Settings::BuildTarget->New($builderPackage->New(), $directory);
    +                    }
    +                else
    +                    {
    +                    push @errorMessages, 'The output format ' . $format . ' doesn\'t exist or is not installed.';
    +                    $valueRef = \$ignored;
    +                    };
    +                };
    +            };
    +        };
    +
    +    if (!scalar @buildTargets)
    +        {  push @errorMessages, 'You did not specify an output directory.';  };
    +
    +
    +    # Decode and validate the image strings.
    +
    +    foreach my $imageString (@imageStrings)
    +        {
    +        if ($imageString =~ /^ *\*/)
    +            {
    +            # The below NaturalDocs::File functions assume everything is canonized.
    +            $imageString = NaturalDocs::File->CanonizePath($imageString);
    +
    +            my ($volume, $directoryString) = NaturalDocs::File->SplitPath($imageString, 1);
    +            my @directories = NaturalDocs::File->SplitDirectories($directoryString);
    +
    +            shift @directories;
    +
    +            $directoryString = NaturalDocs::File->JoinDirectories(@directories);
    +            push @relativeImageDirectories, NaturalDocs::File->JoinPath($volume, $directoryString);
    +            }
    +        else
    +            {
    +            if (!NaturalDocs::File->PathIsAbsolute($imageString))
    +                {  $imageString = NaturalDocs::File->JoinPaths(Cwd::cwd(), $imageString, 1);  };
    +
    +            $imageString = NaturalDocs::File->CanonizePath($imageString);
    +
    +            if (! -e $imageString || ! -d $imageString)
    +                {  push @errorMessages, 'The image directory ' . $imageString . ' does not exist.';  };
    +
    +            push @imageDirectories, $imageString;
    +            };
    +        };
    +
    +
    +    # Make sure the input and project directories are specified, canonized, and exist.
    +
    +    if (scalar @inputDirectories)
    +        {
    +        for (my $i = 0; $i < scalar @inputDirectories; $i++)
    +            {
    +            if (!NaturalDocs::File->PathIsAbsolute($inputDirectories[$i]))
    +                {  $inputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $inputDirectories[$i], 1);  };
    +
    +            $inputDirectories[$i] = NaturalDocs::File->CanonizePath($inputDirectories[$i]);
    +
    +            if (! -e $inputDirectories[$i] || ! -d $inputDirectories[$i])
    +                {  push @errorMessages, 'The input directory ' . $inputDirectories[$i] . ' does not exist.';  };
    +            };
    +        }
    +    else
    +        {  push @errorMessages, 'You did not specify an input (source) directory.';  };
    +
    +    if (defined $projectDirectory)
    +        {
    +        if (!NaturalDocs::File->PathIsAbsolute($projectDirectory))
    +            {  $projectDirectory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $projectDirectory, 1);  };
    +
    +        $projectDirectory = NaturalDocs::File->CanonizePath($projectDirectory);
    +
    +        if (! -e $projectDirectory || ! -d $projectDirectory)
    +            {  push @errorMessages, 'The project directory ' . $projectDirectory . ' does not exist.';  };
    +
    +        # Create the Data subdirectory if it doesn't exist.
    +        NaturalDocs::File->CreatePath( NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1) );
    +        }
    +    else
    +        {  push @errorMessages, 'You did not specify a project directory.';  };
    +
    +
    +    # Make sure the excluded input directories are canonized, and add the project and output directories to the list.
    +
    +    for (my $i = 0; $i < scalar @excludedInputDirectories; $i++)
    +        {
    +        if (!NaturalDocs::File->PathIsAbsolute($excludedInputDirectories[$i]))
    +            {  $excludedInputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $excludedInputDirectories[$i], 1);  };
    +
    +        $excludedInputDirectories[$i] = NaturalDocs::File->CanonizePath($excludedInputDirectories[$i]);
    +        };
    +
    +    push @excludedInputDirectories, $projectDirectory;
    +
    +    foreach my $buildTarget (@buildTargets)
    +        {
    +        push @excludedInputDirectories, $buildTarget->Directory();
    +        };
    +
    +
    +    # Determine the tab length, and default to four if not specified.
    +
    +    if (defined $tabLength)
    +        {
    +        if ($tabLength !~ /^[0-9]+$/)
    +            {  push @errorMessages, 'The tab length must be a number.';  };
    +        }
    +    else
    +        {  $tabLength = 4;  };
    +
    +
    +    # Decode and validate the highlight setting.
    +
    +    if (defined $highlightString)
    +    	{
    +    	$highlightString = lc($highlightString);
    +
    +    	if ($highlightString eq 'off')
    +    		{
    +    		$highlightCode = undef;
    +    		$highlightAnonymous = undef;
    +    		}
    +    	elsif ($highlightString eq 'code')
    +    		{
    +    		$highlightCode = 1;
    +    		$highlightAnonymous = undef;
    +    		}
    +    	elsif ($highlightString eq 'all')
    +    		{
    +    		$highlightCode = 1;
    +    		$highlightAnonymous = 1;
    +    		}
    +    	else
    +    		{  push @errorMessages, $highlightString . ' is not a valid value for --highlight.';  }
    +    	}
    +    else
    +    	{
    +    	$highlightCode = 1;
    +    	$highlightAnonymous = undef;
    +    	}
    +
    +
    +    # Exit with the error message if there was one.
    +
    +    if (scalar @errorMessages)
    +        {
    +        print join("\n", @errorMessages) . "\nType NaturalDocs -h to see the syntax reference.\n";
    +        exit;
    +        };
    +    };
    +
    +#
    +#   Function: PrintSyntax
    +#
    +#   Prints the syntax reference.
    +#
    +sub PrintSyntax
    +    {
    +    my ($self) = @_;
    +
    +    # Make sure all line lengths are under 80 characters.
    +
    +    print
    +
    +    "Natural Docs, version " . $self->TextAppVersion() . "\n"
    +    . $self->AppURL() . "\n"
    +    . "This program is licensed under version 3 of the AGPL\n"
    +    . "Refer to License.txt for the complete details\n"
    +    . "--------------------------------------\n"
    +    . "\n"
    +    . "Syntax:\n"
    +    . "\n"
    +    . "    NaturalDocs -i [input (source) directory]\n"
    +    . "               (-i [input (source) directory] ...)\n"
    +    . "                -o [output format] [output directory]\n"
    +    . "               (-o [output format] [output directory] ...)\n"
    +    . "                -p [project directory]\n"
    +    . "                [options]\n"
    +    . "\n"
    +    . "Examples:\n"
    +    . "\n"
    +    . "    NaturalDocs -i C:\\My Project\\Source -o HTML C:\\My Project\\Docs\n"
    +    . "                -p C:\\My Project\\Natural Docs\n"
    +    . "    NaturalDocs -i /src/project -o HTML /doc/project\n"
    +    . "                -p /etc/naturaldocs/project -s Small -q\n"
    +    . "\n"
    +    . "Required Parameters:\n"
    +    . "\n"
    +    . " -i [dir]\n--input [dir]\n--source [dir]\n"
    +    . "     Specifies an input (source) directory.  Required.\n"
    +    . "     Can be specified multiple times.\n"
    +    . "\n"
    +    . " -o [fmt] [dir]\n--output [fmt] [dir]\n"
    +    . "    Specifies an output format and directory.  Required.\n"
    +    . "    Can be specified multiple times, but only once per directory.\n"
    +    . "    Possible output formats:\n";
    +
    +    $self->PrintOutputFormats('    - ');
    +
    +    print
    +    "\n"
    +    . " -p [dir]\n--project [dir]\n"
    +    . "    Specifies the project directory.  Required.\n"
    +    . "    There needs to be a unique project directory for every source directory.\n"
    +    . "\n"
    +    . "Optional Parameters:\n"
    +    . "\n"
    +    . " -s [style] ([style] [style] ...)\n--style [style] ([style] [style] ...)\n"
    +    . "    Specifies the CSS style when building HTML output.  If multiple styles are\n"
    +    . "    specified, they will all be included in the order given.\n"
    +    . "\n"
    +    . " -img [image directory]\n--image [image directory]\n"
    +    . "    Specifies an image directory.  Can be specified multiple times.\n"
    +    . "    Start with * to specify a relative directory, as in -img */images.\n"
    +    . "\n"
    +    . " -do\n--documented-only\n"
    +    . "    Specifies only documented code aspects should be included in the output.\n"
    +    . "\n"
    +    . " -t [len]\n--tab-length [len]\n"
    +    . "    Specifies the number of spaces tabs should be expanded to.  This only needs\n"
    +    . "    to be set if you use tabs in example code and text diagrams.  Defaults to 4.\n"
    +    . "\n"
    +    . " -xi [dir]\n--exclude-input [dir]\n--exclude-source [dir]\n"
    +    . "    Excludes an input (source) directory from the documentation.\n"
    +    . "    Automatically done for the project and output directories.  Can\n"
    +    . "    be specified multiple times.\n"
    +    . "\n"
    +    . " -nag\n--no-auto-group\n"
    +    . "    Turns off auto-grouping completely.\n"
    +    . "\n"
    +    . " -oft\n--only-file-titles\n"
    +    . "    Source files will only use the file name as the title.\n"
    +    . "\n"
    +    . " -hl [option]\n--highlight [option]\n"
    +    . "    Specifies when syntax highlighting should be applied.  Defaults to code.\n"
    +    . "    off  - No syntax highlighting is applied.\n"
    +    . "    code - Syntax highlighting is only applied to prototypes and (start code)\n"
    +    . "           segments.\n"
    +    . "    all  - Systax highlighting is applied to prototypes, (start code) segments,\n"
    +    . "           and lines starting with >, :, or |."
    +    . "\n"
    +    . " -r\n--rebuild\n"
    +    . "    Rebuilds all output and data files from scratch.\n"
    +    . "    Does not affect the menu file.\n"
    +    . "\n"
    +    . " -ro\n--rebuild-output\n"
    +    . "    Rebuilds all output files from scratch.\n"
    +    . "\n"
    +    . " -q\n--quiet\n"
    +    . "    Suppresses all non-error output.\n"
    +    . "\n"
    +    . " -?\n -h\n--help\n"
    +    . "    Displays this syntax reference.\n";
    +    };
    +
    +
    +#
    +#   Function: PrintOutputFormats
    +#
    +#   Prints all the possible output formats that can be specified with -o.  Each one will be placed on its own line.
    +#
    +#   Parameters:
    +#
    +#       prefix - Characters to prefix each one with, such as for indentation.
    +#
    +sub PrintOutputFormats #(prefix)
    +    {
    +    my ($self, $prefix) = @_;
    +
    +    my $outputPackages = NaturalDocs::Builder::OutputPackages();
    +
    +    foreach my $outputPackage (@$outputPackages)
    +        {
    +        print $prefix . $outputPackage->CommandLineOption() . "\n";
    +        };
    +    };
    +
    +
    +#
    +#   Function: LoadAndComparePreviousSettings
    +#
    +#   Loads <PreviousSettings.nd> and compares the values there with those in the command line.  If differences require it,
    +#   sets <rebuildData> and/or <rebuildOutput>.
    +#
    +sub LoadAndComparePreviousSettings
    +    {
    +    my ($self) = @_;
    +
    +    my $fileIsOkay;
    +
    +    if (!NaturalDocs::Settings->RebuildData())
    +        {
    +        my $version;
    +
    +        if (NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('PreviousSettings.nd'),
    +                                                                           NaturalDocs::Version->FromString('1.51') ))
    +            {  $fileIsOkay = 1;  };
    +        };
    +
    +    if (!$fileIsOkay)
    +        {
    +        # We need to reparse everything because --documented-only may have changed.
    +        # We need to rebuild everything because --tab-length may have changed.
    +        NaturalDocs::Project->ReparseEverything();
    +        NaturalDocs::Project->RebuildEverything();
    +        }
    +    else
    +        {
    +        my $raw;
    +
    +        # [UInt8: tab expansion]
    +        # [UInt8: documented only (0 or 1)]
    +        # [UInt8: no auto-group (0 or 1)]
    +        # [UInt8: only file titles (0 or 1)]
    +        # [UInt8: highlight code (0 or 1)]
    +        # [UInt8: highlight anonymous (0 or 1)]
    +
    +        my $prevTabLength = NaturalDocs::BinaryFile->GetUInt8();
    +        my $prevDocumentedOnly = NaturalDocs::BinaryFile->GetUInt8();
    +        my $prevNoAutoGroup = NaturalDocs::BinaryFile->GetUInt8();
    +        my $prevOnlyFileTitles = NaturalDocs::BinaryFile->GetUInt8();
    +        my $prevHighlightCode = NaturalDocs::BinaryFile->GetUInt8();
    +        my $prevHighlightAnonymous = NaturalDocs::BinaryFile->GetUInt8();
    +
    +        if ($prevDocumentedOnly == 0)
    +            {  $prevDocumentedOnly = undef;  };
    +        if ($prevNoAutoGroup == 0)
    +            {  $prevNoAutoGroup = undef;  };
    +        if ($prevOnlyFileTitles == 0)
    +            {  $prevOnlyFileTitles = undef;  };
    +        if ($prevHighlightCode == 0)
    +            {  $prevHighlightCode = undef;  };
    +        if ($prevHighlightAnonymous == 0)
    +            {  $prevHighlightAnonymous = undef;  };
    +
    +        if ($prevTabLength != $self->TabLength() ||
    +        	$prevHighlightCode != $self->HighlightCode() ||
    +        	$prevHighlightAnonymous != $self->HighlightAnonymous())
    +            {
    +            NaturalDocs::Project->RebuildEverything();
    +            };
    +
    +        if ($prevDocumentedOnly != $self->DocumentedOnly() ||
    +            $prevNoAutoGroup != $self->NoAutoGroup() ||
    +            $prevOnlyFileTitles != $self->OnlyFileTitles())
    +            {
    +            NaturalDocs::Project->ReparseEverything();
    +            };
    +
    +
    +        # [UInt8: number of input directories]
    +
    +        my $inputDirectoryCount = NaturalDocs::BinaryFile->GetUInt8();
    +
    +        while ($inputDirectoryCount)
    +            {
    +            # [AString16: input directory] [AString16: input directory name] ...
    +
    +            my $inputDirectory = NaturalDocs::BinaryFile->GetAString16();
    +            my $inputDirectoryName = NaturalDocs::BinaryFile->GetAString16();
    +
    +            # Not doing anything with this for now.
    +
    +            $inputDirectoryCount--;
    +            };
    +
    +
    +        # [UInt8: number of output targets]
    +
    +        my $outputTargetCount = NaturalDocs::BinaryFile->GetUInt8();
    +
    +        # Keys are the directories, values are the command line options.
    +        my %previousOutputDirectories;
    +
    +        while ($outputTargetCount)
    +            {
    +            # [AString16: output directory] [AString16: output format command line option] ...
    +
    +            my $outputDirectory = NaturalDocs::BinaryFile->GetAString16();
    +            my $outputCommand = NaturalDocs::BinaryFile->GetAString16();
    +
    +            $previousOutputDirectories{$outputDirectory} = $outputCommand;
    +
    +            $outputTargetCount--;
    +            };
    +
    +        # Check if any targets were added to the command line, or if their formats changed.  We don't care if targets were
    +        # removed.
    +        my $buildTargets = $self->BuildTargets();
    +
    +        foreach my $buildTarget (@$buildTargets)
    +            {
    +            if (!exists $previousOutputDirectories{$buildTarget->Directory()} ||
    +                $buildTarget->Builder()->CommandLineOption() ne $previousOutputDirectories{$buildTarget->Directory()})
    +                {
    +                NaturalDocs::Project->RebuildEverything();
    +                last;
    +                };
    +            };
    +
    +        NaturalDocs::BinaryFile->Close();
    +        };
    +    };
    +
    +
    +#
    +#   Function: SavePreviousSettings
    +#
    +#   Saves the settings into <PreviousSettings.nd>.
    +#
    +sub SavePreviousSettings
    +    {
    +    my ($self) = @_;
    +
    +    NaturalDocs::BinaryFile->OpenForWriting(  NaturalDocs::Project->DataFile('PreviousSettings.nd') );
    +
    +    # [UInt8: tab length]
    +    # [UInt8: documented only (0 or 1)]
    +    # [UInt8: no auto-group (0 or 1)]
    +    # [UInt8: only file titles (0 or 1)]
    +    # [UInt8: highlight code (0 or 1)]
    +    # [UInt8: highlight anonymous (0 or 1)]
    +    # [UInt8: number of input directories]
    +
    +    my $inputDirectories = $self->InputDirectories();
    +
    +    NaturalDocs::BinaryFile->WriteUInt8($self->TabLength());
    +    NaturalDocs::BinaryFile->WriteUInt8($self->DocumentedOnly() ? 1 : 0);
    +    NaturalDocs::BinaryFile->WriteUInt8($self->NoAutoGroup() ? 1 : 0);
    +    NaturalDocs::BinaryFile->WriteUInt8($self->OnlyFileTitles() ? 1 : 0);
    +    NaturalDocs::BinaryFile->WriteUInt8($self->HighlightCode() ? 1 : 0);
    +    NaturalDocs::BinaryFile->WriteUInt8($self->HighlightAnonymous() ? 1 : 0);
    +    NaturalDocs::BinaryFile->WriteUInt8(scalar @$inputDirectories);
    +
    +    foreach my $inputDirectory (@$inputDirectories)
    +        {
    +        my $inputDirectoryName = $self->InputDirectoryNameOf($inputDirectory);
    +
    +        # [AString16: input directory] [AString16: input directory name] ...
    +        NaturalDocs::BinaryFile->WriteAString16($inputDirectory);
    +        NaturalDocs::BinaryFile->WriteAString16($inputDirectoryName);
    +        };
    +
    +    # [UInt8: number of output targets]
    +
    +    my $buildTargets = $self->BuildTargets();
    +    NaturalDocs::BinaryFile->WriteUInt8(scalar @$buildTargets);
    +
    +    foreach my $buildTarget (@$buildTargets)
    +        {
    +        # [AString16: output directory] [AString16: output format command line option] ...
    +        NaturalDocs::BinaryFile->WriteAString16( $buildTarget->Directory() );
    +        NaturalDocs::BinaryFile->WriteAString16( $buildTarget->Builder()->CommandLineOption() );
    +        };
    +
    +    NaturalDocs::BinaryFile->Close();
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Settings/BuildTarget.pm b/vendor/naturaldocs/Modules/NaturalDocs/Settings/BuildTarget.pm
    new file mode 100644
    index 000000000..81595757e
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Settings/BuildTarget.pm
    @@ -0,0 +1,67 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::Settings::BuildTarget
    +#
    +###############################################################################
    +#
    +#   A class that stores information about a build target.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Settings::BuildTarget;
    +
    +use NaturalDocs::DefineMembers 'BUILDER', 'Builder()', 'SetBuilder()',
    +                                                 'DIRECTORY', 'Directory()', 'SetDirectory()';
    +
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref with the members below.
    +#
    +#       BUILDER      - The <NaturalDocs::Builder::Base>-derived object for the target's output format.
    +#       DIRECTORY - The output directory of the target.
    +#
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       builder - The <NaturalDocs::Builder::Base>-derived object for the target's output format.
    +#       directory - The directory to place the output files in.
    +#
    +sub New #(builder, directory)
    +    {
    +    my ($package, $builder, $directory) = @_;
    +
    +    my $object = [ ];
    +    bless $object, $package;
    +
    +    $object->SetBuilder($builder);
    +    $object->SetDirectory($directory);
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Member Functions
    +#
    +#   Builder - Returns the <NaturalDocs::Builder::Base>-derived object for the target's output format.
    +#   SetBuilder - Replaces the <NaturalDocs::Builder::Base>-derived object for the target's output format.
    +#   Directory - Returns the directory for the target's output files.
    +#   SetDirectory - Replaces the directory for the target's output files.
    +#
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB.pm
    new file mode 100644
    index 000000000..773aadc0b
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB.pm
    @@ -0,0 +1,679 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB
    +#
    +###############################################################################
    +#
    +#   SourceDB is an experimental package meant to unify the tracking of various elements in the source code.
    +#
    +#   Requirements:
    +#
    +#       - All extension packages must call <RegisterExtension()> before they can be used.
    +#
    +#
    +#   Architecture: The Idea
    +#
    +#       For quite a while Natural Docs only needed <SymbolTable>.  However, 1.3 introduced the <ClassHierarchy> package
    +#       which duplicated some of its functionality to track classes and parent references.  1.4 now needs <ImageReferenceTable>,
    +#       so this package was an attempt to isolate the common functionality so the wheel doesn't have to keep being rewritten as
    +#       the scope of Natural Docs expands.
    +#
    +#       SourceDB is designed around <Extensions> and items.  The purposefully vague "items" are anything in the source code
    +#       that we need to track the definitions of.  Extensions are the packages to track them, only they're derived from
    +#       <NaturalDocs::SourceDB::Extension> and registered with this package instead of being free standing and duplicating
    +#       functionality such as watched files.
    +#
    +#       The architecture on this package isn't comprehensive yet.  As more extensions are added or previously made free standing
    +#       packages are migrated to it it will expand to encompass them.  However, it's still experimental so this concept may
    +#       eventually be abandoned for something better instead.
    +#
    +#
    +#   Architecture: Assumptions
    +#
    +#       SourceDB is built around certain assumptions.
    +#
    +#       One item per file:
    +#
    +#           SourceDB assumes that only the first item per file with a particular item string is relevant.  For example, if two functions
    +#           have the exact same name, there's no way to link to the second one either in HTML or internally so it doesn't matter for
    +#           our purposes.  Likewise, if two references are exactly the same they go to the same target, so it doesn't matter whether
    +#           there's one or two or a thousand.  All that matters is that at least one reference exists in this file because you only need
    +#           to determine whether the entire file gets rebuilt.  If two items are different in some meaningful way, they should generate
    +#           different item strings.
    +#
    +#       Watched file parsing:
    +#
    +#           SourceDB assumes the parse method is that the information that was stored from Natural Docs' previous run is loaded, a
    +#           file is watched, that file is reparsed, and then <AnalyzeWatchedFileChanges()> is called.  When the file is reparsed all
    +#           items within it are added the same as if the file was never parsed before.
    +#
    +#           If there's a new item this time around, that's fine no matter what.  However, a changed item wouldn't normally be
    +#           recorded because the previous run's definition is seen as the first one and subsequent ones are ignored.  Also, deleted
    +#           items would normally not be recorded either because we're only adding.
    +#
    +#           The watched file method fixes this because everything is also added to a second, clean database specifically for the
    +#           watched file.  Because it starts clean, it always gets the first definition from the current parse which can then be
    +#           compared to the original by <AnalyzeWatchedFileChanges()>.  Because it starts clean you can also compare it to the
    +#           main database to see if anything was deleted, because it would appear in the main database but not the watched one.
    +#
    +#           This means that functions like <ChangeDefinition()> and <DeleteDefinition()> should only be called by
    +#           <AnalyzeWatchedFileChanges()>.  Externally only <AddDefinition()> should be called.  <DeleteItem()> is okay to be
    +#           called externally because entire items aren't managed by the watched file database, only definitions.
    +#
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +use NaturalDocs::SourceDB::Extension;
    +use NaturalDocs::SourceDB::Item;
    +use NaturalDocs::SourceDB::ItemDefinition;
    +use NaturalDocs::SourceDB::File;
    +use NaturalDocs::SourceDB::WatchedFileDefinitions;
    +
    +
    +package NaturalDocs::SourceDB;
    +
    +
    +###############################################################################
    +# Group: Types
    +
    +
    +#
    +#   Type: ExtensionID
    +#
    +#   A unique identifier for each <NaturalDocs::SourceDB> extension as given out by <RegisterExtension()>.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   array: extensions
    +#
    +#   An array of <NaturalDocs::SourceDB::Extension>-derived extensions, as added with <RegisterExtension()>.  The indexes
    +#   are the <ExtensionIDs> and the values are package references.
    +#
    +my @extensions;
    +
    +#
    +#   array: extensionUsesDefinitionObjects
    +#
    +#   An array where the indexes are <ExtensionIDs> and the values are whether that extension uses its own definition class
    +#   derived from <NaturalDocs::SourceDB::ItemDefinition> or it just tracks their existence.
    +#
    +my @extensionUsesDefinitionObjects;
    +
    +
    +
    +#
    +#   array: items
    +#
    +#   The array of source items.  The <ExtensionIDs> are the indexes, and the values are hashrefs mapping the item
    +#   string to <NaturalDocs::SourceDB::Item>-derived objects.  Hashrefs may be undef.
    +#
    +my @items;
    +
    +
    +#
    +#   hash: files
    +#
    +#   A hashref mapping source <FileNames> to <NaturalDocs::SourceDB::Files>.
    +#
    +my %files;
    +
    +
    +#
    +#   object: watchedFile
    +#
    +#   When a file is being watched for changes, will be a <NaturalDocs::SourceDB::File> for that file.  Is undef otherwise.
    +#
    +#   When the file is parsed, items are added to both this and the version in <files>.  Thus afterwards we can compare the two to
    +#   see if any were deleted since the last time Natural Docs was run, because they would be in the <files> version but not this
    +#   one.
    +#
    +my $watchedFile;
    +
    +
    +#
    +#   string: watchedFileName
    +#
    +#   When a file is being watched for changes, will be the <FileName> of the file being watched.  Is undef otherwise.
    +#
    +my $watchedFileName;
    +
    +
    +#
    +#   object: watchedFileDefinitions
    +#
    +#   When a file is being watched for changes, will be a <NaturalDocs::SourceDB::WatchedFileDefinitions> object.  Is undef
    +#   otherwise.
    +#
    +#   When the file is parsed, items are added to both this and the version in <items>.  Since only the first definition is kept, this
    +#   will always have the definition info from the file whereas the version in <items> will have the first definition as of the last time
    +#   Natural Docs was run.  Thus they can be compared to see if the definitions of items that existed the last time around have
    +#   changed.
    +#
    +my $watchedFileDefinitions;
    +
    +
    +
    +###############################################################################
    +# Group: Extension Functions
    +
    +
    +#
    +#   Function: RegisterExtension
    +#
    +#   Registers a <NaturalDocs::SourceDB::Extension>-derived package and returns a unique <ExtensionID> for it.  All extensions
    +#   must call this before they can be used.
    +#
    +#   Registration Order:
    +#
    +#       The order in which extensions register is important.  Whenever possible, items are added in the order their extensions
    +#       registered.  However, items are changed and deleted in the reverse order.  Take advantage of this to minimize
    +#       churn between extensions that are dependent on each other.
    +#
    +#       For example, when symbols are added or deleted they may cause references to be retargeted and thus their files need to
    +#       be rebuilt.  However, adding or deleting references never causes the symbols' files to be rebuilt.  So it makes sense that
    +#       symbols should be created before references, and that references should be deleted before symbols.
    +#
    +#   Parameters:
    +#
    +#       extension - The package or object of the extension.  Must be derived from <NaturalDocs::SourceDB::Extension>.
    +#       usesDefinitionObjects - Whether the extension uses its own class derived from <NaturalDocs::SourceDB::ItemDefinition>
    +#                                         or simply tracks each definitions existence.
    +#
    +#   Returns:
    +#
    +#       An <ExtensionID> unique to the extension.  This should be saved because it's required in functions such as <AddItem()>.
    +#
    +sub RegisterExtension #(package extension, bool usesDefinitionObjects) => ExtensionID
    +    {
    +    my ($self, $extension, $usesDefinitionObjects) = @_;
    +
    +    push @extensions, $extension;
    +    push @extensionUsesDefinitionObjects, $usesDefinitionObjects;
    +
    +    return scalar @extensions - 1;
    +    };
    +
    +
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads the data of the source database and all the extensions.  Will call <NaturalDocs::SourceDB::Extension->Load()> for
    +#   all of them, unless there's a situation where all the source files are going to be reparsed anyway in which case it's not needed.
    +#
    +sub Load
    +    {
    +    my $self = shift;
    +
    +    # No point loading if RebuildData is set.
    +    if (!NaturalDocs::Settings->RebuildData())
    +        {
    +        # If any load fails, stop loading the rest and just reparse all the source files.
    +        my $success = 1;
    +
    +        for (my $extension = 0; $extension < scalar @extensions && $success; $extension++)
    +            {
    +            $success = $extensions[$extension]->Load();
    +            };
    +
    +        if (!$success)
    +            {  NaturalDocs::Project->ReparseEverything();  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves the data of the source database and all its extensions.  Will call <NaturalDocs::SourceDB::Extension->Save()> for all
    +#   of them.
    +#
    +sub Save
    +    {
    +    my $self = shift;
    +
    +    for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--)
    +        {
    +        $extensions[$extension]->Save();
    +        };
    +    };
    +
    +
    +#
    +#   Function: PurgeDeletedSourceFiles
    +#
    +#   Removes all data associated with deleted source files.
    +#
    +sub PurgeDeletedSourceFiles
    +    {
    +    my $self = shift;
    +
    +    my $filesToPurge = NaturalDocs::Project->FilesToPurge();
    +
    +    # Extension is the outermost loop because we want the extensions added last to have their definitions removed first to cause
    +    # the least amount of churn between interdependent extensions.
    +    for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--)
    +        {
    +        foreach my $file (keys %$filesToPurge)
    +            {
    +            if (exists $files{$file})
    +                {
    +                my @items = $files{$file}->ListItems($extension);
    +
    +                foreach my $item (@items)
    +                    {
    +                    $self->DeleteDefinition($extension, $item, $file);
    +                    };
    +                }; # file exists
    +            }; # each file
    +        }; # each extension
    +    };
    +
    +
    +
    +
    +
    +###############################################################################
    +# Group: Item Functions
    +
    +
    +#
    +#   Function: AddItem
    +#
    +#   Adds the passed item to the database.  This will not work if the item string already exists.  The item added should *not*
    +#   already have definitions attached.  Only use this to add blank items and then call <AddDefinition()> instead.
    +#
    +#   Parameters:
    +#
    +#       extension - An <ExtensionID>.
    +#       itemString - The string serving as the item identifier.
    +#       item - An object derived from <NaturalDocs::SourceDB::Item>.
    +#
    +#   Returns:
    +#
    +#       Whether the item was added, that is, whether it was the first time this item was added.
    +#
    +sub AddItem #(ExtensionID extension, string itemString, NaturalDocs::SourceDB::Item item) => bool
    +    {
    +    my ($self, $extension, $itemString, $item) = @_;
    +
    +    if (!defined $items[$extension])
    +        {  $items[$extension] = { };  };
    +
    +    if (!exists $items[$extension]->{$itemString})
    +        {
    +        if ($item->HasDefinitions())
    +            {  die "Tried to add an item to SourceDB that already had definitions.";  };
    +
    +        $items[$extension]->{$itemString} = $item;
    +        return 1;
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +#
    +#   Function: GetItem
    +#
    +#   Returns the <NaturalDocs::SourceDB::Item>-derived object for the passed <ExtensionID> and item string, or undef if there
    +#   is none.
    +#
    +sub GetItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extensionID, $itemString) = @_;
    +
    +    if (defined $items[$extensionID])
    +        {  return $items[$extensionID]->{$itemString};  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: DeleteItem
    +#
    +#   Deletes the record of the passed <ExtensionID> and item string.  Do *not* delete items that still have definitions.  Use
    +#   <DeleteDefinition()> first.
    +#
    +#   Parameters:
    +#
    +#       extension - The <ExtensionID>.
    +#       itemString - The item's identifying string.
    +#
    +#   Returns:
    +#
    +#       Whether it was successful, meaning whether an entry existed for it.
    +#
    +sub DeleteItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $items[$extension] && exists $items[$extension]->{$itemString})
    +        {
    +        if ($items[$extension]->{$itemString}->HasDefinitions())
    +            {  die "Tried to delete an item from SourceDB that still has definitions.";  };
    +
    +        delete $items[$extension]->{$itemString};
    +        return 1;
    +        }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: HasItem
    +#
    +#   Returns whether there is an item defined for the passed <ExtensionID> and item string.
    +#
    +sub HasItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $items[$extension])
    +        {  return (exists $items[$extension]->{$itemString});  }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: GetAllItemsHashRef
    +#
    +#   Returns a hashref of all the items defined for an extension.  *Do not change the contents.*  The keys are the item strings and
    +#   the values are <NaturalDocs::SourceDB::Items> or derived classes.
    +#
    +sub GetAllItemsHashRef #(ExtensionID extension) => hashref
    +    {
    +    my ($self, $extension) = @_;
    +    return $items[$extension];
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Definition Functions
    +
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a definition to an item.  Assumes the item was already created with <AddItem()>.  If there's already a definition for this
    +#   file in the item, the new definition will be ignored.
    +#
    +#   Parameters:
    +#
    +#       extension - The <ExtensionID>.
    +#       itemString - The item string.
    +#       file - The <FileName> the definition is in.
    +#       definition - If you're using a custom <NaturalDocs::SourceDB::ItemDefinition> class, you must include an object for it here.
    +#                       Otherwise this parameter is ignored.
    +#
    +#   Returns:
    +#
    +#       Whether the definition was added, which is to say, whether this was the first definition for the passed <FileName>.
    +#
    +sub AddDefinition #(ExtensionID extension, string itemString, FileName file, optional NaturalDocs::SourceDB::ItemDefinition definition) => bool
    +    {
    +    my ($self, $extension, $itemString, $file, $definition) = @_;
    +
    +
    +    # Items
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  die "Tried to add a definition to an undefined item in SourceDB.";  };
    +
    +    if (!$extensionUsesDefinitionObjects[$extension])
    +        {  $definition = 1;  };
    +
    +    my $result = $item->AddDefinition($file, $definition);
    +
    +
    +    # Files
    +
    +    if (!exists $files{$file})
    +        {  $files{$file} = NaturalDocs::SourceDB::File->New();  };
    +
    +    $files{$file}->AddItem($extension, $itemString);
    +
    +
    +    # Watched File
    +
    +    if ($self->WatchingFileForChanges())
    +        {
    +        $watchedFile->AddItem($extension, $itemString);
    +
    +        if ($extensionUsesDefinitionObjects[$extension])
    +            {  $watchedFileDefinitions->AddDefinition($extension, $itemString, $definition);  };
    +        };
    +
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: ChangeDefinition
    +#
    +#   Changes the definition of an item.  This function is only used for extensions that use custom
    +#   <NaturalDocs::SourceDB::ItemDefinition>-derived classes.
    +#
    +#   Parameters:
    +#
    +#       extension - The <ExtensionID>.
    +#       itemString - The item string.
    +#       file - The <FileName> the definition is in.
    +#       definition - The definition, which must be an object derived from <NaturalDocs::SourceDB::ItemDefinition>.
    +#
    +sub ChangeDefinition #(ExtensionID extension, string itemString, FileName file, NaturalDocs::SourceDB::ItemDefinition definition)
    +    {
    +    my ($self, $extension, $itemString, $file, $definition) = @_;
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  die "Tried to change the definition of an undefined item in SourceDB.";  };
    +
    +    if (!$extensionUsesDefinitionObjects[$extension])
    +        {  die "Tried to change the definition of an item in an extension that doesn't use definition objects in SourceDB.";  };
    +
    +    if (!$item->HasDefinition($file))
    +        {  die "Tried to change a definition that doesn't exist in SourceDB.";  };
    +
    +    $item->ChangeDefinition($file, $definition);
    +    $extensions[$extension]->OnChangedDefinition($itemString, $file);
    +    };
    +
    +
    +#
    +#   Function: GetDefinition
    +#
    +#   If the extension uses custom <NaturalDocs::SourceDB::ItemDefinition> classes, returns it for the passed definition or undef
    +#   if it doesn't exist.  Otherwise returns whether it exists.
    +#
    +sub GetDefinition #(ExtensionID extension, string itemString, FileName file) => NaturalDocs::SourceDB::ItemDefinition or bool
    +    {
    +    my ($self, $extension, $itemString, $file) = @_;
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  return undef;  };
    +
    +    return $item->GetDefinition($file);
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes the definition for the passed item.  Returns whether it was successful, meaning whether a definition existed for that
    +#   file.
    +#
    +sub DeleteDefinition #(ExtensionID extension, string itemString, FileName file) => bool
    +    {
    +    my ($self, $extension, $itemString, $file) = @_;
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  return 0;  };
    +
    +    my $result = $item->DeleteDefinition($file);
    +
    +    if ($result)
    +        {
    +        $files{$file}->DeleteItem($extension, $itemString);
    +        $extensions[$extension]->OnDeletedDefinition($itemString, $file, !$item->HasDefinitions());
    +        };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: HasDefinitions
    +#
    +#   Returns whether there are any definitions for this item.
    +#
    +sub HasDefinitions #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  return 0;  };
    +
    +    return $item->HasDefinitions();
    +    };
    +
    +
    +#
    +#   Function: HasDefinition
    +#
    +#   Returns whether there is a definition for the passed <FileName>.
    +#
    +sub HasDefinition #(ExtensionID extension, string itemString, FileName file) => bool
    +    {
    +    my ($self, $extension, $itemString, $file) = @_;
    +
    +    my $item = $self->GetItem($extension, $itemString);
    +
    +    if (!defined $item)
    +        {  return 0;  };
    +
    +    return $item->HasDefinition($file);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Watched File Functions
    +
    +
    +#
    +#   Function: WatchFileForChanges
    +#
    +#   Begins watching a file for changes.  Only one file at a time can be watched.
    +#
    +#   This should be called before a file is parsed so the file info goes both into the main database and the watched file info.
    +#   Afterwards you call <AnalyzeWatchedFileChanges()> so item deletions and definition changes can be detected.
    +#
    +#   Parameters:
    +#
    +#       filename - The <FileName> to watch.
    +#
    +sub WatchFileForChanges #(FileName filename)
    +    {
    +    my ($self, $filename) = @_;
    +
    +    $watchedFileName = $filename;
    +    $watchedFile = NaturalDocs::SourceDB::File->New();
    +    $watchedFileDefinitions = NaturalDocs::SourceDB::WatchedFileDefinitions->New();
    +    };
    +
    +
    +#
    +#   Function: WatchingFileForChanges
    +#
    +#   Returns whether we're currently watching a file for changes or not.
    +#
    +sub WatchingFileForChanges # => bool
    +    {
    +    my $self = shift;
    +    return defined $watchedFileName;
    +    };
    +
    +
    +#
    +#   Function: AnalyzeWatchedFileChanges
    +#
    +#   Analyzes the watched file for changes.  Will delete and change definitions as necessary.
    +#
    +sub AnalyzeWatchedFileChanges
    +    {
    +    my $self = shift;
    +
    +    if (!$self->WatchingFileForChanges())
    +        {  die "Tried to analyze watched file for changes in SourceDB when no file was being watched.";  };
    +    if (!$files{$watchedFileName})
    +        {  return;  };
    +
    +
    +    # Process extensions last registered to first.
    +
    +    for (my $extension = scalar @extensions - 1; $extension >= 0; $extension--)
    +        {
    +        my @items = $files{$watchedFileName}->ListItems($extension);
    +
    +        foreach my $item (@items)
    +            {
    +            if ($watchedFile->HasItem($extension, $item))
    +                {
    +                if ($extensionUsesDefinitionObjects[$extension])
    +                    {
    +                    my $originalDefinition = $items[$extension]->GetDefinition($watchedFileName);
    +                    my $watchedDefinition = $watchedFileDefinitions->GetDefinition($extension, $item);
    +
    +                    if (!$originalDefinition->Compare($watchedDefinition))
    +                        {  $self->ChangeDefinition($extension, $item, $watchedFileName, $watchedDefinition);  };
    +                    }
    +                }
    +            else # !$watchedFile->HasItem($item)
    +                {
    +                $self->DeleteDefinition($extension, $item, $watchedFileName);
    +                };
    +            };
    +        };
    +
    +
    +    $watchedFile = undef;
    +    $watchedFileName = undef;
    +    $watchedFileDefinitions = undef;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Extension.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Extension.pm
    new file mode 100644
    index 000000000..d610b24be
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Extension.pm
    @@ -0,0 +1,85 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB::Extension
    +#
    +###############################################################################
    +#
    +#   A base package for all <SourceDB> extensions.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SourceDB::Extension;
    +
    +
    +###############################################################################
    +# Group: Interface Functions
    +# These functions must be overridden by the derived class.
    +
    +
    +#
    +#   Function: Register
    +#
    +#   Override this function to register the package with <NaturalDocs::SourceDB->RegisterExtension()>.
    +#
    +sub Register
    +    {
    +    die "Called SourceDB::Extension->Register().  This function should be overridden by every extension.";
    +    };
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Called by <NaturalDocs::SourceDB->Load()> to load the extension's data.  Returns whether it was successful.
    +#
    +#   *This function might not be called.*  If there's a situation that would cause all the source files to be reparsed anyway,
    +#   <NaturalDocs::SourceDB> may skip calling Load() for the remaining extensions.  You should *not* depend on this function
    +#   for any critical initialization that needs to happen every time regardless.
    +#
    +sub Load # => bool
    +    {
    +    return 1;
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Called by <NaturalDocs::SourceDB->Save()> to save the extension's data.
    +#
    +sub Save
    +    {
    +    };
    +
    +
    +#
    +#   Function: OnDeletedDefinition
    +#
    +#   Called for each definition deleted by <NaturalDocs::SourceDB>.  This is called *after* the definition has been deleted from
    +#   the database, so don't expect to be able to read it.
    +#
    +sub OnDeletedDefinition #(string itemString, FileName file, bool wasLastDefinition)
    +    {
    +    };
    +
    +
    +#
    +#   Function: OnChangedDefinition
    +#
    +#   Called for each definition changed by <NaturalDocs::SourceDB>.  This is called *after* the definition has been changed, so
    +#   don't expect to be able to read the original value.
    +#
    +sub OnChangedDefinition #(string itemString, FileName file)
    +    {
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/File.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/File.pm
    new file mode 100644
    index 000000000..1a4419fd2
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/File.pm
    @@ -0,0 +1,130 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB::File
    +#
    +###############################################################################
    +#
    +#   A class used to index items by file.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SourceDB::File;
    +
    +use NaturalDocs::DefineMembers 'ITEMS';
    +
    +
    +#
    +#   Variables: Members
    +#
    +#   These constants serve as indexes into the object array.
    +#
    +#   ITEMS - An arrayref where an <ExtensionID> is the index and the members are existence hashrefs of the item strigs defined
    +#               in this file.  The arrayref will always exist, but the hashrefs may be undef.
    +#
    +
    +
    +#
    +#   Function: New
    +#
    +#   Returns a new object.
    +#
    +sub New
    +    {
    +    my $package = shift;
    +
    +    my $object = [ ];
    +    $object->[ITEMS] = [ ];
    +
    +    bless $object, $package;
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: AddItem
    +#
    +#   Adds an item to this file.  Returns whether this added a new item.
    +#
    +sub AddItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (!defined $self->[ITEMS]->[$extension])
    +        {
    +        $self->[ITEMS]->[$extension] = { $itemString => 1 };
    +        return 1;
    +        }
    +    elsif (!exists $self->[ITEMS]->[$extension]->{$itemString})
    +        {
    +        $self->[ITEMS]->[$extension]->{$itemString} = 1;
    +        return 1;
    +        }
    +    else
    +        {
    +        return 0;
    +        };
    +    };
    +
    +
    +#
    +#   Function: HasItem
    +#
    +#   Returns whether the item exists in this file.
    +#
    +sub HasItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $self->[ITEMS]->[$extension])
    +        {  return exists $self->[ITEMS]->[$extension]->{$itemString};  }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: DeleteItem
    +#
    +#   Deletes the passed item.  Returns whether it existed.
    +#
    +sub DeleteItem #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (!defined $self->[ITEMS]->[$extension])
    +        {  return 0;  }
    +    elsif (exists $self->[ITEMS]->[$extension]->{$itemString})
    +        {
    +        delete $self->[ITEMS]->[$extension]->{$itemString};
    +        return 1;
    +        }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: ListItems
    +#
    +#   Returns an array of all the item strings defined for a particular extension, or an empty list if none.
    +#
    +sub ListItems #(ExtensionID extension) => string array
    +    {
    +    my ($self, $extension) = @_;
    +
    +    if (defined $self->[ITEMS]->[$extension])
    +        {  return keys %{$self->[ITEMS]->[$extension]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Item.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Item.pm
    new file mode 100644
    index 000000000..b43475697
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/Item.pm
    @@ -0,0 +1,202 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB::Item
    +#
    +###############################################################################
    +#
    +#   A base class for something being tracked in <NaturalDocs::SourceDB>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SourceDB::Item;
    +
    +use NaturalDocs::DefineMembers 'DEFINITIONS';
    +
    +
    +#
    +#   Variables: Members
    +#
    +#   The following constants are indexes into the object array.
    +#
    +#   DEFINITIONS - A hashref that maps <FileNames> to either <NaturalDocs::SourceDB::ItemDefinition>-derived objects or
    +#                         serves as an existence hashref depending on whether the extension only tracks existence.  Will be undef if
    +#                         there are none.
    +#
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $class = shift;
    +
    +    my $object = [ ];
    +    bless $object, $class;
    +
    +    return $object;
    +    };
    +
    +
    +
    +###############################################################################
    +#
    +#   Group: Definition Functions
    +#
    +#   These functions should be called by <NaturalDocs::SourceDB>.  You should not be calling them directly.  Call functions
    +#   like <NaturalDocs::SourceDB->AddDefinition()> instead.
    +#
    +
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a definition for the passed <FileName>.  If it's already defined, the new definition will be ignored.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName>.
    +#       definition - The definition, which must be an object derived from <NaturalDocs::SourceDB::ItemDefinition> or undef if
    +#                       the extension only tracks existence.
    +#
    +#   Returns:
    +#
    +#       Whether the definition was added, which is to say, whether this was the first definition for the passed <FileName>.
    +#
    +sub AddDefinition #(FileName file, optional NaturalDocs::SourceDB::ItemDefinition definition) => bool
    +    {
    +    my ($self, $file, $definition) = @_;
    +
    +    if (!defined $self->[DEFINITIONS])
    +        {  $self->[DEFINITIONS] = { };  };
    +
    +    if (!exists $self->[DEFINITIONS]->{$file})
    +        {
    +        if (!defined $definition)
    +            {  $definition = 1;  };
    +
    +        $self->[DEFINITIONS]->{$file} = $definition;
    +        return 1;
    +        }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: ChangeDefinition
    +#
    +#   Changes the definition for the passed <FileName>.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName>.
    +#       definition - The definition, which must be an object derived from <NaturalDocs::SourceDB::ItemDefinition>.
    +#
    +sub ChangeDefinition #(FileName file, NaturalDocs::SourceDB::ItemDefinition definition)
    +    {
    +    my ($self, $file, $definition) = @_;
    +
    +    if (!defined $self->[DEFINITIONS] || !exists $self->[DEFINITIONS]->{$file})
    +        {  die "Tried to change a non-existant definition in SourceD::Item.";  };
    +
    +    $self->[DEFINITIONS]->{$file} = $definition;
    +    };
    +
    +
    +#
    +#   Function: GetDefinition
    +#
    +#   Returns the <NaturalDocs::SourceDB::ItemDefinition>-derived object for the passed <FileName>, non-zero if it only tracks
    +#   existence, or undef if there is no definition.
    +#
    +sub GetDefinition #(FileName file) => NaturalDocs::SourceDB::ItemDefinition or bool
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {  return $self->[DEFINITIONS]->{$file};  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes the definition for the passed <FileName>.  Returns whether it was successful, meaning whether a definition existed
    +#   for that file.
    +#
    +sub DeleteDefinition #(FileName file) => bool
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {
    +        if (exists $self->[DEFINITIONS]->{$file})
    +            {
    +            delete $self->[DEFINITIONS]->{$file};
    +
    +            if (!scalar keys %{$self->[DEFINITIONS]})
    +                {  $self->[DEFINITIONS] = undef;  };
    +
    +            return 1;
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +#
    +#   Function: HasDefinitions
    +#
    +#   Returns whether there are any definitions for this item.
    +#
    +sub HasDefinitions # => bool
    +    {
    +    my $self = shift;
    +    return (defined $self->[DEFINITIONS]);
    +    };
    +
    +
    +#
    +#   Function: HasDefinition
    +#
    +#   Returns whether there is a definition for the passed <FileName>.
    +#
    +sub HasDefinition #(FileName file) => bool
    +    {
    +    my ($self, $file) = @_;
    +
    +    if (defined $self->[DEFINITIONS])
    +        {  return (exists $self->[DEFINITIONS]->{$file});  }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: GetAllDefinitionsHashRef
    +#
    +#   Returns a hashref of all the definitions of this item.  *Do not change.*  The keys are the <FileNames>, and the values are
    +#   either <NaturalDocs::SourceDB::ItemDefinition>-derived objects or it's just an existence hashref if those aren't used.
    +#
    +sub GetAllDefinitionsHashRef
    +    {
    +    my $self = shift;
    +    return $self->[DEFINITIONS];
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm
    new file mode 100644
    index 000000000..5fe82f839
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm
    @@ -0,0 +1,46 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB::ItemDefinition
    +#
    +###############################################################################
    +#
    +#   A base class for all item definitions for extensions that track more than existence.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SourceDB::ItemDefinition;
    +
    +
    +#
    +#   Function: Compare
    +#
    +#   Returns whether the definitions are equal.  This version returns true by default, you must override it in your subclasses
    +#   to make the results relevant.  This is important for <NaturalDocs::SourceDB->AnalyzeTrackedFileChanges()>.
    +#
    +#   This will only be called between objects of the same <ExtensionID>.  If you use multiple derived classes for the same
    +#   <ExtensionID>, you will have to take that into account yourself.
    +#
    +#   Parameters:
    +#
    +#       other - Another <NaturalDocs::SourceDB::ItemDefinition>-derived object to compare this one to.  It will always be from
    +#                  the same <ExtensionID>.
    +#
    +#   Returns:
    +#
    +#       Whether they are equal.
    +#
    +sub Compare #(other)
    +    {
    +    return 1;
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm
    new file mode 100644
    index 000000000..f42bbab38
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm
    @@ -0,0 +1,160 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SourceDB::WatchedFileDefinitions
    +#
    +###############################################################################
    +#
    +#   A class to track the definitions appearing in a watched file.  This is only used for extensions that track definition info with
    +#   <NaturalDocs::SourceDB::ItemDefinition>-derived objects.  Do not use it for extensions that only track existence.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SourceDB::WatchedFileDefinitions;
    +
    +
    +#
    +#   Variables: Members
    +#
    +#   This object would only have one member, which is an array, so the object itself serves as that member.
    +#
    +#   <ExtensionIDs> are used as indexes into this object.  Each entry is a hashref that maps item strings to
    +#   <NaturalDocs::SourceDB::ItemDefinition>-derived objects.  This is only done for extensions that use those objects to track
    +#   definitions, it's not needed for extensions that only track existence.  If there are no definitions, the entry will be undef.
    +#
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $class = shift;
    +
    +    my $object = [ ];
    +    bless $object, $class;
    +
    +    return $object;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Definition Functions
    +#
    +
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a definition for the passed item string.  If it's already defined, the new definition will be ignored.
    +#
    +#   Parameters:
    +#
    +#       extension - The <ExtensionID>.
    +#       itemString - The item string.
    +#       definition - The definition, which must be an object derived from <NaturalDocs::SourceDB::ItemDefinition>.
    +#
    +#   Returns:
    +#
    +#       Whether the definition was added, which is to say, whether this was the first definition for the passed <FileName>.
    +#
    +sub AddDefinition #(ExtensionID extension, string itemString, NaturalDocs::SourceDB::ItemDefinition definition) => bool
    +    {
    +    my ($self, $extension, $itemString, $definition) = @_;
    +
    +    if (!defined $self->[$extension])
    +        {  $self->[$extension] = { };  };
    +
    +    if (!exists $self->[$extension]->{$itemString})
    +        {
    +        $self->[$extension]->{$itemString} = $definition;
    +        return 1;
    +        }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +#
    +#   Function: GetDefinition
    +#
    +#   Returns the <NaturalDocs::SourceDB::ItemDefinition>-derived object for the passed item string  or undef if there is none.
    +#
    +sub GetDefinition #(ExtensionID extension, string itemString) => NaturalDocs::SourceDB::ItemDefinition
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $self->[$extension])
    +        {  return $self->[$extension]->{$itemString};  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes the definition for the passed item string.  Returns whether it was successful, meaning whether a definition existed
    +#   for that item.
    +#
    +sub DeleteDefinition #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $self->[$extension])
    +        {
    +        if (exists $self->[$extension]->{$itemString})
    +            {
    +            delete $self->[$extension]->{$itemString};
    +
    +            if (!scalar keys %{$self->[$extension]})
    +                {  $self->[$extension] = undef;  };
    +
    +            return 1;
    +            };
    +        };
    +
    +    return 0;
    +    };
    +
    +
    +#
    +#   Function: HasDefinitions
    +#
    +#   Returns whether there are any definitions for this item.
    +#
    +sub HasDefinitions #(ExtensionID extension) => bool
    +    {
    +    my ($self, $extension) = @_;
    +
    +    return (defined $self->[$extension]);
    +    };
    +
    +
    +#
    +#   Function: HasDefinition
    +#
    +#   Returns whether there is a definition for the passed item string.
    +#
    +sub HasDefinition #(ExtensionID extension, string itemString) => bool
    +    {
    +    my ($self, $extension, $itemString) = @_;
    +
    +    if (defined $self->[$extension])
    +        {  return (exists $self->[$extension]->{$itemString});  }
    +    else
    +        {  return 0;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/StatusMessage.pm b/vendor/naturaldocs/Modules/NaturalDocs/StatusMessage.pm
    new file mode 100644
    index 000000000..e374c0e55
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/StatusMessage.pm
    @@ -0,0 +1,103 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::StatusMessage
    +#
    +###############################################################################
    +#
    +#   A package to handle status message updates.  Automatically handles <NaturalDocs::Settings->IsQuiet()>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::StatusMessage;
    +
    +
    +#
    +#   var: message
    +#   The message to display.
    +#
    +my $message;
    +
    +#
    +#   var: total
    +#   The number of items to work through.
    +#
    +my $total;
    +
    +#
    +#   var: completed
    +#   The number of items completed.
    +#
    +my $completed;
    +
    +#
    +#   var: lastMessageTime
    +#   The time the last message was posted.
    +#
    +my $lastMessageTime;
    +
    +
    +#
    +#   constant: TIME_BETWEEN_UPDATES
    +#   The number of seconds that should occur between updates.
    +#
    +use constant TIME_BETWEEN_UPDATES => 10;
    +
    +
    +
    +#
    +#   Function: Start
    +#
    +#   Starts the status message.
    +#
    +#   Parameters:
    +#
    +#       message - The message to post.
    +#       total - The number of items that are going to be worked through.
    +#
    +sub Start #(message, total)
    +    {
    +    my $self = shift;
    +
    +    if (!NaturalDocs::Settings->IsQuiet())
    +        {
    +        ($message, $total) = @_;
    +        $completed = 0;
    +
    +        print $message . "\n";
    +
    +        $lastMessageTime = time();
    +        };
    +    };
    +
    +
    +#
    +#   Function: CompletedItem
    +#
    +#   Should be called every time an item is completed.
    +#
    +sub CompletedItem
    +    {
    +    my $self = shift;
    +
    +    if (!NaturalDocs::Settings->IsQuiet())
    +        {
    +        # We scale completed by 100 since we need to anyway to get the percentage.
    +
    +        $completed += 100;
    +
    +        if (time() >= $lastMessageTime + TIME_BETWEEN_UPDATES && $completed != $total * 100)
    +            {
    +            print $message . ' (' . ($completed / $total) . '%)' . "\n";
    +            $lastMessageTime = time();
    +            };
    +        };
    +    };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolString.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolString.pm
    new file mode 100644
    index 000000000..facebb289
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolString.pm
    @@ -0,0 +1,213 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolString
    +#
    +###############################################################################
    +#
    +#   A package to manage <SymbolString> handling throughout the program.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolString;
    +
    +
    +#
    +#   Function: FromText
    +#
    +#   Extracts and returns a <SymbolString> from plain text.
    +#
    +#   This should be the only way to get a <SymbolString> from plain text, as the splitting and normalization must be consistent
    +#   throughout the application.
    +#
    +sub FromText #(string textSymbol)
    +    {
    +    my ($self, $textSymbol) = @_;
    +
    +    # The internal format of a symbol is all the normalized identifiers separated by 0x1F characters.
    +
    +    # Convert whitespace and reserved characters to spaces, and condense multiple consecutive ones.
    +    $textSymbol =~ tr/ \t\r\n\x1C\x1D\x1E\x1F/ /s;
    +
    +    # DEPENDENCY: ReferenceString->MakeFrom() assumes all 0x1E characters were removed.
    +    # DEPENDENCY: ReferenceString->MakeFrom() assumes this encoding doesn't use 0x1E characters.
    +
    +    # Remove spaces unless they're separating two alphanumeric/underscore characters.
    +    $textSymbol =~ s/^ //;
    +    $textSymbol =~ s/ $//;
    +    $textSymbol =~ s/(\W) /$1/g;
    +    $textSymbol =~ s/ (\W)/$1/g;
    +
    +    # Remove trailing empty parenthesis, so Function and Function() are equivalent.
    +    $textSymbol =~ s/\(\)$//;
    +
    +    # Split the string into pieces.
    +    my @pieces = split(/(\.|::|->)/, $textSymbol);
    +    my $symbolString;
    +
    +    my $lastWasSeparator = 1;
    +
    +    foreach my $piece (@pieces)
    +        {
    +        if ($piece =~ /^(?:\.|::|->)$/)
    +            {
    +            if (!$lastWasSeparator)
    +                {
    +                $symbolString .= "\x1F";
    +                $lastWasSeparator = 1;
    +                };
    +            }
    +        elsif (length $piece)
    +            {
    +            $symbolString .= $piece;
    +            $lastWasSeparator = 0;
    +            };
    +        # Ignore empty pieces
    +        };
    +
    +    $symbolString =~ s/\x1F$//;
    +
    +    return $symbolString;
    +    };
    +
    +
    +#
    +#   Function: ToText
    +#
    +#   Converts a <SymbolString> to text, using the passed separator.
    +#
    +sub ToText #(SymbolString symbolString, string separator)
    +    {
    +    my ($self, $symbolString, $separator) = @_;
    +
    +    my @identifiers = $self->IdentifiersOf($symbolString);
    +    return join($separator, @identifiers);
    +    };
    +
    +
    +#
    +#   Function: ToBinaryFile
    +#
    +#   Writes a <SymbolString> to the passed filehandle.  Can also encode an undef.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The filehandle to write to.
    +#       symbol - The <SymbolString> to write, or undef.
    +#
    +#   Format:
    +#
    +#       > [UInt8: number of identifiers]
    +#       >    [AString16: identifier] [AString16: identifier] ...
    +#
    +#       Undef is represented by a zero for the number of identifiers.
    +#
    +sub ToBinaryFile #(FileHandle fileHandle, SymbolString symbol)
    +    {
    +    my ($self, $fileHandle, $symbol) = @_;
    +
    +    my @identifiers;
    +    if (defined $symbol)
    +        {  @identifiers = $self->IdentifiersOf($symbol);  };
    +
    +    print $fileHandle pack('C', scalar @identifiers);
    +
    +    foreach my $identifier (@identifiers)
    +        {
    +        print $fileHandle pack('nA*', length($identifier), $identifier);
    +        };
    +    };
    +
    +
    +#
    +#   Function: FromBinaryFile
    +#
    +#   Loads a <SymbolString> or undef from the filehandle and returns it.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The filehandle to read from.
    +#
    +#   Returns:
    +#
    +#       The <SymbolString> or undef.
    +#
    +#   See also:
    +#
    +#       See <ToBinaryFile()> for format and dependencies.
    +#
    +sub FromBinaryFile #(FileHandle fileHandle)
    +    {
    +    my ($self, $fileHandle) = @_;
    +
    +    my $raw;
    +
    +    # [UInt8: number of identifiers or 0 if none]
    +
    +    read($fileHandle, $raw, 1);
    +    my $identifierCount = unpack('C', $raw);
    +
    +    my @identifiers;
    +
    +    while ($identifierCount)
    +        {
    +        # [AString16: identifier] [AString16: identifier] ...
    +
    +        read($fileHandle, $raw, 2);
    +        my $identifierLength = unpack('n', $raw);
    +
    +        my $identifier;
    +        read($fileHandle, $identifier, $identifierLength);
    +
    +        push @identifiers, $identifier;
    +
    +        $identifierCount--;
    +        };
    +
    +    if (scalar @identifiers)
    +        {  return $self->Join(@identifiers);  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: IdentifiersOf
    +#
    +#   Returns the <SymbolString> as an array of identifiers.
    +#
    +sub IdentifiersOf #(SymbolString symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +    return split(/\x1F/, $symbol);
    +    };
    +
    +
    +#
    +#   Function: Join
    +#
    +#   Takes a list of identifiers and/or <SymbolStrings> and returns it as a new <SymbolString>.
    +#
    +sub Join #(string/SymbolString identifier/symbol, string/SymolString identifier/symbol, ...)
    +    {
    +    my ($self, @pieces) = @_;
    +
    +    # Can't have undefs screwing everything up.
    +    while (scalar @pieces && !defined $pieces[0])
    +        {  shift @pieces;  };
    +
    +    # We need to test @pieces first because joining on an empty array returns an empty string rather than undef.
    +    if (scalar @pieces)
    +       {  return join("\x1F", @pieces);  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable.pm
    new file mode 100644
    index 000000000..cf868bd6f
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable.pm
    @@ -0,0 +1,1985 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolTable
    +#
    +###############################################################################
    +#
    +#   A package that handles all the gory details of managing symbols.  It handles where they are defined, which files
    +#   reference them, if any are undefined or duplicated, and loading and saving them to a file.
    +#
    +#   Usage and Dependencies:
    +#
    +#       - At any time, <RebuildAllIndexes()> can be called.
    +#
    +#       - <NaturalDocs::Settings>, <NaturalDocs::Languages>, and <NaturalDocs::Project> must be initialized before use.
    +#
    +#       - <Load()> must be called to initialize the package.  At this point, the <Information Functions> will return the symbol
    +#         table as of the last time Natural Docs was run.
    +#
    +#       - Note that <Load()> and <Save()> only manage <REFERENCE_TEXT> references.  All other reference types must be
    +#         managed by their respective classes.  They should be readded after <Load()> to recreate the state of the last time
    +#         Natural Docs was run.
    +#
    +#       - <Purge()> must be called, and then <NaturalDocs::Parser->ParseForInformation()> on all files that have changed so it
    +#         can fully resolve the symbol table via the <Modification Functions>.  Afterwards <PurgeResolvingInfo()> can be called
    +#         to reclaim some memory, and the symbol table will reflect the current state of the code.
    +#
    +#       - <Save()> must be called to commit any changes to the symbol table back to disk.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +
    +use NaturalDocs::SymbolTable::Symbol;
    +use NaturalDocs::SymbolTable::SymbolDefinition;
    +use NaturalDocs::SymbolTable::Reference;
    +use NaturalDocs::SymbolTable::File;
    +use NaturalDocs::SymbolTable::ReferenceTarget;
    +use NaturalDocs::SymbolTable::IndexElement;
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable;
    +
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +#
    +#   handle: SYMBOLTABLE_FILEHANDLE
    +#
    +#   The file handle used with <SymbolTable.nd>.
    +#
    +
    +#
    +#   hash: symbols
    +#
    +#   A hash of all <SymbolStrings>.  The keys are the <SymbolStrings> and the values are <NaturalDocs::SymbolTable::Symbol>
    +#   objects.
    +#
    +#   Prior to <PurgeResolvingInfo()>, both defined symbols and symbols that are merely potential interpretations of references
    +#   will be here.  Afterwards, only defined symbols will be here.
    +#
    +my %symbols;
    +
    +#
    +#   hash: references
    +#
    +#   A hash of all references in the project.  The keys are <ReferenceStrings> and the values are
    +#   <NaturalDocs::SymbolTable::Reference> objects.
    +#
    +#   Prior to <PurgeResolvingInfo()>, all possible interpretations will be stored for each reference.  Afterwards, only the current
    +#   interpretation will be.
    +#
    +my %references;
    +
    +#
    +#   hash: files
    +#
    +#   A hash of all the files that define symbols and references in the project.  The keys are the <FileNames>, and the values are
    +#   <NaturalDocs::SymbolTable::File> objects.
    +#
    +#   After <PurgeResolvingInfo()>, this hash will be empty.
    +#
    +my %files;
    +
    +#
    +#   object: watchedFile
    +#
    +#   A <NaturalDocs::SymbolTable::File> object of the file being watched for changes.  This is compared to the version in <files>
    +#   to see if anything was changed since the last parse.
    +#
    +my $watchedFile;
    +
    +#
    +#   string: watchedFileName
    +#
    +#   The <FileName> of the watched file, if any.  If there is no watched file, this will be undef.
    +#
    +my $watchedFileName;
    +
    +#
    +#   hash: watchedFileSymbolDefinitions
    +#
    +#   A hashref of the symbol definition information for all the <SymbolStrings> in the watched file.  The keys are the symbol strings,
    +#   and the values are <NaturalDocs::SymbolTable::SymbolDefinition> objects.
    +#
    +my %watchedFileSymbolDefinitions;
    +
    +
    +#
    +#   hash: indexes
    +#
    +#   A hash of generated symbol indexes.  The keys are <TopicTypes> and the values are sorted arrayrefs of
    +#   <NaturalDocs::SymbolTable::IndexElements>, or undef if its empty.
    +#
    +my %indexes;
    +
    +
    +#
    +#   hash: indexChanges
    +#
    +#   A hash of all the indexes that have changed.  The keys are the <TopicTypes> and the entries are undef if they have not
    +#   changed, or 1 if they have.  The key will not exist if the <TopicType> has not been checked.
    +#
    +my %indexChanges;
    +
    +
    +#
    +#   hash: indexSectionsWithContent
    +#
    +#   A hash of which sections in an index have content.  The keys are the <TopicTypes> of each index, and the values are
    +#   arrayrefs of bools where the first represents symbols, the second numbers, and the rest A-Z.  If there is no information
    +#   available for an index, it's entry will not exist here.
    +#
    +my %indexSectionsWithContent;
    +
    +
    +#
    +#   bool: rebuildIndexes
    +#
    +#   Whether all indexes should be rebuilt regardless of whether they have been changed.
    +#
    +my $rebuildIndexes;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: SymbolTable.nd
    +#
    +#   The storage file for the symbol table.
    +#
    +#   Format:
    +#
    +#       > [BINARY_FORMAT]
    +#       > [VersionInt: app version]
    +#
    +#       The file starts with the standard <BINARY_FORMAT> <VersionInt> header.
    +#
    +#       The first stage of the file is for symbol definitions, analogous to <symbols>.
    +#
    +#       > [SymbolString: symbol or undef to end] ...
    +#       >
    +#       > [UInt16: number of definitions]
    +#       >
    +#       >    [AString16: global definition file] [AString16: TopicType]
    +#       >       [AString16: prototype] [AString16: summary]
    +#       >
    +#       >    [AString16: definition file] ...
    +#       >
    +#       >    ...
    +#
    +#       These blocks continue until the <SymbolString> is undef.  Only defined symbols will be included in this file, so
    +#       number of definitions will never be zero.  The first one is always the global definition.  If a symbol does not have a
    +#       prototype or summary, the UInt16 length of the string will be zero.
    +#
    +#       The second stage is for references, which is analogous to <references>.  Only <REFERENCE_TEXT> references are
    +#       stored in this file, and their <Resolving Flags> are implied so they aren't stored either.
    +#
    +#       > [ReferenceString (no type, resolving flags): reference or undef to end]
    +#       >
    +#       > [UInt8: number of definition files]
    +#       >    [AString16: definition file] [AString16: definition file] ...
    +#
    +#       These blocks continue until the <ReferenceString> is undef.  Since there can be multiple using <SymbolStrings>, those
    +#       continue until the number of identifiers is zero.  Note that all interpretations are rebuilt rather than stored.
    +#
    +#   See Also:
    +#
    +#       <File Format Conventions>
    +#
    +#   Revisions:
    +#
    +#       1.3:
    +#
    +#           - Symbol <TopicTypes> were changed from UInt8s to AString16s, now that <TopicTypes> are strings instead of
    +#             integer constants.
    +#
    +#       1.22:
    +#
    +#           - File format was completely rebuilt to accommodate the new symbol format and to be in binary.  To see the plain text
    +#             format prior to 1.22, check out 1.21's version of this file from CVS.  It is too big a change to note here.
    +#
    +
    +
    +#
    +#   File: IndexInfo.nd
    +#
    +#   The storage file for information about the indexes.
    +#
    +#   Format:
    +#
    +#       > [Standard Header]
    +#
    +#       The standard binary file header.
    +#
    +#       > [AString16: index topic name]
    +#       > [uint8: symbols have content (0 or 1)]
    +#       > [uint8: numbers have content (0 or 1)]
    +#       > [uint8: A has content] [uint8: B has content] ...
    +#       > ...
    +#
    +#       Every index that has information about it is stored with the topic type name first, then 28 uint8s that say whether that
    +#       part of the index has content or not.  The first is for symbols, the second is for numbers, and the rest are for A-Z.  If an
    +#       index's state is unknown, it won't appear in this file.
    +#
    +#   Revisions:
    +#
    +#       1.4:
    +#
    +#           - The file is introduced.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads all data files from disk.
    +#
    +sub Load
    +    {
    +    my ($self) = @_;
    +
    +    $self->LoadSymbolTable();
    +    $self->LoadIndexInfo();
    +    };
    +
    +
    +#
    +#   Function: LoadSymbolTable
    +#
    +#   Loads <SymbolTable.nd> from disk.
    +#
    +sub LoadSymbolTable
    +    {
    +    my ($self) = @_;
    +
    +    my $fileIsOkay;
    +
    +    if (!NaturalDocs::Settings->RebuildData() &&
    +        open(SYMBOLTABLE_FILEHANDLE, '<' . NaturalDocs::Project->DataFile('SymbolTable.nd')) )
    +        {
    +        # See if it's binary.
    +        binmode(SYMBOLTABLE_FILEHANDLE);
    +
    +        my $firstChar;
    +        read(SYMBOLTABLE_FILEHANDLE, $firstChar, 1);
    +
    +        if ($firstChar == ::BINARY_FORMAT())
    +            {
    +            my $version = NaturalDocs::Version->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE);
    +
    +            # 1.3 is incompatible with previous versions.
    +
    +            if (NaturalDocs::Version->CheckFileFormat( $version, NaturalDocs::Version->FromString('1.3') ))
    +                {  $fileIsOkay = 1;  }
    +            else
    +                {  close(SYMBOLTABLE_FILEHANDLE);  };
    +            }
    +
    +        else
    +            {  close(SYMBOLTABLE_FILEHANDLE);  };
    +        };
    +
    +
    +    if (!$fileIsOkay)
    +        {
    +        NaturalDocs::Project->ReparseEverything();
    +        return;
    +        }
    +
    +    my $raw;
    +
    +
    +    # Symbols
    +
    +    for (;;)
    +        {
    +        # [SymbolString: symbol or undef to end]
    +
    +        my $symbol = NaturalDocs::SymbolString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE);
    +
    +        if (!defined $symbol)
    +            {  last;  };
    +
    +        my $symbolObject = NaturalDocs::SymbolTable::Symbol->New();
    +        $symbols{$symbol} = $symbolObject;
    +
    +        # [UInt16: number of definitions]
    +
    +        read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +        my $definitionCount = unpack('n', $raw);
    +
    +        do
    +            {
    +            # [AString16: (global?) definition file]
    +
    +            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +            my $fileLength = unpack('n', $raw);
    +
    +            my $file;
    +            read(SYMBOLTABLE_FILEHANDLE, $file, $fileLength);
    +
    +            # [AString16: TopicType]
    +
    +            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +            my $typeLength = unpack('n', $raw);
    +
    +            my $type;
    +            read(SYMBOLTABLE_FILEHANDLE, $type, $typeLength);
    +
    +            # [AString16: prototype]
    +
    +            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +            my $prototypeLength = unpack('n', $raw);
    +
    +            my $prototype;
    +            if ($prototypeLength)
    +                {  read(SYMBOLTABLE_FILEHANDLE, $prototype, $prototypeLength);  };
    +
    +            # [AString16: summary]
    +
    +            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +            my $summaryLength = unpack('n', $raw);
    +
    +            my $summary;
    +            if ($summaryLength)
    +                {  read(SYMBOLTABLE_FILEHANDLE, $summary, $summaryLength);  };
    +
    +            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
    +
    +            # Add it.
    +
    +            if (!exists $files{$file})
    +                {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
    +
    +            $files{$file}->AddSymbol($symbol);
    +
    +            $definitionCount--;
    +            }
    +        while ($definitionCount);
    +        };
    +
    +
    +    # References
    +
    +    for (;;)
    +        {
    +        # [ReferenceString (no type, resolving flags): reference or undef to end]
    +
    +        my $referenceString = NaturalDocs::ReferenceString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE,
    +                                                                                                              ::BINARYREF_NOTYPE() |
    +                                                                                                              ::BINARYREF_NORESOLVINGFLAGS(),
    +                                                                                                              ::REFERENCE_TEXT(), undef);
    +
    +        if (!defined $referenceString)
    +            {  last;  };
    +
    +        my $referenceObject = NaturalDocs::SymbolTable::Reference->New();
    +        $references{$referenceString} = $referenceObject;
    +
    +        # [UInt8: number of definition files]
    +
    +        read(SYMBOLTABLE_FILEHANDLE, $raw, 1);
    +        my $definitionCount = unpack('C', $raw);
    +        do
    +            {
    +            # [AString16: definition file] [AString16: definition file] ...
    +
    +            read(SYMBOLTABLE_FILEHANDLE, $raw, 2);
    +            my $definitionLength = unpack('n', $raw);
    +
    +            my $definition;
    +            read(SYMBOLTABLE_FILEHANDLE, $definition, $definitionLength);
    +
    +            # Add it.
    +
    +            $referenceObject->AddDefinition($definition);
    +
    +            if (!exists $files{$definition})
    +                {  $files{$definition} = NaturalDocs::SymbolTable::File->New();  };
    +
    +            $files{$definition}->AddReference($referenceString);
    +
    +            $definitionCount--;
    +            }
    +        while ($definitionCount);
    +
    +        $self->GenerateInterpretations($referenceString);
    +        $self->InterpretReference($referenceString);
    +        };
    +
    +    close(SYMBOLTABLE_FILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: LoadIndexInfo
    +#
    +#   Loads <IndexInfo.nd> from disk.
    +#
    +sub LoadIndexInfo
    +    {
    +    my ($self) = @_;
    +
    +    if (NaturalDocs::Settings->RebuildData())
    +        {  return;  };
    +
    +    my $version = NaturalDocs::BinaryFile->OpenForReading( NaturalDocs::Project->DataFile('IndexInfo.nd') );
    +
    +    if (!defined $version)
    +        {  return;  }
    +
    +    # The file format hasn't changed since it was introduced.
    +    if (!NaturalDocs::Version->CheckFileFormat($version))
    +        {
    +        NaturalDocs::BinaryFile->Close();
    +        return;
    +        };
    +
    +    my $topicTypeName;
    +    while ($topicTypeName = NaturalDocs::BinaryFile->GetAString16())
    +        {
    +        my $topicType = NaturalDocs::Topics->TypeFromName($topicTypeName);
    +        my $content = [ ];
    +
    +        for (my $i = 0; $i < 28; $i++)
    +            {  push @$content, NaturalDocs::BinaryFile->GetUInt8();  };
    +
    +        if (defined $topicType)  # The name in the file could be from a type that was deleted
    +            {  $indexSectionsWithContent{$topicType} = $content;  };
    +        };
    +
    +    NaturalDocs::BinaryFile->Close();
    +    };
    +
    +
    +#
    +#   Function: Purge
    +#
    +#   Purges the symbol table of all symbols and references from files that no longer have Natural Docs content.
    +#
    +sub Purge
    +    {
    +    my ($self) = @_;
    +
    +    my $filesToPurge = NaturalDocs::Project->FilesToPurge();
    +
    +    # We do this in two stages.  First we delete all the references, and then we delete all the definitions.  This causes us to go
    +    # through the list twice, but it makes sure no purged files get added to the build list.  For example, if we deleted all of
    +    # Purge File A's references and definitions, and Purge File B had a reference to one of those symbols, Purge File B
    +    # would be added to the build list because one of its references changed.  By removing all the references in all the files
    +    # before removing the definitions, we avoid this.
    +
    +    foreach my $file (keys %$filesToPurge)
    +        {
    +        if (exists $files{$file})
    +            {
    +            my @references = $files{$file}->References();
    +            foreach my $reference (@references)
    +                {  $self->DeleteReference($reference, $file);  };
    +            };
    +        };
    +
    +    foreach my $file (keys %$filesToPurge)
    +        {
    +        if (exists $files{$file})
    +            {
    +            my @symbols = $files{$file}->Symbols();
    +            foreach my $symbol (@symbols)
    +                {  $self->DeleteSymbol($symbol, $file);  };
    +
    +            delete $files{$file};
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves all data files to disk.
    +#
    +sub Save
    +    {
    +    my ($self) = @_;
    +
    +    $self->SaveSymbolTable();
    +    $self->SaveIndexInfo();
    +    };
    +
    +
    +#
    +#   Function: SaveSymbolTable
    +#
    +#   Saves <SymbolTable.nd> to disk.
    +#
    +sub SaveSymbolTable
    +    {
    +    my ($self) = @_;
    +
    +    open (SYMBOLTABLE_FILEHANDLE, '>' . NaturalDocs::Project->DataFile('SymbolTable.nd'))
    +        or die "Couldn't save " . NaturalDocs::Project->DataFile('SymbolTable.nd') . ".\n";
    +
    +    binmode(SYMBOLTABLE_FILEHANDLE);
    +
    +    print SYMBOLTABLE_FILEHANDLE '' . ::BINARY_FORMAT();
    +
    +    NaturalDocs::Version->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, NaturalDocs::Settings->AppVersion());
    +
    +
    +    # Symbols
    +
    +    while (my ($symbol, $symbolObject) = each %symbols)
    +        {
    +        # Only existing symbols.
    +        if ($symbolObject->IsDefined())
    +            {
    +            # [SymbolString: symbol or undef to end]
    +
    +            NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $symbol);
    +
    +            # [UInt16: number of definitions]
    +
    +            my @definitions = $symbolObject->Definitions();
    +            print SYMBOLTABLE_FILEHANDLE pack('n', scalar @definitions);
    +
    +            # [AString16: global definition file] [AString16: TopicType]
    +
    +            print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $symbolObject->GlobalDefinition(),
    +                                                                                   $symbolObject->GlobalDefinition(),
    +                                                                                   length $symbolObject->GlobalType(),
    +                                                                                   $symbolObject->GlobalType());
    +
    +            # [AString16: prototype]
    +
    +            my $prototype = $symbolObject->GlobalPrototype();
    +
    +            if (defined $prototype)
    +                {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype);  }
    +            else
    +                {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
    +
    +            # [AString16: summary]
    +
    +            my $summary = $symbolObject->GlobalSummary();
    +
    +            if (defined $summary)
    +                {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary);  }
    +            else
    +                {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
    +
    +
    +            foreach my $definition (@definitions)
    +                {
    +                if ($definition ne $symbolObject->GlobalDefinition())
    +                    {
    +                    # [AString16: definition file] [AString16: TopicType]
    +
    +                    print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $definition, $definition,
    +                                                                                           length $symbolObject->TypeDefinedIn($definition),
    +                                                                                           $symbolObject->TypeDefinedIn($definition));
    +
    +                    # [AString16: prototype]
    +
    +                    my $prototype = $symbolObject->PrototypeDefinedIn($definition);
    +
    +                    if (defined $prototype)
    +                        {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype);  }
    +                    else
    +                        {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
    +
    +                    # [AString16: summary]
    +
    +                    my $summary = $symbolObject->SummaryDefinedIn($definition);
    +
    +                    if (defined $summary)
    +                        {  print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary);  }
    +                    else
    +                        {  print SYMBOLTABLE_FILEHANDLE pack('n', 0);  };
    +                    };
    +                };
    +            };
    +        };
    +
    +     # [SymbolString: symbol or undef to end]
    +
    +     NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef);
    +
    +
    +     # References
    +
    +    while (my ($reference, $referenceObject) = each %references)
    +        {
    +        my $type = NaturalDocs::ReferenceString->TypeOf($reference);
    +
    +        if ($type == ::REFERENCE_TEXT())
    +            {
    +            # [ReferenceString (no type, resolving flags): reference or undef to end]
    +
    +            NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $reference,
    +                                                                             ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS());
    +
    +            # [UInt8: number of definition files]
    +
    +            my @definitions = $referenceObject->Definitions();
    +            print SYMBOLTABLE_FILEHANDLE pack('C', scalar @definitions);
    +
    +            # [AString16: definition file] [AString16: definition file] ...
    +
    +            foreach my $definition (@definitions)
    +                {
    +                print SYMBOLTABLE_FILEHANDLE pack('nA*', length($definition), $definition);
    +                };
    +            };
    +        };
    +
    +    # [ReferenceString (no type, resolving flags): reference or undef to end]
    +
    +    NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef,
    +                                                                     ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS());
    +
    +    close(SYMBOLTABLE_FILEHANDLE);
    +    };
    +
    +
    +#
    +#   Function: SaveIndexInfo
    +#
    +#   Saves <IndexInfo.nd> to disk.
    +#
    +sub SaveIndexInfo
    +    {
    +    my ($self) = @_;
    +
    +    NaturalDocs::BinaryFile->OpenForWriting( NaturalDocs::Project->DataFile('IndexInfo.nd') );
    +
    +    while (my ($topicType, $content) = each %indexSectionsWithContent)
    +        {
    +        NaturalDocs::BinaryFile->WriteAString16( NaturalDocs::Topics->NameOfType($topicType) );
    +
    +        for (my $i = 0; $i < 28; $i++)
    +            {
    +            if ($content->[$i])
    +                {  NaturalDocs::BinaryFile->WriteUInt8(1);  }
    +            else
    +                {  NaturalDocs::BinaryFile->WriteUInt8(0);  };
    +            };
    +        };
    +
    +    NaturalDocs::BinaryFile->Close();
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +# These functions should not be called after <PurgeResolvingInfo()>.
    +
    +#
    +#   Function: AddSymbol
    +#
    +#   Adds a symbol definition to the table, if it doesn't already exist.  If the definition changes or otherwise requires the files that
    +#   reference it to be updated, the function will call <NaturalDocs::Project->RebuildFile()> to make sure that they are.
    +#
    +#   Parameters:
    +#
    +#       symbol  - The <SymbolString>.
    +#       file        - The <FileName> where it's defined.
    +#       type      - The symbol's <TopicType>.
    +#       prototype - The symbol's prototype, if applicable.
    +#       summary - The symbol's summary, if applicable.
    +#
    +sub AddSymbol #(symbol, file, type, prototype, summary)
    +    {
    +    my ($self, $symbol, $file, $type, $prototype, $summary) = @_;
    +
    +
    +    # If the symbol doesn't exist...
    +    if (!exists $symbols{$symbol})
    +        {
    +        # Create the symbol.  There are no references that could be interpreted as this or else it would have existed already.
    +
    +        my $newSymbol = NaturalDocs::SymbolTable::Symbol->New();
    +        $newSymbol->AddDefinition($file, $type, $prototype, $summary);
    +
    +        $symbols{$symbol} = $newSymbol;
    +
    +        $self->OnIndexChange($type);
    +        NaturalDocs::Project->RebuildFile($file);
    +        }
    +
    +
    +    # If the symbol already exists...
    +    else
    +        {
    +        my $symbolObject = $symbols{$symbol};
    +
    +        # If the symbol isn't defined, i.e. it was a potential interpretation only...
    +        if (!$symbolObject->IsDefined())
    +            {
    +            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
    +
    +            # See if this symbol provides a better interpretation of any references.  We can assume this symbol has interpretations
    +            # because the object won't exist without either that or definitions.
    +
    +            my %referencesAndScores = $symbolObject->ReferencesAndScores();
    +
    +            while (my ($referenceString, $referenceScore) = each %referencesAndScores)
    +                {
    +                my $referenceObject = $references{$referenceString};
    +
    +                if (!$referenceObject->HasCurrentInterpretation() ||
    +                    $referenceScore > $referenceObject->CurrentScore())
    +                    {
    +                    $referenceObject->SetCurrentInterpretation($symbol);
    +                    $self->OnInterpretationChange($referenceString);
    +                    };
    +                };
    +
    +            $self->OnIndexChange($type);
    +            NaturalDocs::Project->RebuildFile($file);
    +            }
    +
    +        # If the symbol is defined but not in this file...
    +        elsif (!$symbolObject->IsDefinedIn($file))
    +            {
    +            $symbolObject->AddDefinition($file, $type, $prototype, $summary);
    +
    +            $self->OnIndexChange($type);
    +            NaturalDocs::Project->RebuildFile($file);
    +
    +            # We don't have to check other files because if the symbol is defined it already has a global definiton,
    +            # and everything else is either using that or its own definition, and thus wouldn't be affected by this.
    +            };
    +
    +        # If the symbol was already defined in this file, ignore it.
    +
    +        };
    +
    +
    +    # Add it to the file index.
    +
    +    if (!exists $files{$file})
    +        {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
    +
    +    $files{$file}->AddSymbol($symbol);
    +
    +
    +    # Add it to the watched file, if necessary.
    +
    +    if (defined $watchedFileName)
    +        {
    +        $watchedFile->AddSymbol($symbol);
    +
    +        if (!exists $watchedFileSymbolDefinitions{$symbol})
    +            {
    +            $watchedFileSymbolDefinitions{$symbol} =
    +                 NaturalDocs::SymbolTable::SymbolDefinition->New($type, $prototype, $summary);
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: AddReference
    +#
    +#   Adds a reference to the table, if it doesn't already exist.
    +#
    +#   Parameters:
    +#
    +#       type        - The <ReferenceType>.
    +#       symbol    - The reference <SymbolString>.
    +#       scope      - The scope <SymbolString> it appears in.
    +#       using       - An arrayref of scope <SymbolStrings> accessible to the reference via "using" statements, or undef if none.
    +#       file          - The <FileName> where the reference appears.  This is not required unless the type is <REFERENCE_TEXT>.
    +#       resolvingFlags - The <Resolving Flags> of the reference.  They will be ignored if the type is <REFERENCE_TEXT>.
    +#
    +#   Alternate Parameters:
    +#
    +#       referenceString - The <ReferenceString> to add.
    +#       file - The <FileName> where the reference appears.  This is not required unless the type is <REFERENCE_TEXT>.
    +#
    +sub AddReference #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file)
    +    {
    +    my ($self, $referenceString, $file);
    +
    +    if (scalar @_ <= 3)
    +        {
    +        ($self, $referenceString, $file) = @_;
    +        }
    +    else
    +        {
    +        my ($type, $symbol, $scope, $using, $resolvingFlags);
    +        ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_;
    +
    +        $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol,
    +                                                                                                   NaturalDocs::Languages->LanguageOf($file)->Name(),
    +                                                                                                   $scope, $using, $resolvingFlags);
    +        };
    +
    +
    +    # If the reference doesn't exist...
    +    if (!exists $references{$referenceString})
    +        {
    +        my $referenceObject = NaturalDocs::SymbolTable::Reference->New();
    +
    +        $references{$referenceString} = $referenceObject;
    +
    +        $self->GenerateInterpretations($referenceString);
    +        $self->InterpretReference($referenceString);
    +        }
    +
    +
    +    if (defined $file)
    +        {
    +        $references{$referenceString}->AddDefinition($file);
    +
    +
    +        # Add it to the file index.
    +
    +        if (!exists $files{$file})
    +            {  $files{$file} = NaturalDocs::SymbolTable::File->New();  };
    +
    +        $files{$file}->AddReference($referenceString);
    +
    +
    +        # Add it to the watched file, if necessary.
    +
    +        if (defined $watchedFileName)
    +            {  $watchedFile->AddReference($referenceString);  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: WatchFileForChanges
    +#
    +#   Tracks a file to see if any symbols or references were changed or deleted in ways that would require other files to be rebuilt.
    +#   Assumes that after this function call, the entire file will be parsed again, and thus every symbol and reference will go through
    +#   <AddSymbol()> and <AddReference()>.  Afterwards, call <AnalyzeChanges()> to handle any differences.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> to watch.
    +#
    +sub WatchFileForChanges #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    $watchedFile = NaturalDocs::SymbolTable::File->New();
    +    $watchedFileName = $file;
    +    %watchedFileSymbolDefinitions = ( );
    +    };
    +
    +
    +#
    +#   Function: AnalyzeChanges
    +#
    +#   Handles any changes found when reparsing a file using <WatchFileForChanges()>.
    +#
    +sub AnalyzeChanges
    +    {
    +    my ($self) = @_;
    +
    +    if (exists $files{$watchedFileName})
    +        {
    +
    +        # Go through the references and remove any that were deleted.  Ones that were added will have already been added to
    +        # the table in AddReference().
    +
    +        my @references = $files{$watchedFileName}->References();
    +        foreach my $reference (@references)
    +            {
    +            if (!$watchedFile->DefinesReference($reference))
    +                {  $self->DeleteReference($reference, $watchedFileName);  };
    +            };
    +        };
    +
    +    # We have to check if the watched file exists again because DeleteReference() could have removed it.  I'm still not sure how a
    +    # file could have references without symbols, but apparently it's happened in the real world because it's crashed on people.
    +    if (exists $files{$watchedFileName})
    +        {
    +        # Go through the symbols.
    +
    +        my $rebuildFile;
    +
    +        my @symbols = $files{$watchedFileName}->Symbols();
    +        foreach my $symbol (@symbols)
    +            {
    +            # Delete symbols that don't exist.
    +
    +            if (!$watchedFile->DefinesSymbol($symbol))
    +                {
    +                $self->DeleteSymbol($symbol, $watchedFileName);
    +                $rebuildFile = 1;
    +                }
    +
    +            else
    +                {
    +                my $symbolObject = $symbols{$symbol};
    +                my $newSymbolDef = $watchedFileSymbolDefinitions{$symbol};
    +
    +                # Update symbols that changed.
    +
    +                if ( $symbolObject->TypeDefinedIn($watchedFileName) ne $newSymbolDef->Type() ||
    +                     $symbolObject->PrototypeDefinedIn($watchedFileName) ne $newSymbolDef->Prototype() ||
    +                     $symbolObject->SummaryDefinedIn($watchedFileName) ne $newSymbolDef->Summary() )
    +                    {
    +                    $self->OnIndexChange($symbolObject->TypeDefinedIn($watchedFileName));
    +                    $self->OnIndexChange($newSymbolDef->Type());
    +                    $rebuildFile = 1;
    +
    +                    $symbolObject->ChangeDefinition($watchedFileName, $newSymbolDef->Type(), $newSymbolDef->Prototype(),
    +                                                                       $newSymbolDef->Summary());
    +
    +                    # If the symbol definition was the global one, we need to update all files that reference it.  If it wasn't, the only file
    +                    # that could references it is itself, and the only way the symbol definition could change in the first place was if it was
    +                    # itself changed.
    +                    if ($symbolObject->GlobalDefinition() eq $watchedFileName)
    +                        {
    +                        # Rebuild the files that have references to this symbol
    +                        my @references = $symbolObject->References();
    +                        foreach my $reference (@references)
    +                            {
    +                            if ($references{$reference}->CurrentInterpretation() eq $symbol)
    +                                {  $self->OnTargetSymbolChange($reference);  };
    +                            }; # While references
    +                        }; # If global definition is watched file
    +                    }; # If the symbol definition changed
    +                }; # If the symbol still exists
    +            }; # foreach symbol in watched file
    +
    +        if ($rebuildFile)
    +            {  NaturalDocs::Project->RebuildFile($watchedFileName);  };
    +
    +        };
    +
    +
    +    $watchedFile = undef;
    +    $watchedFileName = undef;
    +    %watchedFileSymbolDefinitions = ( );
    +    };
    +
    +
    +#
    +#   Function: DeleteReference
    +#
    +#   Deletes a reference from the table.
    +#
    +#   Be careful with this function, as deleting a reference means there are no more of them in the file at all.  The tables do not
    +#   keep track of how many times references appear in a file.  In these cases you should instead call <WatchFileForChanges()>,
    +#   reparse the file, thus readding all the references, and call <AnalyzeChanges()>.
    +#
    +#   <REFERENCE_TEXT> references should *always* be managed with <WatchFileForChanges()> and <AnalyzeChanges()>.
    +#   This function should only be used externally for other types of references.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString>.
    +#       file - The <FileName> where the reference is.  This is not required unless the type is <REFERENCE_TEXT>.
    +#
    +sub DeleteReference #(referenceString, file)
    +    {
    +    my ($self, $referenceString, $file) = @_;
    +
    +
    +    # If the reference exists...
    +    if (exists $references{$referenceString})
    +        {
    +        my $referenceObject = $references{$referenceString};
    +
    +        if (defined $file)
    +            {  $referenceObject->DeleteDefinition($file);  };
    +
    +        # If there are no other definitions, or it doesn't use file definitions to begin with...
    +        if (!$referenceObject->IsDefined())
    +            {
    +            my @interpretations = $referenceObject->Interpretations();
    +            foreach my $interpretation (@interpretations)
    +                {
    +                $symbols{$interpretation}->DeleteReference($referenceString);
    +                };
    +
    +            delete $references{$referenceString};
    +            };
    +
    +
    +        if (defined $file)
    +            {
    +            # Remove it from the file index.
    +
    +            $files{$file}->DeleteReference($referenceString);
    +
    +            if (!$files{$file}->HasAnything())
    +                {  delete $files{$file};  };
    +
    +            # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and
    +            # LoadAndPurge().
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: RebuildAllIndexes
    +#
    +#   When called, it makes sure all indexes are listed as changed by <IndexChanged()>, regardless of whether they actually did
    +#   or not.
    +#
    +#   This can be called at any time.
    +#
    +sub RebuildAllIndexes
    +    {
    +    my $self = shift;
    +    $rebuildIndexes = 1;
    +    };
    +
    +
    +#
    +#   Function: PurgeResolvingInfo
    +#
    +#   Purges unnecessary information from the symbol table after it is fully resolved.  This will reduce the memory footprint for the
    +#   build stage.  After calling this function, you can only call the <Information Functions> and <Save()>.
    +#
    +sub PurgeResolvingInfo
    +    {
    +    my ($self) = @_;
    +
    +    # Go through the symbols.  We don't need to keep around potential symbols anymore, nor do we need what references can
    +    # be interpreted as the defined ones.
    +
    +    while (my ($symbol, $symbolObject) = each %symbols)
    +        {
    +        if ($symbolObject->IsDefined())
    +            {  $symbolObject->DeleteAllReferences();  }
    +        else
    +            {  delete $symbols{$symbol};  };
    +        };
    +
    +
    +    # Go through the references.  We don't need any of the interpretations except for the current.
    +
    +    foreach my $referenceObject (values %references)
    +        {  $referenceObject->DeleteAllInterpretationsButCurrent();  };
    +
    +
    +    # We don't need the information by file at all.
    +
    +    %files = ( );
    +    };
    +
    +
    +#
    +#   Function: PurgeIndexes
    +#
    +#   Clears all generated indexes.
    +#
    +sub PurgeIndexes
    +    {
    +    my ($self) = @_;
    +    %indexes = ( );
    +    };
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +# These functions should not be called until the symbol table is fully resolved.
    +
    +
    +#
    +#   Function: References
    +#
    +#   Returns what the passed reference information resolve to, if anything.  Note that this only works if the reference had
    +#   been previously added to the table via <AddReference()> with the exact same parameters.
    +#
    +#   Parameters:
    +#
    +#       type     - The <ReferenceType>.
    +#       symbol - The reference <SymbolString>.
    +#       scope   - The scope <SymbolString> the reference appears in, or undef if none.
    +#       using    - An arrayref of scope <SymbolStrings> available to the reference via using statements.
    +#       file       - The source <FileName> the reference appears in, or undef if none.
    +#       resolvingFlags - The <Resolving Flags> of the reference.  Ignored if the type is <REFERENCE_TEXT>.
    +#
    +#   Alternate Parameters:
    +#
    +#       referenceString - The <ReferenceString> to resolve.
    +#       file - The source <FileName> the reference appears in, or undef if none.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the reference doesn't resolve to anything.
    +#
    +sub References #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file)
    +    {
    +    my ($self, $referenceString, $file);
    +
    +    if (scalar @_ <= 3)
    +        {  ($self, $referenceString, $file) = @_;  }
    +    else
    +        {
    +        my ($type, $symbol, $scope, $using, $resolvingFlags);
    +        ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_;
    +
    +        $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol,
    +                                                                                                  NaturalDocs::Languages->LanguageOf($file)->Name(),
    +                                                                                                  $scope, $using, $resolvingFlags);
    +        };
    +
    +    if (exists $references{$referenceString} && $references{$referenceString}->HasCurrentInterpretation())
    +        {
    +        my $targetSymbol = $references{$referenceString}->CurrentInterpretation();
    +        my $targetObject = $symbols{$targetSymbol};
    +
    +        my $targetFile;
    +        my $targetType;
    +        my $targetPrototype;
    +        my $targetSummary;
    +
    +        if (defined $file && $targetObject->IsDefinedIn($file))
    +            {
    +            $targetFile = $file;
    +            $targetType = $targetObject->TypeDefinedIn($file);
    +            $targetPrototype = $targetObject->PrototypeDefinedIn($file);
    +            $targetSummary = $targetObject->SummaryDefinedIn($file);
    +            }
    +        else
    +            {
    +            $targetFile = $targetObject->GlobalDefinition();
    +            $targetType = $targetObject->GlobalType();
    +            $targetPrototype = $targetObject->GlobalPrototype();
    +            $targetSummary = $targetObject->GlobalSummary();
    +            };
    +
    +        return NaturalDocs::SymbolTable::ReferenceTarget->New($targetSymbol, $targetFile, $targetType, $targetPrototype,
    +                                                                                             $targetSummary);
    +        }
    +
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: Lookup
    +#
    +#   Returns information on the passed <SymbolString>, if it exists.  Note that the symbol must be fully resolved.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString>.
    +#       file - The source <FileName> the reference appears in, or undef if none.
    +#
    +#   Returns:
    +#
    +#       A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the symbol isn't defined.
    +#
    +sub Lookup #(symbol, file)
    +    {
    +    my ($self, $symbol, $file) = @_;
    +
    +    my $symbolObject = $symbols{$symbol};
    +
    +    if (defined $symbolObject)
    +        {
    +        my $targetFile;
    +        my $targetType;
    +        my $targetPrototype;
    +        my $targetSummary;
    +
    +        if (defined $file && $symbolObject->IsDefinedIn($file))
    +            {
    +            $targetFile = $file;
    +            $targetType = $symbolObject->TypeDefinedIn($file);
    +            $targetPrototype = $symbolObject->PrototypeDefinedIn($file);
    +            $targetSummary = $symbolObject->SummaryDefinedIn($file);
    +            }
    +        else
    +            {
    +            $targetFile = $symbolObject->GlobalDefinition();
    +            $targetType = $symbolObject->GlobalType();
    +            $targetPrototype = $symbolObject->GlobalPrototype();
    +            $targetSummary = $symbolObject->GlobalSummary();
    +            };
    +
    +        return NaturalDocs::SymbolTable::ReferenceTarget->New($symbol, $targetFile, $targetType, $targetPrototype,
    +                                                                                             $targetSummary);
    +        }
    +
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: Index
    +#
    +#   Returns a symbol index.
    +#
    +#   Indexes are generated on demand, but they are stored so subsequent calls for the same index will be fast.  Call
    +#   <PurgeIndexes()> to clear the generated indexes.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> of symbol to limit the index to, or undef for none.
    +#
    +#   Returns:
    +#
    +#       An arrayref of sections.  The first represents all the symbols, the second the numbers, and the rest A through Z.
    +#       Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.  If a section has no content,
    +#       it will be undef.
    +#
    +sub Index #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    if (!exists $indexes{$type})
    +        {  $indexes{$type} = $self->MakeIndex($type);  };
    +
    +    return $indexes{$type};
    +    };
    +
    +
    +#
    +#   Function: HasIndexes
    +#
    +#   Determines which indexes out of a list actually have content.
    +#
    +#   Parameters:
    +#
    +#       types  - An existence hashref of the <TopicTypes> to check for indexes.
    +#
    +#   Returns:
    +#
    +#       An existence hashref of all the specified indexes that have content.  Will return an empty hashref if none.
    +#
    +sub HasIndexes #(types)
    +    {
    +    my ($self, $types) = @_;
    +
    +    # EliminationHash is a copy of all the types, and the types will be deleted as they are found.  This allows us to quit early if
    +    # we've found all the types because the hash will be empty.  We'll later return the original hash minus what was left over
    +    # in here, which are the ones that weren't found.
    +    my %eliminationHash = %$types;
    +
    +    finddefs:
    +    foreach my $symbolObject (values %symbols)
    +        {
    +        foreach my $definition ($symbolObject->Definitions())
    +            {
    +            delete $eliminationHash{ $symbolObject->TypeDefinedIn($definition) };
    +            delete $eliminationHash{ ::TOPIC_GENERAL() };
    +
    +            if (!scalar keys %eliminationHash)
    +                {  last finddefs;  };
    +            };
    +        };
    +
    +    my $result = { %$types };
    +
    +    foreach my $type (keys %eliminationHash)
    +        {  delete $result->{$type};  };
    +
    +    return $result;
    +    };
    +
    +
    +#
    +#   Function: IndexChanged
    +#
    +#   Returns whether the specified index has changed.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> to limit the index to.
    +#
    +sub IndexChanged #(TopicType type)
    +    {
    +    my ($self, $type) = @_;
    +    return ($rebuildIndexes || defined $indexChanges{$type});
    +    };
    +
    +
    +#
    +#   Function: IndexSectionsWithContent
    +#
    +#   Returns an arrayref of whether each section of the specified index has content.  The first entry will be for symbols, the second
    +#   for numbers, and the rest A-Z.  Do not change the arrayref.
    +#
    +sub IndexSectionsWithContent #(TopicType type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    if (!exists $indexSectionsWithContent{$type})
    +        {
    +        # This is okay because Index() stores generated indexes.  It's not an expensive operation unless the index was never asked
    +        # for before or it will never be asked for otherwise, and this shouldn't be the case.
    +
    +        my $index = $self->Index($type);
    +        my $content = [ ];
    +
    +        for (my $i = 0; $i < 28; $i++)
    +            {
    +            push @$content, (defined $index->[$i] ? 1 : 0);
    +            };
    +
    +        $indexSectionsWithContent{$type} = $content;
    +        };
    +
    +    return $indexSectionsWithContent{$type};
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Event Handlers
    +
    +
    +#
    +#   Function: OnIndexChange
    +#
    +#   Called whenever a change happens to a symbol that would cause an index to be regenerated.
    +#
    +#   Parameters:
    +#
    +#       type - The <TopicType> of the symbol that caused the change.
    +#
    +sub OnIndexChange #(TopicType type)
    +    {
    +    my ($self, $type) = @_;
    +
    +    $indexChanges{$type} = 1;
    +    $indexChanges{::TOPIC_GENERAL()} = 1;
    +    delete $indexSectionsWithContent{$type};
    +    };
    +
    +
    +#
    +#   Function: OnInterpretationChange
    +#
    +#   Called whenever the current interpretation of a reference changes, meaning it switched from one symbol to another.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> whose current interpretation changed.
    +#
    +sub OnInterpretationChange #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString);
    +
    +    if ($referenceType == ::REFERENCE_TEXT())
    +        {
    +        my @referenceDefinitions = $references{$referenceString}->Definitions();
    +
    +        foreach my $referenceDefinition (@referenceDefinitions)
    +            {
    +            NaturalDocs::Project->RebuildFile($referenceDefinition);
    +            };
    +        }
    +
    +    elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType))
    +        {
    +        NaturalDocs::ClassHierarchy->OnInterpretationChange($referenceString);
    +        };
    +    };
    +
    +
    +#
    +#   Function: OnTargetSymbolChange
    +#
    +#   Called whenever the symbol that serves as the interpretation of a reference changes, but the reference still resolves to
    +#   the same symbol.  This would happen if the type, prototype, summary, or which file serves as global definition of the symbol
    +#   changes.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> whose interpretation's symbol changed.
    +#
    +sub OnTargetSymbolChange #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString);
    +
    +    if ($referenceType == ::REFERENCE_TEXT())
    +        {
    +        my @referenceDefinitions = $references{$referenceString}->Definitions();
    +
    +        foreach my $referenceDefinition (@referenceDefinitions)
    +            {
    +            NaturalDocs::Project->RebuildFile($referenceDefinition);
    +            };
    +        }
    +
    +    elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType))
    +        {
    +        NaturalDocs::ClassHierarchy->OnTargetSymbolChange($referenceString);
    +        };
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: DeleteSymbol
    +#
    +#   Removes a symbol definition from the table.  It will call <OnInterpretationChange()> for all references that have it as their
    +#   current interpretation.
    +#
    +#   External code should not attempt to delete symbols using this function.  Instead it should call <WatchFileFoChanges()>,
    +#   reparse the file, and call <AnalyzeChanges()>.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString>.
    +#       file       - The <FileName> where the definition is.
    +#
    +sub DeleteSymbol #(symbol, file)
    +    {
    +    my ($self, $symbol, $file) = @_;
    +
    +
    +    # If the symbol and definition exist...
    +    if (exists $symbols{$symbol} && $symbols{$symbol}->IsDefinedIn($file))
    +        {
    +        my $symbolObject = $symbols{$symbol};
    +        my $wasGlobal = ($symbolObject->GlobalDefinition() eq $file);
    +
    +        $self->OnIndexChange($symbolObject->TypeDefinedIn($file));
    +
    +        $symbolObject->DeleteDefinition($file);
    +
    +        # If this was one definition of many...
    +        if ($symbolObject->IsDefined())
    +            {
    +
    +            # If this was the global definition...
    +            if ($wasGlobal)
    +                {
    +                # Update every file that referenced the global symbol; i.e. every file that doesn't have its own definition.
    +
    +                my @references = $symbolObject->References();
    +
    +                foreach my $reference (@references)
    +                    {
    +                    if ($references{$reference}->CurrentInterpretation() eq $symbol)
    +                        {
    +                        $self->OnTargetSymbolChange($reference);
    +                        };
    +                    };
    +                }
    +
    +            # If this wasn't the global definition...
    +            else
    +                {
    +                # It's a safe bet that we don't need to do anything here.  The only thing that we even need to look for here is if the
    +                # file referenced its own symbol and thus should be rebuilt.  However, if the file is having a symbol deleted, it either
    +                # changed or was itself deleted.  If it changed and still has other Natural Docs content, it should already be on the
    +                # rebuild list.  If it was deleted or no longer has Natural Docs content, we certainly don't want to add it to the rebuild
    +                # list.
    +                };
    +            }
    +
    +        # If this is the only definition...
    +        else
    +            {
    +            # If this symbol is the interpretation of any references...
    +            if ($symbolObject->HasReferences())
    +                {
    +                # If this was the current interpretation of any references, reinterpret them and rebuild their files.
    +
    +                my @references = $symbolObject->References();
    +
    +                foreach my $reference (@references)
    +                    {
    +                    if ($references{$reference}->CurrentInterpretation() eq $symbol)
    +                        {
    +                        $self->InterpretReference($reference);
    +                        $self->OnInterpretationChange($reference);
    +                        };
    +                    };
    +                }
    +
    +            # If there are no interpretations of the symbol...
    +            else
    +                {
    +                # Delete the symbol entirely.
    +                delete $symbols{$symbol};
    +                };
    +            };
    +
    +        # Remove it from the file index.
    +
    +        $files{$file}->DeleteSymbol($symbol);
    +
    +        if (!$files{$file}->HasAnything())
    +            {  delete $files{$file};  };
    +
    +
    +        # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and
    +        # LoadAndPurge().
    +        };
    +    };
    +
    +
    +#
    +#   Function: GenerateInterpretations
    +#
    +#   Generates the list of interpretations for the passed reference.  Also creates potential symbols as necessary.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to generate the interpretations of.
    +#
    +sub GenerateInterpretations #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    my ($type, $symbol, $languageName, $scope, $using, $resolvingFlags) =
    +        NaturalDocs::ReferenceString->InformationOf($referenceString);
    +
    +    # RESOLVE_NOPLURAL is handled by having @singulars be empty.
    +    my @singulars;
    +    if (!($resolvingFlags & ::RESOLVE_NOPLURAL()))
    +        {  @singulars = $self->SingularInterpretationsOf($symbol);  };
    +
    +    # Since higher scores are better, we'll start at a high number and decrement.
    +    my $score = 50000;
    +
    +
    +    # If RESOLVE_RELATIVE is set, we do all the scope relatives before the global.
    +    if ($resolvingFlags & ::RESOLVE_RELATIVE())
    +        {
    +        $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score);
    +        }
    +
    +    # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we only do the local before the global.
    +    elsif (!($resolvingFlags & ::RESOLVE_ABSOLUTE()))
    +        {
    +        $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $symbol), $score);
    +        $score--;
    +
    +        foreach my $singular (@singulars)
    +            {
    +            $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $singular), $score);
    +            $score--;
    +            };
    +        };
    +
    +
    +    # Do the global.
    +
    +    $self->AddInterpretation($referenceString, $symbol, $score);
    +    $score--;
    +
    +    foreach my $singular (@singulars)
    +        {
    +        $self->AddInterpretation($referenceString, $singular, $score);
    +        $score--;
    +        };
    +
    +
    +    # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we need to do the rest of the scope relatives after the global.
    +    if (!($resolvingFlags & ::RESOLVE_RELATIVE()) && !($resolvingFlags & ::RESOLVE_ABSOLUTE()))
    +        {
    +        $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score, 1);
    +        };
    +
    +
    +    # Finally, if RESOLVE_NOUSING isn't set, go through the using scopes.
    +    if (!($resolvingFlags & ::RESOLVE_NOUSING()) && defined $using)
    +        {
    +        foreach my $usingScope (@$using)
    +            {
    +            if ($resolvingFlags & ::RESOLVE_ABSOLUTE())
    +                {
    +                $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $symbol), $score);
    +                $score--;
    +
    +                foreach my $singular (@singulars)
    +                    {
    +                    $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $singular), $score);
    +                    $score--;
    +                    };
    +                }
    +            else
    +                {
    +                $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $usingScope, $score);
    +                };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: GenerateRelativeInterpretations
    +#
    +#   Generates the list of relative interpretations for the passed reference and packages.  Also creates potential symbols as
    +#   necessary.
    +#
    +#   This function will _not_ create global interpretations.  It _will_ create a local interpretations (symbol + all packages) unless
    +#   you set dontUseFull.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to generate interpretations for.
    +#       symbol - The <SymbolString> to generate interpretations of.
    +#       singulars - A reference to an array of singular <SymbolStrings> to also generate interpretations of.  Set to an empty array
    +#                       if none.
    +#       package - The package <SymbolString> to use.  May be undef.
    +#       score - The starting score to apply.
    +#       dontUseFull - Whether to not generate an interpretation including the full package identifier.  If set, generated interpretations
    +#                           will start one level down.
    +#
    +#   Returns:
    +#
    +#       The next unused score.  This is basically the passed score minus the number of interpretations created.
    +#
    +sub GenerateRelativeInterpretations #(referenceString, symbol, singulars, package, score, dontUseFull)
    +    {
    +    my ($self, $referenceString, $symbol, $singulars, $package, $score, $dontUseFull) = @_;
    +
    +    my @packages = NaturalDocs::SymbolString->IdentifiersOf($package);
    +
    +    # The last package index to include.  This number is INCLUSIVE!
    +    my $packageLevel = scalar @packages - 1;
    +
    +    if ($dontUseFull)
    +        {  $packageLevel--;  };
    +
    +    while ($packageLevel >= 0)
    +        {
    +        $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $symbol),
    +                                             $score);
    +        $score--;
    +
    +        foreach my $singular (@$singulars)
    +            {
    +            $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $singular),
    +                                                 $score);
    +            $score--;
    +            };
    +
    +        $packageLevel--;
    +        };
    +
    +    return $score;
    +    };
    +
    +
    +#
    +#   Function: SingularInterpretationsOf
    +#
    +#   Generates singular interpretations of a <SymbolString> if it can be interpreted as a plural.  Not all of them will be valid singular
    +#   forms, but that doesn't matter since it's incredibly unlikely an invalid form would exist as a symbol.  What matters is that the
    +#   legimate singular is present on the list.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString>.
    +#
    +#   Returns:
    +#
    +#       An array of potential singular interpretations as <SymbolStrings>, in no particular order.  If the symbol can't be interpreted
    +#       as a plural, returns an empty array.
    +#
    +sub SingularInterpretationsOf #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +
    +    my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
    +    my $lastIdentifier = pop @identifiers;
    +    my $preIdentifiers = NaturalDocs::SymbolString->Join(@identifiers);
    +
    +    my @results;
    +
    +    # First cut off any 's or ' at the end, since they can appear after other plural forms.
    +    if ($lastIdentifier =~ s/\'s?$//i)
    +        {
    +        push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $lastIdentifier);
    +        };
    +
    +    # See http://www.gsu.edu/~wwwesl/egw/crump.htm for a good list of potential plural forms.  There are a couple more than
    +    # listed below, but they're fairly rare and this is already seriously over-engineered.  This is split by suffix length to make
    +    # comparisons more efficient.
    +
    +    # The fact that this will generate some impossible combinations (leaves => leave, leav, leaf, leafe) doesn't matter.  It's very
    +    # unlikely that more than one will manage to match a defined symbol.  Even if they do (leave, leaf), it's incredibly unlikely
    +    # that someone has defined an impossible one (leav, leafe).  So it's not so important that we remove impossible combinations,
    +    # just that we include all the possible ones.
    +
    +    my @suffixGroups = ( [ 's', undef,  # boys => boy
    +                                       'i', 'us',  # alumni => alumnus
    +                                       'a', 'um', # errata => erratum
    +                                       'a', 'on' ],  # phenomena => phenomenon
    +
    +                                    [ 'es', undef,  # foxes => fox
    +                                      'ae', 'a' ],  # amoebae => amoeba
    +
    +                                    [ 'ies', 'y',  # pennies => penny
    +                                      'ves', 'f',  # calves => calf
    +                                      'ves', 'fe',  # knives => knife
    +                                      'men', 'man',  # women => woman
    +                                      'ice', 'ouse',  # mice => mouse
    +                                      'oes', 'o',  # vetoes => veto
    +                                      'ces', 'x',  # matrices => matrix
    +                                      'xen', 'x' ],  # oxen => ox
    +
    +                                    [ 'ices', 'ex',  # indices => index
    +                                      'feet', 'foot',  # feet => foot
    +                                      'eese', 'oose',  # geese => goose
    +                                      'eeth', 'ooth',  # teeth => tooth
    +                                      'dren', 'd' ] );  # children => child
    +
    +    my $suffixLength = 1;
    +
    +    foreach my $suffixGroup (@suffixGroups)
    +        {
    +        my $identifierSuffix = lc( substr($lastIdentifier, 0 - $suffixLength) );
    +        my $cutIdentifier = substr($lastIdentifier, 0, 0 - $suffixLength);
    +
    +        for (my $i = 0; $i + 1 < scalar @$suffixGroup; $i += 2)
    +            {
    +            my $suffix = $suffixGroup->[$i];
    +            my $replacement = $suffixGroup->[$i + 1];
    +
    +            if ($identifierSuffix eq $suffix)
    +                {
    +                if (defined $replacement)
    +                    {
    +                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . $replacement);
    +                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . uc($replacement));
    +                    }
    +                else
    +                    {
    +                    push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier);
    +                    };
    +                };
    +            };
    +
    +        $suffixLength++;
    +        };
    +
    +    return @results;
    +    };
    +
    +
    +#
    +#   Function: AddInterpretation
    +#
    +#   Adds an interpretation to an existing reference.  Creates potential symbols as necessary.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to add the interpretation to.
    +#       symbol - The <SymbolString> the reference can be interpreted as.
    +#       score - The score of the interpretation.
    +#
    +sub AddInterpretation #(referenceString, symbol, score)
    +    {
    +    my ($self, $referenceString, $symbol, $score) = @_;
    +
    +    $references{$referenceString}->AddInterpretation($symbol, $score);
    +
    +    # Create a potential symbol if it doesn't exist.
    +
    +    if (!exists $symbols{$symbol})
    +        {  $symbols{$symbol} = NaturalDocs::SymbolTable::Symbol->New();  };
    +
    +    $symbols{$symbol}->AddReference($referenceString, $score);
    +    };
    +
    +
    +#
    +#   Function: InterpretReference
    +#
    +#   Interprets the passed reference, matching it to the defined symbol with the highest score.  If the symbol is already
    +#   interpreted, it will reinterpret it.  If there are no matches, it will make it an undefined reference.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to interpret.
    +#
    +sub InterpretReference #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    my $interpretation;
    +    my $currentInterpretation;
    +    my $score;
    +    my $currentScore = -1;
    +
    +    my $referenceObject = $references{$referenceString};
    +
    +    my %interpretationsAndScores = $referenceObject->InterpretationsAndScores();
    +    while ( ($interpretation, $score) = each %interpretationsAndScores )
    +        {
    +        if ($score > $currentScore && $symbols{$interpretation}->IsDefined())
    +            {
    +            $currentScore = $score;
    +            $currentInterpretation = $interpretation;
    +            };
    +        };
    +
    +    if ($currentScore > -1)
    +        {  $referenceObject->SetCurrentInterpretation($currentInterpretation);  }
    +    else
    +        {  $referenceObject->SetCurrentInterpretation(undef);  };
    +    };
    +
    +
    +#
    +#   Function: MakeIndex
    +#
    +#   Generates a symbol index.
    +#
    +#   Parameters:
    +#
    +#       type  - The <TopicType> to limit the index to.
    +#
    +#   Returns:
    +#
    +#       An arrayref of sections.  The first represents all the symbols, the second the numbers, and the rest A through Z.
    +#       Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.  If a section has no content,
    +#       it will be undef.
    +#
    +sub MakeIndex #(type)
    +    {
    +    my ($self, $type) = @_;
    +
    +
    +    # Go through the symbols and generate IndexElements for any that belong in the index.
    +
    +    # Keys are the symbol strings, values are IndexElements.
    +    my %indexSymbols;
    +
    +    while (my ($symbolString, $object) = each %symbols)
    +        {
    +        my ($symbol, $package) = $self->SplitSymbolForIndex($symbolString, $object->GlobalType());
    +        my @definitions = $object->Definitions();
    +
    +        foreach my $definition (@definitions)
    +            {
    +            my $definitionType = $object->TypeDefinedIn($definition);
    +
    +            if ($type eq ::TOPIC_GENERAL() || $type eq $definitionType)
    +                {
    +                if (!exists $indexSymbols{$symbol})
    +                    {
    +                    $indexSymbols{$symbol} =
    +                        NaturalDocs::SymbolTable::IndexElement->New($symbol, $package, $definition, $definitionType,
    +                                                                                               $object->PrototypeDefinedIn($definition),
    +                                                                                               $object->SummaryDefinedIn($definition) );
    +                    }
    +                else
    +                    {
    +                    $indexSymbols{$symbol}->Merge($package, $definition, $definitionType,
    +                                                                       $object->PrototypeDefinedIn($definition),
    +                                                                       $object->SummaryDefinedIn($definition) );
    +                    };
    +                }; # If type matches
    +            }; # Each definition
    +        }; # Each symbol
    +
    +
    +    # Generate sortable symbols for each IndexElement, sort them internally, and divide them into sections.
    +
    +    my $sections = [ ];
    +
    +    foreach my $indexElement (values %indexSymbols)
    +        {
    +        $indexElement->Sort();
    +        $indexElement->MakeSortableSymbol();
    +
    +        my $sectionNumber;
    +
    +        if ($indexElement->SortableSymbol() =~ /^([a-z])/i)
    +            {  $sectionNumber = ord(lc($1)) - ord('a') + 2;  }
    +        elsif ($indexElement->SortableSymbol() =~ /^[0-9]/)
    +            {  $sectionNumber = 1;  }
    +        else
    +            {  $sectionNumber = 0;  };
    +
    +        if (!defined $sections->[$sectionNumber])
    +            {  $sections->[$sectionNumber] = [ ];  };
    +
    +        push @{$sections->[$sectionNumber]}, $indexElement;
    +        };
    +
    +
    +    # Sort each section.
    +
    +    for (my $i = 0; $i < scalar @$sections; $i++)
    +        {
    +        if (defined $sections->[$i])
    +            {
    +            @{$sections->[$i]} = sort
    +                {
    +                my $result = ::StringCompare($a->SortableSymbol(), $b->SortableSymbol());
    +
    +                if ($result == 0)
    +                    {  $result = ::StringCompare($a->IgnoredPrefix(), $b->IgnoredPrefix());  };
    +
    +                return $result;
    +                }
    +            @{$sections->[$i]};
    +            };
    +        };
    +
    +    return $sections;
    +    };
    +
    +
    +#
    +#   Function: SplitSymbolForIndex
    +#
    +#   Splits a <SymbolString> into its symbol and package portions for indexing.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString>.
    +#       type - Its <TopicType>.
    +#
    +#   Returns:
    +#
    +#       The array ( symbol, package ), which are both <SymbolStrings>.  If the symbol is global, package will be undef.
    +#
    +sub SplitSymbolForIndex #(symbol, type)
    +    {
    +    my ($self, $symbol, $type) = @_;
    +
    +    my $scope = NaturalDocs::Topics->TypeInfo($type)->Scope();
    +
    +    if ($scope == ::SCOPE_START() || $scope == ::SCOPE_ALWAYS_GLOBAL())
    +        {  return ( $symbol, undef );  }
    +    else
    +        {
    +        my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol);
    +
    +        $symbol = pop @identifiers;
    +        my $package = NaturalDocs::SymbolString->Join(@identifiers);
    +
    +        return ( $symbol, $package );
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/File.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/File.pm
    new file mode 100644
    index 000000000..f07c8c939
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/File.pm
    @@ -0,0 +1,187 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolTable::File
    +#
    +###############################################################################
    +#
    +#   A class representing a file, keeping track of what symbols and references are defined in it.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable::File;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#       SYMBOLS       - An existence hashref of the <SymbolStrings> it defines.
    +#       REFERENCES  - An existence hashref of the <ReferenceStrings> in the file.
    +#
    +
    +# DEPENDENCY: New() depends on the order of these constants.  If they change, New() has to be updated.
    +use constant SYMBOLS => 0;
    +use constant REFERENCES => 1;
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $package = shift;
    +
    +    # Let's make it safe, since normally you can pass values to New.  Having them just be ignored would be an obscure error.
    +    if (scalar @_)
    +        {  die "You can't pass values to NaturalDocs::SymbolTable::File->New()\n";  };
    +
    +    # DEPENDENCY: This code depends on the order of the member constants.
    +    my $object = [ { }, { } ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: AddSymbol
    +#
    +#   Adds a <SymbolString> definition.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString> being added.
    +#
    +sub AddSymbol #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +    $self->[SYMBOLS]{$symbol} = 1;
    +    };
    +
    +
    +#
    +#   Function: DeleteSymbol
    +#
    +#   Removes a <SymbolString> definition.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString> to delete.
    +#
    +sub DeleteSymbol #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +    delete $self->[SYMBOLS]{$symbol};
    +    };
    +
    +
    +#
    +#   Function: AddReference
    +#
    +#   Adds a reference definition.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> being added.
    +#
    +sub AddReference #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    $self->[REFERENCES]{$referenceString} = 1;
    +    };
    +
    +
    +#
    +#   Function: DeleteReference
    +#
    +#   Removes a reference definition.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The <ReferenceString> to delete.
    +#
    +sub DeleteReference #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    delete $self->[REFERENCES]{$referenceString};
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +
    +#
    +#   Function: HasAnything
    +#
    +#   Returns whether the file has any symbol or reference definitions at all.
    +#
    +sub HasAnything
    +    {
    +    return (scalar keys %{$_[0]->[SYMBOLS]} || scalar keys %{$_[0]->[REFERENCES]});
    +    };
    +
    +#
    +#   Function: Symbols
    +#
    +#   Returns an array of all the <SymbolStrings> defined in this file.  If none, returns an empty array.
    +#
    +sub Symbols
    +    {
    +    return keys %{$_[0]->[SYMBOLS]};
    +    };
    +
    +
    +#
    +#   Function: References
    +#
    +#   Returns an array of all the <ReferenceStrings> defined in this file.  If none, returns an empty array.
    +#
    +sub References
    +    {
    +    return keys %{$_[0]->[REFERENCES]};
    +    };
    +
    +
    +#
    +#   Function: DefinesSymbol
    +#
    +#   Returns whether the file defines the passed <SymbolString> or not.
    +#
    +sub DefinesSymbol #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +    return exists $self->[SYMBOLS]{$symbol};
    +    };
    +
    +
    +#
    +#   Function: DefinesReference
    +#
    +#   Returns whether the file defines the passed <ReferenceString> or not.
    +#
    +sub DefinesReference #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +    return exists $self->[REFERENCES]{$referenceString};
    +    };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm
    new file mode 100644
    index 000000000..ee6a9e8cc
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm
    @@ -0,0 +1,523 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::SymbolTable::IndexElement
    +#
    +###############################################################################
    +#
    +#   A class representing part of an indexed symbol.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use Tie::RefHash;
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::SymbolTable::IndexElement;
    +
    +
    +#
    +#   Topic: How IndexElements Work
    +#
    +#   This is a little tricky, so make sure you understand this.  Indexes are sorted by symbol, then packages, then file.  If there is only
    +#   one package for a symbol, or one file definition for a package/symbol, they are added inline to the entry.  However, if there are
    +#   multiple packages or files, the function for it returns an arrayref of IndexElements instead.  Which members are defined and
    +#   undefined should follow common sense.  For example, if a symbol is defined in multiple packages, the symbol's IndexElement
    +#   will not define <File()>, <Type()>, or <Prototype()>; those will be defined in child elements.  Similarly, the child elements will
    +#   not define <Symbol()> since it's redundant.
    +#
    +#   Diagrams may be clearer.  If a member isn't listed for an element, it isn't defined.
    +#
    +#   A symbol that only has one package and file:
    +#   > [Element]
    +#   > - Symbol
    +#   > - Package
    +#   > - File
    +#   > - Type
    +#   > - Prototype
    +#   > - Summary
    +#
    +#   A symbol that is defined by multiple packages, each with only one file:
    +#   > [Element]
    +#   > - Symbol
    +#   > - Package
    +#   >     [Element]
    +#   >     - Package
    +#   >     - File
    +#   >     - Type
    +#   >     - Prototype
    +#   >     - Summary
    +#   >     [Element]
    +#   >     - ...
    +#
    +#   A symbol that is defined by one package, but has multiple files
    +#   > [Element]
    +#   > - Symbol
    +#   > - Package
    +#   > - File
    +#   >    [Element]
    +#   >    - File
    +#   >    - Type
    +#   >    - Protype
    +#   >    - Summary
    +#   >    [Element]
    +#   >    - ...
    +#
    +#   A symbol that is defined by multiple packages which have multiple files:
    +#   > [Element]
    +#   > - Symbol
    +#   > - Package
    +#   >    [Element]
    +#   >    - Package
    +#   >    - File
    +#   >      [Element]
    +#   >      - File
    +#   >      - Type
    +#   >      - Prototype
    +#   >      - Summary
    +#   >      [Element]
    +#   >      - ...
    +#   >    [Element]
    +#   >    - ...
    +#
    +#   Why is it done this way?:
    +#
    +#   Because it makes it easier to generate nice indexes since all the splitting and combining is done for you.  If a symbol
    +#   has only one package, you just want to link to it, you don't want to break out a subindex for just one package.  However, if
    +#   it has multiple package, you do want the subindex and to link to each one individually.  Use <HasMultiplePackages()> and
    +#   <HasMultipleFiles()> to determine whether you need to add a subindex for it.
    +#
    +#
    +#   Combining Properties:
    +#
    +#   All IndexElements also have combining properties set.
    +#
    +#   CombinedType - The general <TopicType> of the entry.  Conflicts combine into <TOPIC_GENERAL>.
    +#   PackageSeparator - The package separator symbol of the entry.  Conflicts combine into a dot.
    +#
    +#   So if an IndexElement only has one definition, <CombinedType()> is the same as the <TopicType> and <PackageSeparator()>
    +#   is that of the definition's language.  If other definitions are added and they have the same properties, the combined properties
    +#   will remain the same.  However, if they're different, they switch values as noted above.
    +#
    +#
    +#   Sortable Symbol:
    +#
    +#   <SortableSymbol()> is a pseudo-combining property.  There were a few options for dealing with multiple languages defining
    +#   the same symbol but stripping different prefixes off it, but ultimately I decided to go with whatever the language does that
    +#   has the most definitions.  There's not likely to be many conflicts here in the real world; probably the only thing would be
    +#   defining it in a text file and forgetting to specify the prefixes to strip there too.  So this works.
    +#
    +#   Ties are broken pretty much randomly, except that text files always lose if its one of the options.
    +#
    +#   It's a pseudo-combining property because it's done after the IndexElements are all filled in and only stored in the top-level
    +#   ones.
    +#
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#   SYMBOL - The <SymbolString> without the package portion.
    +#   PACKAGE - The package <SymbolString>.  Will be a package <SymbolString>, undef for global, or an arrayref of
    +#                    <NaturalDocs::SymbolTable::IndexElement> objects if multiple packages define the symbol.
    +#   FILE - The <FileName> the package/symbol is defined in.  Will be the file name or an arrayref of
    +#            <NaturalDocs::SymbolTable::IndexElements> if multiple files define the package/symbol.
    +#   TYPE - The package/symbol/file <TopicType>.
    +#   PROTOTYPE - The package/symbol/file prototype, or undef if not applicable.
    +#   SUMMARY - The package/symbol/file summary, or undef if not applicable.
    +#   COMBINED_TYPE - The combined <TopicType> of the element.
    +#   PACKAGE_SEPARATOR - The combined package separator symbol of the element.
    +#   SORTABLE_SYMBOL - The sortable symbol as a text string.
    +#   IGNORED_PREFIX - The part of the symbol that was stripped off to make the sortable symbol.
    +#
    +use NaturalDocs::DefineMembers 'SYMBOL', 'Symbol()',
    +                                                 'PACKAGE', 'Package()',
    +                                                 'FILE', 'File()',
    +                                                 'TYPE', 'Type()',
    +                                                 'PROTOTYPE', 'Prototype()',
    +                                                 'SUMMARY', 'Summary()',
    +                                                 'COMBINED_TYPE', 'CombinedType()',
    +                                                 'PACKAGE_SEPARATOR', 'PackageSeparator()',
    +                                                 'SORTABLE_SYMBOL', 'SortableSymbol()',
    +                                                 'IGNORED_PREFIX', 'IgnoredPrefix()';
    +# DEPENDENCY: New() depends on the order of these constants and that there is no inheritance..
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +#
    +#   Function: New
    +#
    +#   Returns a new object.
    +#
    +#   This should only be used for creating an entirely new symbol.  You should *not* pass arrayrefs as package or file parameters
    +#   if you are calling this externally.  Use <Merge()> instead.
    +#
    +#   Parameters:
    +#
    +#       symbol  - The <SymbolString> without the package portion.
    +#       package - The package <SymbolString>, or undef for global.
    +#       file  - The symbol's definition file.
    +#       type  - The symbol's <TopicType>.
    +#       prototype  - The symbol's prototype, if applicable.
    +#       summary  - The symbol's summary, if applicable.
    +#
    +#   Optional Parameters:
    +#
    +#       These parameters don't need to be specified.  You should ignore them when calling this externally.
    +#
    +#       combinedType - The symbol's combined <TopicType>.
    +#       packageSeparator - The symbol's combined package separator symbol.
    +#
    +sub New #(symbol, package, file, type, prototype, summary, combinedType, packageSeparator)
    +    {
    +    # DEPENDENCY: This depends on the parameter list being in the same order as the constants.
    +
    +    my $self = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $self;
    +
    +    if (!defined $object->[COMBINED_TYPE])
    +        {  $object->[COMBINED_TYPE] = $object->[TYPE];  };
    +
    +    if (!defined $object->[PACKAGE_SEPARATOR])
    +        {
    +        if ($object->[TYPE] eq ::TOPIC_FILE())
    +            {  $object->[PACKAGE_SEPARATOR] = '.';  }
    +        else
    +            {
    +            $object->[PACKAGE_SEPARATOR] = NaturalDocs::Languages->LanguageOf($object->[FILE])->PackageSeparator();
    +            };
    +        };
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: Merge
    +#
    +#   Adds another definition of the same symbol.  Perhaps it has a different package or defining file.
    +#
    +#   Parameters:
    +#
    +#       package - The package <SymbolString>, or undef for global.
    +#       file  - The symbol's definition file.
    +#       type  - The symbol's <TopicType>.
    +#       prototype  - The symbol's protoype if applicable.
    +#       summary  - The symbol's summary if applicable.
    +#
    +sub Merge #(package, file, type, prototype, summary)
    +    {
    +    my ($self, $package, $file, $type, $prototype, $summary) = @_;
    +
    +    # If there's only one package...
    +    if (!$self->HasMultiplePackages())
    +        {
    +        # If there's one package and it's the same as the new one...
    +        if ($package eq $self->Package())
    +            {
    +            $self->MergeFile($file, $type, $prototype, $summary);
    +            }
    +
    +        # If there's one package and the new one is different...
    +        else
    +            {
    +            my $selfDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $self->Package(), $self->File(),
    +                                                                                                                 $self->Type(), $self->Prototype(),
    +                                                                                                                 $self->Summary(), $self->CombinedType(),
    +                                                                                                                 $self->PackageSeparator());
    +            my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $package, $file, $type, $prototype,
    +                                                                                                                  $summary);
    +
    +            $self->[PACKAGE] = [ $selfDefinition, $newDefinition ];
    +            $self->[FILE] = undef;
    +            $self->[TYPE] = undef;
    +            $self->[PROTOTYPE] = undef;
    +            $self->[SUMMARY] = undef;
    +
    +            if ($newDefinition->Type() ne $self->CombinedType())
    +                {  $self->[COMBINED_TYPE] = ::TOPIC_GENERAL();  };
    +            if ($newDefinition->PackageSeparator() ne $self->PackageSeparator())
    +                {  $self->[PACKAGE_SEPARATOR] = '.';  };
    +            };
    +        }
    +
    +    # If there's more than one package...
    +    else
    +        {
    +        # See if the new package is one of them.
    +        my $selfPackages = $self->Package();
    +        my $matchingPackage;
    +
    +        foreach my $testPackage (@$selfPackages)
    +            {
    +            if ($package eq $testPackage->Package())
    +                {
    +                $testPackage->MergeFile($file, $type, $prototype, $summary);;
    +                return;
    +                };
    +            };
    +
    +        my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $package, $file, $type, $prototype,
    +                                                                                                              $summary);
    +        push @{$self->[PACKAGE]}, $newDefinition;
    +
    +        if ($newDefinition->Type() ne $self->CombinedType())
    +            {  $self->[COMBINED_TYPE] = ::TOPIC_GENERAL();  };
    +        if ($newDefinition->PackageSeparator() ne $self->PackageSeparator())
    +            {  $self->[PACKAGE_SEPARATOR] = '.';  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: Sort
    +#
    +#   Sorts the package and file lists of the symbol.
    +#
    +sub Sort
    +    {
    +    my $self = shift;
    +
    +    if ($self->HasMultipleFiles())
    +        {
    +        @{$self->[FILE]} = sort { ::StringCompare($a->File(), $b->File()) } @{$self->File()};
    +        }
    +
    +    elsif ($self->HasMultiplePackages())
    +        {
    +        @{$self->[PACKAGE]} = sort { ::StringCompare( $a->Package(), $b->Package()) } @{$self->[PACKAGE]};
    +
    +        foreach my $packageElement ( @{$self->[PACKAGE]} )
    +            {
    +            if ($packageElement->HasMultipleFiles())
    +                {  $packageElement->Sort();  };
    +            };
    +        };
    +    };
    +
    +
    +#
    +#   Function: MakeSortableSymbol
    +#
    +#   Generates <SortableSymbol()> and <IgnoredPrefix()>.  Should only be called after everything is merged.
    +#
    +sub MakeSortableSymbol
    +    {
    +    my $self = shift;
    +
    +    my $finalLanguage;
    +
    +    if ($self->HasMultiplePackages() || $self->HasMultipleFiles())
    +        {
    +        # Collect all the files that define this symbol.
    +
    +        my @files;
    +
    +        if ($self->HasMultipleFiles())
    +            {
    +            my $fileElements = $self->File();
    +
    +            foreach my $fileElement (@$fileElements)
    +                {  push @files, $fileElement->File();  };
    +            }
    +        else # HasMultiplePackages
    +            {
    +            my $packages = $self->Package();
    +
    +            foreach my $package (@$packages)
    +                {
    +                if ($package->HasMultipleFiles())
    +                    {
    +                    my $fileElements = $package->File();
    +
    +                    foreach my $fileElement (@$fileElements)
    +                        {  push @files, $fileElement->File();  };
    +                    }
    +                else
    +                    {  push @files, $package->File();  };
    +                };
    +            };
    +
    +
    +        # Determine which language defines it the most.
    +
    +        # Keys are language objects, values are counts.
    +        my %languages;
    +        tie %languages, 'Tie::RefHash';
    +
    +        foreach my $file (@files)
    +            {
    +            my $language = NaturalDocs::Languages->LanguageOf($file);
    +
    +            if (exists $languages{$language})
    +                {  $languages{$language}++;  }
    +            else
    +                {  $languages{$language} = 1;  };
    +            };
    +
    +        my $topCount = 0;
    +        my @topLanguages;
    +
    +        while (my ($language, $count) = each %languages)
    +            {
    +            if ($count > $topCount)
    +                {
    +                $topCount = $count;
    +                @topLanguages = ( $language );
    +                }
    +            elsif ($count == $topCount)
    +                {
    +                push @topLanguages, $language;
    +                };
    +            };
    +
    +        if (scalar @topLanguages == 1)
    +            {  $finalLanguage = $topLanguages[0];  }
    +        else
    +            {
    +            if ($topLanguages[0]->Name() ne 'Text File')
    +                {  $finalLanguage = $topLanguages[0];  }
    +            else
    +                {  $finalLanguage = $topLanguages[1];  };
    +            };
    +        }
    +
    +    else # !hasMultiplePackages && !hasMultipleFiles
    +        {  $finalLanguage = NaturalDocs::Languages->LanguageOf($self->File());  };
    +
    +    my $textSymbol = NaturalDocs::SymbolString->ToText($self->Symbol(), $self->PackageSeparator());
    +    my $ignoredPrefixLength = $finalLanguage->IgnoredPrefixLength($textSymbol, $self->CombinedType());
    +
    +    if ($ignoredPrefixLength)
    +        {
    +        $self->[IGNORED_PREFIX] = substr($textSymbol, 0, $ignoredPrefixLength);
    +        $self->[SORTABLE_SYMBOL] = substr($textSymbol, $ignoredPrefixLength);
    +        }
    +    else
    +        {  $self->[SORTABLE_SYMBOL] = $textSymbol;  };
    +    };
    +
    +
    +
    +###############################################################################
    +#
    +#   Functions: Information Functions
    +#
    +#   Symbol - Returns the <SymbolString> without the package portion.
    +#   Package - If <HasMultiplePackages()> is true, returns an arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.
    +#                  Otherwise returns the package <SymbolString>, or undef if global.
    +#   File - If <HasMultipleFiles()> is true, returns an arrayref of <NaturalDocs::SymbolTable::IndexElement> objects.  Otherwise
    +#           returns the name of the definition file.
    +#   Type - Returns the <TopicType> of the package/symbol/file, if applicable.
    +#   Prototype - Returns the prototype of the package/symbol/file, if applicable.
    +#   Summary - Returns the summary of the package/symbol/file, if applicable.
    +#   CombinedType - Returns the combined <TopicType> of the element.
    +#   PackageSeparator - Returns the combined package separator symbol of the element.
    +#   SortableSymbol - Returns the sortable symbol as a text string.  Only available after calling <MakeSortableSymbol()>.
    +#   IgnoredPrefix - Returns the part of the symbol that was stripped off to make the <SortableSymbol()>, or undef if none.
    +#                          Only available after calling <MakeSortableSymbol()>.
    +#
    +
    +#   Function: HasMultiplePackages
    +#   Returns whether <Packages()> is broken out into more elements.
    +sub HasMultiplePackages
    +    {  return ref($_[0]->[PACKAGE]);  };
    +
    +#   Function: HasMultipleFiles
    +#   Returns whether <File()> is broken out into more elements.
    +sub HasMultipleFiles
    +    {  return ref($_[0]->[FILE]);  };
    +
    +
    +
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +#
    +#   Function: MergeFile
    +#
    +#   Adds another definition of the same package/symbol.  Perhaps the file is different.
    +#
    +#   Parameters:
    +#
    +#       file  - The package/symbol's definition file.
    +#       type  - The package/symbol's <TopicType>.
    +#       prototype  - The package/symbol's protoype if applicable.
    +#       summary  - The package/symbol's summary if applicable.
    +#
    +sub MergeFile #(file, type, prototype, summary)
    +    {
    +    my ($self, $file, $type, $prototype, $summary) = @_;
    +
    +    # If there's only one file...
    +    if (!$self->HasMultipleFiles())
    +        {
    +        # If there's one file and it's the different from the new one...
    +        if ($file ne $self->File())
    +            {
    +            my $selfDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $self->File(), $self->Type(),
    +                                                                                                                 $self->Prototype(), $self->Summary(),
    +                                                                                                                 $self->CombinedType(),
    +                                                                                                                 $self->PackageSeparator());
    +            my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $file, $type, $prototype,
    +                                                                                                                  $summary);
    +
    +            $self->[FILE] = [ $selfDefinition, $newDefinition ];
    +            $self->[TYPE] = undef;
    +            $self->[PROTOTYPE] = undef;
    +            $self->[SUMMARY] = undef;
    +
    +            if ($newDefinition->Type() ne $self->CombinedType())
    +                {  $self->[COMBINED_TYPE] = ::TOPIC_GENERAL();  };
    +            if ($newDefinition->PackageSeparator() ne $self->PackageSeparator())
    +                {  $self->[PACKAGE_SEPARATOR] = '.';  };
    +            }
    +
    +        # If the file was the same, just ignore the duplicate in the index.
    +        }
    +
    +    # If there's more than one file...
    +    else
    +        {
    +        # See if the new file is one of them.
    +        my $files = $self->File();
    +
    +        foreach my $testElement (@$files)
    +            {
    +            if ($testElement->File() eq $file)
    +                {
    +                # If the new file's already in the index, ignore the duplicate.
    +                return;
    +                };
    +            };
    +
    +        my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $file, $type, $prototype,
    +                                                                                                              $summary);
    +        push @{$self->[FILE]}, $newDefinition;
    +
    +        if ($newDefinition->Type() ne $self->CombinedType())
    +            {  $self->[COMBINED_TYPE] = ::TOPIC_GENERAL();  };
    +        if ($newDefinition->PackageSeparator() ne $self->PackageSeparator())
    +            {  $self->[PACKAGE_SEPARATOR] = '.';  };
    +        };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Reference.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Reference.pm
    new file mode 100644
    index 000000000..fd5ef7047
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Reference.pm
    @@ -0,0 +1,274 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolTable::Reference
    +#
    +###############################################################################
    +#
    +#   A class representing a symbol or a potential symbol.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable::Reference;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#       DEFINITIONS                        - An existence hashref of the <FileNames> that define this reference.
    +#       INTERPRETATIONS                - A hashref of the possible interpretations of this reference.  The keys are the <SymbolStrings>
    +#                                                     and the values are the scores.
    +#       CURRENT_INTERPRETATION  - The interpretation currently used as the reference target.  It will be the interpretation with
    +#                                                     the highest score that is actually defined.  If none are defined, this item will be undef.
    +#
    +
    +# DEPENDENCY: New() depends on the order of these constants.  If they change, New() has to be updated.
    +use constant DEFINITIONS => 0;
    +use constant INTERPRETATIONS => 1;
    +use constant CURRENT_INTERPRETATION => 2;
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $package = shift;
    +
    +    # Let's make it safe, since normally you can pass values to New.  Having them just be ignored would be an obscure error.
    +    if (scalar @_)
    +        {  die "You can't pass values to NaturalDocs::SymbolTable::Reference->New()\n";  };
    +
    +    # DEPENDENCY: This code depends on the order of the member constants.
    +    my $object = [ { }, { }, undef ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a reference definition.
    +#
    +#   Parameters:
    +#
    +#       file   - The <FileName> that defines the reference.
    +#
    +sub AddDefinition #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    $self->[DEFINITIONS]{$file} = 1;
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes a reference definition.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> which has the definition to delete.
    +#
    +sub DeleteDefinition #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    delete $self->[DEFINITIONS]{$file};
    +    };
    +
    +
    +#
    +#   Function: AddInterpretation
    +#
    +#   Adds a symbol that this reference can be interpreted as.
    +#
    +#   Parameters:
    +#
    +#       symbol  - The <SymbolString>.
    +#       score     - The score of this interpretation.
    +#
    +sub AddInterpretation #(symbol, score)
    +    {
    +    my ($self, $symbol, $score) = @_;
    +
    +    $self->[INTERPRETATIONS]{$symbol} = $score;
    +    };
    +
    +
    +#
    +#   Function: DeleteInterpretation
    +#
    +#   Deletes a symbol that this reference can be interpreted as.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString> to delete.
    +#
    +sub DeleteInterpretation #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +
    +    delete $self->[INTERPRETATIONS]{$symbol};
    +    };
    +
    +
    +#
    +#   Function: DeleteAllInterpretationsButCurrent
    +#
    +#   Deletes all interpretations except for the current one.
    +#
    +sub DeleteAllInterpretationsButCurrent
    +    {
    +    my $self = shift;
    +
    +    if ($self->HasCurrentInterpretation())
    +        {
    +        my $score = $self->CurrentScore();
    +
    +        # Fastest way to clear a hash except for one item?  Make a new hash with just that item.
    +        %{$self->[INTERPRETATIONS]} = ( $self->[CURRENT_INTERPRETATION] => $score );
    +        };
    +    };
    +
    +
    +#
    +#   Function: SetCurrentInterpretation
    +#
    +#   Changes the current interpretation.  The new one must already have been added via <AddInterpretation()>.
    +#
    +#   Parameters:
    +#
    +#       symbol - The <SymbolString>l to make the current interpretation.  Can be set to undef to clear it.
    +#
    +sub SetCurrentInterpretation #(symbol)
    +    {
    +    my ($self, $symbol) = @_;
    +
    +    $self->[CURRENT_INTERPRETATION] = $symbol;
    +    };
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +
    +#
    +#   Function: Definitions
    +#
    +#   Returns an array of all the <FileNames> that define this reference.  If none do, returns an empty array.
    +#
    +sub Definitions
    +    {
    +    return keys %{$_[0]->[DEFINITIONS]};
    +    };
    +
    +
    +#
    +#   Function: IsDefined
    +#
    +#   Returns whether the reference has any definitions or not.
    +#
    +sub IsDefined
    +    {
    +    return scalar keys %{$_[0]->[DEFINITIONS]};
    +    };
    +
    +
    +#
    +#   Function: IsDefinedIn
    +#
    +#   Returns whether the reference is defined in the passed <FileName>.
    +#
    +sub IsDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    return exists $self->[DEFINITIONS]{$file};
    +    };
    +
    +
    +#
    +#   Function: Interpretations
    +#
    +#   Returns an array of all the <SymbolStrings> that this reference can be interpreted as.  If none, returns an empty array.
    +#
    +sub Interpretations
    +    {
    +    return keys %{$_[0]->[INTERPRETATIONS]};
    +    };
    +
    +
    +#
    +#   Function: InterpretationsAndScores
    +#
    +#   Returns a hash of all the <SymbolStrings> that this reference can be interpreted as and their scores.  The keys are the <SymbolStrings>
    +#   and the values are the scores.  If none, returns an empty hash.
    +#
    +sub InterpretationsAndScores
    +    {
    +    return %{$_[0]->[INTERPRETATIONS]};
    +    };
    +
    +
    +#
    +#   Function: HasCurrentInterpretation
    +#
    +#   Returns whether the reference has a current interpretation or not.
    +#
    +sub HasCurrentInterpretation
    +    {
    +    return defined $_[0]->[CURRENT_INTERPRETATION];
    +    };
    +
    +
    +#
    +#   Function: CurrentInterpretation
    +#
    +#   Returns the <SymbolString> of the current interpretation, or undef if none.
    +#
    +sub CurrentInterpretation
    +    {
    +    return $_[0]->[CURRENT_INTERPRETATION];
    +    };
    +
    +
    +#
    +#   Function: CurrentScore
    +#
    +#   Returns the score of the current interpretation, or undef if none.
    +#
    +sub CurrentScore
    +    {
    +    my $self = shift;
    +
    +    if (defined $self->[CURRENT_INTERPRETATION])
    +        {
    +        return $self->[INTERPRETATIONS]{ $self->[CURRENT_INTERPRETATION] };
    +        }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm
    new file mode 100644
    index 000000000..4f9ee407f
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm
    @@ -0,0 +1,98 @@
    +###############################################################################
    +#
    +#   Class: NaturalDocs::SymbolTable::ReferenceTarget
    +#
    +###############################################################################
    +#
    +#   A class for storing information about a reference target.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable::ReferenceTarget;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#       SYMBOL  - The target <SymbolString>.
    +#       FILE        - The <FileName> the target is defined in.
    +#       TYPE       - The target <TopicType>.
    +#       PROTOTYPE - The target's prototype, or undef if none.
    +#       SUMMARY    - The target's summary, or undef if none.
    +#
    +
    +# DEPENDENCY: New() depends on the order of these constants.  If they change, New() has to be updated.
    +use constant SYMBOL => 0;
    +use constant FILE => 1;
    +use constant TYPE => 2;
    +use constant PROTOTYPE => 3;
    +use constant SUMMARY => 4;
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       symbol - The target <SymbolString>.
    +#       file       - The <FileName> the target is defined in.
    +#       type     - The <TopicType> of the target symbol.
    +#       prototype - The target's prototype.  Set to undef if not defined or not applicable.
    +#       summary - The target's summary.  Set to undef if not defined or not applicable.
    +#
    +sub New #(symbol, file, type, prototype, summary)
    +    {
    +    # DEPENDENCY: This code depends on the order of the member constants.
    +
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +# Function: Symbol
    +# Returns the target's <SymbolString>.
    +sub Symbol
    +    {  return $_[0]->[SYMBOL];  };
    +
    +# Function: File
    +# Returns the <FileName> the target is defined in.
    +sub File
    +    {  return $_[0]->[FILE];  };
    +
    +# Function: Type
    +# Returns the target's <TopicType>.
    +sub Type
    +    {  return $_[0]->[TYPE];  };
    +
    +# Function: Prototype
    +# Returns the target's prototype, or undef if not defined or not applicable.
    +sub Prototype
    +    {  return $_[0]->[PROTOTYPE];  };
    +
    +# Function: Summary
    +# Returns the target's summary, or undef if not defined or not applicable.
    +sub Summary
    +    {  return $_[0]->[SUMMARY];  };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Symbol.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Symbol.pm
    new file mode 100644
    index 000000000..83a7148a3
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/Symbol.pm
    @@ -0,0 +1,429 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolTable::Symbol
    +#
    +###############################################################################
    +#
    +#   A class representing a symbol or a potential symbol.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable::Symbol;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#       DEFINITIONS             - A hashref of all the files which define this symbol.  The keys are the <FileNames>, and the values are
    +#                                         <NaturalDocs::SymbolTable::SymbolDefinition> objects.  If no files define this symbol, this item will
    +#                                          be undef.
    +#       GLOBAL_DEFINITION  - The <FileName> which defines the global version of the symbol, which is what is used if
    +#                                          a file references the symbol but does not have its own definition.  If there are no definitions, this
    +#                                          item will be undef.
    +#       REFERENCES              - A hashref of the references that can be interpreted as this symbol.  This doesn't mean these
    +#                                          references necessarily are.  The keys are the reference strings, and the values are the scores of
    +#                                          the interpretations.  If no references can be interpreted as this symbol, this item will be undef.
    +#
    +use constant DEFINITIONS => 0;
    +use constant GLOBAL_DEFINITION => 1;
    +use constant REFERENCES => 2;
    +
    +
    +###############################################################################
    +# Group: Modification Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +sub New
    +    {
    +    my $package = shift;
    +
    +    # Let's make it safe, since normally you can pass values to New.  Having them just be ignored would be an obscure error.
    +    if (scalar @_)
    +        {  die "You can't pass values to NaturalDocs::SymbolTable::Symbol->New()\n";  };
    +
    +    my $object = [ undef, undef, undef ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +#
    +#   Function: AddDefinition
    +#
    +#   Adds a symbol definition.  If this is the first definition for this symbol, it will become the global definition.  If the definition
    +#   already exists for the file, it will be ignored.
    +#
    +#   Parameters:
    +#
    +#       file   - The <FileName> that defines the symbol.
    +#       type - The <TopicType> of the definition.
    +#       prototype - The prototype of the definition, if applicable.  Undef otherwise.
    +#       summary - The summary for the definition, if applicable.  Undef otherwise.
    +#
    +#   Returns:
    +#
    +#       Whether this provided the first definition for this symbol.
    +#
    +sub AddDefinition #(file, type, prototype, summary)
    +    {
    +    my ($self, $file, $type, $prototype, $summary) = @_;
    +
    +    my $isFirst;
    +
    +    if (!defined $self->[DEFINITIONS])
    +        {
    +        $self->[DEFINITIONS] = { };
    +        $self->[GLOBAL_DEFINITION] = $file;
    +        $isFirst = 1;
    +        };
    +
    +    if (!exists $self->[DEFINITIONS]{$file})
    +        {
    +        $self->[DEFINITIONS]{$file} = NaturalDocs::SymbolTable::SymbolDefinition->New($type, $prototype, $summary);
    +        };
    +
    +    return $isFirst;
    +    };
    +
    +
    +#
    +#   Function: ChangeDefinition
    +#
    +#   Changes the information about an existing definition.
    +#
    +#   Parameters:
    +#
    +#       file   - The <FileName> that defines the symbol.  Must exist.
    +#       type - The new <TopicType> of the definition.
    +#       prototype - The new prototype of the definition, if applicable.  Undef otherwise.
    +#       summary - The new summary of the definition, if applicable.  Undef otherwise.
    +#
    +sub ChangeDefinition #(file, type, prototype, summary)
    +    {
    +    my ($self, $file, $type, $prototype, $summary) = @_;
    +
    +    if (defined $self->[DEFINITIONS] &&
    +        exists $self->[DEFINITIONS]{$file})
    +        {
    +        $self->[DEFINITIONS]{$file}->SetType($type);
    +        $self->[DEFINITIONS]{$file}->SetPrototype($prototype);
    +        $self->[DEFINITIONS]{$file}->SetSummary($summary);
    +        };
    +    };
    +
    +
    +#
    +#   Function: DeleteDefinition
    +#
    +#   Removes a symbol definition.  If the definition served as the global definition, a new one will be selected.
    +#
    +#   Parameters:
    +#
    +#       file - The <FileName> which contains definition to delete.
    +#
    +#   Returns:
    +#
    +#       Whether that was the only definition, and the symbol is now undefined.
    +#
    +sub DeleteDefinition #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    # If there are no definitions...
    +    if (!defined $self->[DEFINITIONS])
    +        {  return undef;  };
    +
    +    delete $self->[DEFINITIONS]{$file};
    +
    +    # If there are no more definitions...
    +    if (!scalar keys %{$self->[DEFINITIONS]})
    +        {
    +        $self->[DEFINITIONS] = undef;
    +
    +        # If definitions was previously defined, and now is empty, we can safely assume that the global definition was just deleted
    +        # without checking it against $file.
    +
    +        $self->[GLOBAL_DEFINITION] = undef;
    +
    +        return 1;
    +        }
    +
    +    # If there are more definitions and the global one was just deleted...
    +    elsif ($self->[GLOBAL_DEFINITION] eq $file)
    +        {
    +        # Which one becomes global is pretty much random.
    +        $self->[GLOBAL_DEFINITION] = (keys %{$self->[DEFINITIONS]})[0];
    +        return undef;
    +        };
    +    };
    +
    +
    +#
    +#   Function: AddReference
    +#
    +#   Adds a reference that can be interpreted as this symbol.  It can be, but not necessarily is.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The string of the reference.
    +#       score                - The score of this interpretation.
    +#
    +sub AddReference #(referenceString, score)
    +    {
    +    my ($self, $referenceString, $score) = @_;
    +
    +    if (!defined $self->[REFERENCES])
    +        {  $self->[REFERENCES] = { };  };
    +
    +    $self->[REFERENCES]{$referenceString} = $score;
    +    };
    +
    +
    +#
    +#   Function: DeleteReference
    +#
    +#   Deletes a reference that can be interpreted as this symbol.
    +#
    +#   Parameters:
    +#
    +#       referenceString - The string of the reference to delete.
    +#
    +sub DeleteReference #(referenceString)
    +    {
    +    my ($self, $referenceString) = @_;
    +
    +    # If there are no definitions...
    +    if (!defined $self->[REFERENCES])
    +        {  return;  };
    +
    +    delete $self->[REFERENCES]{$referenceString};
    +
    +    # If there are no more definitions...
    +    if (!scalar keys %{$self->[REFERENCES]})
    +        {
    +        $self->[REFERENCES] = undef;
    +        };
    +    };
    +
    +
    +#
    +#   Function: DeleteAllReferences
    +#
    +#   Removes all references that can be interpreted as this symbol.
    +#
    +sub DeleteAllReferences
    +    {
    +    $_[0]->[REFERENCES] = undef;
    +    };
    +
    +
    +###############################################################################
    +# Group: Information Functions
    +
    +#
    +#   Function: IsDefined
    +#
    +#   Returns whether the symbol is defined anywhere or not.  If it's not, that means it's just a potential interpretation of a
    +#   reference.
    +#
    +sub IsDefined
    +    {
    +    return defined $_[0]->[GLOBAL_DEFINITION];
    +    };
    +
    +#
    +#   Function: IsDefinedIn
    +#
    +#   Returns whether the symbol is defined in the passed <FileName>.
    +#
    +sub IsDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +    return ($self->IsDefined() && exists $self->[DEFINITIONS]{$file});
    +    };
    +
    +
    +#
    +#   Function: Definitions
    +#
    +#   Returns an array of all the <FileNames> that define this symbol.  If none do, will return an empty array.
    +#
    +sub Definitions
    +    {
    +    my $self = shift;
    +
    +    if ($self->IsDefined())
    +        {  return keys %{$self->[DEFINITIONS]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +#
    +#   Function: GlobalDefinition
    +#
    +#   Returns the <FileName> that contains the global definition of this symbol, or undef if the symbol isn't defined.
    +#
    +sub GlobalDefinition
    +    {
    +    return $_[0]->[GLOBAL_DEFINITION];
    +    };
    +
    +
    +#
    +#   Function: TypeDefinedIn
    +#
    +#   Returns the <TopicType> of the symbol defined in the passed <FileName>, or undef if it's not defined in that file.
    +#
    +sub TypeDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if ($self->IsDefined())
    +        {  return $self->[DEFINITIONS]{$file}->Type();  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: GlobalType
    +#
    +#   Returns the <TopicType> of the global definition, or undef if the symbol isn't defined.
    +#
    +sub GlobalType
    +    {
    +    my $self = shift;
    +
    +    my $globalDefinition = $self->GlobalDefinition();
    +
    +    if (!defined $globalDefinition)
    +        {  return undef;  }
    +    else
    +        {  return $self->[DEFINITIONS]{$globalDefinition}->Type();  };
    +    };
    +
    +
    +#
    +#   Function: PrototypeDefinedIn
    +#
    +#   Returns the prototype of symbol defined in the passed <FileName>, or undef if it doesn't exist or is not defined in that file.
    +#
    +sub PrototypeDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if ($self->IsDefined())
    +        {  return $self->[DEFINITIONS]{$file}->Prototype();  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: GlobalPrototype
    +#
    +#   Returns the prototype of the global definition.  Will be undef if it doesn't exist or the symbol isn't defined.
    +#
    +sub GlobalPrototype
    +    {
    +    my $self = shift;
    +
    +    my $globalDefinition = $self->GlobalDefinition();
    +
    +    if (!defined $globalDefinition)
    +        {  return undef;  }
    +    else
    +        {  return $self->[DEFINITIONS]{$globalDefinition}->Prototype();  };
    +    };
    +
    +
    +#
    +#   Function: SummaryDefinedIn
    +#
    +#   Returns the summary of symbol defined in the passed <FileName>, or undef if it doesn't exist or is not defined in that file.
    +#
    +sub SummaryDefinedIn #(file)
    +    {
    +    my ($self, $file) = @_;
    +
    +    if ($self->IsDefined())
    +        {  return $self->[DEFINITIONS]{$file}->Summary();  }
    +    else
    +        {  return undef;  };
    +    };
    +
    +
    +#
    +#   Function: GlobalSummary
    +#
    +#   Returns the summary of the global definition.  Will be undef if it doesn't exist or the symbol isn't defined.
    +#
    +sub GlobalSummary
    +    {
    +    my $self = shift;
    +
    +    my $globalDefinition = $self->GlobalDefinition();
    +
    +    if (!defined $globalDefinition)
    +        {  return undef;  }
    +    else
    +        {  return $self->[DEFINITIONS]{$globalDefinition}->Summary();  };
    +    };
    +
    +
    +#
    +#   Function: HasReferences
    +#
    +#   Returns whether the symbol can be interpreted as any references.
    +#
    +sub HasReferences
    +    {
    +    return defined $_[0]->[REFERENCES];
    +    };
    +
    +#
    +#   Function: References
    +#
    +#   Returns an array of all the reference strings that can be interpreted as this symbol.  If none, will return an empty array.
    +#
    +sub References
    +    {
    +    if (defined $_[0]->[REFERENCES])
    +        {  return keys %{$_[0]->[REFERENCES]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +#
    +#   Function: ReferencesAndScores
    +#
    +#   Returns a hash of all the references that can be interpreted as this symbol and their scores.  The keys are the reference
    +#   strings, and the values are the scores.  If none, will return an empty hash.
    +#
    +sub ReferencesAndScores
    +    {
    +    if (defined $_[0]->[REFERENCES])
    +        {  return %{$_[0]->[REFERENCES]};  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm
    new file mode 100644
    index 000000000..9d5746057
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm
    @@ -0,0 +1,97 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::SymbolTable::SymbolDefinition
    +#
    +###############################################################################
    +#
    +#   A class representing a symbol definition.  This does not store the definition symbol, class, or file.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::SymbolTable::SymbolDefinition;
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   Constants: Members
    +#
    +#   The class is implemented as a blessed arrayref.  The following constants are its members.
    +#
    +#       TYPE  - The symbol <TopicType>.
    +#       PROTOTYPE  - The symbol's prototype, if applicable.  Will be undef otherwise.
    +#       SUMMARY - The symbol's summary, if applicable.  Will be undef otherwise.
    +#
    +use constant TYPE => 0;
    +use constant PROTOTYPE => 1;
    +use constant SUMMARY => 2;
    +# New depends on the order of the constants.
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       type - The symbol <TopicType>.
    +#       prototype  - The symbol prototype, if applicable.  Undef otherwise.
    +#       summary - The symbol's summary, if applicable.  Undef otherwise.
    +#
    +sub New #(type, prototype, summary)
    +    {
    +    # This depends on the parameter list being the same as the constant order.
    +
    +    my $package = shift;
    +
    +    my $object = [ @_ ];
    +    bless $object, $package;
    +
    +    return $object;
    +    };
    +
    +
    +#   Function: Type
    +#   Returns the definition's <TopicType>.
    +sub Type
    +    {  return $_[0]->[TYPE];  };
    +
    +# Function: SetType
    +# Changes the <TopicType>.
    +sub SetType #(type)
    +    {  $_[0]->[TYPE] = $_[1];  };
    +
    +#   Function: Prototype
    +#   Returns the definition's prototype, or undef if it doesn't have one.
    +sub Prototype
    +    {  return $_[0]->[PROTOTYPE];  };
    +
    +# Function: SetPrototype
    +# Changes the prototype.
    +sub SetPrototype #(prototype)
    +    {  $_[0]->[PROTOTYPE] = $_[1];  };
    +
    +#   Function: Summary
    +#   Returns the definition's summary, or undef if it doesn't have one.
    +sub Summary
    +    {  return $_[0]->[SUMMARY];  };
    +
    +# Function: SetSummary
    +# Changes the summary.
    +sub SetSummary #(summary)
    +    {  $_[0]->[SUMMARY] = $_[1];  };
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Topics.pm b/vendor/naturaldocs/Modules/NaturalDocs/Topics.pm
    new file mode 100644
    index 000000000..d2668782a
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Topics.pm
    @@ -0,0 +1,1320 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Topics
    +#
    +###############################################################################
    +#
    +#   The topic constants and functions to convert them to and from strings used throughout the script.  All constants are exported
    +#   by default.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use Text::Wrap ( );
    +use Tie::RefHash ( );
    +
    +use strict;
    +use integer;
    +
    +use NaturalDocs::Topics::Type;
    +
    +package NaturalDocs::Topics;
    +
    +use base 'Exporter';
    +our @EXPORT = ( 'TOPIC_GENERAL', 'TOPIC_GENERIC', 'TOPIC_GROUP', 'TOPIC_CLASS', 'TOPIC_FILE', 'TOPIC_FUNCTION',
    +                          'TOPIC_VARIABLE', 'TOPIC_PROPERTY', 'TOPIC_TYPE', 'TOPIC_ENUMERATION', 'TOPIC_CONSTANT',
    +                          'TOPIC_INTERFACE', 'TOPIC_EVENT', 'TOPIC_DELEGATE', 'TOPIC_SECTION' );
    +
    +
    +
    +###############################################################################
    +# Group: Types
    +
    +
    +#
    +#   Type: TopicType
    +#
    +#   A string representing a topic type as defined in <Topics.txt>.  It's format should be treated as opaque; use <MakeTopicType()>
    +#   to get them from topic names.  However, they can be compared for equality with string functions.
    +#
    +
    +
    +#
    +#   Constants: Default TopicTypes
    +#
    +#   Exported constants of the default <TopicTypes>, so you don't have to go through <TypeFromName()> every time.
    +#
    +#   TOPIC_GENERAL - The general <TopicType>, which has the special meaning of none in particular.
    +#   TOPIC_GENERIC - Generic <TopicType>.
    +#   TOPIC_GROUP - Group <TopicType>.
    +#   TOPIC_CLASS - Class <TopicType>.
    +#   TOPIC_INTERFACE - Interface <TopicType>.
    +#   TOPIC_FILE - File <TopicType>.
    +#   TOPIC_SECTION - Section <TopicType>.
    +#   TOPIC_FUNCTION - Function <TopicType>.
    +#   TOPIC_VARIABLE - Variable <TopicType>.
    +#   TOPIC_PROPERTY - Property <TopicType>.
    +#   TOPIC_TYPE - Type <TopicType>.
    +#   TOPIC_CONSTANT - Constant <TopicType>.
    +#   TOPIC_ENUMERATION - Enum <TopicType>.
    +#   TOPIC_DELEGATE - Delegate <TopicType>.
    +#   TOPIC_EVENT - Event <TopicType>.
    +#
    +use constant TOPIC_GENERAL => 'general';
    +use constant TOPIC_GENERIC => 'generic';
    +use constant TOPIC_GROUP => 'group';
    +use constant TOPIC_CLASS => 'class';
    +use constant TOPIC_INTERFACE => 'interface';
    +use constant TOPIC_FILE => 'file';
    +use constant TOPIC_SECTION => 'section';
    +use constant TOPIC_FUNCTION => 'function';
    +use constant TOPIC_VARIABLE => 'variable';
    +use constant TOPIC_PROPERTY => 'property';
    +use constant TOPIC_TYPE => 'type';
    +use constant TOPIC_CONSTANT => 'constant';
    +use constant TOPIC_ENUMERATION => 'enumeration';
    +use constant TOPIC_DELEGATE => 'delegate';
    +use constant TOPIC_EVENT => 'event';
    +# Dependency: The values of these constants must match what is generated by MakeTopicType().
    +# Dependency: These types must be added to requiredTypeNames so that they always exist.
    +
    +
    +
    +
    +###############################################################################
    +# Group: Variables
    +
    +
    +#
    +#   handle: FH_TOPICS
    +#
    +#   The file handle used when writing to <Topics.txt>.
    +#
    +
    +
    +#
    +#   hash: types
    +#
    +#   A hashref that maps <TopicTypes> to <NaturalDocs::Topics::Type>s.
    +#
    +my %types;
    +
    +
    +#
    +#   hash: names
    +#
    +#   A hashref that maps various forms of the all-lowercase type names to <TopicTypes>.  All are in the same hash because
    +#   two names that reduce to the same thing it would cause big problems, and we need to catch that.  Keys include
    +#
    +#   - Topic names
    +#   - Plural topic names
    +#   - Alphanumeric-only topic names
    +#   - Alphanumeric-only plural topic names
    +#
    +my %names;
    +
    +
    +#
    +#   hash: keywords
    +#
    +#   A hashref that maps all-lowercase keywords to their <TopicTypes>.  Must not have any of the same keys as
    +#   <pluralKeywords>.
    +#
    +my %keywords;
    +
    +
    +#
    +#   hash: pluralKeywords
    +#
    +#   A hashref that maps all-lowercase plural keywords to their <TopicTypes>.  Must not have any of the same keys as
    +#   <keywords>.
    +#
    +my %pluralKeywords;
    +
    +
    +#
    +#   hash: indexable
    +#
    +#   An existence hash of all the indexable <TopicTypes>.
    +#
    +my %indexable;
    +
    +
    +#
    +#   array: requiredTypeNames
    +#
    +#   An array of the <TopicType> names which are required to be defined in the main file.  Are in the order they should appear
    +#   when reformatting.
    +#
    +my @requiredTypeNames = ( 'Generic', 'Class', 'Interface', 'Section', 'File', 'Group', 'Function', 'Variable', 'Property', 'Type',
    +                                           'Constant', 'Enumeration', 'Event', 'Delegate' );
    +
    +
    +#
    +#   array: legacyTypes
    +#
    +#   An array that converts the legacy topic types, which were numeric constants prior to 1.3, to the current <TopicTypes>.
    +#   The legacy types are used as an index into the array.  Note that this does not support list type values.
    +#
    +my @legacyTypes = ( TOPIC_GENERAL, TOPIC_CLASS, TOPIC_SECTION, TOPIC_FILE, TOPIC_GROUP, TOPIC_FUNCTION,
    +                                TOPIC_VARIABLE, TOPIC_GENERIC, TOPIC_TYPE, TOPIC_CONSTANT, TOPIC_PROPERTY );
    +
    +
    +#
    +#   array: mainTopicNames
    +#
    +#   An array of the <TopicType> names that are defined in the main <Topics.txt>.
    +#
    +my @mainTopicNames;
    +
    +
    +
    +###############################################################################
    +# Group: Files
    +
    +
    +#
    +#   File: Topics.txt
    +#
    +#   The configuration file that defines or overrides the topic definitions for Natural Docs.  One version sits in Natural Docs'
    +#   configuration directory, and another can be in a project directory to add to or override them.
    +#
    +#   > # [comments]
    +#
    +#   Everything after a # symbol is ignored.
    +#
    +#   Except when specifying topic names, everything below is case-insensitive.
    +#
    +#   > Format: [version]
    +#
    +#   Specifies the file format version of the file.
    +#
    +#
    +#   Sections:
    +#
    +#       > Ignore[d] Keyword[s]: [keyword], [keyword] ...
    +#       >    [keyword]
    +#       >    [keyword], [keyword]
    +#       >    ...
    +#
    +#       Ignores the keywords so that they're not recognized as Natural Docs topics anymore.  Can be specified as a list on the same
    +#       line and/or following like a normal Keywords section.
    +#
    +#       > Topic Type: [name]
    +#       > Alter Topic Type: [name]
    +#
    +#       Creates a new topic type or alters an existing one.  The name can only contain <CFChars> and isn't case sensitive, although
    +#       the original case is remembered for presentation.
    +#
    +#       The name General is reserved.  There are a number of default types that must be defined in the main file as well, but those
    +#       are governed by <NaturalDocs::Topics> and are not included here.  The default types can have their keywords or behaviors
    +#       changed, though, either by editing the default file or by overriding them in the user file.
    +#
    +#       Enumeration is a special type.  It is indexed with Types and its definition list members are listed with Constants according
    +#       to the rules in <Languages.txt>.
    +#
    +#
    +#   Topic Type Sections:
    +#
    +#       > Plural: [name]
    +#
    +#       Specifies the plural name of the topic type.  Defaults to the singular name.  Has the same restrictions as the topic type
    +#       name.
    +#
    +#       > Index: [yes|no]
    +#
    +#       Whether the topic type gets an index.  Defaults to yes.
    +#
    +#       > Scope: [normal|start|end|always global]
    +#
    +#       How the topic affects scope.  Defaults to normal.
    +#
    +#       normal - The topic stays within the current scope.
    +#       start - The topic starts a new scope for all the topics beneath it, like class topics.
    +#       end - The topic resets the scope back to global for all the topics beneath it, like section topics.
    +#       always global - The topic is defined as a global symbol, but does not change the scope for any other topics.
    +#
    +#       > Class Hierarchy: [yes|no]
    +#
    +#       Whether the topic is part of the class hierarchy.  Defaults to no.
    +#
    +#       > Page Title if First: [yes|no]
    +#
    +#       Whether the title of this topic becomes the page title if it is the first topic in a file.  Defaults to no.
    +#
    +#       > Break Lists: [yes|no]
    +#
    +#       Whether list topics should be broken into individual topics in the output.  Defaults to no.
    +#
    +#       > Can Group With: [topic type], [topic type], ...
    +#
    +#       The list of <TopicTypes> the topic can possibly be grouped with.
    +#
    +#       > [Add] Keyword[s]:
    +#       >    [keyword]
    +#       >    [keyword], [plural keyword]
    +#       >    ...
    +#
    +#       A list of the topic type's keywords.  Each line after the heading is the keyword and optionally its plural form.  This continues
    +#       until the next line in "keyword: value" format.  "Add" isn't required.
    +#
    +#       - Keywords can only have letters and numbers.  No punctuation or spaces are allowed.
    +#       - Keywords are not case sensitive.
    +#       - Subsequent keyword sections add to the list.  They don't replace it.
    +#       - Keywords can be redefined by other keyword sections.
    +#
    +#
    +#   Revisions:
    +#
    +#       1.3:
    +#
    +#           The initial version of this file.
    +#
    +
    +
    +###############################################################################
    +# Group: File Functions
    +
    +
    +#
    +#   Function: Load
    +#
    +#   Loads both the master and the project version of <Topics.txt>.
    +#
    +sub Load
    +    {
    +    my $self = shift;
    +
    +    # Add the special General topic type.
    +
    +    $types{::TOPIC_GENERAL()} = NaturalDocs::Topics::Type->New('General', 'General', 1, ::SCOPE_NORMAL(), undef);
    +    $names{'general'} = ::TOPIC_GENERAL();
    +    $indexable{::TOPIC_GENERAL()} = 1;
    +    # There are no keywords for the general topic.
    +
    +
    +    $self->LoadFile(1);  # Main
    +
    +    # Dependency: All the default topic types must be checked for existence.
    +
    +    # Check to see if the required types are defined.
    +    foreach my $name (@requiredTypeNames)
    +        {
    +        if (!exists $names{lc($name)})
    +            {  NaturalDocs::ConfigFile->AddError('The ' . $name . ' topic type must be defined in the main topics file.');  };
    +        };
    +
    +    my $errorCount = NaturalDocs::ConfigFile->ErrorCount();
    +
    +    if ($errorCount)
    +        {
    +        NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile();
    +        NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors')
    +                                                    . ' in ' . NaturalDocs::Project->MainConfigFile('Topics.txt'));
    +        }
    +
    +
    +    $self->LoadFile();  # User
    +
    +    $errorCount = NaturalDocs::ConfigFile->ErrorCount();
    +
    +    if ($errorCount)
    +        {
    +        NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile();
    +        NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors')
    +                                                    . ' in ' . NaturalDocs::Project->UserConfigFile('Topics.txt'));
    +        }
    +    };
    +
    +
    +#
    +#   Function: LoadFile
    +#
    +#   Loads a particular version of <Topics.txt>.
    +#
    +#   Parameters:
    +#
    +#       isMain - Whether the file is the main file or not.
    +#
    +sub LoadFile #(isMain)
    +    {
    +    my ($self, $isMain) = @_;
    +
    +    my ($file, $status);
    +
    +    if ($isMain)
    +        {
    +        $file = NaturalDocs::Project->MainConfigFile('Topics.txt');
    +        $status = NaturalDocs::Project->MainConfigFileStatus('Topics.txt');
    +        }
    +    else
    +        {
    +        $file = NaturalDocs::Project->UserConfigFile('Topics.txt');
    +        $status = NaturalDocs::Project->UserConfigFileStatus('Topics.txt');
    +        };
    +
    +    my $version;
    +
    +    if ($version = NaturalDocs::ConfigFile->Open($file))
    +        {
    +        # The format hasn't changed since the file was introduced.
    +
    +        if ($status == ::FILE_CHANGED())
    +            {  NaturalDocs::Project->ReparseEverything();  };
    +
    +        my ($topicTypeKeyword, $topicTypeName, $topicType, $topicTypeObject, $inKeywords, $inIgnoredKeywords);
    +
    +        # Keys are topic type objects, values are unparsed strings.
    +        my %canGroupWith;
    +        tie %canGroupWith, 'Tie::RefHash';
    +
    +        while (my ($keyword, $value) = NaturalDocs::ConfigFile->GetLine())
    +            {
    +            if ($keyword)
    +                {
    +                $inKeywords = 0;
    +                $inIgnoredKeywords = 0;
    +                };
    +
    +            if ($keyword eq 'topic type')
    +                {
    +                $topicTypeKeyword = $keyword;
    +                $topicTypeName = $value;
    +
    +                # Resolve conflicts and create the type if necessary.
    +
    +                $topicType = $self->MakeTopicType($topicTypeName);
    +                my $lcTopicTypeName = lc($topicTypeName);
    +
    +                my $lcTopicTypeAName = $lcTopicTypeName;
    +                $lcTopicTypeAName =~ tr/a-z0-9//cd;
    +
    +                if (!NaturalDocs::ConfigFile->HasOnlyCFChars($topicTypeName))
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Topic names can only have ' . NaturalDocs::ConfigFile->CFCharNames() . '.');
    +                    }
    +                elsif ($topicType eq ::TOPIC_GENERAL())
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('You cannot define a General topic type.');
    +                    }
    +                elsif (defined $types{$topicType} || defined $names{$lcTopicTypeName} || defined $names{$lcTopicTypeAName})
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Topic type ' . $topicTypeName . ' is already defined or its name is too '
    +                                                                     . 'similar to an existing name.  Use Alter Topic Type if you meant to override '
    +                                                                     . 'its settings.');
    +                    }
    +                else
    +                    {
    +                    $topicTypeObject = NaturalDocs::Topics::Type->New($topicTypeName, $topicTypeName, 1, ::SCOPE_NORMAL(),
    +                                                                                                  0, 0);
    +
    +                    $types{$topicType} = $topicTypeObject;
    +                    $names{$lcTopicTypeName} = $topicType;
    +                    $names{$lcTopicTypeAName} = $topicType;
    +
    +                    $indexable{$topicType} = 1;
    +
    +                    if ($isMain)
    +                        {  push @mainTopicNames, $topicTypeName;  };
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'alter topic type')
    +                {
    +                $topicTypeKeyword = $keyword;
    +                $topicTypeName = $value;
    +
    +                # Resolve conflicts and create the type if necessary.
    +
    +                $topicType = $names{lc($topicTypeName)};
    +
    +                if (!defined $topicType)
    +                    {  NaturalDocs::ConfigFile->AddError('Topic type ' . $topicTypeName . ' doesn\'t exist.');  }
    +                elsif ($topicType eq ::TOPIC_GENERAL())
    +                    {  NaturalDocs::ConfigFile->AddError('You cannot alter the General topic type.');  }
    +                else
    +                    {
    +                    $topicTypeObject = $types{$topicType};
    +                    };
    +                }
    +
    +            elsif ($keyword =~ /^ignored? keywords?$/)
    +                {
    +                $inIgnoredKeywords = 1;
    +
    +                my @ignoredKeywords = split(/ ?, ?/, lc($value));
    +
    +                foreach my $ignoredKeyword (@ignoredKeywords)
    +                    {
    +                    delete $keywords{$ignoredKeyword};
    +                    delete $pluralKeywords{$ignoredKeyword};
    +                    };
    +                }
    +
    +            # We continue even if there are errors in the topic type line so that we can find any other errors in the file as well.  We'd
    +            # rather them all show up at once instead of them showing up one at a time between Natural Docs runs.  So we just ignore
    +            # the settings if $topicTypeObject is undef.
    +
    +
    +            elsif ($keyword eq 'plural')
    +                {
    +                my $pluralName = $value;
    +                my $lcPluralName = lc($pluralName);
    +
    +                my $lcPluralAName = $lcPluralName;
    +                $lcPluralAName =~ tr/a-zA-Z0-9//cd;
    +
    +                if (!NaturalDocs::ConfigFile->HasOnlyCFChars($pluralName))
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Plural names can only have '
    +                                                                     . NaturalDocs::ConfigFile->CFCharNames() . '.');
    +                    }
    +                elsif ($lcPluralAName eq 'general')
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('You cannot use General as a plural name for ' . $topicTypeName . '.');
    +                    }
    +                elsif ( (defined $names{$lcPluralName} && $names{$lcPluralName} ne $topicType) ||
    +                         (defined $names{$lcPluralAName} && $names{$lcPluralAName} ne $topicType) )
    +                    {
    +                    NaturalDocs::ConfigFile->AddError($topicTypeName . "'s plural name, " . $pluralName
    +                                                                     . ', is already defined or is too similar to an existing name.');
    +                    }
    +
    +                elsif (defined $topicTypeObject)
    +                    {
    +                    $topicTypeObject->SetPluralName($pluralName);
    +
    +                    $names{$lcPluralName} = $topicType;
    +                    $names{$lcPluralAName} = $topicType;
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'index')
    +                {
    +                $value = lc($value);
    +
    +                if ($value eq 'yes')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {
    +                        $topicTypeObject->SetIndex(1);
    +                        $indexable{$topicType} = 1;
    +                        };
    +                    }
    +                elsif ($value eq 'no')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {
    +                        $topicTypeObject->SetIndex(0);
    +                        delete $indexable{$topicType};
    +                        };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Index lines can only be "yes" or "no".');
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'class hierarchy')
    +                {
    +                $value = lc($value);
    +
    +                if ($value eq 'yes')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetClassHierarchy(1);  };
    +                    }
    +                elsif ($value eq 'no')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetClassHierarchy(0);  };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Class Hierarchy lines can only be "yes" or "no".');
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'scope')
    +                {
    +                $value = lc($value);
    +
    +                if ($value eq 'normal')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetScope(::SCOPE_NORMAL());  };
    +                    }
    +                elsif ($value eq 'start')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetScope(::SCOPE_START());  };
    +                    }
    +                elsif ($value eq 'end')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetScope(::SCOPE_END());  };
    +                    }
    +                elsif ($value eq 'always global')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetScope(::SCOPE_ALWAYS_GLOBAL());  };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Scope lines can only be "normal", "start", "end", or "always global".');
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'page title if first')
    +                {
    +                $value = lc($value);
    +
    +                if ($value eq 'yes')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetPageTitleIfFirst(1);  };
    +                    }
    +                elsif ($value eq 'no')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetPageTitleIfFirst(undef);  };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Page Title if First lines can only be "yes" or "no".');
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'break lists')
    +                {
    +                $value = lc($value);
    +
    +                if ($value eq 'yes')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetBreakLists(1);  };
    +                    }
    +                elsif ($value eq 'no')
    +                    {
    +                    if (defined $topicTypeObject)
    +                        {  $topicTypeObject->SetBreakLists(undef);  };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Break Lists lines can only be "yes" or "no".');
    +                    };
    +                }
    +
    +            elsif ($keyword eq 'can group with')
    +                {
    +                if (defined $topicTypeObject)
    +                    {  $canGroupWith{$topicTypeObject} = lc($value);  };
    +                }
    +
    +            elsif ($keyword =~ /^(?:add )?keywords?$/)
    +                {
    +                $inKeywords = 1;
    +                }
    +
    +            elsif (defined $keyword)
    +                {  NaturalDocs::ConfigFile->AddError($keyword . ' is not a valid keyword.');  }
    +
    +            elsif (!$inKeywords && !$inIgnoredKeywords)
    +                {
    +                NaturalDocs::ConfigFile->AddError('All lines in ' . $topicTypeKeyword . ' sections must begin with a keyword.');
    +                }
    +
    +            else # No keyword but in keyword section.
    +                {
    +                $value = lc($value);
    +
    +                if ($value =~ /^([a-z0-9 ]*[a-z0-9]) ?, ?([a-z0-9 ]+)$/)
    +                    {
    +                    my ($singular, $plural) = ($1, $2);
    +
    +                    if ($inIgnoredKeywords)
    +                        {
    +                        delete $keywords{$singular};
    +                        delete $keywords{$plural};
    +                        delete $pluralKeywords{$singular};
    +                        delete $pluralKeywords{$plural};
    +                        }
    +                    elsif (defined $topicTypeObject)
    +                        {
    +                        $keywords{$singular} = $topicType;
    +                        delete $pluralKeywords{$singular};
    +
    +                        $pluralKeywords{$plural} = $topicType;
    +                        delete $keywords{$plural};
    +                        };
    +                    }
    +                elsif ($value =~ /^[a-z0-9 ]+$/)
    +                    {
    +                    if ($inIgnoredKeywords)
    +                        {
    +                        delete $keywords{$value};
    +                        delete $pluralKeywords{$value};
    +                        }
    +                    elsif (defined $topicType)
    +                        {
    +                        $keywords{$value} = $topicType;
    +                        delete $pluralKeywords{$value};
    +                        };
    +                    }
    +                else
    +                    {
    +                    NaturalDocs::ConfigFile->AddError('Keywords can only have letters, numbers, and spaces.  '
    +                                                                     . 'Plurals must be separated by a comma.');
    +                    };
    +                };
    +            };
    +
    +        NaturalDocs::ConfigFile->Close();
    +
    +
    +        # Parse out the Can Group With lines now that everything's defined.
    +
    +        while (my ($typeObject, $value) = each %canGroupWith)
    +            {
    +            my @values = split(/ ?, ?/, $value);
    +            my @types;
    +
    +            foreach my $value (@values)
    +                {
    +                # We're just going to ignore invalid items.
    +                if (exists $names{$value})
    +                    {  push @types, $names{$value};  };
    +                };
    +
    +            if (scalar @types)
    +                {  $typeObject->SetCanGroupWith(\@types);  };
    +            };
    +        }
    +
    +    else # couldn't open file
    +        {
    +        if ($isMain)
    +            {  die "Couldn't open topics file " . $file . "\n";  }
    +        else
    +            {  NaturalDocs::Project->ReparseEverything();  };
    +        };
    +    };
    +
    +
    +#
    +#   Function: Save
    +#
    +#   Saves the main and user versions of <Topics.txt>.
    +#
    +sub Save
    +    {
    +    my $self = shift;
    +
    +    $self->SaveFile(1); # Main
    +    $self->SaveFile(0); # User
    +    };
    +
    +
    +#
    +#   Function: SaveFile
    +#
    +#   Saves a particular version of <Topics.txt>.
    +#
    +#   Parameters:
    +#
    +#       isMain - Whether the file is the main file or not.
    +#
    +sub SaveFile #(isMain)
    +    {
    +    my ($self, $isMain) = @_;
    +
    +    my $file;
    +
    +    if ($isMain)
    +        {
    +        if (NaturalDocs::Project->MainConfigFileStatus('Topics.txt') == ::FILE_SAME())
    +            {  return;  };
    +        $file = NaturalDocs::Project->MainConfigFile('Topics.txt');
    +        }
    +    else
    +        {
    +        # We have to check the main one two because this lists the topics defined in it.
    +        if (NaturalDocs::Project->UserConfigFileStatus('Topics.txt') == ::FILE_SAME() &&
    +            NaturalDocs::Project->MainConfigFileStatus('Topics.txt') == ::FILE_SAME())
    +            {  return;  };
    +        $file = NaturalDocs::Project->UserConfigFile('Topics.txt');
    +        };
    +
    +
    +    # Array of topic type names in the order they appear in the file.  If Alter Topic Type is used, the name will end with an asterisk.
    +    my @topicTypeOrder;
    +
    +    # Keys are topic type names, values are property hashrefs.  Hashref keys are the property names, values the value.
    +    # For keywords, the key is Keywords and the values are arrayrefs of singular and plural pairs.  If no plural is defined, the entry
    +    # will be undef.
    +    my %properties;
    +
    +    # List of ignored keywords specified as Ignore Keywords: [keyword], [keyword], ...
    +    my @inlineIgnoredKeywords;
    +
    +    # List of ignored keywords specified in [keyword], [plural keyword] lines.  Done in pairs, like for regular keywords.
    +    my @separateIgnoredKeywords;
    +
    +    my $inIgnoredKeywords;
    +
    +    if (NaturalDocs::ConfigFile->Open($file))
    +        {
    +        # We can assume the file is valid.
    +
    +        my ($keyword, $value, $topicTypeName);
    +
    +        while (($keyword, $value) = NaturalDocs::ConfigFile->GetLine())
    +            {
    +            $keyword = lc($keyword);
    +
    +            if ($keyword eq 'topic type' || $keyword eq 'alter topic type')
    +                {
    +                $topicTypeName = $types{ $names{lc($value)} }->Name();
    +
    +                if ($keyword eq 'alter topic type')
    +                    {  $topicTypeName .= '*';  };
    +
    +                push @topicTypeOrder, $topicTypeName;
    +
    +                if (!exists $properties{$topicTypeName})
    +                    {  $properties{$topicTypeName} = { 'keywords' => [ ] };  };
    +                }
    +
    +            elsif ($keyword eq 'plural')
    +                {
    +                $properties{$topicTypeName}->{$keyword} = $value;
    +                }
    +
    +            elsif ($keyword eq 'index' ||
    +                    $keyword eq 'scope' ||
    +                    $keyword eq 'page title if first' ||
    +                    $keyword eq 'class hierarchy' ||
    +                    $keyword eq 'break lists' ||
    +                    $keyword eq 'can group with')
    +                {
    +                $properties{$topicTypeName}->{$keyword} = lc($value);
    +                }
    +
    +            elsif ($keyword =~ /^(?:add )?keywords?$/)
    +                {
    +                $inIgnoredKeywords = 0;
    +                }
    +
    +            elsif ($keyword =~ /^ignored? keywords?$/)
    +                {
    +                $inIgnoredKeywords = 1;
    +                if ($value)
    +                    {  push @inlineIgnoredKeywords, split(/ ?, ?/, $value);  };
    +                }
    +
    +            elsif (!$keyword)
    +                {
    +                my ($singular, $plural) = split(/ ?, ?/, lc($value));
    +
    +                if ($inIgnoredKeywords)
    +                    {  push @separateIgnoredKeywords, $singular, $plural;  }
    +                else
    +                    {  push @{$properties{$topicTypeName}->{'keywords'}}, $singular, $plural;  };
    +                };
    +            };
    +
    +        NaturalDocs::ConfigFile->Close();
    +        };
    +
    +
    +    if (!open(FH_TOPICS, '>' . $file))
    +        {
    +        # The main file may be on a shared volume or some other place the user doesn't have write access to.  Since this is only to
    +        # reformat the file, we can ignore the failure.
    +        if ($isMain)
    +            {  return;  }
    +        else
    +            {  die "Couldn't save " . $file;  };
    +        };
    +
    +    print FH_TOPICS 'Format: ' . NaturalDocs::Settings->TextAppVersion() . "\n\n";
    +
    +    # Remember the 80 character limit.
    +
    +    if ($isMain)
    +        {
    +        print FH_TOPICS
    +        "# This is the main Natural Docs topics file.  If you change anything here, it\n"
    +        . "# will apply to EVERY PROJECT you use Natural Docs on.  If you'd like to\n"
    +        . "# change something for just one project, edit the Topics.txt in its project\n"
    +        . "# directory instead.\n";
    +        }
    +    else
    +        {
    +        print FH_TOPICS
    +        "# This is the Natural Docs topics file for this project.  If you change anything\n"
    +        . "# here, it will apply to THIS PROJECT ONLY.  If you'd like to change something\n"
    +        . "# for all your projects, edit the Topics.txt in Natural Docs' Config directory\n"
    +        . "# instead.\n\n\n";
    +
    +        if (scalar @inlineIgnoredKeywords || scalar @separateIgnoredKeywords)
    +            {
    +            if (scalar @inlineIgnoredKeywords == 1 && !scalar @separateIgnoredKeywords)
    +                {
    +                print FH_TOPICS 'Ignore Keyword: ' . $inlineIgnoredKeywords[0] . "\n";
    +                }
    +            else
    +                {
    +                print FH_TOPICS
    +                'Ignore Keywords: ' . join(', ', @inlineIgnoredKeywords) . "\n";
    +
    +                for (my $i = 0; $i < scalar @separateIgnoredKeywords; $i += 2)
    +                    {
    +                    print FH_TOPICS '   ' . $separateIgnoredKeywords[$i];
    +
    +                    if (defined $separateIgnoredKeywords[$i + 1])
    +                        {  print FH_TOPICS ', ' . $separateIgnoredKeywords[$i + 1];  };
    +
    +                    print FH_TOPICS "\n";
    +                    };
    +                };
    +            }
    +        else
    +            {
    +            print FH_TOPICS
    +            "# If you'd like to prevent keywords from being recognized by Natural Docs, you\n"
    +            . "# can do it like this:\n"
    +            . "# Ignore Keywords: [keyword], [keyword], ...\n"
    +            . "#\n"
    +            . "# Or you can use the list syntax like how they are defined:\n"
    +            . "# Ignore Keywords:\n"
    +            . "#    [keyword]\n"
    +            . "#    [keyword], [plural keyword]\n"
    +            . "#    ...\n";
    +            };
    +        };
    +
    +    print FH_TOPICS # [CFChars]
    +    "\n\n"
    +    . "#-------------------------------------------------------------------------------\n"
    +    . "# SYNTAX:\n"
    +    . "#\n";
    +
    +    if ($isMain)
    +        {
    +        print FH_TOPICS
    +        "# Topic Type: [name]\n"
    +        . "#    Creates a new topic type.  Each type gets its own index and behavior\n"
    +        . "#    settings.  Its name can have letters, numbers, spaces, and these\n"
    +        . "#    charaters: - / . '\n"
    +        . "#\n"
    +        . "#    The Enumeration type is special.  It's indexed with Types but its members\n"
    +        . "#    are indexed with Constants according to the rules in Languages.txt.\n"
    +        . "#\n"
    +        }
    +    else
    +        {
    +        print FH_TOPICS
    +        "# Topic Type: [name]\n"
    +        . "# Alter Topic Type: [name]\n"
    +        . "#    Creates a new topic type or alters one from the main file.  Each type gets\n"
    +        . "#    its own index and behavior settings.  Its name can have letters, numbers,\n"
    +        . "#    spaces, and these charaters: - / . '\n"
    +        . "#\n";
    +        };
    +
    +    print FH_TOPICS
    +    "# Plural: [name]\n"
    +    . "#    Sets the plural name of the topic type, if different.\n"
    +    . "#\n"
    +    . "# Keywords:\n"
    +    . "#    [keyword]\n"
    +    . "#    [keyword], [plural keyword]\n"
    +    . "#    ...\n";
    +
    +    if ($isMain)
    +        {
    +        print FH_TOPICS
    +        "#    Defines a list of keywords for the topic type.  They may only contain\n"
    +        . "#    letters, numbers, and spaces and are not case sensitive.  Plural keywords\n"
    +        . "#    are used for list topics.\n";
    +        }
    +    else
    +        {
    +        print FH_TOPICS
    +        "#    Defines or adds to the list of keywords for the topic type.  They may only\n"
    +        . "#    contain letters, numbers, and spaces and are not case sensitive.  Plural\n"
    +        . "#    keywords are used for list topics.  You can redefine keywords found in the\n"
    +        . "#    main topics file.\n";
    +        }
    +
    +    print FH_TOPICS
    +    "#\n"
    +    . "# Index: [yes|no]\n"
    +    . "#    Whether the topics get their own index.  Defaults to yes.  Everything is\n"
    +    . "#    included in the general index regardless of this setting.\n"
    +    . "#\n"
    +    . "# Scope: [normal|start|end|always global]\n"
    +    . "#    How the topics affects scope.  Defaults to normal.\n"
    +    . "#    normal        - Topics stay within the current scope.\n"
    +    . "#    start         - Topics start a new scope for all the topics beneath it,\n"
    +    . "#                    like class topics.\n"
    +    . "#    end           - Topics reset the scope back to global for all the topics\n"
    +    . "#                    beneath it.\n"
    +    . "#    always global - Topics are defined as global, but do not change the scope\n"
    +    . "#                    for any other topics.\n"
    +    . "#\n"
    +    . "# Class Hierarchy: [yes|no]\n"
    +    . "#    Whether the topics are part of the class hierarchy.  Defaults to no.\n"
    +    . "#\n"
    +    . "# Page Title If First: [yes|no]\n"
    +    . "#    Whether the topic's title becomes the page title if it's the first one in\n"
    +    . "#    a file.  Defaults to no.\n"
    +    . "#\n"
    +    . "# Break Lists: [yes|no]\n"
    +    . "#    Whether list topics should be broken into individual topics in the output.\n"
    +    . "#    Defaults to no.\n"
    +    . "#\n"
    +    . "# Can Group With: [type], [type], ...\n"
    +    . "#    Defines a list of topic types that this one can possibly be grouped with.\n"
    +    . "#    Defaults to none.\n"
    +    . "#-------------------------------------------------------------------------------\n\n";
    +
    +    my $listToPrint;
    +
    +    if ($isMain)
    +        {
    +        print FH_TOPICS
    +        "# The following topics MUST be defined in this file:\n"
    +        . "#\n";
    +        $listToPrint = \@requiredTypeNames;
    +        }
    +    else
    +        {
    +        print FH_TOPICS
    +        "# The following topics are defined in the main file, if you'd like to alter\n"
    +        . "# their behavior or add keywords:\n"
    +        . "#\n";
    +        $listToPrint = \@mainTopicNames;
    +        }
    +
    +    print FH_TOPICS
    +    Text::Wrap::wrap('#    ', '#    ', join(', ', @$listToPrint)) . "\n"
    +    . "\n"
    +    . "# If you add something that you think would be useful to other developers\n"
    +    . "# and should be included in Natural Docs by default, please e-mail it to\n"
    +    . "# topics [at] naturaldocs [dot] org.\n";
    +
    +    # Existence hash.  We do this because we want the required ones to go first by adding them to @topicTypeOrder, but we don't
    +    # want them to appear twice.
    +    my %doneTopicTypes;
    +    my ($altering, $numberOfProperties);
    +
    +    if ($isMain)
    +        {  unshift @topicTypeOrder, @requiredTypeNames;  };
    +
    +    my @propertyOrder = ('Plural', 'Index', 'Scope', 'Class Hierarchy', 'Page Title If First', 'Break Lists');
    +
    +    foreach my $topicType (@topicTypeOrder)
    +        {
    +        if (!exists $doneTopicTypes{$topicType})
    +            {
    +            if (substr($topicType, -1) eq '*')
    +                {
    +                print FH_TOPICS "\n\n"
    +                . 'Alter Topic Type: ' . substr($topicType, 0, -1) . "\n\n";
    +
    +                $altering = 1;
    +                $numberOfProperties = 0;
    +                }
    +            else
    +                {
    +                print FH_TOPICS "\n\n"
    +                . 'Topic Type: ' . $topicType . "\n\n";
    +
    +                $altering = 0;
    +                $numberOfProperties = 0;
    +                };
    +
    +            foreach my $property (@propertyOrder)
    +                {
    +                if (exists $properties{$topicType}->{lc($property)})
    +                    {
    +                    print FH_TOPICS
    +                    '   ' . $property . ': ' . ucfirst( $properties{$topicType}->{lc($property)} ) . "\n";
    +
    +                    $numberOfProperties++;
    +                    };
    +                };
    +
    +            if (exists $properties{$topicType}->{'can group with'})
    +                {
    +                my @typeStrings = split(/ ?, ?/, lc($properties{$topicType}->{'can group with'}));
    +                my @types;
    +
    +                foreach my $typeString (@typeStrings)
    +                    {
    +                    if (exists $names{$typeString})
    +                        {  push @types, $names{$typeString};  };
    +                    };
    +
    +                if (scalar @types)
    +                    {
    +                    for (my $i = 0; $i < scalar @types; $i++)
    +                        {
    +                        my $name = NaturalDocs::Topics->NameOfType($types[$i], 1);
    +
    +                        if ($i == 0)
    +                            {  print FH_TOPICS '   Can Group With: ' . $name;  }
    +                        else
    +                            {  print FH_TOPICS ', ' . $name;  };
    +                        };
    +
    +                    print FH_TOPICS "\n";
    +                    $numberOfProperties++;
    +                    };
    +                };
    +
    +            if (scalar @{$properties{$topicType}->{'keywords'}})
    +                {
    +                if ($numberOfProperties > 1)
    +                    {  print FH_TOPICS "\n";  };
    +
    +                print FH_TOPICS
    +                '   ' . ($altering ? 'Add ' : '') . 'Keywords:' . "\n";
    +
    +                my $keywords = $properties{$topicType}->{'keywords'};
    +
    +                for (my $i = 0; $i < scalar @$keywords; $i += 2)
    +                    {
    +                    print FH_TOPICS '      ' . $keywords->[$i];
    +
    +                    if (defined $keywords->[$i + 1])
    +                        {  print FH_TOPICS ', ' . $keywords->[$i + 1];  };
    +
    +                    print FH_TOPICS "\n";
    +                    };
    +                };
    +
    +            $doneTopicTypes{$topicType} = 1;
    +            };
    +        };
    +
    +    close(FH_TOPICS);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: KeywordInfo
    +#
    +#   Returns information about a topic keyword.
    +#
    +#   Parameters:
    +#
    +#       keyword - The keyword, which may be plural.
    +#
    +#   Returns:
    +#
    +#       The array ( topicType, info, isPlural ), or an empty array if the keyword doesn't exist.
    +#
    +#       topicType - The <TopicType> of the keyword.
    +#       info - The <NaturalDocs::Topics::Type> of its type.
    +#       isPlural - Whether the keyword was plural or not.
    +#
    +sub KeywordInfo #(keyword)
    +    {
    +    my ($self, $keyword) = @_;
    +
    +    $keyword = lc($keyword);
    +
    +    my $type = $keywords{$keyword};
    +
    +    if (defined $type)
    +        {  return ( $type, $types{$type}, undef );  };
    +
    +    $type = $pluralKeywords{$keyword};
    +
    +    if (defined $type)
    +        {  return ( $type, $types{$type}, 1 );  };
    +
    +    return ( );
    +    };
    +
    +
    +#
    +#   Function: NameInfo
    +#
    +#   Returns information about a topic name.
    +#
    +#   Parameters:
    +#
    +#      name - The topic type name, which can be plural and/or alphanumeric only.
    +#
    +#   Returns:
    +#
    +#       The array ( topicType, info ), or an empty array if the name doesn't exist.  Note that unlike <KeywordInfo()>, this
    +#       does *not* tell you whether the name is plural or not.
    +#
    +#       topicType - The <TopicType> of the name.
    +#       info - The <NaturalDocs::Topics::Type> of the type.
    +#
    +sub NameInfo #(name)
    +    {
    +    my ($self, $name) = @_;
    +
    +    my $type = $names{lc($name)};
    +
    +    if (defined $type)
    +        {  return ( $type, $types{$type} );  }
    +    else
    +        {  return ( );  };
    +    };
    +
    +
    +#
    +#   Function: TypeInfo
    +#
    +#   Returns information about a <TopicType>.
    +#
    +#   Parameters:
    +#
    +#      type - The <TopicType>.
    +#
    +#   Returns:
    +#
    +#       The <NaturalDocs::Topics::Type> of the type, or undef if it didn't exist.
    +#
    +sub TypeInfo #(type)
    +    {
    +    my ($self, $type) = @_;
    +    return $types{$type};
    +    };
    +
    +
    +#
    +#   Function: NameOfType
    +#
    +#   Returns the name of the passed <TopicType>, or undef if it doesn't exist.
    +#
    +#   Parameters:
    +#
    +#       topicType - The <TopicType>.
    +#       plural - Whether to return the plural instead of the singular.
    +#       alphanumericOnly - Whether to strips everything but alphanumeric characters out.  Case isn't modified.
    +#
    +#   Returns:
    +#
    +#       The topic type name, according to what was specified in the parameters, or undef if it doesn't exist.
    +#
    +sub NameOfType #(topicType, plural, alphanumericOnly)
    +    {
    +    my ($self, $topicType, $plural, $alphanumericOnly) = @_;
    +
    +    my $topicObject = $types{$topicType};
    +
    +    if (!defined $topicObject)
    +        {  return undef;  };
    +
    +    my $topicName = ($plural ? $topicObject->PluralName() : $topicObject->Name());
    +
    +    if ($alphanumericOnly)
    +        {  $topicName =~ tr/a-zA-Z0-9//cd;  };
    +
    +    return $topicName;
    +    };
    +
    +
    +#
    +#   Function: TypeFromName
    +#
    +#   Returns a <TopicType> for the passed topic name.
    +#
    +#   Parameters:
    +#
    +#       topicName - The name of the topic, which can be plural and/or alphanumeric only.
    +#
    +#   Returns:
    +#
    +#       The <TopicType>.  It does not specify whether the name was plural or not.
    +#
    +sub TypeFromName #(topicName)
    +    {
    +    my ($self, $topicName) = @_;
    +
    +    return $names{lc($topicName)};
    +    };
    +
    +
    +#
    +#   Function: IsValidType
    +#
    +#   Returns whether the passed <TopicType> is defined.
    +#
    +sub IsValidType #(type)
    +    {
    +    my ($self, $type) = @_;
    +    return exists $types{$type};
    +    };
    +
    +
    +#
    +#   Function: TypeFromLegacy
    +#
    +#   Returns a <TopicType> for the passed legacy topic type integer.  <TopicTypes> were changed from integer constants to
    +#   strings in 1.3.
    +#
    +sub TypeFromLegacy #(legacyInt)
    +    {
    +    my ($self, $int) = @_;
    +    return $legacyTypes[$int];
    +    };
    +
    +
    +#
    +#   Function: AllIndexableTypes
    +#
    +#   Returns an array of all possible indexable <TopicTypes>.
    +#
    +sub AllIndexableTypes
    +    {
    +    my ($self) = @_;
    +    return keys %indexable;
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +
    +
    +#
    +#   Function: MakeTopicType
    +#
    +#   Returns a <TopicType> for the passed topic name.  It does not check to see if it exists already.
    +#
    +#   Parameters:
    +#
    +sub MakeTopicType #(topicName)
    +    {
    +    my ($self, $topicName) = @_;
    +
    +    # Dependency: The values of the default topic type constants must match what is generated here.
    +
    +    # Turn everything to lowercase and strip non-alphanumeric characters.
    +    $topicName = lc($topicName);
    +    $topicName =~ tr/a-z0-9//cd;
    +
    +    return $topicName;
    +    };
    +
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Topics/Type.pm b/vendor/naturaldocs/Modules/NaturalDocs/Topics/Type.pm
    new file mode 100644
    index 000000000..2ed959e9e
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Topics/Type.pm
    @@ -0,0 +1,152 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Topics::Type
    +#
    +###############################################################################
    +#
    +#   A class storing information about a <TopicType>.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +
    +package NaturalDocs::Topics::Type;
    +
    +use NaturalDocs::DefineMembers 'NAME',                         'Name()',
    +                                                 'PLURAL_NAME',             'PluralName()',      'SetPluralName()',
    +                                                 'INDEX',                        'Index()',              'SetIndex()',
    +                                                 'SCOPE',                       'Scope()',              'SetScope()',
    +                                                 'PAGE_TITLE_IF_FIRST', 'PageTitleIfFirst()', 'SetPageTitleIfFirst()',
    +                                                 'BREAK_LISTS',             'BreakLists()',        'SetBreakLists()',
    +                                                 'CLASS_HIERARCHY',    'ClassHierarchy()',  'SetClassHierarchy()',
    +                                                 'CAN_GROUP_WITH';
    +
    +# Dependency: New() depends on the order of these and that there are no parent classes.
    +
    +use base 'Exporter';
    +our @EXPORT = ('SCOPE_NORMAL', 'SCOPE_START', 'SCOPE_END', 'SCOPE_ALWAYS_GLOBAL');
    +
    +#
    +#   Constants: Members
    +#
    +#   The object is implemented as a blessed arrayref, with the following constants as its indexes.
    +#
    +#   NAME - The topic's name.
    +#   PLURAL_NAME - The topic's plural name.
    +#   INDEX - Whether the topic is indexed.
    +#   SCOPE - The topic's <ScopeType>.
    +#   PAGE_TITLE_IF_FIRST - Whether the topic becomes the page title if it's first in a file.
    +#   BREAK_LISTS - Whether list topics should be broken into individual topics in the output.
    +#   CLASS_HIERARCHY - Whether the topic is part of the class hierarchy.
    +#   CAN_GROUP_WITH - The existence hashref of <TopicTypes> the type can be grouped with.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: Types
    +
    +
    +#
    +#   Constants: ScopeType
    +#
    +#   The possible values for <Scope()>.
    +#
    +#   SCOPE_NORMAL - The topic stays in the current scope without affecting it.
    +#   SCOPE_START - The topic starts a scope.
    +#   SCOPE_END - The topic ends a scope, returning it to global.
    +#   SCOPE_ALWAYS_GLOBAL - The topic is always global, but it doesn't affect the current scope.
    +#
    +use constant SCOPE_NORMAL => 1;
    +use constant SCOPE_START => 2;
    +use constant SCOPE_END => 3;
    +use constant SCOPE_ALWAYS_GLOBAL => 4;
    +
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: New
    +#
    +#   Creates and returns a new object.
    +#
    +#   Parameters:
    +#
    +#       name - The topic name.
    +#       pluralName - The topic's plural name.
    +#       index - Whether the topic is indexed.
    +#       scope - The topic's <ScopeType>.
    +#       pageTitleIfFirst - Whether the topic becomes the page title if it's the first one in a file.
    +#       breakLists - Whether list topics should be broken into individual topics in the output.
    +#
    +sub New #(name, pluralName, index, scope, pageTitleIfFirst, breakLists)
    +    {
    +    my ($self, @params) = @_;
    +
    +    # Dependency: Depends on the parameter order matching the member order and that there are no parent classes.
    +
    +    my $object = [ @params ];
    +    bless $object, $self;
    +
    +    return $object;
    +    };
    +
    +
    +#
    +#   Functions: Accessors
    +#
    +#   Name - Returns the topic name.
    +#   PluralName - Returns the topic's plural name.
    +#   SetPluralName - Replaces the topic's plural name.
    +#   Index - Whether the topic is indexed.
    +#   SetIndex - Sets whether the topic is indexed.
    +#   Scope - Returns the topic's <ScopeType>.
    +#   SetScope - Replaces the topic's <ScopeType>.
    +#   PageTitleIfFirst - Returns whether the topic becomes the page title if it's first in the file.
    +#   SetPageTitleIfFirst - Sets whether the topic becomes the page title if it's first in the file.
    +#   BreakLists - Returns whether list topics should be broken into individual topics in the output.
    +#   SetBreakLists - Sets whether list topics should be broken into individual topics in the output.
    +#   ClassHierarchy - Returns whether the topic is part of the class hierarchy.
    +#   SetClassHierarchy - Sets whether the topic is part of the class hierarchy.
    +#
    +
    +
    +#
    +#   Function: CanGroupWith
    +#
    +#   Returns whether the type can be grouped with the passed <TopicType>.
    +#
    +sub CanGroupWith #(TopicType type) -> bool
    +    {
    +    my ($self, $type) = @_;
    +    return ( defined $self->[CAN_GROUP_WITH] && exists $self->[CAN_GROUP_WITH]->{$type} );
    +    };
    +
    +
    +#
    +#   Function: SetCanGroupWith
    +#
    +#   Sets the list of <TopicTypes> the type can be grouped with.
    +#
    +sub SetCanGroupWith #(TopicType[] types)
    +    {
    +    my ($self, $types) = @_;
    +
    +    $self->[CAN_GROUP_WITH] = { };
    +
    +    foreach my $type (@$types)
    +        {  $self->[CAN_GROUP_WITH]->{$type} = 1;  };
    +    };
    +
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/Modules/NaturalDocs/Version.pm b/vendor/naturaldocs/Modules/NaturalDocs/Version.pm
    new file mode 100644
    index 000000000..27a22ecb8
    --- /dev/null
    +++ b/vendor/naturaldocs/Modules/NaturalDocs/Version.pm
    @@ -0,0 +1,361 @@
    +###############################################################################
    +#
    +#   Package: NaturalDocs::Version
    +#
    +###############################################################################
    +#
    +#   A package for handling version information.  What?  That's right.  Although it should be easy and obvious, version numbers
    +#   need to be dealt with in a variety of formats, plus there's compatibility with older releases which handled it differently.  I
    +#   wanted to centralize the code after it started getting complicated.  So there ya go.
    +#
    +###############################################################################
    +
    +# This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    +# Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    +# Refer to License.txt for the complete details
    +
    +use strict;
    +use integer;
    +
    +package NaturalDocs::Version;
    +
    +
    +###############################################################################
    +# Group: Functions
    +
    +
    +#
    +#   Function: ToString
    +#
    +#   Converts a <VersionInt> to a string.
    +#
    +sub ToString #(VersionInt version) => string
    +    {
    +    my ($self, $version) = @_;
    +
    +    my ($major, $minor, $month, $day, $year) = $self->ToValues($version);
    +
    +    if ($minor % 10 == 0)
    +        {  $minor /= 10;  };
    +
    +    if ($day)
    +        {  return sprintf('Development Release %02d-%02d-%d (%d.%d base)', $month, $day, $year, $major, $minor);  }
    +    else
    +        {  return $major . '.' . $minor;  };
    +    };
    +
    +
    +#
    +#   Function: FromString
    +#
    +#   Converts a version string to a <VersionInt>.
    +#
    +sub FromString #(string string) => VersionInt
    +    {
    +    my ($self, $string) = @_;
    +
    +    if ($string eq '1')
    +        {
    +        return $self->FromValues(0, 91, 0, 0, 0);  # 0.91
    +        }
    +    else
    +        {
    +        my ($major, $minor, $month, $day, $year);
    +
    +        if ($string =~ /^(\d{1,2})\.(\d{1,2})$/)
    +            {
    +            ($major, $minor) = ($1, $2);
    +            ($month, $day, $year) = (0, 0, 0);
    +            }
    +        elsif ($string =~ /^Development Release (\d{1,2})-(\d{1,2})-(\d\d\d\d) \((\d{1,2})\.(\d{1,2}) base\)$/)
    +            {
    +            ($month, $day, $year, $major, $minor) = ($1, $2, $3, $4, $5);
    +
    +            # We have to do sanity checking because these can come from user-editable text files.  The version numbers should
    +            # already be constrained simply by being forced to have only two digits.
    +
    +            if ($month > 12 || $month < 1 || $day > 31 || $day < 1 || $year > 2255 || $year < 2000)
    +                {  die 'The version string ' . $string . " doesn't have a valid date.\n";  };
    +            }
    +        else
    +            {
    +            die 'The version string ' . $string . " isn't in a recognized format.\n";
    +            };
    +
    +        if (length $minor == 1)
    +            {  $minor *= 10;  };
    +
    +        return $self->FromValues($major, $minor, $month, $day, $year);
    +        };
    +    };
    +
    +
    +#
    +#   Function: ToTextFile
    +#
    +#   Writes a <VersionInt> to a text file.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The handle of the file to write it to.  It should be at the correct location.
    +#       version - The <VersionInt> to write.
    +#
    +sub ToTextFile #(handle fileHandle, VersionInt version)
    +    {
    +    my ($self, $fileHandle, $version) = @_;
    +
    +    print $fileHandle $self->ToString($version) . "\n";
    +    };
    +
    +
    +#
    +#   Function: ToBinaryFile
    +#
    +#   Writes a <VersionInt> to a binary file.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The handle of the file to write it to.  It should be at the correct location.
    +#       version - The <VersionInt> to write.
    +#
    +sub ToBinaryFile #(handle fileHandle, VersionInt version)
    +    {
    +    my ($self, $fileHandle, $version) = @_;
    +
    +    my ($major, $minor, $month, $day, $year) = $self->ToValues($version);
    +
    +    # 1.35 development releases are encoded as 1.36.  Everything else is literal.
    +    if ($day && $major == 1 && $minor == 35)
    +        {  $minor = 36;  };
    +
    +    print $fileHandle pack('CC', $major, $minor);
    +
    +    # Date fields didn't exist with 1.35 stable and earlier.  1.35 development releases are encoded as 1.36, so this works.
    +    if ($major > 1 || ($major == 1 && $minor > 35))
    +        {
    +        if ($day)
    +            {  $year -= 2000;  };
    +
    +        print $fileHandle pack('CCC', $month, $day, $year);
    +        };
    +    };
    +
    +
    +#
    +#   Function: FromBinaryFile
    +#
    +#   Retrieves a <VersionInt> from a binary file.
    +#
    +#   Parameters:
    +#
    +#       fileHandle - The handle of the file to read it from.  It should be at the correct location.
    +#
    +#   Returns:
    +#
    +#       The <VersionInt>.
    +#
    +sub FromBinaryFile #(handle fileHandle) => VersionInt
    +    {
    +    my ($self, $fileHandle) = @_;
    +
    +    my ($major, $minor, $month, $day, $year);
    +
    +    my $raw;
    +    read($fileHandle, $raw, 2);
    +
    +    ($major, $minor) = unpack('CC', $raw);
    +
    +    # 1.35 stable is the last release without the date fields.  1.35 development releases are encoded as 1.36, so this works.
    +    if ($major > 1 || ($major == 1 && $minor > 35))
    +        {
    +        read($fileHandle, $raw, 3);
    +        ($month, $day, $year) = unpack('CCC', $raw);
    +
    +        if ($day)
    +            {  $year += 2000;  };
    +        }
    +    else
    +        {  ($month, $day, $year) = (0, 0, 0);  };
    +
    +    # Fix the 1.35 development release special encoding.
    +    if ($major == 1 && $minor == 36)
    +        {  $minor = 35;  };
    +
    +
    +    return $self->FromValues($major, $minor, $month, $day, $year);
    +    };
    +
    +
    +#
    +#   Function: ToValues
    +#
    +#   Converts a <VersionInt> to the array ( major, minor, month, day, year ).  The minor version will be in two digit form, so x.2
    +#   will return 20.  The date fields will be zero for stable releases.
    +#
    +sub ToValues #(VersionInt version) => ( int, int, int, int, int )
    +    {
    +    my ($self, $version) = @_;
    +
    +    my $major = ($version & 0x00003F80) >> 7;
    +    my $minor = ($version & 0x0000007F);
    +    my $month = ($version & 0x00780000) >> 19;
    +    my $day = ($version & 0x0007C000) >> 14;
    +    my $year = ($version & 0x7F800000) >> 23;
    +
    +    if ($year)
    +        {  $year += 2000;  };
    +
    +    return ( $major, $minor, $month, $day, $year );
    +    };
    +
    +
    +#
    +#   Function: FromValues
    +#
    +#   Returns a <VersionInt> created from the passed values.
    +#
    +#   Parameters:
    +#
    +#       major - The major version number.  For development releases, it should be the stable version it's based off of.
    +#       minor - The minor version number.  It should always be two digits, so x.2 should pass 20.  For development
    +#                  releases, it should be the stable version it's based off of.
    +#       month - The numeric month of the development release.  For stable releases it should be zero.
    +#       day - The day of the development release.  For stable releases it should be zero.
    +#       year - The year of the development release.  For stable releases it should be zero.
    +#
    +#   Returns:
    +#
    +#       The <VersionInt>.
    +#
    +sub FromValues #(int major, int minor, int month, int day, int year) => VersionInt
    +    {
    +    my ($self, $major, $minor, $month, $day, $year) = @_;
    +
    +    if ($day)
    +        {  $year -= 2000;  };
    +
    +    return ($major << 7) + ($minor) + ($month << 19) + ($day << 14) + ($year << 23);
    +    };
    +
    +
    +#
    +#   Function: CheckFileFormat
    +#
    +#   Checks if a file's format is compatible with the current release.
    +#
    +#   - If the application is a development release or the file is from one, this only returns true if they are from the exact same
    +#     development release.
    +#   - If neither of them are development releases, this only returns true if the file is from a release between the minimum specified
    +#     and the current version.  If there's no minimum it just checks that it's below the current version.
    +#
    +#   Parameters:
    +#
    +#       fileVersion - The <VersionInt> of the file format.
    +#       minimumVersion - The minimum <VersionInt> required of the file format.  May be undef.
    +#
    +#   Returns:
    +#
    +#       Whether the file's format is compatible per the above rules.
    +#
    +sub CheckFileFormat #(VersionInt fileVersion, optional VersionInt minimumVersion) => bool
    +    {
    +    my ($self, $fileVersion, $minimumVersion) = @_;
    +
    +    my $appVersion = NaturalDocs::Settings->AppVersion();
    +
    +    if ($self->IsDevelopmentRelease($appVersion) || $self->IsDevelopmentRelease($fileVersion))
    +        {  return ($appVersion == $fileVersion);  }
    +    elsif ($minimumVersion && $fileVersion < $minimumVersion)
    +        {  return 0;  }
    +    else
    +        {  return ($fileVersion <= $appVersion);  };
    +    };
    +
    +
    +#
    +#   Function: IsDevelopmentRelease
    +#
    +#   Returns whether the passed <VersionInt> is for a development release.
    +#
    +sub IsDevelopmentRelease #(VersionInt version) => bool
    +    {
    +    my ($self, $version) = @_;
    +
    +    # Return if any of the date fields are set.
    +    return ($version & 0x7FFFC000);
    +    };
    +
    +
    +
    +###############################################################################
    +# Group: Implementation
    +
    +#
    +#   About: String Format
    +#
    +#   Full Releases:
    +#
    +#       Full releases are in the common major.minor format.  Either part can be up to two digits.  The minor version is interpreted
    +#       as decimal places, so 1.3 > 1.22.  There are no leading or trailing zeroes.
    +#
    +#   Development Releases:
    +#
    +#       Development releases are in the format "Development Release mm-dd-yyyy (vv.vv base)" where vv.vv is the version
    +#       number of the full release it's based off of.  The month and day will have leading zeroes where applicable.  Example:
    +#       "Development Release 07-09-2006 (1.35 base)".
    +#
    +#   0.91 and Earlier:
    +#
    +#       Text files from releases prior to 0.95 had a separate file format version number that was used instead of the application
    +#       version.  These were never changed between 0.85 and 0.91, so they are simply "1".  Text version numbers that are "1"
    +#       instead of "1.0" will be interpreted as 0.91.
    +#
    +
    +#
    +#   About: Integer Format
    +#
    +#   <VersionInts> are 32-bit values with the bit distribution below.
    +#
    +#   > s yyyyyyyy mmmm ddddd vvvvvvv xxxxxxx
    +#   > [syyy|yyyy] [ymmm|mddd] [ddvv|vvvv] [vxxx|xxxx]
    +#
    +#   s - The sign bit.  Always zero, so it's always interpreted as positive.
    +#   y - The year bits if it's a development release, zero otherwise.  2000 is added to the value, so the range is from 2000 to 2255.
    +#   m - The month bits if it's a development release, zero otherwise.
    +#   d - The day bits if it's a development release, zero otherwise.
    +#   v - The major version bits.  For development releases, it's the last stable version it was based off of.
    +#   x - The minor version bits.  It's always stored as two decimals, so x.2 would store 20 here.  For development releases, it's the
    +#        last stable version it was based off of.
    +#
    +#   It's stored with the development release date at a higher significance than the version because we want a stable release to
    +#   always treat a development release as higher than itself, and thus not attempt to read any of the data files.  I'm not tracking
    +#   data file formats at the development release level.
    +#
    +
    +#
    +#   About: Binary File Format
    +#
    +#   Current:
    +#
    +#       Five 8-bit unsigned values, appearing major, minor, month, day, year.  Minor is always stored with two digits, so x.2 would
    +#       store 20.  Year is stored minus 2000, so 2006 is stored 6.  Stable releases store zero for all the date fields.
    +#
    +#   1.35 Development Releases:
    +#
    +#       1.35-based development releases are stored the same as current releases, but with 1.36 as the version number.  This is
    +#       done so previous versions of Natural Docs that didn't include the date fields would still know it's a higher version.  There is
    +#       no actual 1.36 release.
    +#
    +#   1.35 and Earlier:
    +#
    +#       Two 8-bit unsigned values, appearing major then minor.  Minor is always stored with two digits, so x.2 would store 20.
    +#
    +
    +#
    +#   About: Text File Format
    +#
    +#   In text files, versions are the <String Format> followed by a native line break.
    +#
    +
    +
    +1;
    diff --git a/vendor/naturaldocs/NaturalDocs b/vendor/naturaldocs/NaturalDocs
    new file mode 100755
    index 000000000..1d523d6bf
    --- /dev/null
    +++ b/vendor/naturaldocs/NaturalDocs
    @@ -0,0 +1,367 @@
    +#!/usr/bin/perl
    +
    +=begin nd
    +
    +    Script: NaturalDocs
    +    ___________________________________________________________________________
    +
    +    Version 1.51
    +
    +    Copyright © 2003-2010 Greg Valure
    +
    +    http://www.naturaldocs.org
    +
    +	Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL).  Refer to the <License> for the
    +	complete details.
    +
    +
    +    Topic: Code Conventions
    +
    +        - Every package function is called with an arrow operator.  It's needed for inheritance in some places, and consistency
    +         when it's not.
    +
    +        - No constant will ever be zero or undef.  Those are reserved so any piece of code can allow a "none of the above" option
    +         and not worry about conflicts with an existing value.
    +
    +        - Existence hashes are hashes where the value doesn't matter.  It acts more as a set, where the existence of the key is
    +         the significant part.
    +
    +
    +    Topic: File Format Conventions
    +
    +        - All integers appear in big-endian format.  So a UInt16 should be handled with a 'n' in pack and unpack, not with a 'S'.
    +
    +        - AString16's are a big-endian UInt16 followed by that many ASCII characters.  A null-terminator is not stored.
    +
    +        - If a higher-level type is described in a file format, that means the loading and saving format is handled by that package.
    +         For example, if you see <SymbolString> in the format, that means <NaturalDocs::SymbolString->ToBinaryFile()> and
    +         <NaturalDocs::SymbolString->FromBinaryFile()> are used to manipulate it, and the underlying format should be treated
    +         as opaque.
    +
    +=cut
    +
    +
    +use strict;
    +use integer;
    +
    +use 5.008;  # When :encoding modifiers were allowed with file access.
    +
    +use English '-no_match_vars';
    +
    +use FindBin;
    +use lib "$FindBin::RealBin/Modules";
    +
    +sub INIT
    +    {
    +    # This function is just here so that when I start the debugger, it doesn't open a new file.  Normally it would jump to an INIT
    +    # function in some other file since that's the first piece of code to execute.
    +    };
    +
    +
    +use NaturalDocs::Constants;
    +use NaturalDocs::Version;
    +use NaturalDocs::File;
    +use NaturalDocs::Error;
    +
    +use NaturalDocs::LineReader;
    +use NaturalDocs::ConfigFile;
    +use NaturalDocs::BinaryFile;
    +use NaturalDocs::StatusMessage;
    +use NaturalDocs::SymbolString;
    +use NaturalDocs::ReferenceString;
    +use NaturalDocs::NDMarkup;
    +
    +use NaturalDocs::Settings;
    +use NaturalDocs::Topics;
    +use NaturalDocs::Languages;
    +use NaturalDocs::Project;
    +use NaturalDocs::Menu;
    +use NaturalDocs::SymbolTable;
    +use NaturalDocs::ClassHierarchy;
    +use NaturalDocs::SourceDB;
    +use NaturalDocs::ImageReferenceTable;
    +use NaturalDocs::Parser;
    +use NaturalDocs::Builder;
    +
    +
    +
    +###############################################################################
    +#
    +#   Group: Basic Types
    +#
    +#   Types used throughout the program.  As Perl is a weakly-typed language unless you box things into objects, these types are
    +#   for documentation purposes and are not enforced.
    +#
    +#
    +#   Type: FileName
    +#
    +#   A string representing the absolute, platform-dependent path to a file.  Relative file paths are no longer in use anywhere in the
    +#   program.  All path manipulation should be done through <NaturalDocs::File>.
    +#
    +#
    +#   Type: VersionInt
    +#
    +#   A comparable integer representing a version number.  Converting them to and from text and binary should be handled by
    +#   <NaturalDocs::Version>.
    +#
    +#
    +#   Type: SymbolString
    +#
    +#   A scalar which encodes a normalized array of identifier strings representing a full or partially-resolved symbol.  All symbols
    +#   must be retrieved from plain text via <NaturalDocs::SymbolString->FromText()> so that the separation and normalization is
    +#   always consistent.  SymbolStrings are comparable via string compare functions and are sortable.
    +#
    +#
    +#   Type: ReferenceString
    +#
    +#   All the information about a reference that makes it unique encoded into a string.  This includes the <SymbolString> of the
    +#   reference, the scope <SymbolString> it appears in, the scope <SymbolStrings> it has access to via "using", and the
    +#   <ReferenceType>.  This is done because if any of those parameters change, it needs to be treated as a completely separate
    +#   reference.
    +#
    +
    +
    +
    +###############################################################################
    +# Group: Support Functions
    +# General functions that are used throughout the program, and that don't really fit anywhere else.
    +
    +
    +#
    +#   Function: StringCompare
    +#
    +#   Compares two strings so that the result is good for proper sorting.  A proper sort orders the characters as
    +#   follows:
    +#
    +#   - End of string.
    +#   - Whitespace.  Line break-tab-space.
    +#   - Symbols, which is anything not included in the other entries.
    +#   - Numbers, 0-9.
    +#   - Letters, case insensitive except to break ties.
    +#
    +#   If you use cmp instead of this function, the result would go by ASCII/Unicode values which would place certain symbols
    +#   between letters and numbers instead of having them all grouped together.  Also, you would have to choose between case
    +#   sensitivity or complete case insensitivity, in which ties are broken arbitrarily.
    +#
    +#   Returns:
    +#
    +#   Like cmp, it returns zero if A and B are equal, a positive value if A is greater than B, and a negative value if A is less than B.
    +#
    +sub StringCompare #(a, b)
    +    {
    +    my ($a, $b) = @_;
    +
    +    if (!defined $a)
    +        {
    +        if (!defined $b)
    +            {  return 0;  }
    +        else
    +            {  return -1;  };
    +        }
    +    elsif (!defined $b)
    +        {
    +        return 1;
    +        };
    +
    +    my $translatedA = lc($a);
    +    my $translatedB = lc($b);
    +
    +    $translatedA =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/;
    +    $translatedB =~ tr/\n\r\t 0-9a-z/\x01\x02\x03\x04\xDB-\xFE/;
    +
    +    my $result = $translatedA cmp $translatedB;
    +
    +    if ($result == 0)
    +        {
    +        # Break the tie by comparing their case.  Lowercase before uppercase.
    +
    +        # If statement just to keep everything theoretically kosher, even though in practice we don't need this.
    +        if (ord('A') > ord('a'))
    +            {  return ($a cmp $b);  }
    +        else
    +            {  return ($b cmp $a);  };
    +        }
    +    else
    +        {  return $result;  };
    +    };
    +
    +
    +#
    +#   Function: ShortenToMatchStrings
    +#
    +#   Compares two arrayrefs and shortens the first array to only contain shared entries.  Assumes all entries are strings.
    +#
    +#   Parameters:
    +#
    +#       sharedArrayRef - The arrayref that will be shortened to only contain common elements.
    +#       compareArrayRef - The arrayref to match.
    +#
    +sub ShortenToMatchStrings #(sharedArrayRef, compareArrayRef)
    +    {
    +    my ($sharedArrayRef, $compareArrayRef) = @_;
    +
    +    my $index = 0;
    +
    +    while ($index < scalar @$sharedArrayRef && $index < scalar @$compareArrayRef &&
    +             $sharedArrayRef->[$index] eq $compareArrayRef->[$index])
    +        {  $index++;  };
    +
    +    if ($index < scalar @$sharedArrayRef)
    +        {  splice(@$sharedArrayRef, $index);  };
    +    };
    +
    +
    +#
    +#   Function: FindFirstSymbol
    +#
    +#   Searches a string for a number of symbols to see which appears first.
    +#
    +#   Parameters:
    +#
    +#       string - The string to search.
    +#       symbols - An arrayref of symbols to look for.
    +#       index - The index to start at, if any.
    +#
    +#   Returns:
    +#
    +#       The array ( index, symbol ).
    +#
    +#       index - The index the first symbol appears at, or -1 if none appear.
    +#       symbol - The symbol that appeared, or undef if none.
    +#
    +sub FindFirstSymbol #(string, symbols, index)
    +    {
    +    my ($string, $symbols, $index) = @_;
    +
    +    if (!defined $index)
    +        {  $index = 0;  };
    +
    +    my $lowestIndex = -1;
    +    my $lowestSymbol;
    +
    +    foreach my $symbol (@$symbols)
    +        {
    +        my $testIndex = index($string, $symbol, $index);
    +
    +        if ($testIndex != -1 && ($lowestIndex == -1 || $testIndex < $lowestIndex))
    +            {
    +            $lowestIndex = $testIndex;
    +            $lowestSymbol = $symbol;
    +            };
    +        };
    +
    +    return ($lowestIndex, $lowestSymbol);
    +    };
    +
    +
    +
    +
    +###############################################################################
    +#
    +#   Main Code
    +#
    +#   The order in which functions are called here is critically important.  Read the "Usage and Dependencies" sections of all the
    +#   packages before even thinking about rearranging these.
    +#
    +
    +
    +eval {
    +
    +    # Check that our required packages are okay.
    +
    +    NaturalDocs::File->CheckCompatibility();
    +
    +
    +    # Almost everything requires Settings to be initialized.
    +
    +    NaturalDocs::Settings->Load();
    +
    +
    +    NaturalDocs::Project->LoadConfigFileInfo();
    +
    +    NaturalDocs::Topics->Load();
    +    NaturalDocs::Languages->Load();
    +
    +
    +    # Migrate from the old file names that were used prior to 1.14.
    +
    +    NaturalDocs::Project->MigrateOldFiles();
    +
    +
    +    if (!NaturalDocs::Settings->IsQuiet())
    +        {  print "Finding files and detecting changes...\n";  };
    +
    +    NaturalDocs::Project->LoadSourceFileInfo();
    +    NaturalDocs::Project->LoadImageFileInfo();
    +
    +    # Register SourceDB extensions.  Order is important.
    +    NaturalDocs::ImageReferenceTable->Register();
    +
    +    NaturalDocs::SymbolTable->Load();
    +    NaturalDocs::ClassHierarchy->Load();
    +    NaturalDocs::SourceDB->Load();
    +
    +    NaturalDocs::SymbolTable->Purge();
    +    NaturalDocs::ClassHierarchy->Purge();
    +    NaturalDocs::SourceDB->PurgeDeletedSourceFiles();
    +
    +
    +    # Parse any supported files that have changed.
    +
    +    my $filesToParse = NaturalDocs::Project->FilesToParse();
    +    my $amount = scalar keys %$filesToParse;
    +
    +    if ($amount > 0)
    +        {
    +        NaturalDocs::StatusMessage->Start('Parsing ' . $amount . ' file' . ($amount > 1 ? 's' : '') . '...', $amount);
    +
    +        foreach my $file (keys %$filesToParse)
    +            {
    +            NaturalDocs::Parser->ParseForInformation($file);
    +            NaturalDocs::StatusMessage->CompletedItem();
    +            };
    +        };
    +
    +
    +    # The symbol table is now fully resolved, so we can reduce its memory footprint.
    +
    +    NaturalDocs::SymbolTable->PurgeResolvingInfo();
    +
    +
    +    # Load and update the menu file.  We need to do this after parsing so when it is updated, it will detect files where the
    +    # default menu title has changed and files that have added or deleted Natural Docs content.
    +
    +    NaturalDocs::Menu->LoadAndUpdate();
    +
    +
    +    # Build any files that need it.  This needs to be run regardless of whether there are any files to build.  It will handle its own
    +    # output messages.
    +
    +    NaturalDocs::Builder->Run();
    +
    +
    +    # Write the changes back to disk.
    +
    +    NaturalDocs::Menu->Save();
    +    NaturalDocs::Project->SaveImageFileInfo();
    +    NaturalDocs::Project->SaveSourceFileInfo();
    +    NaturalDocs::SymbolTable->Save();
    +    NaturalDocs::ClassHierarchy->Save();
    +    NaturalDocs::SourceDB->Save();
    +    NaturalDocs::Settings->Save();
    +    NaturalDocs::Topics->Save();
    +    NaturalDocs::Languages->Save();
    +
    +    # Must be done last.
    +    NaturalDocs::Project->SaveConfigFileInfo();
    +
    +    if (!NaturalDocs::Settings->IsQuiet())
    +        {  print "Done.\n";  };
    +
    +};
    +
    +if ($EVAL_ERROR)  # Oops.
    +    {
    +    NaturalDocs::Error->HandleDeath();
    +    };
    +
    diff --git a/vendor/naturaldocs/NaturalDocs.bat b/vendor/naturaldocs/NaturalDocs.bat
    new file mode 100644
    index 000000000..59e396319
    --- /dev/null
    +++ b/vendor/naturaldocs/NaturalDocs.bat
    @@ -0,0 +1,17 @@
    +@echo off
    +
    +set NaturalDocsParams=
    +
    +rem Shift and loop so we can get more than nine parameters.
    +rem This is especially important if we have spaces in file names.
    +
    +:MORE
    +if "%1"=="" goto NOMORE
    +set NaturalDocsParams=%NaturalDocsParams% %1
    +shift
    +goto MORE
    +:NOMORE
    +
    +perl NaturalDocs %NaturalDocsParams%
    +
    +set NaturalDocsParams=
    \ No newline at end of file
    diff --git a/vendor/naturaldocs/Styles/Default.css b/vendor/naturaldocs/Styles/Default.css
    new file mode 100644
    index 000000000..511703fc4
    --- /dev/null
    +++ b/vendor/naturaldocs/Styles/Default.css
    @@ -0,0 +1,828 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure.
    +   Natural Docs is licensed under version 3 of the GNU Affero General Public
    +   License (AGPL).  Refer to License.txt for the complete details.
    +
    +   This file may be distributed with documentation files generated by Natural Docs.
    +   Such documentation is not covered by Natural Docs' copyright and licensing,
    +   and may have its own copyright and distribution terms as decided by its author.
    +*/
    +
    +body {
    +    font: 10pt Verdana, Arial, sans-serif;
    +    color: #000000;
    +    margin: 0; padding: 0;
    +    }
    +
    +.ContentPage,
    +.IndexPage,
    +.FramedMenuPage {
    +    background-color: #E8E8E8;
    +    }
    +.FramedContentPage,
    +.FramedIndexPage,
    +.FramedSearchResultsPage,
    +.PopupSearchResultsPage {
    +    background-color: #FFFFFF;
    +    }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +img { border: 0;  }
    +
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Opera doesn't break with just wbr, but will if you add this.  */
    +.Opera wbr:after {
    +	content: "\00200B";
    +	}
    +
    +
    +/*  Blockquotes are used as containers for things that may need to scroll.  */
    +blockquote {
    +    padding: 0;
    +    margin: 0;
    +    overflow: auto;
    +    }
    +
    +
    +.Firefox1 blockquote {
    +    padding-bottom: .5em;
    +    }
    +
    +/*  Turn off scrolling when printing.  */
    +@media print {
    +    blockquote {
    +        overflow: visible;
    +        }
    +    .IE blockquote {
    +        width: auto;
    +        }
    +    }
    +
    +
    +
    +#Menu {
    +    font-size: 9pt;
    +    padding: 10px 0 0 0;
    +    }
    +.ContentPage #Menu,
    +.IndexPage #Menu {
    +    position: absolute;
    +    top: 0;
    +    left: 0;
    +    width: 31ex;
    +    overflow: hidden;
    +    }
    +.ContentPage .Firefox #Menu,
    +.IndexPage .Firefox #Menu {
    +    width: 27ex;
    +    }
    +
    +
    +    .MTitle {
    +        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 9pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px;
    +        }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0;
    +        }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px;
    +        }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Firefox .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +    #MSearchPanel {
    +        padding: 0px 6px;
    +        margin: .25em 0;
    +        }
    +
    +
    +    #MSearchField {
    +        font: italic 9pt Verdana, sans-serif;
    +        color: #606060;
    +        background-color: #E8E8E8;
    +        border: none;
    +        padding: 2px 4px;
    +        width: 100%;
    +        }
    +    /* Only Opera gets it right. */
    +    .Firefox #MSearchField,
    +    .IE #MSearchField,
    +    .Safari #MSearchField {
    +        width: 94%;
    +        }
    +    .Opera9 #MSearchField,
    +    .Konqueror #MSearchField {
    +        width: 97%;
    +        }
    +    .FramedMenuPage .Firefox #MSearchField,
    +    .FramedMenuPage .Safari #MSearchField,
    +    .FramedMenuPage .Konqueror #MSearchField {
    +        width: 98%;
    +        }
    +
    +    /* Firefox doesn't do this right in frames without #MSearchPanel added on.
    +        It's presence doesn't hurt anything other browsers. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        padding: 1px 3px;
    +        }
    +    .MSearchPanelActive #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        font-style: normal;
    +        padding: 1px 3px;
    +        }
    +
    +    #MSearchType {
    +        visibility: hidden;
    +        font: 8pt Verdana, sans-serif;
    +        width: 98%;
    +        padding: 0;
    +        border: 1px solid #C0C0C0;
    +        }
    +    .MSearchPanelActive #MSearchType,
    +    /*  As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchType,
    +    #MSearchType:focus {
    +        visibility: visible;
    +        color: #606060;
    +        }
    +    #MSearchType option#MSearchEverything {
    +        font-weight: bold;
    +        }
    +
    +    .Opera8 .MSearchPanelInactive:hover,
    +    .Opera8 .MSearchPanelActive {
    +        margin-left: -1px;
    +        }
    +
    +
    +    iframe#MSearchResults {
    +        width: 60ex;
    +        height: 15em;
    +        }
    +    #MSearchResultsWindow {
    +        display: none;
    +        position: absolute;
    +        left: 0; top: 0;
    +        border: 1px solid #000000;
    +        background-color: #E8E8E8;
    +        }
    +    #MSearchResultsWindowClose {
    +        font-weight: bold;
    +        font-size: 8pt;
    +        display: block;
    +        padding: 2px 5px;
    +        }
    +    #MSearchResultsWindowClose:link,
    +    #MSearchResultsWindowClose:visited {
    +        color: #000000;
    +        text-decoration: none;
    +        }
    +    #MSearchResultsWindowClose:active,
    +    #MSearchResultsWindowClose:hover {
    +        color: #800000;
    +        text-decoration: none;
    +        background-color: #F4F4F4;
    +        }
    +
    +
    +
    +
    +#Content {
    +    padding-bottom: 15px;
    +    }
    +
    +.ContentPage #Content {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    background-color: #FFFFFF;
    +    font-size: 9pt;  /* To make 31ex match the menu's 31ex. */
    +    margin-left: 31ex;
    +    }
    +.ContentPage .Firefox #Content {
    +    margin-left: 27ex;
    +    }
    +
    +
    +
    +    .CTopic {
    +        font-size: 10pt;
    +        margin-bottom: 3em;
    +        }
    +
    +
    +    .CTitle {
    +        font-size: 12pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 16pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 18pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 8pt;
    +        }
    +
    +    .Opera .CToolTip {
    +        max-width: 98%;
    +        }
    +
    +    /*  Scrollbars would be useless.  */
    +    .CToolTip blockquote {
    +        overflow: hidden;
    +        }
    +    .IE6 .CToolTip blockquote {
    +        overflow: visible;
    +        }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 10pt;
    +        margin: 1.5em 0 .5em 0;
    +        }
    +
    +    .CBody pre {
    +        font: 10pt "Courier New", Courier, monospace;
    +	    background-color: #FCFCFC;
    +	    margin: 1em 35px;
    +	    padding: 10px 15px 10px 10px;
    +	    border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4;
    +	    border-width: 1px 1px 1px 6px;
    +	    border-style: dashed dashed dashed solid;
    +        }
    +
    +    .CBody ul {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +        .CDLEntry {
    +            font: 10pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 10pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +    .CTopic img {
    +        text-align: center;
    +        display: block;
    +        margin: 1em auto;
    +        }
    +    .CImageCaption {
    +        font-variant: small-caps;
    +        font-size: 8pt;
    +        color: #808080;
    +        text-align: center;
    +        position: relative;
    +        top: 1em;
    +        }
    +
    +    .CImageLink {
    +        color: #808080;
    +        font-style: italic;
    +        }
    +    a.CImageLink:link,
    +    a.CImageLink:visited,
    +    a.CImageLink:hover { color: #808080 }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 10pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 10pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PDefaultValuePrefix,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +    .PAfterParameters {
    +        vertical-align: bottom;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CClass .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        background-color: #F4F4F4;
    +        }
    +    .CInterface .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0;
    +        background-color: #F4F4FF;
    +        }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype,
    +    .CEnumeration .Prototype {
    +        background-color: #FAF0F0; border-color: #E0B0B0;
    +        }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 12pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* In a frame IE 6 will make them too long unless you set the width to 100%.  Without frames it will be correct without a width
    +        or slightly too long (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  IE 7 has the same
    +        problem with frames, haven't tested it without.  */
    +    .FramedContentPage .IE .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 9pt; width: 100% }
    +
    +    .SEntry {
    +        width: 30% }
    +    .SDescription {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +    .SDescription { padding-left: 2ex }
    +    .SIndent1 .SEntry { padding-left: 1.5ex }   .SIndent1 .SDescription { padding-left: 3.5ex }
    +    .SIndent2 .SEntry { padding-left: 3.0ex }   .SIndent2 .SDescription { padding-left: 5.0ex }
    +    .SIndent3 .SEntry { padding-left: 4.5ex }   .SIndent3 .SDescription { padding-left: 6.5ex }
    +    .SIndent4 .SEntry { padding-left: 6.0ex }   .SIndent4 .SDescription { padding-left: 8.0ex }
    +    .SIndent5 .SEntry { padding-left: 7.5ex }   .SIndent5 .SDescription { padding-left: 9.5ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +    .SGroup td {
    +        padding-top: .5em; padding-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain td,
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        font-size: 10pt;
    +        padding-bottom: .25em }
    +
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        padding-top: 1em }
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold;
    +        }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 10pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Firefox .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +#Index {
    +    background-color: #FFFFFF;
    +    }
    +
    +/*  As opposed to .PopupSearchResultsPage #Index  */
    +.IndexPage #Index,
    +.FramedIndexPage #Index,
    +.FramedSearchResultsPage #Index {
    +    padding: 15px;
    +    }
    +
    +.IndexPage #Index {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    font-size: 9pt;  /* To make 27ex match the menu's 27ex. */
    +    margin-left: 27ex;
    +    }
    +
    +
    +    .IPageTitle {
    +        font-size: 20pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .FramedSearchResultsPage .IPageTitle {
    +        margin-bottom: 15px;
    +        }
    +
    +    .INavigationBar {
    +        font-size: 10pt;
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px;
    +        }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 16pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        font-size: 10pt;
    +        padding-left: 1ex;
    +        }
    +    .PopupSearchResultsPage .IEntry {
    +        font-size: 8pt;
    +        padding: 1px 5px;
    +        }
    +    .PopupSearchResultsPage .Opera9 .IEntry,
    +    .FramedSearchResultsPage .Opera9 .IEntry {
    +        text-align: left;
    +        }
    +    .FramedSearchResultsPage .IEntry {
    +        padding: 0;
    +        }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +    .PopupSearchResultsPage .ISubIndex {
    +        display: none;
    +        }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .IndexPage .ISymbolPrefix,
    +    .FramedIndexPage .ISymbolPrefix {
    +        font-size: 10pt;
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix,
    +    .FramedSearchResultsPage .ISymbolPrefix {
    +        color: #900000;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix {
    +        font-size: 8pt;
    +        }
    +
    +    .IndexPage #IFirstSymbolPrefix,
    +    .FramedIndexPage #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #ILastSymbolPrefix,
    +    .FramedIndexPage #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #IOnlySymbolPrefix,
    +    .FramedIndexPage #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +    .PopupSearchResultsPage .SRStatus {
    +        padding: 2px 5px;
    +        font-size: 8pt;
    +        font-style: italic;
    +        }
    +    .FramedSearchResultsPage .SRStatus {
    +        font-size: 10pt;
    +        font-style: italic;
    +        }
    +
    +    .SRResult {
    +        display: none;
    +        }
    +
    +
    +
    +#Footer {
    +    font-size: 8pt;
    +    color: #989898;
    +    text-align: right;
    +    }
    +
    +#Footer p {
    +    text-indent: 0;
    +    margin-bottom: .5em;
    +    }
    +
    +.ContentPage #Footer,
    +.IndexPage #Footer {
    +    text-align: right;
    +    margin: 2px;
    +    }
    +
    +.FramedMenuPage #Footer {
    +    text-align: center;
    +    margin: 5em 10px 10px 10px;
    +    padding-top: 1em;
    +    border-top: 1px solid #C8C8C8;
    +    }
    +
    +    #Footer a:link,
    +    #Footer a:hover,
    +    #Footer a:visited { color: #989898 }
    +    #Footer a:active { color: #A00000 }
    +
    +
    +
    +.prettyprint .kwd { color: #800000; }  /* keywords */
    +
    +    .prettyprint.PDefaultValue .kwd,
    +    .prettyprint.PDefaultValuePrefix .kwd,
    +    .prettyprint.PTypePrefix .kwd {
    +        color: #C88F8F;
    +        }
    +
    +.prettyprint .com { color: #008000; }  /* comments */
    +
    +    .prettyprint.PDefaultValue .com,
    +    .prettyprint.PDefaultValuePrefix .com,
    +    .prettyprint.PTypePrefix .com {
    +        color: #8FC88F;
    +        }
    +
    +.prettyprint .str { color: #0000B0; }  /* strings */
    +.prettyprint .lit { color: #0000B0; }  /* literals */
    +
    +    .prettyprint.PDefaultValue .str,
    +    .prettyprint.PDefaultValuePrefix .str,
    +    .prettyprint.PTypePrefix .str,
    +    .prettyprint.PDefaultValue .lit,
    +    .prettyprint.PDefaultValuePrefix .lit,
    +    .prettyprint.PTypePrefix .lit {
    +        color: #8F8FC0;
    +        }
    +
    +.prettyprint .typ { color: #000000; }  /* types */
    +.prettyprint .pun { color: #000000; }  /* punctuation */
    +.prettyprint .pln { color: #000000; }  /* punctuation */
    +
    +    .prettyprint.PDefaultValue .typ,
    +    .prettyprint.PDefaultValuePrefix .typ,
    +    .prettyprint.PTypePrefix .typ,
    +    .prettyprint.PDefaultValue .pun,
    +    .prettyprint.PDefaultValuePrefix .pun,
    +    .prettyprint.PTypePrefix .pun,
    +    .prettyprint.PDefaultValue .pln,
    +    .prettyprint.PDefaultValuePrefix .pln,
    +    .prettyprint.PTypePrefix .pln {
    +        color: #8F8F8F;
    +        }
    +
    +.prettyprint .tag { color: #008; }
    +.prettyprint .atn { color: #606; }
    +.prettyprint .atv { color: #080; }
    +.prettyprint .dec { color: #606; }
    +
    diff --git a/vendor/naturaldocs/Styles/Roman.css b/vendor/naturaldocs/Styles/Roman.css
    new file mode 100644
    index 000000000..6c3f0cd72
    --- /dev/null
    +++ b/vendor/naturaldocs/Styles/Roman.css
    @@ -0,0 +1,826 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure.
    +   Natural Docs is licensed under version 3 of the GNU Affero General Public
    +   License (AGPL).  Refer to License.txt for the complete details.
    +
    +   This file may be distributed with documentation files generated by Natural Docs.
    +   Such documentation is not covered by Natural Docs' copyright and licensing,
    +   and may have its own copyright and distribution terms as decided by its author.
    +*/
    +
    +body {
    +    font: 12pt "Times New Roman", Roman, serif;
    +    color: #000000;
    +    margin: 0; padding: 0;
    +    }
    +
    +.ContentPage,
    +.IndexPage,
    +.FramedMenuPage {
    +    background-color: #E8E8E8;
    +    }
    +.FramedContentPage,
    +.FramedIndexPage,
    +.FramedSearchResultsPage,
    +.PopupSearchResultsPage {
    +    background-color: #FFFFFF;
    +    }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +img { border: 0;  }
    +
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Opera doesn't break with just wbr, but will if you add this.  */
    +.Opera wbr:after {
    +	content: "\00200B";
    +	}
    +
    +/*  Blockquotes are used as containers for things that may need to scroll.  */
    +blockquote {
    +    padding: 0;
    +    margin: 0;
    +    overflow: auto;
    +    }
    +
    +
    +.Firefox1 blockquote {
    +    padding-bottom: .5em;
    +    }
    +
    +/*  Turn off scrolling when printing.  */
    +@media print {
    +    blockquote {
    +        overflow: visible;
    +        }
    +    .IE blockquote {
    +        width: auto;
    +        }
    +    }
    +
    +
    +
    +#Menu {
    +    font-size: 10pt;
    +    padding: 10px 0 0 0;
    +    }
    +.ContentPage #Menu,
    +.IndexPage #Menu {
    +    position: absolute;
    +    top: 0;
    +    left: 0;
    +    width: 31ex;
    +    overflow: hidden;
    +    }
    +.ContentPage .Firefox #Menu,
    +.IndexPage .Firefox #Menu {
    +    width: 27ex;
    +    }
    +
    +
    +    .MTitle {
    +        font-size: 18pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 10pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px;
    +        }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0;
    +        }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px;
    +        }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Firefox .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +    #MSearchPanel {
    +        padding: 0px 6px;
    +        margin: .25em 0;
    +        }
    +
    +
    +    #MSearchField {
    +        font: italic 10pt "Times New Roman", Roman, serif;
    +        color: #606060;
    +        background-color: #E8E8E8;
    +        border: none;
    +        padding: 2px 4px;
    +        width: 100%;
    +        }
    +    /* Only Opera gets it right. */
    +    .Firefox #MSearchField,
    +    .IE #MSearchField,
    +    .Safari #MSearchField {
    +        width: 94%;
    +        }
    +    .Opera9 #MSearchField,
    +    .Konqueror #MSearchField {
    +        width: 97%;
    +        }
    +    .FramedMenuPage .Firefox #MSearchField,
    +    .FramedMenuPage .Safari #MSearchField,
    +    .FramedMenuPage .Konqueror #MSearchField {
    +        width: 98%;
    +        }
    +
    +    /* Firefox doesn't do this right in frames without #MSearchPanel added on.
    +        It's presence doesn't hurt anything other browsers. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        padding: 1px 3px;
    +        }
    +    .MSearchPanelActive #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        font-style: normal;
    +        padding: 1px 3px;
    +        }
    +
    +    #MSearchType {
    +        visibility: hidden;
    +        font: 10pt "Times New Roman", Roman, serif;
    +        width: 98%;
    +        padding: 0;
    +        border: 1px solid #C0C0C0;
    +        }
    +    .MSearchPanelActive #MSearchType,
    +    /*  As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchType,
    +    #MSearchType:focus {
    +        visibility: visible;
    +        color: #606060;
    +        }
    +    #MSearchType option#MSearchEverything {
    +        font-weight: bold;
    +        }
    +
    +    .Opera8 .MSearchPanelInactive:hover,
    +    .Opera8 .MSearchPanelActive {
    +        margin-left: -1px;
    +        }
    +
    +
    +    iframe#MSearchResults {
    +        width: 60ex;
    +        height: 15em;
    +        }
    +    #MSearchResultsWindow {
    +        display: none;
    +        position: absolute;
    +        left: 0; top: 0;
    +        border: 1px solid #000000;
    +        background-color: #E8E8E8;
    +        }
    +    #MSearchResultsWindowClose {
    +        font-weight: bold;
    +        font-size: 8pt;
    +        display: block;
    +        padding: 2px 5px;
    +        }
    +    #MSearchResultsWindowClose:link,
    +    #MSearchResultsWindowClose:visited {
    +        color: #000000;
    +        text-decoration: none;
    +        }
    +    #MSearchResultsWindowClose:active,
    +    #MSearchResultsWindowClose:hover {
    +        color: #800000;
    +        text-decoration: none;
    +        background-color: #F4F4F4;
    +        }
    +
    +
    +
    +
    +#Content {
    +    padding-bottom: 15px;
    +    }
    +
    +.ContentPage #Content {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    background-color: #FFFFFF;
    +    font-size: 10pt;  /* To make 31ex match the menu's 31ex. */
    +    margin-left: 31ex;
    +    }
    +.ContentPage .Firefox #Content {
    +    margin-left: 27ex;
    +    }
    +
    +
    +
    +    .CTopic {
    +        font-size: 12pt;
    +        margin-bottom: 3em;
    +        }
    +
    +
    +    .CTitle {
    +        font-size: 16pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 18pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 24pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 10pt;
    +        }
    +
    +    .Opera .CToolTip {
    +        max-width: 98%;
    +        }
    +
    +    /*  Scrollbars would be useless.  */
    +    .CToolTip blockquote {
    +        overflow: hidden;
    +        }
    +    .IE6 .CToolTip blockquote {
    +        overflow: visible;
    +        }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 10pt;
    +        margin: 1.5em 0 .5em 0;
    +        }
    +
    +    .CBody pre {
    +        font: 10pt "Courier New", Courier, monospace;
    +	    background-color: #FCFCFC;
    +	    margin: 1em 35px;
    +	    padding: 10px 15px 10px 10px;
    +	    border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4;
    +	    border-width: 1px 1px 1px 6px;
    +	    border-style: dashed dashed dashed solid;
    +        }
    +
    +    .CBody ul {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +        .CDLEntry {
    +            font: 10pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 12pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +    .CTopic img {
    +        text-align: center;
    +        display: block;
    +        margin: 1em auto;
    +        }
    +    .CImageCaption {
    +        font-variant: small-caps;
    +        font-size: 10pt;
    +        color: #808080;
    +        text-align: center;
    +        position: relative;
    +        top: 1em;
    +        }
    +
    +    .CImageLink {
    +        color: #808080;
    +        font-style: italic;
    +        }
    +    a.CImageLink:link,
    +    a.CImageLink:visited,
    +    a.CImageLink:hover { color: #808080 }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 10pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 10pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PDefaultValuePrefix,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +    .PAfterParameters {
    +        vertical-align: bottom;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CClass .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        background-color: #F4F4F4;
    +        }
    +    .CInterface .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0;
    +        background-color: #F4F4FF;
    +        }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype,
    +    .CEnumeration .Prototype {
    +        background-color: #FAF0F0; border-color: #E0B0B0;
    +        }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 14pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* In a frame IE 6 will make them too long unless you set the width to 100%.  Without frames it will be correct without a width
    +        or slightly too long (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  IE 7 has the same
    +        problem with frames, haven't tested it without.  */
    +    .FramedContentPage .IE .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 10pt; width: 100% }
    +
    +    .SEntry {
    +        width: 30% }
    +    .SDescription {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +    .SDescription { padding-left: 2ex }
    +    .SIndent1 .SEntry { padding-left: 1.5ex }   .SIndent1 .SDescription { padding-left: 3.5ex }
    +    .SIndent2 .SEntry { padding-left: 3.0ex }   .SIndent2 .SDescription { padding-left: 5.0ex }
    +    .SIndent3 .SEntry { padding-left: 4.5ex }   .SIndent3 .SDescription { padding-left: 6.5ex }
    +    .SIndent4 .SEntry { padding-left: 6.0ex }   .SIndent4 .SDescription { padding-left: 8.0ex }
    +    .SIndent5 .SEntry { padding-left: 7.5ex }   .SIndent5 .SDescription { padding-left: 9.5ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +    .SGroup td {
    +        padding-top: .5em; padding-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain td,
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        font-size: 12pt;
    +        padding-bottom: .25em }
    +
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        padding-top: 1em }
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold;
    +        }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 12pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Firefox .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 10pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +#Index {
    +    background-color: #FFFFFF;
    +    }
    +
    +/*  As opposed to .PopupSearchResultsPage #Index  */
    +.IndexPage #Index,
    +.FramedIndexPage #Index,
    +.FramedSearchResultsPage #Index {
    +    padding: 15px;
    +    }
    +
    +.IndexPage #Index {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    font-size: 10pt;  /* To make 27ex match the menu's 27ex. */
    +    margin-left: 27ex;
    +    }
    +
    +
    +    .IPageTitle {
    +        font-size: 24pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .FramedSearchResultsPage .IPageTitle {
    +        margin-bottom: 15px;
    +        }
    +
    +    .INavigationBar {
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px;
    +        }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 20pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        font-size: 12pt;
    +        padding-left: 1ex;
    +        }
    +    .PopupSearchResultsPage .IEntry {
    +        font-size: 10pt;
    +        padding: 1px 5px;
    +        }
    +    .PopupSearchResultsPage .Opera9 .IEntry,
    +    .FramedSearchResultsPage .Opera9 .IEntry {
    +        text-align: left;
    +        }
    +    .FramedSearchResultsPage .IEntry {
    +        padding: 0;
    +        }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +    .PopupSearchResultsPage .ISubIndex {
    +        display: none;
    +        }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .IndexPage .ISymbolPrefix,
    +    .FramedIndexPage .ISymbolPrefix {
    +        font-size: 12pt;
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix,
    +    .FramedSearchResultsPage .ISymbolPrefix {
    +        color: #900000;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix {
    +        font-size: 10pt;
    +        }
    +
    +    .IndexPage #IFirstSymbolPrefix,
    +    .FramedIndexPage #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #ILastSymbolPrefix,
    +    .FramedIndexPage #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #IOnlySymbolPrefix,
    +    .FramedIndexPage #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +    .PopupSearchResultsPage .SRStatus {
    +        padding: 2px 5px;
    +        font-size: 10pt;
    +        font-style: italic;
    +        }
    +    .FramedSearchResultsPage .SRStatus {
    +        font-size: 12pt;
    +        font-style: italic;
    +        }
    +
    +    .SRResult {
    +        display: none;
    +        }
    +
    +
    +
    +#Footer {
    +    font-size: 8pt;
    +    color: #989898;
    +    text-align: right;
    +    }
    +
    +#Footer p {
    +    text-indent: 0;
    +    margin-bottom: .5em;
    +    }
    +
    +.ContentPage #Footer,
    +.IndexPage #Footer {
    +    text-align: right;
    +    margin: 2px;
    +    }
    +
    +.FramedMenuPage #Footer {
    +    text-align: center;
    +    margin: 5em 10px 10px 10px;
    +    padding-top: 1em;
    +    border-top: 1px solid #C8C8C8;
    +    }
    +
    +    #Footer a:link,
    +    #Footer a:hover,
    +    #Footer a:visited { color: #989898 }
    +    #Footer a:active { color: #A00000 }
    +
    +
    +
    +.prettyprint .kwd { color: #800000; }  /* keywords */
    +
    +    .prettyprint.PDefaultValue .kwd,
    +    .prettyprint.PDefaultValuePrefix .kwd,
    +    .prettyprint.PTypePrefix .kwd {
    +        color: #C88F8F;
    +        }
    +
    +.prettyprint .com { color: #008000; }  /* comments */
    +
    +    .prettyprint.PDefaultValue .com,
    +    .prettyprint.PDefaultValuePrefix .com,
    +    .prettyprint.PTypePrefix .com {
    +        color: #8FC88F;
    +        }
    +
    +.prettyprint .str { color: #0000B0; }  /* strings */
    +.prettyprint .lit { color: #0000B0; }  /* literals */
    +
    +    .prettyprint.PDefaultValue .str,
    +    .prettyprint.PDefaultValuePrefix .str,
    +    .prettyprint.PTypePrefix .str,
    +    .prettyprint.PDefaultValue .lit,
    +    .prettyprint.PDefaultValuePrefix .lit,
    +    .prettyprint.PTypePrefix .lit {
    +        color: #8F8FC0;
    +        }
    +
    +.prettyprint .typ { color: #000000; }  /* types */
    +.prettyprint .pun { color: #000000; }  /* punctuation */
    +.prettyprint .pln { color: #000000; }  /* punctuation */
    +
    +    .prettyprint.PDefaultValue .typ,
    +    .prettyprint.PDefaultValuePrefix .typ,
    +    .prettyprint.PTypePrefix .typ,
    +    .prettyprint.PDefaultValue .pun,
    +    .prettyprint.PDefaultValuePrefix .pun,
    +    .prettyprint.PTypePrefix .pun,
    +    .prettyprint.PDefaultValue .pln,
    +    .prettyprint.PDefaultValuePrefix .pln,
    +    .prettyprint.PTypePrefix .pln {
    +        color: #8F8F8F;
    +        }
    +
    +.prettyprint .tag { color: #008; }
    +.prettyprint .atn { color: #606; }
    +.prettyprint .atv { color: #080; }
    +.prettyprint .dec { color: #606; }
    +
    diff --git a/vendor/naturaldocs/Styles/Small.css b/vendor/naturaldocs/Styles/Small.css
    new file mode 100644
    index 000000000..1832d8f39
    --- /dev/null
    +++ b/vendor/naturaldocs/Styles/Small.css
    @@ -0,0 +1,824 @@
    +/*
    +   IMPORTANT: If you're editing this file in the output directory of one of
    +   your projects, your changes will be overwritten the next time you run
    +   Natural Docs.  Instead, copy this file to your project directory, make your
    +   changes, and you can use it with -s.  Even better would be to make a CSS
    +   file in your project directory with only your changes, which you can then
    +   use with -s [original style] [your changes].
    +
    +   On the other hand, if you're editing this file in the Natural Docs styles
    +   directory, the changes will automatically be applied to all your projects
    +   that use this style the next time Natural Docs is run on them.
    +
    +   This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure.
    +   Natural Docs is licensed under version 3 of the GNU Affero General Public
    +   License (AGPL).  Refer to License.txt for the complete details.
    +
    +   This file may be distributed with documentation files generated by Natural Docs.
    +   Such documentation is not covered by Natural Docs' copyright and licensing,
    +   and may have its own copyright and distribution terms as decided by its author.
    +*/
    +
    +body {
    +    font: 8pt Verdana, Arial, sans-serif;
    +    color: #000000;
    +    margin: 0; padding: 0;
    +    }
    +
    +.ContentPage,
    +.IndexPage,
    +.FramedMenuPage {
    +    background-color: #E8E8E8;
    +    }
    +.FramedContentPage,
    +.FramedIndexPage,
    +.FramedSearchResultsPage,
    +.PopupSearchResultsPage {
    +    background-color: #FFFFFF;
    +    }
    +
    +
    +a:link,
    +a:visited { color: #900000; text-decoration: none }
    +a:hover { color: #900000; text-decoration: underline }
    +a:active { color: #FF0000; text-decoration: underline }
    +
    +td {
    +    vertical-align: top }
    +
    +img { border: 0;  }
    +
    +
    +/*
    +    Comment out this line to use web-style paragraphs (blank line between
    +    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    +    indented.)
    +*/
    +p {
    +    text-indent: 5ex; margin: 0 }
    +
    +
    +/*  Opera doesn't break with just wbr, but will if you add this.  */
    +.Opera wbr:after {
    +	content: "\00200B";
    +	}
    +
    +/*  Blockquotes are used as containers for things that may need to scroll.  */
    +blockquote {
    +    padding: 0;
    +    margin: 0;
    +    overflow: auto;
    +    }
    +
    +
    +.Firefox1 blockquote {
    +    padding-bottom: .5em;
    +    }
    +
    +/*  Turn off scrolling when printing.  */
    +@media print {
    +    blockquote {
    +        overflow: visible;
    +        }
    +    .IE blockquote {
    +        width: auto;
    +        }
    +    }
    +
    +
    +
    +#Menu {
    +    font-size: 8pt;
    +    padding: 10px 0 0 0;
    +    }
    +.ContentPage #Menu,
    +.IndexPage #Menu {
    +    position: absolute;
    +    top: 0;
    +    left: 0;
    +    width: 31ex;
    +    overflow: hidden;
    +    }
    +.ContentPage .Firefox #Menu,
    +.IndexPage .Firefox #Menu {
    +    width: 27ex;
    +    }
    +
    +
    +    .MTitle {
    +        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    +        text-align: center;
    +        padding: 5px 10px 15px 10px;
    +        border-bottom: 1px dotted #000000;
    +        margin-bottom: 15px }
    +
    +    .MSubTitle {
    +        font-size: 9pt; font-weight: normal; font-variant: normal;
    +        margin-top: 1ex; margin-bottom: 5px }
    +
    +
    +    .MEntry a:link,
    +    .MEntry a:hover,
    +    .MEntry a:visited { color: #606060; margin-right: 0 }
    +    .MEntry a:active { color: #A00000; margin-right: 0 }
    +
    +
    +    .MGroup {
    +        font-variant: small-caps; font-weight: bold;
    +        margin: 1em 0 1em 10px;
    +        }
    +
    +    .MGroupContent {
    +        font-variant: normal; font-weight: normal }
    +
    +    .MGroup a:link,
    +    .MGroup a:hover,
    +    .MGroup a:visited { color: #545454; margin-right: 10px }
    +    .MGroup a:active { color: #A00000; margin-right: 10px }
    +
    +
    +    .MFile,
    +    .MText,
    +    .MLink,
    +    .MIndex {
    +        padding: 1px 17px 2px 10px;
    +        margin: .25em 0 .25em 0;
    +        }
    +
    +    .MText {
    +        font-size: 8pt; font-style: italic }
    +
    +    .MLink {
    +        font-style: italic }
    +
    +    #MSelected {
    +        color: #000000; background-color: #FFFFFF;
    +        /*  Replace padding with border.  */
    +        padding: 0 10px 0 10px;
    +        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    +        margin-right: 5px;
    +        }
    +
    +    /*  Close off the left side when its in a group.  */
    +    .MGroup #MSelected {
    +        padding-left: 9px; border-left-width: 1px }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox #MSelected {
    +        -moz-border-radius-topright: 10px;
    +        -moz-border-radius-bottomright: 10px }
    +    .Firefox .MGroup #MSelected {
    +        -moz-border-radius-topleft: 10px;
    +        -moz-border-radius-bottomleft: 10px }
    +
    +
    +    #MSearchPanel {
    +        padding: 0px 6px;
    +        margin: .25em 0;
    +        }
    +
    +
    +    #MSearchField {
    +        font: italic 8pt Verdana, sans-serif;
    +        color: #606060;
    +        background-color: #E8E8E8;
    +        border: none;
    +        padding: 2px 4px;
    +        width: 100%;
    +        }
    +    /* Only Opera gets it right. */
    +    .Firefox #MSearchField,
    +    .IE #MSearchField,
    +    .Safari #MSearchField {
    +        width: 94%;
    +        }
    +    .Opera9 #MSearchField,
    +    .Konqueror #MSearchField {
    +        width: 97%;
    +        }
    +    .FramedMenuPage .Firefox #MSearchField,
    +    .FramedMenuPage .Safari #MSearchField,
    +    .FramedMenuPage .Konqueror #MSearchField {
    +        width: 98%;
    +        }
    +
    +    /* Firefox doesn't do this right in frames without #MSearchPanel added on.
    +        It's presence doesn't hurt anything other browsers. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        padding: 1px 3px;
    +        }
    +    .MSearchPanelActive #MSearchField {
    +        background-color: #FFFFFF;
    +        border: 1px solid #C0C0C0;
    +        font-style: normal;
    +        padding: 1px 3px;
    +        }
    +
    +    #MSearchType {
    +        visibility: hidden;
    +        font: 8pt Verdana, sans-serif;
    +        width: 98%;
    +        padding: 0;
    +        border: 1px solid #C0C0C0;
    +        }
    +    .MSearchPanelActive #MSearchType,
    +    /*  As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */
    +    #MSearchPanel.MSearchPanelInactive:hover #MSearchType,
    +    #MSearchType:focus {
    +        visibility: visible;
    +        color: #606060;
    +        }
    +    #MSearchType option#MSearchEverything {
    +        font-weight: bold;
    +        }
    +
    +    .Opera8 .MSearchPanelInactive:hover,
    +    .Opera8 .MSearchPanelActive {
    +        margin-left: -1px;
    +        }
    +
    +
    +    iframe#MSearchResults {
    +        width: 60ex;
    +        height: 15em;
    +        }
    +    #MSearchResultsWindow {
    +        display: none;
    +        position: absolute;
    +        left: 0; top: 0;
    +        border: 1px solid #000000;
    +        background-color: #E8E8E8;
    +        }
    +    #MSearchResultsWindowClose {
    +        font-weight: bold;
    +        font-size: 8pt;
    +        display: block;
    +        padding: 2px 5px;
    +        }
    +    #MSearchResultsWindowClose:link,
    +    #MSearchResultsWindowClose:visited {
    +        color: #000000;
    +        text-decoration: none;
    +        }
    +    #MSearchResultsWindowClose:active,
    +    #MSearchResultsWindowClose:hover {
    +        color: #800000;
    +        text-decoration: none;
    +        background-color: #F4F4F4;
    +        }
    +
    +
    +
    +
    +#Content {
    +    padding-bottom: 15px;
    +    }
    +
    +.ContentPage #Content {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    background-color: #FFFFFF;
    +    font-size: 8pt;  /* To make 31ex match the menu's 31ex. */
    +    margin-left: 31ex;
    +    }
    +.ContentPage .Firefox #Content {
    +    margin-left: 27ex;
    +    }
    +
    +
    +
    +    .CTopic {
    +        font-size: 8pt;
    +        margin-bottom: 3em;
    +        }
    +
    +
    +    .CTitle {
    +        font-size: 11pt; font-weight: bold;
    +        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    +        margin: 0 15px .5em 15px }
    +
    +    .CGroup .CTitle {
    +        font-size: 16pt; font-variant: small-caps;
    +        padding-left: 15px; padding-right: 15px;
    +        border-width: 0 0 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CClass .CTitle,
    +    .CInterface .CTitle,
    +    .CDatabase .CTitle,
    +    .CDatabaseTable .CTitle,
    +    .CSection .CTitle {
    +        font-size: 18pt;
    +        color: #FFFFFF; background-color: #A0A0A0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 2px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    #MainTopic .CTitle {
    +        font-size: 20pt;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000;
    +        margin-left: 0; margin-right: 0 }
    +
    +    .CBody {
    +        margin-left: 15px; margin-right: 15px }
    +
    +
    +    .CToolTip {
    +        position: absolute; visibility: hidden;
    +        left: 0; top: 0;
    +        background-color: #FFFFE0;
    +        padding: 5px;
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    +        font-size: 8pt;
    +        }
    +
    +    .Opera .CToolTip {
    +        max-width: 98%;
    +        }
    +
    +    /*  Scrollbars would be useless.  */
    +    .CToolTip blockquote {
    +        overflow: hidden;
    +        }
    +    .IE6 .CToolTip blockquote {
    +        overflow: visible;
    +        }
    +
    +    .CHeading {
    +        font-weight: bold; font-size: 9pt;
    +        margin: 1.5em 0 .5em 0;
    +        }
    +
    +    .CBody pre {
    +        font: 8pt "Courier New", Courier, monospace;
    +	    background-color: #FCFCFC;
    +	    margin: 1em 35px;
    +	    padding: 10px 15px 10px 10px;
    +	    border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4;
    +	    border-width: 1px 1px 1px 6px;
    +	    border-style: dashed dashed dashed solid;
    +        }
    +
    +    .CBody ul {
    +        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    +             Reapply it here as padding.  */
    +        padding-left: 15px; padding-right: 15px;
    +        margin: .5em 5ex .5em 5ex;
    +        }
    +
    +    .CDescriptionList {
    +        margin: .5em 5ex 0 5ex }
    +
    +        .CDLEntry {
    +            font: 8pt "Courier New", Courier, monospace; color: #808080;
    +            padding-bottom: .25em;
    +            white-space: nowrap }
    +
    +        .CDLDescription {
    +            font-size: 8pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    +            padding-bottom: .5em; padding-left: 5ex }
    +
    +
    +    .CTopic img {
    +        text-align: center;
    +        display: block;
    +        margin: 1em auto;
    +        }
    +    .CImageCaption {
    +        font-variant: small-caps;
    +        font-size: 8pt;
    +        color: #808080;
    +        text-align: center;
    +        position: relative;
    +        top: 1em;
    +        }
    +
    +    .CImageLink {
    +        color: #808080;
    +        font-style: italic;
    +        }
    +    a.CImageLink:link,
    +    a.CImageLink:visited,
    +    a.CImageLink:hover { color: #808080 }
    +
    +
    +
    +
    +
    +.Prototype {
    +    font: 8pt "Courier New", Courier, monospace;
    +    padding: 5px 3ex;
    +    border-width: 1px; border-style: solid;
    +    margin: 0 5ex 1.5em 5ex;
    +    }
    +
    +    .Prototype td {
    +        font-size: 8pt;
    +        }
    +
    +    .PDefaultValue,
    +    .PDefaultValuePrefix,
    +    .PTypePrefix {
    +        color: #8F8F8F;
    +        }
    +    .PTypePrefix {
    +        text-align: right;
    +        }
    +    .PAfterParameters {
    +        vertical-align: bottom;
    +        }
    +
    +    .IE .Prototype table {
    +        padding: 0;
    +        }
    +
    +    .CFunction .Prototype {
    +        background-color: #F4F4F4; border-color: #D0D0D0 }
    +    .CProperty .Prototype {
    +        background-color: #F4F4FF; border-color: #C0C0E8 }
    +    .CVariable .Prototype {
    +        background-color: #FFFFF0; border-color: #E0E0A0 }
    +
    +    .CClass .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        background-color: #F4F4F4;
    +        }
    +    .CInterface .Prototype {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0;
    +        background-color: #F4F4FF;
    +        }
    +
    +    .CDatabaseIndex .Prototype,
    +    .CConstant .Prototype {
    +        background-color: #D0D0D0; border-color: #000000 }
    +    .CType .Prototype,
    +    .CEnumeration .Prototype {
    +        background-color: #FAF0F0; border-color: #E0B0B0;
    +        }
    +    .CDatabaseTrigger .Prototype,
    +    .CEvent .Prototype,
    +    .CDelegate .Prototype {
    +        background-color: #F0FCF0; border-color: #B8E4B8 }
    +
    +    .CToolTip .Prototype {
    +        margin: 0 0 .5em 0;
    +        white-space: nowrap;
    +        }
    +
    +
    +
    +
    +
    +.Summary {
    +    margin: 1.5em 5ex 0 5ex }
    +
    +    .STitle {
    +        font-size: 11pt; font-weight: bold;
    +        margin-bottom: .5em }
    +
    +
    +    .SBorder {
    +        background-color: #FFFFF0;
    +        padding: 15px;
    +        border: 1px solid #C0C060 }
    +
    +    /* In a frame IE 6 will make them too long unless you set the width to 100%.  Without frames it will be correct without a width
    +        or slightly too long (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  IE 7 has the same
    +        problem with frames, haven't tested it without.  */
    +    .FramedContentPage .IE .SBorder {
    +        width: 100% }
    +
    +    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    +    .Firefox .SBorder {
    +        -moz-border-radius: 20px }
    +
    +
    +    .STable {
    +        font-size: 8pt; width: 100% }
    +
    +    .SEntry {
    +        width: 30% }
    +    .SDescription {
    +        width: 70% }
    +
    +
    +    .SMarked {
    +        background-color: #F8F8D8 }
    +
    +    .SDescription { padding-left: 2ex }
    +    .SIndent1 .SEntry { padding-left: 1.5ex }   .SIndent1 .SDescription { padding-left: 3.5ex }
    +    .SIndent2 .SEntry { padding-left: 3.0ex }   .SIndent2 .SDescription { padding-left: 5.0ex }
    +    .SIndent3 .SEntry { padding-left: 4.5ex }   .SIndent3 .SDescription { padding-left: 6.5ex }
    +    .SIndent4 .SEntry { padding-left: 6.0ex }   .SIndent4 .SDescription { padding-left: 8.0ex }
    +    .SIndent5 .SEntry { padding-left: 7.5ex }   .SIndent5 .SDescription { padding-left: 9.5ex }
    +
    +    .SDescription a { color: #800000}
    +    .SDescription a:active { color: #A00000 }
    +
    +    .SGroup td {
    +        padding-top: .5em; padding-bottom: .25em }
    +
    +    .SGroup .SEntry {
    +        font-weight: bold; font-variant: small-caps }
    +
    +    .SGroup .SEntry a { color: #800000 }
    +    .SGroup .SEntry a:active { color: #F00000 }
    +
    +
    +    .SMain td,
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        font-size: 10pt;
    +        padding-bottom: .25em }
    +
    +    .SClass td,
    +    .SDatabase td,
    +    .SDatabaseTable td,
    +    .SSection td {
    +        padding-top: 1em }
    +
    +    .SMain .SEntry,
    +    .SClass .SEntry,
    +    .SDatabase .SEntry,
    +    .SDatabaseTable .SEntry,
    +    .SSection .SEntry {
    +        font-weight: bold;
    +        }
    +
    +    .SMain .SEntry a,
    +    .SClass .SEntry a,
    +    .SDatabase .SEntry a,
    +    .SDatabaseTable .SEntry a,
    +    .SSection .SEntry a { color: #000000 }
    +
    +    .SMain .SEntry a:active,
    +    .SClass .SEntry a:active,
    +    .SDatabase .SEntry a:active,
    +    .SDatabaseTable .SEntry a:active,
    +    .SSection .SEntry a:active { color: #A00000 }
    +
    +
    +
    +
    +
    +.ClassHierarchy {
    +    margin: 0 15px 1em 15px }
    +
    +    .CHEntry {
    +        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    +        margin-bottom: 3px;
    +        padding: 2px 2ex;
    +        font-size: 8pt;
    +        background-color: #F4F4F4; color: #606060;
    +        }
    +
    +    .Firefox .CHEntry {
    +        -moz-border-radius: 4px;
    +        }
    +
    +    .CHCurrent .CHEntry {
    +        font-weight: bold;
    +        border-color: #000000;
    +        color: #000000;
    +        }
    +
    +    .CHChildNote .CHEntry {
    +        font-style: italic;
    +        font-size: 8pt;
    +        }
    +
    +    .CHIndent {
    +        margin-left: 3ex;
    +        }
    +
    +    .CHEntry a:link,
    +    .CHEntry a:visited,
    +    .CHEntry a:hover {
    +        color: #606060;
    +        }
    +    .CHEntry a:active {
    +        color: #800000;
    +        }
    +
    +
    +
    +
    +
    +#Index {
    +    background-color: #FFFFFF;
    +    }
    +
    +/*  As opposed to .PopupSearchResultsPage #Index  */
    +.IndexPage #Index,
    +.FramedIndexPage #Index,
    +.FramedSearchResultsPage #Index {
    +    padding: 15px;
    +    }
    +
    +.IndexPage #Index {
    +    border-width: 0 0 1px 1px;
    +    border-style: solid;
    +    border-color: #000000;
    +    font-size: 8pt;  /* To make 27ex match the menu's 27ex. */
    +    margin-left: 27ex;
    +    }
    +
    +
    +    .IPageTitle {
    +        font-size: 20pt; font-weight: bold;
    +        color: #FFFFFF; background-color: #7070C0;
    +        padding: 10px 15px 10px 15px;
    +        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    +        margin: -15px -15px 0 -15px }
    +
    +    .FramedSearchResultsPage .IPageTitle {
    +        margin-bottom: 15px;
    +        }
    +
    +    .INavigationBar {
    +        text-align: center;
    +        background-color: #FFFFF0;
    +        padding: 5px;
    +        border-bottom: solid 1px black;
    +        margin: 0 -15px 15px -15px;
    +        }
    +
    +    .INavigationBar a {
    +        font-weight: bold }
    +
    +    .IHeading {
    +        font-size: 14pt; font-weight: bold;
    +        padding: 2.5em 0 .5em 0;
    +        text-align: center;
    +        width: 3.5ex;
    +        }
    +    #IFirstHeading {
    +        padding-top: 0;
    +        }
    +
    +    .IEntry {
    +        padding-left: 1ex;
    +        }
    +    .PopupSearchResultsPage .IEntry {
    +        font-size: 8pt;
    +        padding: 1px 5px;
    +        }
    +    .PopupSearchResultsPage .Opera9 .IEntry,
    +    .FramedSearchResultsPage .Opera9 .IEntry {
    +        text-align: left;
    +        }
    +    .FramedSearchResultsPage .IEntry {
    +        padding: 0;
    +        }
    +
    +    .ISubIndex {
    +        padding-left: 3ex; padding-bottom: .5em }
    +    .PopupSearchResultsPage .ISubIndex {
    +        display: none;
    +        }
    +
    +    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    +         index if everything's the same color.  */
    +    .ISymbol {
    +        font-weight: bold; color: #900000  }
    +
    +    .IndexPage .ISymbolPrefix,
    +    .FramedIndexPage .ISymbolPrefix {
    +        text-align: right;
    +        color: #C47C7C;
    +        background-color: #F8F8F8;
    +        border-right: 3px solid #E0E0E0;
    +        border-left: 1px solid #E0E0E0;
    +        padding: 0 1px 0 2px;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix,
    +    .FramedSearchResultsPage .ISymbolPrefix {
    +        color: #900000;
    +        }
    +    .PopupSearchResultsPage .ISymbolPrefix {
    +        font-size: 8pt;
    +        }
    +
    +    .IndexPage #IFirstSymbolPrefix,
    +    .FramedIndexPage #IFirstSymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #ILastSymbolPrefix,
    +    .FramedIndexPage #ILastSymbolPrefix {
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +    .IndexPage #IOnlySymbolPrefix,
    +    .FramedIndexPage #IOnlySymbolPrefix {
    +        border-top: 1px solid #E0E0E0;
    +        border-bottom: 1px solid #E0E0E0;
    +        }
    +
    +    a.IParent,
    +    a.IFile {
    +        display: block;
    +        }
    +
    +    .PopupSearchResultsPage .SRStatus {
    +        padding: 2px 5px;
    +        font-size: 8pt;
    +        font-style: italic;
    +        }
    +    .FramedSearchResultsPage .SRStatus {
    +        font-size: 8pt;
    +        font-style: italic;
    +        }
    +
    +    .SRResult {
    +        display: none;
    +        }
    +
    +
    +
    +#Footer {
    +    font-size: 8pt;
    +    color: #989898;
    +    text-align: right;
    +    }
    +
    +#Footer p {
    +    text-indent: 0;
    +    margin-bottom: .5em;
    +    }
    +
    +.ContentPage #Footer,
    +.IndexPage #Footer {
    +    text-align: right;
    +    margin: 2px;
    +    }
    +
    +.FramedMenuPage #Footer {
    +    text-align: center;
    +    margin: 5em 10px 10px 10px;
    +    padding-top: 1em;
    +    border-top: 1px solid #C8C8C8;
    +    }
    +
    +    #Footer a:link,
    +    #Footer a:hover,
    +    #Footer a:visited { color: #989898 }
    +    #Footer a:active { color: #A00000 }
    +
    +
    +
    +.prettyprint .kwd { color: #800000; }  /* keywords */
    +
    +    .prettyprint.PDefaultValue .kwd,
    +    .prettyprint.PDefaultValuePrefix .kwd,
    +    .prettyprint.PTypePrefix .kwd {
    +        color: #C88F8F;
    +        }
    +
    +.prettyprint .com { color: #008000; }  /* comments */
    +
    +    .prettyprint.PDefaultValue .com,
    +    .prettyprint.PDefaultValuePrefix .com,
    +    .prettyprint.PTypePrefix .com {
    +        color: #8FC88F;
    +        }
    +
    +.prettyprint .str { color: #0000B0; }  /* strings */
    +.prettyprint .lit { color: #0000B0; }  /* literals */
    +
    +    .prettyprint.PDefaultValue .str,
    +    .prettyprint.PDefaultValuePrefix .str,
    +    .prettyprint.PTypePrefix .str,
    +    .prettyprint.PDefaultValue .lit,
    +    .prettyprint.PDefaultValuePrefix .lit,
    +    .prettyprint.PTypePrefix .lit {
    +        color: #8F8FC0;
    +        }
    +
    +.prettyprint .typ { color: #000000; }  /* types */
    +.prettyprint .pun { color: #000000; }  /* punctuation */
    +.prettyprint .pln { color: #000000; }  /* punctuation */
    +
    +    .prettyprint.PDefaultValue .typ,
    +    .prettyprint.PDefaultValuePrefix .typ,
    +    .prettyprint.PTypePrefix .typ,
    +    .prettyprint.PDefaultValue .pun,
    +    .prettyprint.PDefaultValuePrefix .pun,
    +    .prettyprint.PTypePrefix .pun,
    +    .prettyprint.PDefaultValue .pln,
    +    .prettyprint.PDefaultValuePrefix .pln,
    +    .prettyprint.PTypePrefix .pln {
    +        color: #8F8F8F;
    +        }
    +
    +.prettyprint .tag { color: #008; }
    +.prettyprint .atn { color: #606; }
    +.prettyprint .atv { color: #080; }
    +.prettyprint .dec { color: #606; }
    +
    
    From d4b64de70d3dd0d8e656ad5180d5b1be641e30d5 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 31 Mar 2011 21:27:30 -0400
    Subject: [PATCH 09/37] updated natural docs to not flood root folder
    
    ---
     Data/ClassHierarchy.nd      | Bin 175 -> 0 bytes
     Data/ConfigFileInfo.nd      | Bin 26 -> 0 bytes
     Data/FileInfo.nd            |   4 --
     Data/ImageFileInfo.nd       | Bin 8 -> 0 bytes
     Data/ImageReferenceTable.nd | Bin 8 -> 0 bytes
     Data/IndexInfo.nd           | Bin 154 -> 0 bytes
     Data/PreviousMenuState.nd   | Bin 198 -> 0 bytes
     Data/PreviousSettings.nd    | Bin 82 -> 0 bytes
     Data/SymbolTable.nd         | Bin 3366 -> 0 bytes
     Languages.txt               | 113 ------------------------------------
     Makefile                    |   2 +-
     Menu.txt                    |  59 -------------------
     Topics.txt                  |  81 --------------------------
     13 files changed, 1 insertion(+), 258 deletions(-)
     delete mode 100644 Data/ClassHierarchy.nd
     delete mode 100644 Data/ConfigFileInfo.nd
     delete mode 100644 Data/FileInfo.nd
     delete mode 100644 Data/ImageFileInfo.nd
     delete mode 100644 Data/ImageReferenceTable.nd
     delete mode 100644 Data/IndexInfo.nd
     delete mode 100644 Data/PreviousMenuState.nd
     delete mode 100644 Data/PreviousSettings.nd
     delete mode 100644 Data/SymbolTable.nd
     delete mode 100644 Languages.txt
     delete mode 100644 Menu.txt
     delete mode 100644 Topics.txt
    
    diff --git a/Data/ClassHierarchy.nd b/Data/ClassHierarchy.nd
    deleted file mode 100644
    index 162a7f59ba7dafc3838b3409fcf6d0ae91b7181f..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 175
    zcmZQ$G-hC6U}WIS$<NO&Er>5lEi6qfE&+?F=x5~Trs|hu=IW<smgwi@r=)`D%)I2B
    t(v(#Fq@4UDy$qmUcK6H@Cm@F;O&mZ?u0=)pMPOa3_^nC>DFoWZ004&yH6;K5
    
    diff --git a/Data/ConfigFileInfo.nd b/Data/ConfigFileInfo.nd
    deleted file mode 100644
    index 005a6aa2552ad5274c2e779fa4cbc3d58b905311..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 26
    ScmZQ$G-hC6@SQ57jE(_AMFrph
    
    diff --git a/Data/FileInfo.nd b/Data/FileInfo.nd
    deleted file mode 100644
    index 2fe726f5b..000000000
    --- a/Data/FileInfo.nd
    +++ /dev/null
    @@ -1,4 +0,0 @@
    -1.51
    -C/C++
    -/home/tim/git/nodegit/include/blob.h	1301617421	1	GitBlob
    -/home/tim/git/nodegit/include/error.h	1301617768	1	GitError
    diff --git a/Data/ImageFileInfo.nd b/Data/ImageFileInfo.nd
    deleted file mode 100644
    index b6cb43bc50d6a1723dadb0392ba74e8f4c83d110..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    diff --git a/Data/ImageReferenceTable.nd b/Data/ImageReferenceTable.nd
    deleted file mode 100644
    index b6cb43bc50d6a1723dadb0392ba74e8f4c83d110..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    diff --git a/Data/IndexInfo.nd b/Data/IndexInfo.nd
    deleted file mode 100644
    index 5c9e0d47bd933d2811bb5c569d1a54eacdc36058..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 154
    zcmZQ$G-dz-cK6i0)S|>3Ad`_1h=3T#0udlSm}X$$a4XGAF3HT#1L*)sqNxH216743
    c7G)+T<)i}HAidZDt8-3baWPB|tPD*p04LN7jsO4v
    
    diff --git a/Data/PreviousMenuState.nd b/Data/PreviousMenuState.nd
    deleted file mode 100644
    index a83adbe61fa9836006f83e7edae5618d429111f7..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 198
    zcmaKjK@Ng25JkUHq<RkB(uFtBpz#9k3ygG<GELe-^!Bz1Yghl}=e_;r0$>Z&4^CI-
    zJDjxj6Y0d09yw|81C?dz-8_))veVccs2sE*_<-~Of{N7G9jd1c^iepr3x#j8EF<cX
    mgo1Gj_05?WDWd8NC>-vy^ohMhsP~HbeVM?fb6Hf&iueIlAwA*%
    
    diff --git a/Data/PreviousSettings.nd b/Data/PreviousSettings.nd
    deleted file mode 100644
    index 89bac756af185e3036ff5d65ea797af93516539d..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 82
    zcmZQ$G-hC6U;$!AAe7b5$j?pHFUic+PtPpT&&y9q1<{##$vLGdsSJ#Uj0{rPm89e+
    M7c;PUg!uXZ03zrXi2wiq
    
    diff --git a/Data/SymbolTable.nd b/Data/SymbolTable.nd
    deleted file mode 100644
    index 5aebce9574d6ba2fa1b56771498ed3caa5db87dc..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 3366
    zcmcguZBG+H5Z<c1HHs!`qA{3NMB4^wBGDK$M5q)<3>Z-SNVeB4UD)2%-8~S0kiXNJ
    z-Fqcqt~G%VSJLawJ~PiV^UP#rnGkY=Ol=CgE>(xz-PqneT<ck`zbJJQ4M~N(X!g~B
    zH?0^nkA!W8s>|_Hgn^8@yxD<cOMNoki^9MP6_T2<%!+`D(9#RhYFwy8&3`s1JnDsu
    zZ(*GBx1BOASVwX)3s?^EY=gQ83)>-p51E83dR+4`;M6Lb{EDj1F%N8o%u1IZqKUY%
    zrEi`HBr}FQG*(A}r5B?7X{pdM5t{6FgV)<Wr$M9vj&(2>;r&o8QWkcp5?$I;nlc)w
    z!9XN2;iWVRzPmIa3n|wcP6a>*9V(MM1~DNPld{8t6PoaXF!(PXM3yh*2%j9A=Vc_=
    zpRQqfF(V_a(&tDoW(U{-7rw(B8=_k$ulxZ3h-}4d(4JNU_$rG42&XrN-M|#d%t1`H
    z0hSj2twvWAkmJOWR-j%d?krMuI}}zhDSq?vp)zH^jg@_h>13g5r|(x*wtz~>KjOc;
    zPGYOnGJV8toix82-o^4m!TjY;4;!bTh7BMb^y(|H16(d0n|$s!clg;jP(LJ4i_i|3
    zjQA>rpfyhp;$726(EzZRmlP12I&mMg@G6rL3M;3d2QCiwg3^bU4~CLiEN;oEk@oSY
    z9N@298(}O!a`kM-ljU0+ebo!=;+^wJnFGxM1C6Vh|8YT9)B9F?ft6NOCWSd)zM#)^
    zgC>D$*b$(>I(fIrtrIlszE+_;r=6$=(}<o4+jrz^c80((N2+>pJLLN^Ccb1DlO`wE
    z#(S}qMumB8os=3|6|HFOSvP;d)8esrwVll*qQ#k2t~!^GtU{oXhhHUg_R=uC!98;y
    z6o&9R4(b&;j5-or-4C}PnhnVuK8HBGz=x#hz@UGV8f{+7Q+w=e-_P7~rO5j14z^T$
    zyf%t`q{J88qulj*Cek=nK+*CYTbj}=k4Ju9pmQ`ZpEwev^#7w`q9v6XVdkd8;<hs~
    zcN&1)Z)?tr#=KO?*>LG88LvpsQw6yxd#Qw;UL_&#<2j#6c_^#ntoAdph#|-Psyo|q
    zKGBBVqb*>Q96j(BinAESZ9<M@cr3bZ$hMl|UgW)F$ZWq(9`A96TU!r+;F{n!#@)~B
    z1;@I|YfFxW>1#LJl(c(UmDZgTj!|twp2Ss(&GgDTWeZ<v&1Pz;CVMB!#sB>UsUT;D
    
    diff --git a/Languages.txt b/Languages.txt
    deleted file mode 100644
    index 85d5fde47..000000000
    --- a/Languages.txt
    +++ /dev/null
    @@ -1,113 +0,0 @@
    -Format: 1.51
    -
    -# This is the Natural Docs languages file for this project.  If you change
    -# anything here, it will apply to THIS PROJECT ONLY.  If you'd like to change
    -# something for all your projects, edit the Languages.txt in Natural Docs'
    -# Config directory instead.
    -
    -
    -# You can prevent certain file extensions from being scanned like this:
    -# Ignore Extensions: [extension] [extension] ...
    -
    -
    -#-------------------------------------------------------------------------------
    -# SYNTAX:
    -#
    -# Unlike other Natural Docs configuration files, in this file all comments
    -# MUST be alone on a line.  Some languages deal with the # character, so you
    -# cannot put comments on the same line as content.
    -#
    -# Also, all lists are separated with spaces, not commas, again because some
    -# languages may need to use them.
    -#
    -# Language: [name]
    -# Alter Language: [name]
    -#    Defines a new language or alters an existing one.  Its name can use any
    -#    characters.  If any of the properties below have an add/replace form, you
    -#    must use that when using Alter Language.
    -#
    -#    The language Shebang Script is special.  It's entry is only used for
    -#    extensions, and files with those extensions have their shebang (#!) lines
    -#    read to determine the real language of the file.  Extensionless files are
    -#    always treated this way.
    -#
    -#    The language Text File is also special.  It's treated as one big comment
    -#    so you can put Natural Docs content in them without special symbols.  Also,
    -#    if you don't specify a package separator, ignored prefixes, or enum value
    -#    behavior, it will copy those settings from the language that is used most
    -#    in the source tree.
    -#
    -# Extensions: [extension] [extension] ...
    -# [Add/Replace] Extensions: [extension] [extension] ...
    -#    Defines the file extensions of the language's source files.  You can
    -#    redefine extensions found in the main languages file.  You can use * to
    -#    mean any undefined extension.
    -#
    -# Shebang Strings: [string] [string] ...
    -# [Add/Replace] Shebang Strings: [string] [string] ...
    -#    Defines a list of strings that can appear in the shebang (#!) line to
    -#    designate that it's part of the language.  You can redefine strings found
    -#    in the main languages file.
    -#
    -# Ignore Prefixes in Index: [prefix] [prefix] ...
    -# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ...
    -#
    -# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    -# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    -#    Specifies prefixes that should be ignored when sorting symbols in an
    -#    index.  Can be specified in general or for a specific topic type.
    -#
    -#------------------------------------------------------------------------------
    -# For basic language support only:
    -#
    -# Line Comments: [symbol] [symbol] ...
    -#    Defines a space-separated list of symbols that are used for line comments,
    -#    if any.
    -#
    -# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...
    -#    Defines a space-separated list of symbol pairs that are used for block
    -#    comments, if any.
    -#
    -# Package Separator: [symbol]
    -#    Defines the default package separator symbol.  The default is a dot.
    -#
    -# [Topic Type] Prototype Enders: [symbol] [symbol] ...
    -#    When defined, Natural Docs will attempt to get a prototype from the code
    -#    immediately following the topic type.  It stops when it reaches one of
    -#    these symbols.  Use \n for line breaks.
    -#
    -# Line Extender: [symbol]
    -#    Defines the symbol that allows a prototype to span multiple lines if
    -#    normally a line break would end it.
    -#
    -# Enum Values: [global|under type|under parent]
    -#    Defines how enum values are referenced.  The default is global.
    -#    global       - Values are always global, referenced as 'value'.
    -#    under type   - Values are under the enum type, referenced as
    -#               'package.enum.value'.
    -#    under parent - Values are under the enum's parent, referenced as
    -#               'package.value'.
    -#
    -# Perl Package: [perl package]
    -#    Specifies the Perl package used to fine-tune the language behavior in ways
    -#    too complex to do in this file.
    -#
    -#------------------------------------------------------------------------------
    -# For full language support only:
    -#
    -# Full Language Support: [perl package]
    -#    Specifies the Perl package that has the parsing routines necessary for full
    -#    language support.
    -#
    -#-------------------------------------------------------------------------------
    -
    -# The following languages are defined in the main file, if you'd like to alter
    -# them:
    -#
    -#    Text File, Shebang Script, C/C++, C#, Java, JavaScript, Perl, Python,
    -#    PHP, SQL, Visual Basic, Pascal, Assembly, Ada, Tcl, Ruby, Makefile,
    -#    ActionScript, ColdFusion, R, Fortran
    -
    -# If you add a language that you think would be useful to other developers
    -# and should be included in Natural Docs by default, please e-mail it to
    -# languages [at] naturaldocs [dot] org.
    diff --git a/Makefile b/Makefile
    index c421c3cab..4b586d20b 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -43,6 +43,6 @@ lint:
     	@@$(NODE_JS) $(BASE)/util/hint-check.js
     
     doc:
    -	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/docs -p $(BASE) -s $(BASE)/Theme
    +	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/docs -p $(BASE)/docs -s $(BASE)/../Theme
     
     .PHONY: test build
    diff --git a/Menu.txt b/Menu.txt
    deleted file mode 100644
    index 3b7b30960..000000000
    --- a/Menu.txt
    +++ /dev/null
    @@ -1,59 +0,0 @@
    -Format: 1.51
    -
    -
    -# You can add a title and sub-title to your menu like this:
    -# Title: [project name]
    -# SubTitle: [subtitle]
    -
    -# You can add a footer to your documentation like this:
    -# Footer: [text]
    -# If you want to add a copyright notice, this would be the place to do it.
    -
    -# You can add a timestamp to your documentation like one of these:
    -# Timestamp: Generated on month day, year
    -# Timestamp: Updated mm/dd/yyyy
    -# Timestamp: Last updated mon day
    -#
    -#   m     - One or two digit month.  January is "1"
    -#   mm    - Always two digit month.  January is "01"
    -#   mon   - Short month word.  January is "Jan"
    -#   month - Long month word.  January is "January"
    -#   d     - One or two digit day.  1 is "1"
    -#   dd    - Always two digit day.  1 is "01"
    -#   day   - Day with letter extension.  1 is "1st"
    -#   yy    - Two digit year.  2006 is "06"
    -#   yyyy  - Four digit year.  2006 is "2006"
    -#   year  - Four digit year.  2006 is "2006"
    -
    -
    -# --------------------------------------------------------------------------
    -# 
    -# Cut and paste the lines below to change the order in which your files
    -# appear on the menu.  Don't worry about adding or removing files, Natural
    -# Docs will take care of that.
    -# 
    -# You can further organize the menu by grouping the entries.  Add a
    -# "Group: [name] {" line to start a group, and add a "}" to end it.
    -# 
    -# You can add text and web links to the menu by adding "Text: [text]" and
    -# "Link: [name] ([URL])" lines, respectively.
    -# 
    -# The formatting and comments are auto-generated, so don't worry about
    -# neatness when editing the file.  Natural Docs will clean it up the next
    -# time it is run.  When working with groups, just deal with the braces and
    -# forget about the indentation and comments.
    -# 
    -# --------------------------------------------------------------------------
    -
    -
    -File: GitBlob  (blob.h)
    -File: GitError  (error.h)
    -
    -Group: Index  {
    -
    -   Index: Everything
    -   Class Index: Classes
    -   Function Index: Functions
    -   Variable Index: Variables
    -   }  # Group: Index
    -
    diff --git a/Topics.txt b/Topics.txt
    deleted file mode 100644
    index 21530908d..000000000
    --- a/Topics.txt
    +++ /dev/null
    @@ -1,81 +0,0 @@
    -Format: 1.51
    -
    -# This is the Natural Docs topics file for this project.  If you change anything
    -# here, it will apply to THIS PROJECT ONLY.  If you'd like to change something
    -# for all your projects, edit the Topics.txt in Natural Docs' Config directory
    -# instead.
    -
    -
    -# If you'd like to prevent keywords from being recognized by Natural Docs, you
    -# can do it like this:
    -# Ignore Keywords: [keyword], [keyword], ...
    -#
    -# Or you can use the list syntax like how they are defined:
    -# Ignore Keywords:
    -#    [keyword]
    -#    [keyword], [plural keyword]
    -#    ...
    -
    -
    -#-------------------------------------------------------------------------------
    -# SYNTAX:
    -#
    -# Topic Type: [name]
    -# Alter Topic Type: [name]
    -#    Creates a new topic type or alters one from the main file.  Each type gets
    -#    its own index and behavior settings.  Its name can have letters, numbers,
    -#    spaces, and these charaters: - / . '
    -#
    -# Plural: [name]
    -#    Sets the plural name of the topic type, if different.
    -#
    -# Keywords:
    -#    [keyword]
    -#    [keyword], [plural keyword]
    -#    ...
    -#    Defines or adds to the list of keywords for the topic type.  They may only
    -#    contain letters, numbers, and spaces and are not case sensitive.  Plural
    -#    keywords are used for list topics.  You can redefine keywords found in the
    -#    main topics file.
    -#
    -# Index: [yes|no]
    -#    Whether the topics get their own index.  Defaults to yes.  Everything is
    -#    included in the general index regardless of this setting.
    -#
    -# Scope: [normal|start|end|always global]
    -#    How the topics affects scope.  Defaults to normal.
    -#    normal        - Topics stay within the current scope.
    -#    start         - Topics start a new scope for all the topics beneath it,
    -#                    like class topics.
    -#    end           - Topics reset the scope back to global for all the topics
    -#                    beneath it.
    -#    always global - Topics are defined as global, but do not change the scope
    -#                    for any other topics.
    -#
    -# Class Hierarchy: [yes|no]
    -#    Whether the topics are part of the class hierarchy.  Defaults to no.
    -#
    -# Page Title If First: [yes|no]
    -#    Whether the topic's title becomes the page title if it's the first one in
    -#    a file.  Defaults to no.
    -#
    -# Break Lists: [yes|no]
    -#    Whether list topics should be broken into individual topics in the output.
    -#    Defaults to no.
    -#
    -# Can Group With: [type], [type], ...
    -#    Defines a list of topic types that this one can possibly be grouped with.
    -#    Defaults to none.
    -#-------------------------------------------------------------------------------
    -
    -# The following topics are defined in the main file, if you'd like to alter
    -# their behavior or add keywords:
    -#
    -#    Generic, Class, Interface, Section, File, Group, Function, Variable,
    -#    Property, Type, Constant, Enumeration, Event, Delegate, Macro,
    -#    Database, Database Table, Database View, Database Index, Database
    -#    Cursor, Database Trigger, Cookie, Build Target
    -
    -# If you add something that you think would be useful to other developers
    -# and should be included in Natural Docs by default, please e-mail it to
    -# topics [at] naturaldocs [dot] org.
    
    From ef0f26bf09c51c935d5e2f0bb6f39a25de812199 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 31 Mar 2011 21:32:22 -0400
    Subject: [PATCH 10/37] updated makefile and renamed docs folder to doc updated
     readme
    
    ---
     Makefile                               |   4 +-
     README.md                              |  11 +++
     doc/Data/ClassHierarchy.nd             | Bin 0 -> 175 bytes
     doc/Data/ConfigFileInfo.nd             | Bin 0 -> 26 bytes
     doc/Data/FileInfo.nd                   |   4 +
     doc/Data/ImageFileInfo.nd              | Bin 0 -> 8 bytes
     doc/Data/ImageReferenceTable.nd        | Bin 0 -> 8 bytes
     doc/Data/IndexInfo.nd                  | Bin 0 -> 154 bytes
     doc/Data/PreviousMenuState.nd          | Bin 0 -> 198 bytes
     doc/Data/PreviousSettings.nd           | Bin 0 -> 81 bytes
     doc/Data/SymbolTable.nd                | Bin 0 -> 3366 bytes
     doc/Languages.txt                      | 113 +++++++++++++++++++++++++
     doc/Menu.txt                           |  59 +++++++++++++
     doc/Topics.txt                         |  81 ++++++++++++++++++
     {docs => doc}/api/commit.html          |   0
     {docs => doc}/api/docco.css            |   0
     {docs => doc}/api/error.html           |   0
     {docs => doc}/api/index.html           |   0
     {docs => doc}/api/oid.html             |   0
     {docs => doc}/api/ref.html             |   0
     {docs => doc}/api/repo.html            |   0
     {docs => doc}/api/revwalk.html         |   0
     {docs => doc}/api/sig.html             |   0
     {docs => doc}/api/tree.html            |   0
     {docs => doc}/api/util.html            |   0
     {docs => doc}/files/blob-h.html        |   0
     {docs => doc}/files/error-h.html       |   0
     {docs => doc}/index.html               |   0
     {docs => doc}/index/Classes.html       |   0
     {docs => doc}/index/Functions.html     |   0
     {docs => doc}/index/General.html       |   0
     {docs => doc}/index/Variables.html     |   0
     {docs => doc}/javascript/main.js       |   0
     {docs => doc}/javascript/prettify.js   |   0
     {docs => doc}/javascript/searchdata.js |   0
     {docs => doc}/search/ClassesG.html     |   0
     {docs => doc}/search/ClassesL.html     |   0
     {docs => doc}/search/FunctionsC.html   |   0
     {docs => doc}/search/FunctionsE.html   |   0
     {docs => doc}/search/FunctionsG.html   |   0
     {docs => doc}/search/FunctionsI.html   |   0
     {docs => doc}/search/FunctionsL.html   |   0
     {docs => doc}/search/FunctionsN.html   |   0
     {docs => doc}/search/FunctionsR.html   |   0
     {docs => doc}/search/FunctionsS.html   |   0
     {docs => doc}/search/GeneralB.html     |   0
     {docs => doc}/search/GeneralC.html     |   0
     {docs => doc}/search/GeneralE.html     |   0
     {docs => doc}/search/GeneralF.html     |   0
     {docs => doc}/search/GeneralG.html     |   0
     {docs => doc}/search/GeneralI.html     |   0
     {docs => doc}/search/GeneralL.html     |   0
     {docs => doc}/search/GeneralN.html     |   0
     {docs => doc}/search/GeneralR.html     |   0
     {docs => doc}/search/GeneralS.html     |   0
     {docs => doc}/search/GeneralV.html     |   0
     {docs => doc}/search/NoResults.html    |   0
     {docs => doc}/search/VariablesB.html   |   0
     {docs => doc}/search/VariablesC.html   |   0
     {docs => doc}/styles/main.css          |   0
     60 files changed, 270 insertions(+), 2 deletions(-)
     create mode 100644 doc/Data/ClassHierarchy.nd
     create mode 100644 doc/Data/ConfigFileInfo.nd
     create mode 100644 doc/Data/FileInfo.nd
     create mode 100644 doc/Data/ImageFileInfo.nd
     create mode 100644 doc/Data/ImageReferenceTable.nd
     create mode 100644 doc/Data/IndexInfo.nd
     create mode 100644 doc/Data/PreviousMenuState.nd
     create mode 100644 doc/Data/PreviousSettings.nd
     create mode 100644 doc/Data/SymbolTable.nd
     create mode 100644 doc/Languages.txt
     create mode 100644 doc/Menu.txt
     create mode 100644 doc/Topics.txt
     rename {docs => doc}/api/commit.html (100%)
     rename {docs => doc}/api/docco.css (100%)
     rename {docs => doc}/api/error.html (100%)
     rename {docs => doc}/api/index.html (100%)
     rename {docs => doc}/api/oid.html (100%)
     rename {docs => doc}/api/ref.html (100%)
     rename {docs => doc}/api/repo.html (100%)
     rename {docs => doc}/api/revwalk.html (100%)
     rename {docs => doc}/api/sig.html (100%)
     rename {docs => doc}/api/tree.html (100%)
     rename {docs => doc}/api/util.html (100%)
     rename {docs => doc}/files/blob-h.html (100%)
     rename {docs => doc}/files/error-h.html (100%)
     rename {docs => doc}/index.html (100%)
     rename {docs => doc}/index/Classes.html (100%)
     rename {docs => doc}/index/Functions.html (100%)
     rename {docs => doc}/index/General.html (100%)
     rename {docs => doc}/index/Variables.html (100%)
     rename {docs => doc}/javascript/main.js (100%)
     rename {docs => doc}/javascript/prettify.js (100%)
     rename {docs => doc}/javascript/searchdata.js (100%)
     rename {docs => doc}/search/ClassesG.html (100%)
     rename {docs => doc}/search/ClassesL.html (100%)
     rename {docs => doc}/search/FunctionsC.html (100%)
     rename {docs => doc}/search/FunctionsE.html (100%)
     rename {docs => doc}/search/FunctionsG.html (100%)
     rename {docs => doc}/search/FunctionsI.html (100%)
     rename {docs => doc}/search/FunctionsL.html (100%)
     rename {docs => doc}/search/FunctionsN.html (100%)
     rename {docs => doc}/search/FunctionsR.html (100%)
     rename {docs => doc}/search/FunctionsS.html (100%)
     rename {docs => doc}/search/GeneralB.html (100%)
     rename {docs => doc}/search/GeneralC.html (100%)
     rename {docs => doc}/search/GeneralE.html (100%)
     rename {docs => doc}/search/GeneralF.html (100%)
     rename {docs => doc}/search/GeneralG.html (100%)
     rename {docs => doc}/search/GeneralI.html (100%)
     rename {docs => doc}/search/GeneralL.html (100%)
     rename {docs => doc}/search/GeneralN.html (100%)
     rename {docs => doc}/search/GeneralR.html (100%)
     rename {docs => doc}/search/GeneralS.html (100%)
     rename {docs => doc}/search/GeneralV.html (100%)
     rename {docs => doc}/search/NoResults.html (100%)
     rename {docs => doc}/search/VariablesB.html (100%)
     rename {docs => doc}/search/VariablesC.html (100%)
     rename {docs => doc}/styles/main.css (100%)
    
    diff --git a/Makefile b/Makefile
    index 4b586d20b..88f0fe029 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -43,6 +43,6 @@ lint:
     	@@$(NODE_JS) $(BASE)/util/hint-check.js
     
     doc:
    -	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/docs -p $(BASE)/docs -s $(BASE)/../Theme
    +	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/doc -p $(BASE)/doc -s $(BASE)/../Theme
     
    -.PHONY: test build
    +.PHONY: test build doc
    diff --git a/README.md b/README.md
    index 1d67cf173..1248a2c25 100644
    --- a/README.md
    +++ b/README.md
    @@ -184,6 +184,17 @@ If they are not, `cd` into the `nodegit` dir and run the following `git` command
     
     Then simply run `make test` in the project root.
     
    +Generating documentation
    +------------------------
    +
    +__ `nodegit` native and library code is documented to be built with `Natural Docs`. __
    +
    +To create the documentation, `cd` into the `nodegit` dir and run the following:
    +    $ cd nodegit
    +    $ make doc
    +
    +The documentation will then generate in the `doc/` subfolder as HTML.
    +
     Release information
     -------------------
     
    diff --git a/doc/Data/ClassHierarchy.nd b/doc/Data/ClassHierarchy.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..162a7f59ba7dafc3838b3409fcf6d0ae91b7181f
    GIT binary patch
    literal 175
    zcmZQ$G-hC6U}WIS$<NO&Er>5lEi6qfE&+?F=x5~Trs|hu=IW<smgwi@r=)`D%)I2B
    t(v(#Fq@4UDy$qmUcK6H@Cm@F;O&mZ?u0=)pMPOa3_^nC>DFoWZ004&yH6;K5
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/ConfigFileInfo.nd b/doc/Data/ConfigFileInfo.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..ebae7c4189d0e855d2ea0e42a0a4cca47024e9d7
    GIT binary patch
    literal 26
    XcmZQ$G-hC6@SUp7;X74E85sirLWl*_
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/FileInfo.nd b/doc/Data/FileInfo.nd
    new file mode 100644
    index 000000000..f8470c7df
    --- /dev/null
    +++ b/doc/Data/FileInfo.nd
    @@ -0,0 +1,4 @@
    +1.51
    +C/C++
    +/home/tim/git/nodegit/include/blob.h	1301620615	1	GitBlob
    +/home/tim/git/nodegit/include/error.h	1301620603	1	GitError
    diff --git a/doc/Data/ImageFileInfo.nd b/doc/Data/ImageFileInfo.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..b6cb43bc50d6a1723dadb0392ba74e8f4c83d110
    GIT binary patch
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/ImageReferenceTable.nd b/doc/Data/ImageReferenceTable.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..b6cb43bc50d6a1723dadb0392ba74e8f4c83d110
    GIT binary patch
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/IndexInfo.nd b/doc/Data/IndexInfo.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..5c9e0d47bd933d2811bb5c569d1a54eacdc36058
    GIT binary patch
    literal 154
    zcmZQ$G-dz-cK6i0)S|>3Ad`_1h=3T#0udlSm}X$$a4XGAF3HT#1L*)sqNxH216743
    c7G)+T<)i}HAidZDt8-3baWPB|tPD*p04LN7jsO4v
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/PreviousMenuState.nd b/doc/Data/PreviousMenuState.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..a83adbe61fa9836006f83e7edae5618d429111f7
    GIT binary patch
    literal 198
    zcmaKjK@Ng25JkUHq<RkB(uFtBpz#9k3ygG<GELe-^!Bz1Yghl}=e_;r0$>Z&4^CI-
    zJDjxj6Y0d09yw|81C?dz-8_))veVccs2sE*_<-~Of{N7G9jd1c^iepr3x#j8EF<cX
    mgo1Gj_05?WDWd8NC>-vy^ohMhsP~HbeVM?fb6Hf&iueIlAwA*%
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/PreviousSettings.nd b/doc/Data/PreviousSettings.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..199e3d0ef315b9d49a2f4dfa290949385545bb35
    GIT binary patch
    literal 81
    zcmZQ$G-hC6U;$!AAe7b5$j?pHFUic+PtPpT&&y9q1<{##$vLGdsSJ#Uj0}?4m89e+
    LGq8As`1$|<3dR-{
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Data/SymbolTable.nd b/doc/Data/SymbolTable.nd
    new file mode 100644
    index 0000000000000000000000000000000000000000..5aebce9574d6ba2fa1b56771498ed3caa5db87dc
    GIT binary patch
    literal 3366
    zcmcguZBG+H5Z<c1HHs!`qA{3NMB4^wBGDK$M5q)<3>Z-SNVeB4UD)2%-8~S0kiXNJ
    z-Fqcqt~G%VSJLawJ~PiV^UP#rnGkY=Ol=CgE>(xz-PqneT<ck`zbJJQ4M~N(X!g~B
    zH?0^nkA!W8s>|_Hgn^8@yxD<cOMNoki^9MP6_T2<%!+`D(9#RhYFwy8&3`s1JnDsu
    zZ(*GBx1BOASVwX)3s?^EY=gQ83)>-p51E83dR+4`;M6Lb{EDj1F%N8o%u1IZqKUY%
    zrEi`HBr}FQG*(A}r5B?7X{pdM5t{6FgV)<Wr$M9vj&(2>;r&o8QWkcp5?$I;nlc)w
    z!9XN2;iWVRzPmIa3n|wcP6a>*9V(MM1~DNPld{8t6PoaXF!(PXM3yh*2%j9A=Vc_=
    zpRQqfF(V_a(&tDoW(U{-7rw(B8=_k$ulxZ3h-}4d(4JNU_$rG42&XrN-M|#d%t1`H
    z0hSj2twvWAkmJOWR-j%d?krMuI}}zhDSq?vp)zH^jg@_h>13g5r|(x*wtz~>KjOc;
    zPGYOnGJV8toix82-o^4m!TjY;4;!bTh7BMb^y(|H16(d0n|$s!clg;jP(LJ4i_i|3
    zjQA>rpfyhp;$726(EzZRmlP12I&mMg@G6rL3M;3d2QCiwg3^bU4~CLiEN;oEk@oSY
    z9N@298(}O!a`kM-ljU0+ebo!=;+^wJnFGxM1C6Vh|8YT9)B9F?ft6NOCWSd)zM#)^
    zgC>D$*b$(>I(fIrtrIlszE+_;r=6$=(}<o4+jrz^c80((N2+>pJLLN^Ccb1DlO`wE
    z#(S}qMumB8os=3|6|HFOSvP;d)8esrwVll*qQ#k2t~!^GtU{oXhhHUg_R=uC!98;y
    z6o&9R4(b&;j5-or-4C}PnhnVuK8HBGz=x#hz@UGV8f{+7Q+w=e-_P7~rO5j14z^T$
    zyf%t`q{J88qulj*Cek=nK+*CYTbj}=k4Ju9pmQ`ZpEwev^#7w`q9v6XVdkd8;<hs~
    zcN&1)Z)?tr#=KO?*>LG88LvpsQw6yxd#Qw;UL_&#<2j#6c_^#ntoAdph#|-Psyo|q
    zKGBBVqb*>Q96j(BinAESZ9<M@cr3bZ$hMl|UgW)F$ZWq(9`A96TU!r+;F{n!#@)~B
    z1;@I|YfFxW>1#LJl(c(UmDZgTj!|twp2Ss(&GgDTWeZ<v&1Pz;CVMB!#sB>UsUT;D
    
    literal 0
    HcmV?d00001
    
    diff --git a/doc/Languages.txt b/doc/Languages.txt
    new file mode 100644
    index 000000000..85d5fde47
    --- /dev/null
    +++ b/doc/Languages.txt
    @@ -0,0 +1,113 @@
    +Format: 1.51
    +
    +# This is the Natural Docs languages file for this project.  If you change
    +# anything here, it will apply to THIS PROJECT ONLY.  If you'd like to change
    +# something for all your projects, edit the Languages.txt in Natural Docs'
    +# Config directory instead.
    +
    +
    +# You can prevent certain file extensions from being scanned like this:
    +# Ignore Extensions: [extension] [extension] ...
    +
    +
    +#-------------------------------------------------------------------------------
    +# SYNTAX:
    +#
    +# Unlike other Natural Docs configuration files, in this file all comments
    +# MUST be alone on a line.  Some languages deal with the # character, so you
    +# cannot put comments on the same line as content.
    +#
    +# Also, all lists are separated with spaces, not commas, again because some
    +# languages may need to use them.
    +#
    +# Language: [name]
    +# Alter Language: [name]
    +#    Defines a new language or alters an existing one.  Its name can use any
    +#    characters.  If any of the properties below have an add/replace form, you
    +#    must use that when using Alter Language.
    +#
    +#    The language Shebang Script is special.  It's entry is only used for
    +#    extensions, and files with those extensions have their shebang (#!) lines
    +#    read to determine the real language of the file.  Extensionless files are
    +#    always treated this way.
    +#
    +#    The language Text File is also special.  It's treated as one big comment
    +#    so you can put Natural Docs content in them without special symbols.  Also,
    +#    if you don't specify a package separator, ignored prefixes, or enum value
    +#    behavior, it will copy those settings from the language that is used most
    +#    in the source tree.
    +#
    +# Extensions: [extension] [extension] ...
    +# [Add/Replace] Extensions: [extension] [extension] ...
    +#    Defines the file extensions of the language's source files.  You can
    +#    redefine extensions found in the main languages file.  You can use * to
    +#    mean any undefined extension.
    +#
    +# Shebang Strings: [string] [string] ...
    +# [Add/Replace] Shebang Strings: [string] [string] ...
    +#    Defines a list of strings that can appear in the shebang (#!) line to
    +#    designate that it's part of the language.  You can redefine strings found
    +#    in the main languages file.
    +#
    +# Ignore Prefixes in Index: [prefix] [prefix] ...
    +# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ...
    +#
    +# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    +# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    +#    Specifies prefixes that should be ignored when sorting symbols in an
    +#    index.  Can be specified in general or for a specific topic type.
    +#
    +#------------------------------------------------------------------------------
    +# For basic language support only:
    +#
    +# Line Comments: [symbol] [symbol] ...
    +#    Defines a space-separated list of symbols that are used for line comments,
    +#    if any.
    +#
    +# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...
    +#    Defines a space-separated list of symbol pairs that are used for block
    +#    comments, if any.
    +#
    +# Package Separator: [symbol]
    +#    Defines the default package separator symbol.  The default is a dot.
    +#
    +# [Topic Type] Prototype Enders: [symbol] [symbol] ...
    +#    When defined, Natural Docs will attempt to get a prototype from the code
    +#    immediately following the topic type.  It stops when it reaches one of
    +#    these symbols.  Use \n for line breaks.
    +#
    +# Line Extender: [symbol]
    +#    Defines the symbol that allows a prototype to span multiple lines if
    +#    normally a line break would end it.
    +#
    +# Enum Values: [global|under type|under parent]
    +#    Defines how enum values are referenced.  The default is global.
    +#    global       - Values are always global, referenced as 'value'.
    +#    under type   - Values are under the enum type, referenced as
    +#               'package.enum.value'.
    +#    under parent - Values are under the enum's parent, referenced as
    +#               'package.value'.
    +#
    +# Perl Package: [perl package]
    +#    Specifies the Perl package used to fine-tune the language behavior in ways
    +#    too complex to do in this file.
    +#
    +#------------------------------------------------------------------------------
    +# For full language support only:
    +#
    +# Full Language Support: [perl package]
    +#    Specifies the Perl package that has the parsing routines necessary for full
    +#    language support.
    +#
    +#-------------------------------------------------------------------------------
    +
    +# The following languages are defined in the main file, if you'd like to alter
    +# them:
    +#
    +#    Text File, Shebang Script, C/C++, C#, Java, JavaScript, Perl, Python,
    +#    PHP, SQL, Visual Basic, Pascal, Assembly, Ada, Tcl, Ruby, Makefile,
    +#    ActionScript, ColdFusion, R, Fortran
    +
    +# If you add a language that you think would be useful to other developers
    +# and should be included in Natural Docs by default, please e-mail it to
    +# languages [at] naturaldocs [dot] org.
    diff --git a/doc/Menu.txt b/doc/Menu.txt
    new file mode 100644
    index 000000000..3b7b30960
    --- /dev/null
    +++ b/doc/Menu.txt
    @@ -0,0 +1,59 @@
    +Format: 1.51
    +
    +
    +# You can add a title and sub-title to your menu like this:
    +# Title: [project name]
    +# SubTitle: [subtitle]
    +
    +# You can add a footer to your documentation like this:
    +# Footer: [text]
    +# If you want to add a copyright notice, this would be the place to do it.
    +
    +# You can add a timestamp to your documentation like one of these:
    +# Timestamp: Generated on month day, year
    +# Timestamp: Updated mm/dd/yyyy
    +# Timestamp: Last updated mon day
    +#
    +#   m     - One or two digit month.  January is "1"
    +#   mm    - Always two digit month.  January is "01"
    +#   mon   - Short month word.  January is "Jan"
    +#   month - Long month word.  January is "January"
    +#   d     - One or two digit day.  1 is "1"
    +#   dd    - Always two digit day.  1 is "01"
    +#   day   - Day with letter extension.  1 is "1st"
    +#   yy    - Two digit year.  2006 is "06"
    +#   yyyy  - Four digit year.  2006 is "2006"
    +#   year  - Four digit year.  2006 is "2006"
    +
    +
    +# --------------------------------------------------------------------------
    +# 
    +# Cut and paste the lines below to change the order in which your files
    +# appear on the menu.  Don't worry about adding or removing files, Natural
    +# Docs will take care of that.
    +# 
    +# You can further organize the menu by grouping the entries.  Add a
    +# "Group: [name] {" line to start a group, and add a "}" to end it.
    +# 
    +# You can add text and web links to the menu by adding "Text: [text]" and
    +# "Link: [name] ([URL])" lines, respectively.
    +# 
    +# The formatting and comments are auto-generated, so don't worry about
    +# neatness when editing the file.  Natural Docs will clean it up the next
    +# time it is run.  When working with groups, just deal with the braces and
    +# forget about the indentation and comments.
    +# 
    +# --------------------------------------------------------------------------
    +
    +
    +File: GitBlob  (blob.h)
    +File: GitError  (error.h)
    +
    +Group: Index  {
    +
    +   Index: Everything
    +   Class Index: Classes
    +   Function Index: Functions
    +   Variable Index: Variables
    +   }  # Group: Index
    +
    diff --git a/doc/Topics.txt b/doc/Topics.txt
    new file mode 100644
    index 000000000..21530908d
    --- /dev/null
    +++ b/doc/Topics.txt
    @@ -0,0 +1,81 @@
    +Format: 1.51
    +
    +# This is the Natural Docs topics file for this project.  If you change anything
    +# here, it will apply to THIS PROJECT ONLY.  If you'd like to change something
    +# for all your projects, edit the Topics.txt in Natural Docs' Config directory
    +# instead.
    +
    +
    +# If you'd like to prevent keywords from being recognized by Natural Docs, you
    +# can do it like this:
    +# Ignore Keywords: [keyword], [keyword], ...
    +#
    +# Or you can use the list syntax like how they are defined:
    +# Ignore Keywords:
    +#    [keyword]
    +#    [keyword], [plural keyword]
    +#    ...
    +
    +
    +#-------------------------------------------------------------------------------
    +# SYNTAX:
    +#
    +# Topic Type: [name]
    +# Alter Topic Type: [name]
    +#    Creates a new topic type or alters one from the main file.  Each type gets
    +#    its own index and behavior settings.  Its name can have letters, numbers,
    +#    spaces, and these charaters: - / . '
    +#
    +# Plural: [name]
    +#    Sets the plural name of the topic type, if different.
    +#
    +# Keywords:
    +#    [keyword]
    +#    [keyword], [plural keyword]
    +#    ...
    +#    Defines or adds to the list of keywords for the topic type.  They may only
    +#    contain letters, numbers, and spaces and are not case sensitive.  Plural
    +#    keywords are used for list topics.  You can redefine keywords found in the
    +#    main topics file.
    +#
    +# Index: [yes|no]
    +#    Whether the topics get their own index.  Defaults to yes.  Everything is
    +#    included in the general index regardless of this setting.
    +#
    +# Scope: [normal|start|end|always global]
    +#    How the topics affects scope.  Defaults to normal.
    +#    normal        - Topics stay within the current scope.
    +#    start         - Topics start a new scope for all the topics beneath it,
    +#                    like class topics.
    +#    end           - Topics reset the scope back to global for all the topics
    +#                    beneath it.
    +#    always global - Topics are defined as global, but do not change the scope
    +#                    for any other topics.
    +#
    +# Class Hierarchy: [yes|no]
    +#    Whether the topics are part of the class hierarchy.  Defaults to no.
    +#
    +# Page Title If First: [yes|no]
    +#    Whether the topic's title becomes the page title if it's the first one in
    +#    a file.  Defaults to no.
    +#
    +# Break Lists: [yes|no]
    +#    Whether list topics should be broken into individual topics in the output.
    +#    Defaults to no.
    +#
    +# Can Group With: [type], [type], ...
    +#    Defines a list of topic types that this one can possibly be grouped with.
    +#    Defaults to none.
    +#-------------------------------------------------------------------------------
    +
    +# The following topics are defined in the main file, if you'd like to alter
    +# their behavior or add keywords:
    +#
    +#    Generic, Class, Interface, Section, File, Group, Function, Variable,
    +#    Property, Type, Constant, Enumeration, Event, Delegate, Macro,
    +#    Database, Database Table, Database View, Database Index, Database
    +#    Cursor, Database Trigger, Cookie, Build Target
    +
    +# If you add something that you think would be useful to other developers
    +# and should be included in Natural Docs by default, please e-mail it to
    +# topics [at] naturaldocs [dot] org.
    diff --git a/docs/api/commit.html b/doc/api/commit.html
    similarity index 100%
    rename from docs/api/commit.html
    rename to doc/api/commit.html
    diff --git a/docs/api/docco.css b/doc/api/docco.css
    similarity index 100%
    rename from docs/api/docco.css
    rename to doc/api/docco.css
    diff --git a/docs/api/error.html b/doc/api/error.html
    similarity index 100%
    rename from docs/api/error.html
    rename to doc/api/error.html
    diff --git a/docs/api/index.html b/doc/api/index.html
    similarity index 100%
    rename from docs/api/index.html
    rename to doc/api/index.html
    diff --git a/docs/api/oid.html b/doc/api/oid.html
    similarity index 100%
    rename from docs/api/oid.html
    rename to doc/api/oid.html
    diff --git a/docs/api/ref.html b/doc/api/ref.html
    similarity index 100%
    rename from docs/api/ref.html
    rename to doc/api/ref.html
    diff --git a/docs/api/repo.html b/doc/api/repo.html
    similarity index 100%
    rename from docs/api/repo.html
    rename to doc/api/repo.html
    diff --git a/docs/api/revwalk.html b/doc/api/revwalk.html
    similarity index 100%
    rename from docs/api/revwalk.html
    rename to doc/api/revwalk.html
    diff --git a/docs/api/sig.html b/doc/api/sig.html
    similarity index 100%
    rename from docs/api/sig.html
    rename to doc/api/sig.html
    diff --git a/docs/api/tree.html b/doc/api/tree.html
    similarity index 100%
    rename from docs/api/tree.html
    rename to doc/api/tree.html
    diff --git a/docs/api/util.html b/doc/api/util.html
    similarity index 100%
    rename from docs/api/util.html
    rename to doc/api/util.html
    diff --git a/docs/files/blob-h.html b/doc/files/blob-h.html
    similarity index 100%
    rename from docs/files/blob-h.html
    rename to doc/files/blob-h.html
    diff --git a/docs/files/error-h.html b/doc/files/error-h.html
    similarity index 100%
    rename from docs/files/error-h.html
    rename to doc/files/error-h.html
    diff --git a/docs/index.html b/doc/index.html
    similarity index 100%
    rename from docs/index.html
    rename to doc/index.html
    diff --git a/docs/index/Classes.html b/doc/index/Classes.html
    similarity index 100%
    rename from docs/index/Classes.html
    rename to doc/index/Classes.html
    diff --git a/docs/index/Functions.html b/doc/index/Functions.html
    similarity index 100%
    rename from docs/index/Functions.html
    rename to doc/index/Functions.html
    diff --git a/docs/index/General.html b/doc/index/General.html
    similarity index 100%
    rename from docs/index/General.html
    rename to doc/index/General.html
    diff --git a/docs/index/Variables.html b/doc/index/Variables.html
    similarity index 100%
    rename from docs/index/Variables.html
    rename to doc/index/Variables.html
    diff --git a/docs/javascript/main.js b/doc/javascript/main.js
    similarity index 100%
    rename from docs/javascript/main.js
    rename to doc/javascript/main.js
    diff --git a/docs/javascript/prettify.js b/doc/javascript/prettify.js
    similarity index 100%
    rename from docs/javascript/prettify.js
    rename to doc/javascript/prettify.js
    diff --git a/docs/javascript/searchdata.js b/doc/javascript/searchdata.js
    similarity index 100%
    rename from docs/javascript/searchdata.js
    rename to doc/javascript/searchdata.js
    diff --git a/docs/search/ClassesG.html b/doc/search/ClassesG.html
    similarity index 100%
    rename from docs/search/ClassesG.html
    rename to doc/search/ClassesG.html
    diff --git a/docs/search/ClassesL.html b/doc/search/ClassesL.html
    similarity index 100%
    rename from docs/search/ClassesL.html
    rename to doc/search/ClassesL.html
    diff --git a/docs/search/FunctionsC.html b/doc/search/FunctionsC.html
    similarity index 100%
    rename from docs/search/FunctionsC.html
    rename to doc/search/FunctionsC.html
    diff --git a/docs/search/FunctionsE.html b/doc/search/FunctionsE.html
    similarity index 100%
    rename from docs/search/FunctionsE.html
    rename to doc/search/FunctionsE.html
    diff --git a/docs/search/FunctionsG.html b/doc/search/FunctionsG.html
    similarity index 100%
    rename from docs/search/FunctionsG.html
    rename to doc/search/FunctionsG.html
    diff --git a/docs/search/FunctionsI.html b/doc/search/FunctionsI.html
    similarity index 100%
    rename from docs/search/FunctionsI.html
    rename to doc/search/FunctionsI.html
    diff --git a/docs/search/FunctionsL.html b/doc/search/FunctionsL.html
    similarity index 100%
    rename from docs/search/FunctionsL.html
    rename to doc/search/FunctionsL.html
    diff --git a/docs/search/FunctionsN.html b/doc/search/FunctionsN.html
    similarity index 100%
    rename from docs/search/FunctionsN.html
    rename to doc/search/FunctionsN.html
    diff --git a/docs/search/FunctionsR.html b/doc/search/FunctionsR.html
    similarity index 100%
    rename from docs/search/FunctionsR.html
    rename to doc/search/FunctionsR.html
    diff --git a/docs/search/FunctionsS.html b/doc/search/FunctionsS.html
    similarity index 100%
    rename from docs/search/FunctionsS.html
    rename to doc/search/FunctionsS.html
    diff --git a/docs/search/GeneralB.html b/doc/search/GeneralB.html
    similarity index 100%
    rename from docs/search/GeneralB.html
    rename to doc/search/GeneralB.html
    diff --git a/docs/search/GeneralC.html b/doc/search/GeneralC.html
    similarity index 100%
    rename from docs/search/GeneralC.html
    rename to doc/search/GeneralC.html
    diff --git a/docs/search/GeneralE.html b/doc/search/GeneralE.html
    similarity index 100%
    rename from docs/search/GeneralE.html
    rename to doc/search/GeneralE.html
    diff --git a/docs/search/GeneralF.html b/doc/search/GeneralF.html
    similarity index 100%
    rename from docs/search/GeneralF.html
    rename to doc/search/GeneralF.html
    diff --git a/docs/search/GeneralG.html b/doc/search/GeneralG.html
    similarity index 100%
    rename from docs/search/GeneralG.html
    rename to doc/search/GeneralG.html
    diff --git a/docs/search/GeneralI.html b/doc/search/GeneralI.html
    similarity index 100%
    rename from docs/search/GeneralI.html
    rename to doc/search/GeneralI.html
    diff --git a/docs/search/GeneralL.html b/doc/search/GeneralL.html
    similarity index 100%
    rename from docs/search/GeneralL.html
    rename to doc/search/GeneralL.html
    diff --git a/docs/search/GeneralN.html b/doc/search/GeneralN.html
    similarity index 100%
    rename from docs/search/GeneralN.html
    rename to doc/search/GeneralN.html
    diff --git a/docs/search/GeneralR.html b/doc/search/GeneralR.html
    similarity index 100%
    rename from docs/search/GeneralR.html
    rename to doc/search/GeneralR.html
    diff --git a/docs/search/GeneralS.html b/doc/search/GeneralS.html
    similarity index 100%
    rename from docs/search/GeneralS.html
    rename to doc/search/GeneralS.html
    diff --git a/docs/search/GeneralV.html b/doc/search/GeneralV.html
    similarity index 100%
    rename from docs/search/GeneralV.html
    rename to doc/search/GeneralV.html
    diff --git a/docs/search/NoResults.html b/doc/search/NoResults.html
    similarity index 100%
    rename from docs/search/NoResults.html
    rename to doc/search/NoResults.html
    diff --git a/docs/search/VariablesB.html b/doc/search/VariablesB.html
    similarity index 100%
    rename from docs/search/VariablesB.html
    rename to doc/search/VariablesB.html
    diff --git a/docs/search/VariablesC.html b/doc/search/VariablesC.html
    similarity index 100%
    rename from docs/search/VariablesC.html
    rename to doc/search/VariablesC.html
    diff --git a/docs/styles/main.css b/doc/styles/main.css
    similarity index 100%
    rename from docs/styles/main.css
    rename to doc/styles/main.css
    
    From 180d3c9288ceb317b2b2958fc7b5cea725bb741f Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Sat, 2 Apr 2011 12:39:54 -0400
    Subject: [PATCH 11/37] updated base.cc
    
    ---
     example/stress/commit.js | 6 +++---
     src/base.cc              | 4 ----
     2 files changed, 3 insertions(+), 7 deletions(-)
    
    diff --git a/example/stress/commit.js b/example/stress/commit.js
    index d97facdfb..e475c4ea8 100644
    --- a/example/stress/commit.js
    +++ b/example/stress/commit.js
    @@ -11,7 +11,7 @@ var git = require( '../../' ).raw;
             repo.open( '/home/tim/git/nodegit/.git', function() {
               var commit = new git.Commit( repo );
     
    -          //console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
    +          console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
             });
     
           })();
    @@ -34,8 +34,8 @@ var git = require( '../../' ).raw;
     
               var commit = new git.Commit( repo );
               commit.lookup( oid, function( err ) {
    -            //console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
    -          } );
    +            console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
    +          });
             });
     
           })();
    diff --git a/src/base.cc b/src/base.cc
    index 09fc59f4d..6215185bd 100755
    --- a/src/base.cc
    +++ b/src/base.cc
    @@ -20,8 +20,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     #include "tree.h"
     #include "tree_entry.h"
     
    -namespace {
    -
     extern "C" void init(Handle<v8::Object> target) {
       HandleScope scope;
     
    @@ -37,5 +35,3 @@ extern "C" void init(Handle<v8::Object> target) {
       GitTree::Initialize(target);
       GitTreeEntry::Initialize(target);
     }
    -
    -}
    
    From ae8027658d153d21146b0cb3beb5fffcac7f9b88 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:05:42 -0400
    Subject: [PATCH 12/37] Potential fix for windows build, copying DLLs and
     dynamically modifying the PATH'
    
    ---
     lib/index.js | 13 +++++++++++++
     src/blob.cc  |  1 +
     wscript      | 10 +++++++---
     3 files changed, 21 insertions(+), 3 deletions(-)
    
    diff --git a/lib/index.js b/lib/index.js
    index 2756a5cdc..08b3f6eed 100755
    --- a/lib/index.js
    +++ b/lib/index.js
    @@ -1,3 +1,7 @@
    +// System
    +var os = require( 'os' );
    +
    +// Library
     var util = require( './util.js' ).util,
         blob = require( './blob.js' ).blob,
         repo = require( './repo.js' ).repo,
    @@ -11,7 +15,16 @@ var util = require( './util.js' ).util,
         tree = require( './tree.js' ).tree,
         entry = require( './tree_entry.js' ).entry;
     
    +// Required for Windows/Cygwin support
    +var root = [ __dirname, '/../build/default' ].join( '' ), path = process.env.PATH
    +if( ~os.type().indexOf( 'CYGWIN' ) && !~path.indexOf( root ) ) {
    +  process.env.PATH = root + ':' + path;
    +}
    +
    +// Assign raw api to module
     exports.raw = require( '../build/default/nodegit' );
    +
    +// Assign to module
     exports.blob = blob;
     exports.util = util;
     exports.repo = repo;
    diff --git a/src/blob.cc b/src/blob.cc
    index 1ba9205ad..8948aca02 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -6,6 +6,7 @@
     #include <v8.h>
     #include <node.h>
     #include <node_events.h>
    +#include <node_buffer.h>
     
     #include "../vendor/libgit2/include/git2.h"
     
    diff --git a/wscript b/wscript
    index a6079f57a..08a55a442 100755
    --- a/wscript
    +++ b/wscript
    @@ -1,6 +1,6 @@
     import Options, Utils
     from subprocess import Popen
    -import os
    +import os, shutil, platform
     from os import system
     from os.path import exists, abspath
     
    @@ -33,9 +33,13 @@ def build(bld):
       Popen('python waf build-shared', shell=True).wait()
     
       os.chdir('../../')
    -    
    +
    +  # Copy the DLLs into the build/shared if Windows/Cygwin
    +  if 'CYGWIN' in platform.system():
    +    shutil.copy('build/shared/cyggit2-0.dll', '../../build/default/cyggit2-0.dll')
    +    shutil.copy('build/shared/libgit2.dll.a', '../../build/default/libgit2.dll.a')
    + 
       main = bld.new_task_gen('cxx', 'shlib', 'node_addon')
       main.target = 'nodegit'
       main.source = 'src/base.cc src/sig.cc src/blob.cc src/error.cc src/object.cc src/reference.cc src/repo.cc src/commit.cc src/oid.cc src/revwalk.cc src/tree.cc src/tree_entry.cc'
    -  main.rpath = abspath('vendor/libgit2/build/shared')
       main.uselib = 'GIT2'
    
    From 66361c5c685176280d34d8d141c9d87ea4aa8a9b Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:12:08 -0400
    Subject: [PATCH 13/37] Required RPATH for linux build
    
    ---
     package.json | 5 +----
     wscript      | 1 +
     2 files changed, 2 insertions(+), 4 deletions(-)
    
    diff --git a/package.json b/package.json
    index 24a88aa5a..9853a8222 100644
    --- a/package.json
    +++ b/package.json
    @@ -1,7 +1,7 @@
     {
       "name": "nodegit",
       "description": "NodeJS libgit2 asynchronous native bindings",
    -  "version": "0.0.2",
    +  "version": "0.0.3",
       "homepage": "https://github.com/tbranyen/nodegit",
       "author": "Tim Branyen <tim@tabdeveloper.com> (http://twitter.com/tbranyen)",
       "main": "./lib/index.js",
    @@ -22,8 +22,5 @@
       "scripts": {
         "preinstall": "./configure",
         "install": "make"
    -  },
    -  "dependencies": { 
    -    "diff": "1.0.0"
       }
     }
    diff --git a/wscript b/wscript
    index 08a55a442..8c5ab0474 100755
    --- a/wscript
    +++ b/wscript
    @@ -42,4 +42,5 @@ def build(bld):
       main = bld.new_task_gen('cxx', 'shlib', 'node_addon')
       main.target = 'nodegit'
       main.source = 'src/base.cc src/sig.cc src/blob.cc src/error.cc src/object.cc src/reference.cc src/repo.cc src/commit.cc src/oid.cc src/revwalk.cc src/tree.cc src/tree_entry.cc'
    +  main.rpath = abspath('vendor/libgit2/build/shared')
       main.uselib = 'GIT2'
    
    From cbc2c6808a04fbd8310fb48c603f4d48e386cda9 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:13:57 -0400
    Subject: [PATCH 14/37] Corrected accidential build order
    
    ---
     wscript | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/wscript b/wscript
    index 8c5ab0474..43399914d 100755
    --- a/wscript
    +++ b/wscript
    @@ -4,7 +4,7 @@ import os, shutil, platform
     from os import system
     from os.path import exists, abspath
     
    -VERSION = '0.0.2'
    +VERSION = '0.0.3'
     APPNAME = 'nodegit'
     srcdir = '.'
     blddir = 'build'
    @@ -32,13 +32,13 @@ def build(bld):
       except: pass
       Popen('python waf build-shared', shell=True).wait()
     
    -  os.chdir('../../')
    -
       # Copy the DLLs into the build/shared if Windows/Cygwin
       if 'CYGWIN' in platform.system():
         shutil.copy('build/shared/cyggit2-0.dll', '../../build/default/cyggit2-0.dll')
         shutil.copy('build/shared/libgit2.dll.a', '../../build/default/libgit2.dll.a')
    - 
    +
    +  os.chdir('../../')
    +
       main = bld.new_task_gen('cxx', 'shlib', 'node_addon')
       main.target = 'nodegit'
       main.source = 'src/base.cc src/sig.cc src/blob.cc src/error.cc src/object.cc src/reference.cc src/repo.cc src/commit.cc src/oid.cc src/revwalk.cc src/tree.cc src/tree_entry.cc'
    
    From ab8c7bdd446fa5162e2f4d9c3ea2c80bc1e2924d Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:21:42 -0400
    Subject: [PATCH 15/37] no need to copy files, just change the path
    
    ---
     lib/index.js | 2 +-
     wscript      | 5 -----
     2 files changed, 1 insertion(+), 6 deletions(-)
    
    diff --git a/lib/index.js b/lib/index.js
    index 08b3f6eed..6ccd0ebbe 100755
    --- a/lib/index.js
    +++ b/lib/index.js
    @@ -16,7 +16,7 @@ var util = require( './util.js' ).util,
         entry = require( './tree_entry.js' ).entry;
     
     // Required for Windows/Cygwin support
    -var root = [ __dirname, '/../build/default' ].join( '' ), path = process.env.PATH
    +var root = [ __dirname, '/../vendor/libgit2/build/shared' ].join( '' ), path = process.env.PATH
     if( ~os.type().indexOf( 'CYGWIN' ) && !~path.indexOf( root ) ) {
       process.env.PATH = root + ':' + path;
     }
    diff --git a/wscript b/wscript
    index 43399914d..e6a5aa07b 100755
    --- a/wscript
    +++ b/wscript
    @@ -32,11 +32,6 @@ def build(bld):
       except: pass
       Popen('python waf build-shared', shell=True).wait()
     
    -  # Copy the DLLs into the build/shared if Windows/Cygwin
    -  if 'CYGWIN' in platform.system():
    -    shutil.copy('build/shared/cyggit2-0.dll', '../../build/default/cyggit2-0.dll')
    -    shutil.copy('build/shared/libgit2.dll.a', '../../build/default/libgit2.dll.a')
    -
       os.chdir('../../')
     
       main = bld.new_task_gen('cxx', 'shlib', 'node_addon')
    
    From 8d75b4f4a0b5d14848800c858202e50357495494 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:30:28 -0400
    Subject: [PATCH 16/37] removed clear entires
    
    ---
     Makefile    | 4 ++--
     src/tree.cc | 8 ++++----
     2 files changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/Makefile b/Makefile
    index 88f0fe029..b3eabe5d9 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -33,8 +33,8 @@ uninstall:
     	@@echo "Uninstalled from $(INSTALL_PATH)"
     
     clean:
    -	@@rm -rf $(BASE)/build
    -	@@rm -rf $(BASE)/vendor/libgit2/build
    +	@@rm -rf $(BASE)/build/
    +	@@rm -rf $(BASE)/vendor/libgit2/build/
     
     test:
     	@@$(NODE_JS) $(BASE)/test/index.js test
    diff --git a/src/tree.cc b/src/tree.cc
    index 8f1989120..d20f6f4ef 100755
    --- a/src/tree.cc
    +++ b/src/tree.cc
    @@ -134,12 +134,12 @@ Handle<Value> GitTree::SortEntries(const Arguments& args) {
     }
     
     Handle<Value> GitTree::ClearEntries(const Arguments& args) {
    -  HandleScope scope;
    +  //HandleScope scope;
     
    -  GitTree *tree = ObjectWrap::Unwrap<GitTree>(args.This());
    +  //GitTree *tree = ObjectWrap::Unwrap<GitTree>(args.This());
     
    -  tree->ClearEntries();
    -    
    +  //tree->ClearEntries();
    +  //  
       return Undefined();
     }
     Persistent<FunctionTemplate> GitTree::constructor_template;
    
    From c49eb3c5c048d9c1b0c5e4e0e511824afd3434d1 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 19:38:28 -0400
    Subject: [PATCH 17/37] updated readme to reflect windows and shorter update
    
    ---
     README.md     | 3 ++-
     test/index.js | 3 +--
     2 files changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/README.md b/README.md
    index 1248a2c25..96ed6bc74 100644
    --- a/README.md
    +++ b/README.md
    @@ -40,7 +40,7 @@ This will install and configure everything you need to use `nodegit`.
     
     ### Windows via Cygwin ###
     
    -#### `nodegit` has a build issue under Windows that current makes it impossible to use. ####
    +#### `nodegit` has been compiled and tested to work with teh setup required to build and run `Node.js` itself. ####
     
     Instructions on compiling `Node.js` on a Windows platform can be found here:
     [https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows)](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows\))
    @@ -206,6 +206,7 @@ __ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http
         * More unit tests
         * Blob write support
         * Updated libgit2 to version 0.11.0
    +    * Windows Cygwin support! *albiet hacky*
     
     ### v0.0.2: ###
         * More methods implemented
    diff --git a/test/index.js b/test/index.js
    index 843987578..4f5bf5887 100644
    --- a/test/index.js
    +++ b/test/index.js
    @@ -10,8 +10,7 @@ catch( e ) {
       sys.puts( 'Cannot find nodeunit module.' );
       sys.puts( 'You can download submodules for this project by doing:' );
       sys.puts( '' );
    -  sys.puts( '    git submodule init vendor/nodeunit' );
    -  sys.puts( '    git submodule update vendor/nodeunit' );
    +  sys.puts( '    git submodule update --init' );
       sys.puts( '' );
       process.exit();
     }
    
    From be183db8c48c9ba959fb1c41fd2e5d0e3806b3cf Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 22:26:14 -0400
    Subject: [PATCH 18/37] More blob tests
    
    ---
     src/blob.cc      | 20 +++++------
     test/raw-blob.js | 93 ++++++++++++++++++++++++++++++++++++++++++------
     2 files changed, 92 insertions(+), 21 deletions(-)
    
    diff --git a/src/blob.cc b/src/blob.cc
    index 8948aca02..7d3662cd2 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -68,14 +68,6 @@ Handle<Value> GitBlob::New(const Arguments& args) {
       return args.This();
     }
     
    -Handle<Value> GitBlob::RawContent(const Arguments& args) {
    -  HandleScope scope;
    -
    -  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    -
    -  return String::New((const char*)blob->RawContent());
    -}
    -
     Handle<Value> GitBlob::Lookup(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
       Local<Function> callback;
    @@ -90,11 +82,11 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be a Object.")));
       }
     
    -  if(args.Length() == 3 || !args[3]->IsFunction()) {
    +  if(args.Length() == 2 || !args[2]->IsFunction()) {
         return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
       }
     
    -  callback = Local<Function>::Cast(args[3]);
    +  callback = Local<Function>::Cast(args[2]);
     
       lookup_request* ar = new lookup_request();
       ar->blob = blob;
    @@ -142,6 +134,14 @@ int GitBlob::EIO_AfterLookup(eio_req* req) {
       return 0;
     }
     
    +Handle<Value> GitBlob::RawContent(const Arguments& args) {
    +  HandleScope scope;
    +
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
    +
    +  return String::New((const char*)blob->RawContent());
    +}
    +
     Handle<Value> GitBlob::RawSize(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    diff --git a/test/raw-blob.js b/test/raw-blob.js
    index c775298df..dfdf8a49e 100644
    --- a/test/raw-blob.js
    +++ b/test/raw-blob.js
    @@ -32,7 +32,7 @@ exports.constructor = function( test ){
       helper.testFunction( test.equals, git.Blob, 'Blob' );
     
       // Ensure we get an instance of Blob
    -  test.ok( new git.Blob( testRepo ) instanceof git.Blob, 'Invocation returns an instance of Blob' );
    +  test.ok( new git.Blob() instanceof git.Blob, 'Invocation returns an instance of Blob' );
     
       test.done();
     };
    @@ -40,9 +40,9 @@ exports.constructor = function( test ){
     // Blob::Lookup
     exports.lookup = function( test ) {
       var testOid = new git.Oid(),
    -      testBlob = new git.Blob( testRepo );
    +      testBlob = new git.Blob();
     
    -  test.expect( 3 );
    +  test.expect( 5 );
     
       // Test for function
       helper.testFunction( test.equals, testBlob.lookup, 'Blob::Lookup' );
    @@ -51,17 +51,88 @@ exports.lookup = function( test ) {
       helper.testException( test.ok, function() {
         testBlob.lookup();
       }, 'Throw an exception if no repo Object' );
    +
    +  // Test Oid argument existence
    +  helper.testException( test.ok, function() {
    +    testBlob.lookup( testRepo );
    +  }, 'Throw an exception if no oid Object' );
    +
    +  // Test Callback argument existence
    +  helper.testException( test.ok, function() {
    +    testBlob.lookup( testRepo, testOid );
    +  }, 'Throw an exception if no callback Object' );
    +
    +  // Test invalid oid lookup
    +  //testBlob.lookup( testRepo, testOid, function( err ) {
    +  //  
    +  //  console.log( err );
    +
    +  //});
    + 
    +  test.done();
    +};
    +
    +// Blob::RawContent
    +exports.rawContent = function( test ) {
    +  var testOid = new git.Oid(),
    +      testBlob = new git.Blob();
    +
    +  test.expect( 2 );
    +
    +  // Test for function
    +  helper.testFunction( test.equals, testBlob.rawContent, 'Blob::RawContent' );
    + 
    +  test.done();
    +};
    +
    +// Blob::RawSize
    +exports.rawSize = function( test ) {
    +  var testOid = new git.Oid(),
    +      testBlob = new git.Blob();
    +
    +  test.expect( 2 );
    +
    +  // Test for function
    +  helper.testFunction( test.equals, testBlob.rawSize, 'Blob::RawSize' );
    + 
    +  test.done();
    +};
    +
    +// Blob::Close
    +exports.close = function( test ) {
    +  var testOid = new git.Oid(),
    +      testBlob = new git.Blob();
    +
    +  test.expect( 2 );
    +
    +  // Test for function
    +  helper.testFunction( test.equals, testBlob.close, 'Blob::Close' );
    + 
    +  test.done();
    +};
    +
    +// Blob::CreateFromFile
    +exports.createFromFile = function( test ) {
    +  var testOid = new git.Oid(),
    +      testBlob = new git.Blob();
    +
    +  test.expect( 2 );
    +
    +  // Test for function
    +  helper.testFunction( test.equals, testBlob.createFromFile, 'Blob::Close' );
      
    -  // Test that both arguments result correctly
    -  //helper.testException( test.ifError, function() {
    -  //  testOid.mkstr( "somestr" );
    -  //}, 'No exception is thrown with proper arguments' );
    +  test.done();
    +};
     
    -  // Test invalid hex id string
    -  //test.equals( -2, testOid.mkstr( '1392DLFJIOS' ), 'Invalid hex id String' );
    +// Blob::CreateFromBuffer
    +exports.createFromBuffer = function( test ) {
    +  var testOid = new git.Oid(),
    +      testBlob = new git.Blob();
     
    -  // Test valid hex id string
    -  //test.equals( 0, testOid.mkstr( '1810DFF58D8A660512D4832E740F692884338CCD' ), 'Valid hex id String' );
    +  test.expect( 2 );
     
    +  // Test for function
    +  helper.testFunction( test.equals, testBlob.createFromBuffer, 'Blob::CreateFromBuffer' );
    + 
       test.done();
     };
    
    From 2feff3678684892a917168a37342f026549b2168 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 22:31:21 -0400
    Subject: [PATCH 19/37] updated repo lib code to not internally throw on error,
     Closes #18
    
    ---
     lib/repo.js | 7 ++++++-
     1 file changed, 6 insertions(+), 1 deletion(-)
    
    diff --git a/lib/repo.js b/lib/repo.js
    index 84d1b5ec5..e9e6a47f6 100644
    --- a/lib/repo.js
    +++ b/lib/repo.js
    @@ -27,7 +27,12 @@ var _Repo = function( dir, callback ) {
         if( !callback ) { return; }
     
         git.ref( self.repo ).lookup( 'refs/heads/' + name, function( err, ref ) {
    -      if( err ) { throw err; }
    +      if( err ) {
    +        var args = Array.prototype.slice.call( arguments );
    +        args[0] = git.util().error( args[0] );
    +
    +        callback.apply( this, args.concat( this ) );
    +      }
     
           git.commit( self.repo ).lookup( ref.oid().oid, function() {
             var args = Array.prototype.slice.call( arguments );
    
    From b4da7a67be4eb008ff90384570ba966da72cb06c Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Thu, 7 Apr 2011 22:37:29 -0400
    Subject: [PATCH 20/37] removed some 0.0.3 stuff that isn't quite done yet
    
    ---
     README.md | 6 ++----
     1 file changed, 2 insertions(+), 4 deletions(-)
    
    diff --git a/README.md b/README.md
    index 96ed6bc74..7400823ce 100644
    --- a/README.md
    +++ b/README.md
    @@ -40,7 +40,7 @@ This will install and configure everything you need to use `nodegit`.
     
     ### Windows via Cygwin ###
     
    -#### `nodegit` has been compiled and tested to work with teh setup required to build and run `Node.js` itself. ####
    +#### `nodegit` has been compiled and tested to work with the setup required to build and run `Node.js` itself. ####
     
     Instructions on compiling `Node.js` on a Windows platform can be found here:
     [https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows)](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows\))
    @@ -202,11 +202,9 @@ __ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http
     
     ### v0.0.3: ###
         * Fully documented native source code
    -    * Reworked convenience API to make development significantly easier
         * More unit tests
    -    * Blob write support
         * Updated libgit2 to version 0.11.0
    -    * Windows Cygwin support! *albiet hacky*
    +    * Windows Cygwin support! *albeit hacky*
     
     ### v0.0.2: ###
         * More methods implemented
    
    From 9e60c82fba3788040905108b40df8ff152622b54 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 00:17:17 -0400
    Subject: [PATCH 21/37] Updated all header files to include folder
    
    ---
     include/blob.h                |  2 +-
     {src => include}/commit.h     |  0
     {src => include}/object.h     |  0
     {src => include}/oid.h        |  0
     {src => include}/reference.h  |  0
     {src => include}/repo.h       |  0
     {src => include}/revwalk.h    |  0
     {src => include}/sig.h        |  0
     {src => include}/tree.h       |  0
     {src => include}/tree_entry.h |  0
     src/base.cc                   | 18 +++++++++---------
     src/blob.cc                   |  2 +-
     src/commit.cc                 | 12 ++++++------
     src/object.cc                 |  6 +++---
     src/oid.cc                    |  2 +-
     src/reference.cc              |  6 +++---
     src/repo.cc                   |  6 +++---
     src/revwalk.cc                |  6 +++---
     src/sig.cc                    |  4 ++--
     src/tree.cc                   |  6 +++---
     src/tree_entry.cc             | 10 +++++-----
     21 files changed, 40 insertions(+), 40 deletions(-)
     rename {src => include}/commit.h (100%)
     rename {src => include}/object.h (100%)
     rename {src => include}/oid.h (100%)
     rename {src => include}/reference.h (100%)
     rename {src => include}/repo.h (100%)
     rename {src => include}/revwalk.h (100%)
     rename {src => include}/sig.h (100%)
     rename {src => include}/tree.h (100%)
     rename {src => include}/tree_entry.h (100%)
    
    diff --git a/include/blob.h b/include/blob.h
    index 31b2b697e..e5cc3df3f 100755
    --- a/include/blob.h
    +++ b/include/blob.h
    @@ -12,7 +12,7 @@
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "../src/repo.h"
    +#include "repo.h"
     
     using namespace node;
     
    diff --git a/src/commit.h b/include/commit.h
    similarity index 100%
    rename from src/commit.h
    rename to include/commit.h
    diff --git a/src/object.h b/include/object.h
    similarity index 100%
    rename from src/object.h
    rename to include/object.h
    diff --git a/src/oid.h b/include/oid.h
    similarity index 100%
    rename from src/oid.h
    rename to include/oid.h
    diff --git a/src/reference.h b/include/reference.h
    similarity index 100%
    rename from src/reference.h
    rename to include/reference.h
    diff --git a/src/repo.h b/include/repo.h
    similarity index 100%
    rename from src/repo.h
    rename to include/repo.h
    diff --git a/src/revwalk.h b/include/revwalk.h
    similarity index 100%
    rename from src/revwalk.h
    rename to include/revwalk.h
    diff --git a/src/sig.h b/include/sig.h
    similarity index 100%
    rename from src/sig.h
    rename to include/sig.h
    diff --git a/src/tree.h b/include/tree.h
    similarity index 100%
    rename from src/tree.h
    rename to include/tree.h
    diff --git a/src/tree_entry.h b/include/tree_entry.h
    similarity index 100%
    rename from src/tree_entry.h
    rename to include/tree_entry.h
    diff --git a/src/base.cc b/src/base.cc
    index 6215185bd..0cc3151f9 100755
    --- a/src/base.cc
    +++ b/src/base.cc
    @@ -8,17 +8,17 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "reference.h"
    -#include "sig.h"
    +#include "../include/reference.h"
    +#include "../include/sig.h"
     #include "../include/error.h"
     #include "../include/blob.h"
    -#include "repo.h"
    -#include "oid.h"
    -#include "object.h"
    -#include "commit.h"
    -#include "revwalk.h"
    -#include "tree.h"
    -#include "tree_entry.h"
    +#include "../include/repo.h"
    +#include "../include/oid.h"
    +#include "../include/object.h"
    +#include "../include/commit.h"
    +#include "../include/revwalk.h"
    +#include "../include/tree.h"
    +#include "../include/tree_entry.h"
     
     extern "C" void init(Handle<v8::Object> target) {
       HandleScope scope;
    diff --git a/src/blob.cc b/src/blob.cc
    index 7d3662cd2..28288955c 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -10,7 +10,7 @@
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "repo.h"
    +#include "../include/repo.h"
     #include "../include/blob.h"
     
     using namespace v8;
    diff --git a/src/commit.cc b/src/commit.cc
    index a12991791..cf314e1c5 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -10,12 +10,12 @@
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "reference.h"
    -#include "sig.h"
    -#include "repo.h"
    -#include "oid.h"
    -#include "tree.h"
    -#include "commit.h"
    +#include "../include/reference.h"
    +#include "../include/sig.h"
    +#include "../include/repo.h"
    +#include "../include/oid.h"
    +#include "../include/tree.h"
    +#include "../include/commit.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/object.cc b/src/object.cc
    index 70476057a..dfd64b482 100755
    --- a/src/object.cc
    +++ b/src/object.cc
    @@ -8,9 +8,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "object.h"
    -#include "repo.h"
    -#include "oid.h"
    +#include "../include/object.h"
    +#include "../include/repo.h"
    +#include "../include/oid.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/oid.cc b/src/oid.cc
    index aadc2ad20..35d5125cf 100755
    --- a/src/oid.cc
    +++ b/src/oid.cc
    @@ -8,7 +8,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "oid.h"
    +#include "../include/oid.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/reference.cc b/src/reference.cc
    index fc2cdc4e9..c6494d8a4 100755
    --- a/src/reference.cc
    +++ b/src/reference.cc
    @@ -9,9 +9,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "repo.h"
    -#include "reference.h"
    -#include "oid.h"
    +#include "../include/repo.h"
    +#include "../include/reference.h"
    +#include "../include/oid.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/repo.cc b/src/repo.cc
    index 2f3bafc2f..35c8e38b1 100755
    --- a/src/repo.cc
    +++ b/src/repo.cc
    @@ -9,9 +9,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "object.h"
    -#include "repo.h"
    -#include "commit.h"
    +#include "../include/object.h"
    +#include "../include/repo.h"
    +#include "../include/commit.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/revwalk.cc b/src/revwalk.cc
    index e13a120b7..3cc470e79 100755
    --- a/src/revwalk.cc
    +++ b/src/revwalk.cc
    @@ -8,9 +8,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "revwalk.h"
    -#include "repo.h"
    -#include "commit.h"
    +#include "../include/revwalk.h"
    +#include "../include/repo.h"
    +#include "../include/commit.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/sig.cc b/src/sig.cc
    index aedac5f82..23eb33765 100755
    --- a/src/sig.cc
    +++ b/src/sig.cc
    @@ -8,8 +8,8 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "repo.h"
    -#include "sig.h"
    +#include "../include/repo.h"
    +#include "../include/sig.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/tree.cc b/src/tree.cc
    index d20f6f4ef..c54f9c16c 100755
    --- a/src/tree.cc
    +++ b/src/tree.cc
    @@ -8,9 +8,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "repo.h"
    -#include "tree.h"
    -#include "tree_entry.h"
    +#include "../include/repo.h"
    +#include "../include/tree.h"
    +#include "../include/tree_entry.h"
     
     using namespace v8;
     using namespace node;
    diff --git a/src/tree_entry.cc b/src/tree_entry.cc
    index e5e34c7f9..a0f913891 100644
    --- a/src/tree_entry.cc
    +++ b/src/tree_entry.cc
    @@ -8,12 +8,12 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     
     #include "../vendor/libgit2/include/git2.h"
     
    -#include "repo.h"
    +#include "../include/repo.h"
     #include "../include/blob.h"
    -#include "tree.h"
    -#include "object.h"
    -#include "oid.h"
    -#include "tree_entry.h"
    +#include "../include/tree.h"
    +#include "../include/object.h"
    +#include "../include/oid.h"
    +#include "../include/tree_entry.h"
     
     using namespace v8;
     using namespace node;
    
    From 7a481bfef3bf0eab75a8a6a5765f05f063780ab5 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 00:28:10 -0400
    Subject: [PATCH 22/37] Updated all classes to have git prefix.  Closes #13
    
    ---
     include/blob.h      |  4 +--
     include/commit.h    |  2 +-
     include/oid.h       |  6 ++--
     include/reference.h | 14 ++++-----
     include/repo.h      | 12 +++----
     include/revwalk.h   | 10 +++---
     include/sig.h       | 12 +++----
     src/base.cc         | 10 +++---
     src/blob.cc         |  4 +--
     src/commit.cc       |  8 ++---
     src/object.cc       |  4 +--
     src/oid.cc          | 76 ++++++++++++++++++++++-----------------------
     src/reference.cc    | 36 ++++++++++-----------
     src/repo.cc         | 52 +++++++++++++++----------------
     src/revwalk.cc      | 62 ++++++++++++++++++------------------
     src/sig.cc          | 44 +++++++++++++-------------
     src/tree_entry.cc   |  2 +-
     17 files changed, 179 insertions(+), 179 deletions(-)
    
    diff --git a/include/blob.h b/include/blob.h
    index e5cc3df3f..60354168a 100755
    --- a/include/blob.h
    +++ b/include/blob.h
    @@ -215,8 +215,8 @@ class GitBlob : public ObjectWrap {
          */
         struct lookup_request {
           GitBlob* blob;
    -      Repo* repo;
    -      Oid* oid;
    +      GitRepo* repo;
    +      GitOid* oid;
           int err;
           v8::Persistent<v8::Function> callback;
         };
    diff --git a/include/commit.h b/include/commit.h
    index 445c98421..ecf3c92f0 100755
    --- a/include/commit.h
    +++ b/include/commit.h
    @@ -83,7 +83,7 @@ class GitCommit : public EventEmitter {
     
         struct lookup_request {
           GitCommit* commit;
    -      Oid* oid;
    +      GitOid* oid;
           int err;
           Persistent<Function> callback;
         };
    diff --git a/include/oid.h b/include/oid.h
    index 92550471e..650062460 100755
    --- a/include/oid.h
    +++ b/include/oid.h
    @@ -14,7 +14,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace node;
     using namespace v8;
     
    -class Oid : public EventEmitter {
    +class GitOid : public EventEmitter {
       public:
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize (Handle<v8::Object> target);
    @@ -31,8 +31,8 @@ class Oid : public EventEmitter {
         void Cpy(git_oid* out);
         int Cmp(const git_oid* a, const git_oid* b);
     
    -    Oid() {}
    -    ~Oid() {}
    +    GitOid() {}
    +    ~GitOid() {}
     
       protected:
         static Handle<Value> New(const Arguments& args);
    diff --git a/include/reference.h b/include/reference.h
    index 195bb438f..ea47c1315 100755
    --- a/include/reference.h
    +++ b/include/reference.h
    @@ -18,32 +18,32 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace node;
     using namespace v8;
     
    -class Reference : public EventEmitter {
    +class GitReference : public EventEmitter {
       public:
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize(Handle<v8::Object> target);
         git_reference* GetValue();
         void SetValue(git_reference* ref);
         int Lookup(git_repository* repo, const char* name);
    -    const git_oid* _Oid();
    +    const git_oid* Oid();
     
       protected:
    -    Reference() {}
    -    ~Reference() {}
    +    GitReference() {}
    +    ~GitReference() {}
         static Handle<Value> New(const Arguments& args);
     
         static Handle<Value> Lookup(const Arguments& args);
         static int EIO_Lookup(eio_req* req);
         static int EIO_AfterLookup(eio_req* req);
     
    -    static Handle<Value> _Oid(const Arguments& args);
    +    static Handle<Value> Oid(const Arguments& args);
     
       private:
         git_reference *ref;
     
         struct lookup_request {
    -      Reference* ref;
    -      Repo* repo;
    +      GitReference* ref;
    +      GitRepo* repo;
           int err;
           std::string name;
           Persistent<Function> callback;
    diff --git a/include/repo.h b/include/repo.h
    index cdfefc5ea..620877a2a 100755
    --- a/include/repo.h
    +++ b/include/repo.h
    @@ -17,7 +17,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace node;
     using namespace v8;
     
    -class Repo : public EventEmitter {
    +class GitRepo : public EventEmitter {
       public:
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize(Handle<v8::Object> target);
    @@ -38,8 +38,8 @@ class Repo : public EventEmitter {
         //int NewObject(git_object **obj, Otype type);
     
       protected:
    -    Repo() {}
    -    ~Repo() {}
    +    GitRepo() {}
    +    ~GitRepo() {}
         static Handle<Value> New(const Arguments& args);
     
         static Handle<Value> Open(const Arguments& args);
    @@ -60,19 +60,19 @@ class Repo : public EventEmitter {
         git_repository* repo;
     
         struct open_request {
    -      Repo* repo;
    +      GitRepo* repo;
           int err;
           std::string path;
           Persistent<Function> callback;
         };
     
         struct lookup_request {
    -      Repo* repo;
    +      GitRepo* repo;
           Persistent<Function> callback;
         };
     
         struct init_request {
    -      Repo* repo;
    +      GitRepo* repo;
           int err;
           std::string path;
           bool is_bare;
    diff --git a/include/revwalk.h b/include/revwalk.h
    index 8b9c7dbb4..0a86ae222 100755
    --- a/include/revwalk.h
    +++ b/include/revwalk.h
    @@ -17,7 +17,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace node;
     using namespace v8;
     
    -class RevWalk : public EventEmitter {
    +class GitRevWalk : public EventEmitter {
       public:
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize(Handle<v8::Object> target);
    @@ -34,8 +34,8 @@ class RevWalk : public EventEmitter {
         git_repository* Repository();
     
       protected:
    -    RevWalk() {}
    -    ~RevWalk() {}
    +    GitRevWalk() {}
    +    ~GitRevWalk() {}
         static Handle<Value> New(const Arguments& args);
         static Handle<Value> Reset(const Arguments& args);
         static Handle<Value> Push(const Arguments& args);
    @@ -54,8 +54,8 @@ class RevWalk : public EventEmitter {
         git_repository* repo;
     
         struct next_request {
    -      RevWalk* revwalk;
    -      Oid* oid;
    +      GitRevWalk* revwalk;
    +      GitOid* oid;
           int err;
           Persistent<Function> callback;
         };
    diff --git a/include/sig.h b/include/sig.h
    index 70a9647e2..2f275b47f 100755
    --- a/include/sig.h
    +++ b/include/sig.h
    @@ -2,8 +2,8 @@
     Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     */
     
    -#ifndef Sig_H
    -#define Sig_H
    +#ifndef GitSig_H
    +#define GitSig_H
     
     #include <v8.h>
     #include <node.h>
    @@ -16,14 +16,14 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -class Sig : public EventEmitter {
    +class GitSig : public EventEmitter {
       public:
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize(Handle<v8::Object> target);
     
         void New(const char *name, const char *email, time_t time, int offset);
         git_signature* GetValue();
    -    void SetValue(git_signature* Sig);
    +    void SetValue(git_signature* GitSig);
         git_signature* Dup();
         void Free();
     
    @@ -31,8 +31,8 @@ class Sig : public EventEmitter {
         char* Email();
     
       protected:
    -    Sig() {};
    -    ~Sig() {};
    +    GitSig() {};
    +    ~GitSig() {};
     
         static Handle<Value> New(const Arguments& args);
         static Handle<Value> Dup(const Arguments& args);
    diff --git a/src/base.cc b/src/base.cc
    index 0cc3151f9..26b5d2d64 100755
    --- a/src/base.cc
    +++ b/src/base.cc
    @@ -23,15 +23,15 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     extern "C" void init(Handle<v8::Object> target) {
       HandleScope scope;
     
    -  Reference::Initialize(target);
    -  Sig::Initialize(target);
    +  GitReference::Initialize(target);
    +  GitSig::Initialize(target);
       GitError::Initialize(target);
       GitBlob::Initialize(target);
    -  Oid::Initialize(target);
    +  GitOid::Initialize(target);
       GitObject::Initialize(target);
    -  Repo::Initialize(target);
    +  GitRepo::Initialize(target);
       GitCommit::Initialize(target);
    -  RevWalk::Initialize(target);
    +  GitRevWalk::Initialize(target);
       GitTree::Initialize(target);
       GitTreeEntry::Initialize(target);
     }
    diff --git a/src/blob.cc b/src/blob.cc
    index 28288955c..1ff6d0b2e 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -90,8 +90,8 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
     
       lookup_request* ar = new lookup_request();
       ar->blob = blob;
    -  ar->repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    -  ar->oid = ObjectWrap::Unwrap<Oid>(args[1]->ToObject());
    +  ar->repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
    +  ar->oid = ObjectWrap::Unwrap<GitOid>(args[1]->ToObject());
       ar->callback = Persistent<Function>::New(callback);
     
       blob->Ref();
    diff --git a/src/commit.cc b/src/commit.cc
    index cf314e1c5..02236eaa7 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -131,7 +131,7 @@ Handle<Value> GitCommit::Lookup(const Arguments& args) {
     
       lookup_request *ar = new lookup_request();
       ar->commit = commit;
    -  ar->oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  ar->oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
       ar->callback = Persistent<Function>::New(callback);
     
       commit->Ref();
    @@ -183,7 +183,7 @@ Handle<Value> GitCommit::Id(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
       oid->SetValue(const_cast<git_oid *>(commit->Id()));
       
    @@ -231,7 +231,7 @@ Handle<Value> GitCommit::Committer(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
     
    -  Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
    +  GitSig *sig = ObjectWrap::Unwrap<GitSig>(args[0]->ToObject());
     
       sig->SetValue(const_cast<git_signature *>(commit->Committer()));
       
    @@ -247,7 +247,7 @@ Handle<Value> GitCommit::Author(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
     
    -  Sig *sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
    +  GitSig *sig = ObjectWrap::Unwrap<GitSig>(args[0]->ToObject());
     
       sig->SetValue(const_cast<git_signature *>(commit->Author()));
       
    diff --git a/src/object.cc b/src/object.cc
    index dfd64b482..0fecbe6f0 100755
    --- a/src/object.cc
    +++ b/src/object.cc
    @@ -105,7 +105,7 @@ Handle<Value> GitObject::Id(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
       oid->SetValue(const_cast<git_oid *>(obj->Id()));
     
    @@ -129,7 +129,7 @@ Handle<Value> GitObject::Owner(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
       }
     
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
     
       repo->SetValue(obj->Owner());
     
    diff --git a/src/oid.cc b/src/oid.cc
    index 35d5125cf..1d4d4c8c4 100755
    --- a/src/oid.cc
    +++ b/src/oid.cc
    @@ -13,14 +13,14 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -void Oid::Initialize(Handle<Object> target) {
    +void GitOid::Initialize(Handle<Object> target) {
       HandleScope scope;
     
       Local<FunctionTemplate> t = FunctionTemplate::New(New);
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("Oid"));
    +  constructor_template->SetClassName(String::NewSymbol("GitOid"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "mkstr", Mkstr);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "mkraw", Mkraw);
    @@ -31,60 +31,60 @@ void Oid::Initialize(Handle<Object> target) {
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "cpy", Cpy);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "cmp", Cmp);
     
    -  target->Set(String::NewSymbol("Oid"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("GitOid"), constructor_template->GetFunction());
     }
     
    -git_oid* Oid::GetValue() {
    +git_oid* GitOid::GetValue() {
       return &this->oid;
     }
     
    -void Oid::SetValue(git_oid* oid) {
    +void GitOid::SetValue(git_oid* oid) {
       this->oid = *oid;
     }
     
    -int Oid::Mkstr(const char* id) {
    +int GitOid::Mkstr(const char* id) {
       return git_oid_mkstr(&this->oid, id);
     }
     
    -void Oid::Mkraw(const unsigned char* raw) {
    +void GitOid::Mkraw(const unsigned char* raw) {
       git_oid_mkraw(&this->oid, raw);
     }
     
    -char* Oid::Fmt(char* buffer) {
    +char* GitOid::Fmt(char* buffer) {
       git_oid_fmt(buffer, &this->oid);
     }
     
    -void Oid::PathFmt(char* str) {
    +void GitOid::PathFmt(char* str) {
       git_oid_pathfmt(str, &this->oid);
     }
     
    -char* Oid::AllocFmt() {
    +char* GitOid::AllocFmt() {
       return git_oid_allocfmt(&this->oid);
     }
     
    -char* Oid::ToString(char* buffer, size_t bufferSize) {
    +char* GitOid::ToString(char* buffer, size_t bufferSize) {
       git_oid_to_string(*&buffer, bufferSize, &this->oid);
     }
     
    -void Oid::Cpy(git_oid* out) {
    +void GitOid::Cpy(git_oid* out) {
       git_oid_cpy(out, &this->oid);
     }
     
    -int Oid::Cmp(const git_oid* a, const git_oid* b) {
    +int GitOid::Cmp(const git_oid* a, const git_oid* b) {
       return git_oid_cmp(a, b);
     }
     
    -Handle<Value> Oid::New(const Arguments& args) {
    +Handle<Value> GitOid::New(const Arguments& args) {
       HandleScope scope;
     
    -  Oid *oid = new Oid();
    +  GitOid *oid = new GitOid();
       oid->Wrap(args.This());
     
       return args.This();
     }
     
    -Handle<Value> Oid::Mkstr(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::Mkstr(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
    @@ -97,8 +97,8 @@ Handle<Value> Oid::Mkstr(const Arguments& args) {
       return Integer::New(oid->Mkstr(*id));
     }
     
    -Handle<Value> Oid::Mkraw(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::Mkraw(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
    @@ -112,8 +112,8 @@ Handle<Value> Oid::Mkraw(const Arguments& args) {
       return Local<Value>::New(args.This());
     }
     
    -Handle<Value> Oid::Fmt(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::Fmt(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
    @@ -122,8 +122,8 @@ Handle<Value> Oid::Fmt(const Arguments& args) {
       return String::New(buffer);
     }
     
    -Handle<Value> Oid::PathFmt(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::PathFmt(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
    @@ -132,16 +132,16 @@ Handle<Value> Oid::PathFmt(const Arguments& args) {
       return String::New(buffer);
     }
     
    -Handle<Value> Oid::AllocFmt(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::AllocFmt(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
       return String::New(oid->AllocFmt());
     }
     
    -Handle<Value> Oid::ToString(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::ToString(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
       
    @@ -154,16 +154,16 @@ Handle<Value> Oid::ToString(const Arguments& args) {
       return String::New(buffer);
     }
     
    -Handle<Value> Oid::Cpy(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::Cpy(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
       
       if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Oid argument is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
       }
     
    -  Oid *clone = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  GitOid *clone = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
       
       git_oid *out;
       oid->Cpy(out);
    @@ -172,24 +172,24 @@ Handle<Value> Oid::Cpy(const Arguments& args) {
       return Undefined();
     }
     
    -Handle<Value> Oid::Cmp(const Arguments& args) {
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args.This());
    +Handle<Value> GitOid::Cmp(const Arguments& args) {
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
       
       if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Oid argument is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
       }
       
       if(args.Length() == 1 || !args[1]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Oid argument is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
       }
     
    -  Oid *a = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    -  Oid *b = ObjectWrap::Unwrap<Oid>(args[1]->ToObject());
    +  GitOid *a = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    +  GitOid *b = ObjectWrap::Unwrap<GitOid>(args[1]->ToObject());
     
       int cmp = oid->Cmp(a->GetValue(), b->GetValue());
     
       return Integer::New(cmp);
     }
    -Persistent<FunctionTemplate> Oid::constructor_template;
    +Persistent<FunctionTemplate> GitOid::constructor_template;
    diff --git a/src/reference.cc b/src/reference.cc
    index c6494d8a4..36459ccb8 100755
    --- a/src/reference.cc
    +++ b/src/reference.cc
    @@ -16,7 +16,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -void Reference::Initialize(Handle<Object> target) {
    +void GitReference::Initialize(Handle<Object> target) {
       HandleScope scope;
     
       Local<FunctionTemplate> t = FunctionTemplate::New(New);
    @@ -25,40 +25,40 @@ void Reference::Initialize(Handle<Object> target) {
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
       constructor_template->SetClassName(String::NewSymbol("Ref"));
     
    -  NODE_SET_PROTOTYPE_METHOD(constructor_template, "oid", _Oid);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "oid", Oid);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
     
       target->Set(String::NewSymbol("Ref"), constructor_template->GetFunction());
     }
     
    -git_reference* Reference::GetValue() {
    +git_reference* GitReference::GetValue() {
       return this->ref;
     }
     
    -void Reference::SetValue(git_reference *ref) {
    +void GitReference::SetValue(git_reference *ref) {
       this->ref = ref;
     }
     
    -int Reference::Lookup(git_repository* repo, const char* name) {
    +int GitReference::Lookup(git_repository* repo, const char* name) {
       return git_reference_lookup(&this->ref, repo, name);
     }
     
    -const git_oid* Reference::_Oid() {
    +const git_oid* GitReference::Oid() {
       return git_reference_oid(*&this->ref);
     }
     
    -Handle<Value> Reference::New(const Arguments& args) {
    +Handle<Value> GitReference::New(const Arguments& args) {
       HandleScope scope;
     
    -  Reference *ref = new Reference();
    +  GitReference *ref = new GitReference();
     
       ref->Wrap(args.This());
     
       return args.This();
     }
     
    -Handle<Value> Reference::Lookup(const Arguments& args) {
    -  Reference *ref = ObjectWrap::Unwrap<Reference>(args.This());
    +Handle<Value> GitReference::Lookup(const Arguments& args) {
    +  GitReference *ref = ObjectWrap::Unwrap<GitReference>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -79,7 +79,7 @@ Handle<Value> Reference::Lookup(const Arguments& args) {
     
       lookup_request *ar = new lookup_request();
       ar->ref = ref;
    -  ar->repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    +  ar->repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
     
       String::Utf8Value name(args[1]);
       ar->name = *name;
    @@ -94,7 +94,7 @@ Handle<Value> Reference::Lookup(const Arguments& args) {
       return Undefined();
     }
     
    -int Reference::EIO_Lookup(eio_req *req) {
    +int GitReference::EIO_Lookup(eio_req *req) {
       lookup_request *ar = static_cast<lookup_request *>(req->data);
     
       git_repository* repo = ar->repo->GetValue();
    @@ -104,7 +104,7 @@ int Reference::EIO_Lookup(eio_req *req) {
       return 0;
     }
     
    -int Reference::EIO_AfterLookup(eio_req *req) {
    +int GitReference::EIO_AfterLookup(eio_req *req) {
       HandleScope scope;
     
       lookup_request *ar = static_cast<lookup_request *>(req->data);
    @@ -128,17 +128,17 @@ int Reference::EIO_AfterLookup(eio_req *req) {
       return 0;
     }
     
    -Handle<Value> Reference::_Oid(const Arguments& args) {
    -  Reference *ref = ObjectWrap::Unwrap<Reference>(args.This());
    +Handle<Value> GitReference::Oid(const Arguments& args) {
    +  GitReference *ref = ObjectWrap::Unwrap<GitReference>(args.This());
       HandleScope scope;
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    -  oid->SetValue( const_cast<git_oid *>(ref->_Oid()) );
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    +  oid->SetValue( const_cast<git_oid *>(ref->Oid()) );
     
       return Undefined();
     }
    -Persistent<FunctionTemplate> Reference::constructor_template;
    +Persistent<FunctionTemplate> GitReference::constructor_template;
    diff --git a/src/repo.cc b/src/repo.cc
    index 35c8e38b1..9d8b45f5f 100755
    --- a/src/repo.cc
    +++ b/src/repo.cc
    @@ -16,40 +16,40 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -void Repo::Initialize(Handle<Object> target) {
    +void GitRepo::Initialize(Handle<Object> target) {
       HandleScope scope;
     
       Local<FunctionTemplate> t = FunctionTemplate::New(New);
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("Repo"));
    +  constructor_template->SetClassName(String::NewSymbol("GitRepo"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "init", Init);
     
    -  target->Set(String::NewSymbol("Repo"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("GitRepo"), constructor_template->GetFunction());
     }
     
    -git_repository* Repo::GetValue() {
    +git_repository* GitRepo::GetValue() {
         return this->repo;
     }
     
    -void Repo::SetValue(git_repository* repo) {
    +void GitRepo::SetValue(git_repository* repo) {
       this->repo = repo;
     }
     
    -int Repo::Open(const char* path) {
    +int GitRepo::Open(const char* path) {
       return git_repository_open(&this->repo, path);
     }
     
    -void Repo::Free() {
    +void GitRepo::Free() {
       git_repository_free(this->repo);
     }
     
    -int Repo::Init(const char* path, bool is_bare) {
    +int GitRepo::Init(const char* path, bool is_bare) {
       git_repository* repo_;
       int err = git_repository_init(&repo_, path, is_bare);
     
    @@ -60,17 +60,17 @@ int Repo::Init(const char* path, bool is_bare) {
       return err;
     }
     
    -Handle<Value> Repo::New(const Arguments& args) {
    +Handle<Value> GitRepo::New(const Arguments& args) {
       HandleScope scope;
     
    -  Repo *repo = new Repo();
    +  GitRepo *repo = new GitRepo();
       repo->Wrap(args.This());
     
       return args.This();
     }
     
    -Handle<Value> Repo::Open(const Arguments& args) {
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args.This());
    +Handle<Value> GitRepo::Open(const Arguments& args) {
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -101,7 +101,7 @@ Handle<Value> Repo::Open(const Arguments& args) {
       return Undefined();
     }
     
    -int Repo::EIO_Open(eio_req *req) {
    +int GitRepo::EIO_Open(eio_req *req) {
       open_request *ar = static_cast<open_request *>(req->data);
     
       ar->err = ar->repo->Open(ar->path.c_str());
    @@ -109,7 +109,7 @@ int Repo::EIO_Open(eio_req *req) {
       return 0;
     }
     
    -int Repo::EIO_AfterOpen(eio_req *req) {
    +int GitRepo::EIO_AfterOpen(eio_req *req) {
       HandleScope scope;
     
       open_request *ar = static_cast<open_request *>(req->data);
    @@ -133,8 +133,8 @@ int Repo::EIO_AfterOpen(eio_req *req) {
       return 0;
     }
     
    -Handle<Value> Repo::Lookup(const Arguments& args) {
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args.This());
    +Handle<Value> GitRepo::Lookup(const Arguments& args) {
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -144,7 +144,7 @@ Handle<Value> Repo::Lookup(const Arguments& args) {
       }
     
       if(args.Length() == 1 || !args[1]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Repo is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("GitRepo is required and must be a Object.")));
       }
     
       if(args.Length() == 2 || !args[2]->IsObject()) {
    @@ -169,7 +169,7 @@ Handle<Value> Repo::Lookup(const Arguments& args) {
       return Undefined();
     }
     
    -int Repo::EIO_Lookup(eio_req *req) {
    +int GitRepo::EIO_Lookup(eio_req *req) {
       //lookup_request *ar = static_cast<lookup_request *>(req->data);
       //
       //String::Utf8Value name(ar->name);
    @@ -185,7 +185,7 @@ int Repo::EIO_Lookup(eio_req *req) {
       //return 0;
     }
     
    -int Repo::EIO_AfterLookup(eio_req *req) {
    +int GitRepo::EIO_AfterLookup(eio_req *req) {
       //HandleScope scope;
     
       //lookup_request *ar = static_cast<lookupref_request *>(req->data);
    @@ -211,8 +211,8 @@ int Repo::EIO_AfterLookup(eio_req *req) {
       //return 0;
     }
     
    -Handle<Value> Repo::Free(const Arguments& args) {
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args.This());
    +Handle<Value> GitRepo::Free(const Arguments& args) {
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args.This());
     
       HandleScope scope;
     
    @@ -221,8 +221,8 @@ Handle<Value> Repo::Free(const Arguments& args) {
       return Undefined();
     }
     
    -Handle<Value> Repo::Init(const Arguments& args) {
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args.This());
    +Handle<Value> GitRepo::Init(const Arguments& args) {
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -258,7 +258,7 @@ Handle<Value> Repo::Init(const Arguments& args) {
       return Undefined();
     }
     
    -int Repo::EIO_Init(eio_req *req) {
    +int GitRepo::EIO_Init(eio_req *req) {
       init_request *ar = static_cast<init_request *>(req->data);
     
       ar->err = ar->repo->Init(ar->path.c_str(), ar->is_bare);
    @@ -266,7 +266,7 @@ int Repo::EIO_Init(eio_req *req) {
       return 0;
     }
     
    -int Repo::EIO_AfterInit(eio_req *req) {
    +int GitRepo::EIO_AfterInit(eio_req *req) {
       HandleScope scope;
     
       init_request *ar = static_cast<init_request *>(req->data);
    @@ -289,4 +289,4 @@ int Repo::EIO_AfterInit(eio_req *req) {
     
       return 0;
     }
    -Persistent<FunctionTemplate> Repo::constructor_template;
    +Persistent<FunctionTemplate> GitRepo::constructor_template;
    diff --git a/src/revwalk.cc b/src/revwalk.cc
    index 3cc470e79..27bc3bf38 100755
    --- a/src/revwalk.cc
    +++ b/src/revwalk.cc
    @@ -15,14 +15,14 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -void RevWalk::Initialize(Handle<Object> target) {
    +void GitRevWalk::Initialize(Handle<Object> target) {
       HandleScope scope;
     
       Local<FunctionTemplate> t = FunctionTemplate::New(New);
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("RevWalk"));
    +  constructor_template->SetClassName(String::NewSymbol("GitRevWalk"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "reset", Reset);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "push", Push);
    @@ -39,58 +39,58 @@ void RevWalk::Initialize(Handle<Object> target) {
     
       constructor_template->Set(String::New("sort"), sort);
     
    -  target->Set(String::NewSymbol("RevWalk"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("GitRevWalk"), constructor_template->GetFunction());
     }
     
    -git_revwalk* RevWalk::GetValue() {
    +git_revwalk* GitRevWalk::GetValue() {
       return this->revwalk;
     }
     
    -void RevWalk::SetValue(git_revwalk* revwalk) {
    +void GitRevWalk::SetValue(git_revwalk* revwalk) {
       this->revwalk = revwalk;
     }
     
    -int RevWalk::New(git_repository* repo) {
    +int GitRevWalk::New(git_repository* repo) {
       this->repo = repo;
     
       return git_revwalk_new(&this->revwalk, this->repo);
     }
     
    -void RevWalk::Reset() {
    +void GitRevWalk::Reset() {
       git_revwalk_reset(this->revwalk);
     }
     
    -int RevWalk::Push(git_oid* oid) {
    +int GitRevWalk::Push(git_oid* oid) {
       return git_revwalk_push(this->revwalk, oid);
     }
     
     // Not for 0.0.1
    -//int RevWalk::Hide() {
    +//int GitRevWalk::Hide() {
     //  git_revwalk_hide(this->revwalk);
     //}
     
    -int RevWalk::Next(git_oid *oid) {
    +int GitRevWalk::Next(git_oid *oid) {
       return git_revwalk_next(oid, this->revwalk);
     }
     
    -void RevWalk::Free() {
    +void GitRevWalk::Free() {
       git_revwalk_free(this->revwalk);
     }
     
    -git_repository* RevWalk::Repository() {
    +git_repository* GitRevWalk::Repository() {
     	return git_revwalk_repository(this->revwalk);
     }
     
    -Handle<Value> RevWalk::New(const Arguments& args) {
    +Handle<Value> GitRevWalk::New(const Arguments& args) {
       HandleScope scope;
     
    -  RevWalk *revwalk = new RevWalk();
    +  GitRevWalk *revwalk = new GitRevWalk();
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
       }
     
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
       revwalk->New(repo->GetValue());
     
       revwalk->Wrap(args.This());
    @@ -98,32 +98,32 @@ Handle<Value> RevWalk::New(const Arguments& args) {
       return args.This();
     }
     
    -Handle<Value> RevWalk::Reset(const Arguments& args) {
    +Handle<Value> GitRevWalk::Reset(const Arguments& args) {
       HandleScope scope;
     
    -  RevWalk *revwalk = ObjectWrap::Unwrap<RevWalk>(args.This());
    +  GitRevWalk *revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
     
       revwalk->Reset();
     
       return Undefined();
     }
     
    -Handle<Value> RevWalk::Push(const Arguments& args) {
    +Handle<Value> GitRevWalk::Push(const Arguments& args) {
       HandleScope scope;
     
    -  RevWalk *revwalk = ObjectWrap::Unwrap<RevWalk>(args.This());
    +  GitRevWalk *revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Oid *oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
       int err = revwalk->Push(oid->GetValue());
     
       return Integer::New(err);
     }
     
    -Handle<Value> RevWalk::Next(const Arguments& args) {
    -  RevWalk *revwalk = ObjectWrap::Unwrap<RevWalk>(args.This());
    +Handle<Value> GitRevWalk::Next(const Arguments& args) {
    +  GitRevWalk *revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -140,7 +140,7 @@ Handle<Value> RevWalk::Next(const Arguments& args) {
     
       next_request *ar = new next_request();
       ar->revwalk = revwalk;
    -  ar->oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  ar->oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
       ar->callback = Persistent<Function>::New(callback);
     
       revwalk->Ref();
    @@ -151,7 +151,7 @@ Handle<Value> RevWalk::Next(const Arguments& args) {
       return Undefined();
     }
     
    -int RevWalk::EIO_Next(eio_req *req) {
    +int GitRevWalk::EIO_Next(eio_req *req) {
       next_request *ar = static_cast<next_request *>(req->data);
       git_oid* oid = ar->oid->GetValue();
     
    @@ -161,7 +161,7 @@ int RevWalk::EIO_Next(eio_req *req) {
       return 0;
     }
     
    -int RevWalk::EIO_AfterNext(eio_req *req) {
    +int GitRevWalk::EIO_AfterNext(eio_req *req) {
       HandleScope scope;
     
       next_request *ar = static_cast<next_request *>(req->data);
    @@ -185,8 +185,8 @@ int RevWalk::EIO_AfterNext(eio_req *req) {
       return 0;
     }
     
    -Handle<Value> RevWalk::Free(const Arguments& args) {
    -  RevWalk *revwalk = ObjectWrap::Unwrap<RevWalk>(args.This());
    +Handle<Value> GitRevWalk::Free(const Arguments& args) {
    +  GitRevWalk *revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
     
       HandleScope scope;
     
    @@ -195,18 +195,18 @@ Handle<Value> RevWalk::Free(const Arguments& args) {
       return Undefined();
     }
     
    -Handle<Value> RevWalk::Repository(const Arguments& args) {
    +Handle<Value> GitRevWalk::Repository(const Arguments& args) {
       HandleScope scope;
     
    -  RevWalk *revwalk = new RevWalk();
    +  GitRevWalk *revwalk = new GitRevWalk();
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
       }
     
    -  Repo *repo = ObjectWrap::Unwrap<Repo>(args[0]->ToObject());
    +  GitRepo *repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
       repo->SetValue(revwalk->Repository());
     
       return Undefined();
     }
    -Persistent<FunctionTemplate> RevWalk::constructor_template;
    +Persistent<FunctionTemplate> GitRevWalk::constructor_template;
    diff --git a/src/sig.cc b/src/sig.cc
    index 23eb33765..9d5468337 100755
    --- a/src/sig.cc
    +++ b/src/sig.cc
    @@ -14,14 +14,14 @@ Copyright (c) 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
     using namespace v8;
     using namespace node;
     
    -void Sig::Initialize (Handle<v8::Object> target) {
    +void GitSig::Initialize (Handle<v8::Object> target) {
       HandleScope scope;
     
       Local<FunctionTemplate> t = FunctionTemplate::New(New);
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(3);
    -  constructor_template->SetClassName(String::NewSymbol("Sig"));
    +  constructor_template->SetClassName(String::NewSymbol("GitSig"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "dup", Dup);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free);
    @@ -30,83 +30,83 @@ void Sig::Initialize (Handle<v8::Object> target) {
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "name", Name);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "email", Email);
     
    -  target->Set(String::NewSymbol("Sig"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("GitSig"), constructor_template->GetFunction());
     }
     
    -git_signature* Sig::GetValue() {
    +git_signature* GitSig::GetValue() {
       return this->sig;
     }
     
    -void Sig::SetValue(git_signature* sig) {
    +void GitSig::SetValue(git_signature* sig) {
       this->sig = sig;
       this->name = sig->name;
       this->email = sig->email;
     }
     
    -void Sig::New(const char *name, const char *email, time_t time, int offset) {
    +void GitSig::New(const char *name, const char *email, time_t time, int offset) {
       this->sig = git_signature_new(name, email, time, offset);
     }
     
    -git_signature* Sig::Dup() {
    +git_signature* GitSig::Dup() {
       return git_signature_dup(this->sig);
     }
     
    -void Sig::Free() {
    +void GitSig::Free() {
       git_signature_free(this->sig);
     }
     
    -char* Sig::Name() {
    +char* GitSig::Name() {
       return this->name;
     }
     
    -char* Sig::Email() {
    +char* GitSig::Email() {
       return this->email;
     }
     
    -Handle<Value> Sig::New(const Arguments& args) {
    +Handle<Value> GitSig::New(const Arguments& args) {
       HandleScope scope;
     
    -  Sig *sig = new Sig();
    +  GitSig *sig = new GitSig();
       sig->Wrap(args.This());
     
       return args.This();
     }
     
    -Handle<Value> Sig::Dup(const Arguments& args) {
    +Handle<Value> GitSig::Dup(const Arguments& args) {
       HandleScope scope;
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
    +    return ThrowException(Exception::Error(String::New("GitSignature is required and must be an Object.")));
       }
     
    -  Sig* sig = ObjectWrap::Unwrap<Sig>(args[0]->ToObject());
    +  GitSig* sig = ObjectWrap::Unwrap<GitSig>(args[0]->ToObject());
       sig->SetValue(sig->Dup());
     
       return Undefined();
     }
     
    -Handle<Value> Sig::Free(const Arguments& args) {
    +Handle<Value> GitSig::Free(const Arguments& args) {
       HandleScope scope;
     
    -  Sig *sig = ObjectWrap::Unwrap<Sig>(args.This());
    +  GitSig *sig = ObjectWrap::Unwrap<GitSig>(args.This());
       sig->Free();
     
       return Undefined();
     }
     
    -Handle<Value> Sig::Name(const Arguments& args) {
    +Handle<Value> GitSig::Name(const Arguments& args) {
       HandleScope scope;
     
    -  Sig *sig = ObjectWrap::Unwrap<Sig>(args.This());
    +  GitSig *sig = ObjectWrap::Unwrap<GitSig>(args.This());
     
       return String::New(sig->Name());
     }
     
    -Handle<Value> Sig::Email(const Arguments& args) {
    +Handle<Value> GitSig::Email(const Arguments& args) {
       HandleScope scope;
     
    -  Sig *sig = ObjectWrap::Unwrap<Sig>(args.This());
    +  GitSig *sig = ObjectWrap::Unwrap<GitSig>(args.This());
     
       return String::New(sig->Email());
     }
    -Persistent<FunctionTemplate> Sig::constructor_template;
    +Persistent<FunctionTemplate> GitSig::constructor_template;
    diff --git a/src/tree_entry.cc b/src/tree_entry.cc
    index a0f913891..a1095a44e 100644
    --- a/src/tree_entry.cc
    +++ b/src/tree_entry.cc
    @@ -75,7 +75,7 @@ Handle<Value> GitTreeEntry::Id(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  Oid* oid = ObjectWrap::Unwrap<Oid>(args[0]->ToObject());
    +  GitOid* oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
       oid->SetValue(const_cast<git_oid *>(entry->Id()));
       
    
    From df634b00880d0e9e2bbe6512a169053d6cb27c5b Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 01:03:44 -0400
    Subject: [PATCH 23/37] Updates to commit revwalk and readme
    
    ---
     README.md     |  5 +----
     lib/commit.js | 56 +++++++++++++++++----------------------------------
     2 files changed, 20 insertions(+), 41 deletions(-)
    
    diff --git a/README.md b/README.md
    index 7400823ce..55b7c1265 100644
    --- a/README.md
    +++ b/README.md
    @@ -75,10 +75,6 @@ API Example Usage
                     console.log( commit.message );
                     console.log( '\n' );
                 });
    -
    -            history.on( 'end', function( err ) {
    -
    -            });
             });
         });
     
    @@ -202,6 +198,7 @@ __ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http
     
     ### v0.0.3: ###
         * Fully documented native source code
    +    * Updated convenience api code
         * More unit tests
         * Updated libgit2 to version 0.11.0
         * Windows Cygwin support! *albeit hacky*
    diff --git a/lib/commit.js b/lib/commit.js
    index c6f63a3b4..82500546e 100644
    --- a/lib/commit.js
    +++ b/lib/commit.js
    @@ -1,4 +1,5 @@
    -var git = require( '../' );
    +var git = require( '../' ),
    +    events = require( 'events' );
     
     var _Commit = function( obj ) {
       var self = { _cache: {} };
    @@ -91,47 +92,28 @@ var _Commit = function( obj ) {
         return git.tree( tree );
       };
     
    -  self.history = function( callback ) {
    -    var revwalk = git.revwalk( self.repo );
    -
    -
    -  };
    -
       self.file = function( path ) {
         return self.tree().entry( path );
       };
     
    -  //Object.defineProperty( self, 'history', {
    -  //  get: function() {
    -  //    // Partially apply the commit
    -  //    var revwalk = git.revwalk( self.repo );
    -  //    revwalk.each = function( callback ) {
    -  //      return function( callback ) {
    -  //        return revwalk.walk.apply( self.commit, [ self.commit, callback ] );
    -  //      };
    -  //    }();
    -
    -  //    return revwalk;
    -  //  },
    -  //  enumerable: true
    -  //});
    -
    -  //};
    +  self.history = function( start, end ) {
    +    var revwalk = git.revwalk( self.repo ),
    +        event = new events.EventEmitter();
    +
    +    revwalk.walk( self.id, function( err, commit ) {
    +      if( err ) {
    +        event.emit( 'error', err );
    +      }
    +      else {
    +        event.emit( 'commit', commit );
    +      }
    +
    +      if( self.stop ) {
    +        
    +      }
    +    });
     
    -  Object.defineProperty( self, 'history', {
    -    get: function() {
    -      // Partially apply the commit
    -      var revwalk = git.revwalk( self.repo );
    -      revwalk.each = function( callback ) {
    -        return function( callback ) {
    -          return revwalk.walk.apply( self.id, [ self.id, callback ] );
    -        };
    -      }();
    -
    -      return revwalk;
    -    },
    -    enumerable: true
    -  });
    +  };
     
     
       return self;
    
    From df757f47767bfe31c2038f778738daa1e6bceb7d Mon Sep 17 00:00:00 2001
    From: Tim Branyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 01:32:27 -0400
    Subject: [PATCH 24/37] added all js files to lint check
    
    ---
     lib/index.js       |  2 +-
     test/index.js      |  4 +---
     util/hint-check.js | 23 ++++++++++++++++++++++-
     util/nodejshint.js |  9 +++++----
     4 files changed, 29 insertions(+), 9 deletions(-)
    
    diff --git a/lib/index.js b/lib/index.js
    index 6ccd0ebbe..1c2bc17c6 100755
    --- a/lib/index.js
    +++ b/lib/index.js
    @@ -16,7 +16,7 @@ var util = require( './util.js' ).util,
         entry = require( './tree_entry.js' ).entry;
     
     // Required for Windows/Cygwin support
    -var root = [ __dirname, '/../vendor/libgit2/build/shared' ].join( '' ), path = process.env.PATH
    +var root = [ __dirname, '/../vendor/libgit2/build/shared' ].join( '' ), path = process.env.PATH;
     if( ~os.type().indexOf( 'CYGWIN' ) && !~path.indexOf( root ) ) {
       process.env.PATH = root + ':' + path;
     }
    diff --git a/test/index.js b/test/index.js
    index 4f5bf5887..b300c8058 100644
    --- a/test/index.js
    +++ b/test/index.js
    @@ -1,9 +1,7 @@
    -#!/usr/bin/env node
    -
     require.paths.unshift( '../vendor' );
     
     try {
    -  var reporter = require( '../vendor/nodeunit' ).reporters.default;
    +  var reporter = require( '../vendor/nodeunit' ).reporters['default'];
     }
     catch( e ) {
       var sys = require( 'sys' );
    diff --git a/util/hint-check.js b/util/hint-check.js
    index 1c531d212..7f31f331b 100644
    --- a/util/hint-check.js
    +++ b/util/hint-check.js
    @@ -1,6 +1,7 @@
     var nodejshint = require( './nodejshint.js' ).test,
     
     files = [
    +  // Test convenience api
       'lib/blob.js',
       'lib/commit.js',
       'lib/error.js',
    @@ -13,7 +14,27 @@ files = [
       'lib/sig.js',
       'lib/tree.js',
       'lib/tree_entry.js',
    -  'lib/util.js'
    +  'lib/util.js',
    +
    +  // Test unit test
    +  'test/convenience-repo.js',
    +  'test/index.js',
    +  'test/raw-blob.js',
    +  'test/raw-commit.js',
    +  'test/raw-error.js',
    +  'test/raw-obj.js',
    +  'test/raw-oid.js',
    +  'test/raw-ref.js',
    +  'test/raw-repo.js',
    +  'test/raw-revwalk.js',
    +
    +  // Test examples
    +  'example/convenience-repo.js',
    +  'example/convenience-tree.js',
    +  'example/raw-error.js',
    +  'example/raw-oid.js',
    +  'example/raw-repo.js',
    +  'example/raw-revwalk.js'
     ];
     
     nodejshint( files, function( failures ) {
    diff --git a/util/nodejshint.js b/util/nodejshint.js
    index faa3309a1..f27558824 100644
    --- a/util/nodejshint.js
    +++ b/util/nodejshint.js
    @@ -14,13 +14,14 @@ var nodejshint = function() {
     
             if( pass = JSHINT( data.toString() ), pass ) {
               counter++;
    -          console.log( '✔ Passed '+ file );
    -        } else {
    -          console.log( 'x Failed '+ file );
    +          console.log( '✔ Passed '+ file +'' );
    +        }
    +        else {
    +          console.log( 'x Failed '+ file +'' );
               JSHINT.errors.forEach( function( err ) {
                 
                 if( err ) {
    -              console.log( 'line '+ err.line +'\t', err.reason );
    +              console.log( 'line '+ err.line +'\t', err.reason +'' );
                 }
               });
             }
    
    From 086151c6af63df19497ebbbec850fce77ac3cc78 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 16:41:42 -0400
    Subject: [PATCH 25/37] removed doc files and moved theme into doc
    
    ---
     .gitignore                 |   2 +
     Makefile                   |   2 +-
     Theme.css                  | 796 -------------------------------------
     doc/Data/ClassHierarchy.nd | Bin 175 -> 175 bytes
     doc/Data/ConfigFileInfo.nd | Bin 26 -> 26 bytes
     doc/Data/FileInfo.nd       |  13 +-
     doc/Data/SymbolTable.nd    | Bin 3366 -> 3366 bytes
     doc/api/commit.html        |  61 ---
     doc/api/docco.css          | 186 ---------
     doc/api/error.html         |  24 --
     doc/api/index.html         |  22 -
     doc/api/oid.html           |  22 -
     doc/api/ref.html           |  27 --
     doc/api/repo.html          |  64 ---
     doc/api/revwalk.html       |  24 --
     doc/api/sig.html           |  28 --
     doc/api/tree.html          |  24 --
     doc/api/util.html          |  19 -
     src/blob.cc                |   2 +-
     util/nodejshint.js         |   4 +-
     20 files changed, 17 insertions(+), 1303 deletions(-)
     delete mode 100644 Theme.css
     delete mode 100644 doc/api/commit.html
     delete mode 100644 doc/api/docco.css
     delete mode 100644 doc/api/error.html
     delete mode 100644 doc/api/index.html
     delete mode 100644 doc/api/oid.html
     delete mode 100644 doc/api/ref.html
     delete mode 100644 doc/api/repo.html
     delete mode 100644 doc/api/revwalk.html
     delete mode 100644 doc/api/sig.html
     delete mode 100644 doc/api/tree.html
     delete mode 100644 doc/api/util.html
    
    diff --git a/.gitignore b/.gitignore
    index 2bd9cb3ff..afc53f86c 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -1,2 +1,4 @@
     .lock-wscript
     build/
    +doc/
    +!doc/Theme.css
    diff --git a/Makefile b/Makefile
    index b3eabe5d9..8680c6a8e 100644
    --- a/Makefile
    +++ b/Makefile
    @@ -43,6 +43,6 @@ lint:
     	@@$(NODE_JS) $(BASE)/util/hint-check.js
     
     doc:
    -	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/doc -p $(BASE)/doc -s $(BASE)/../Theme
    +	@@$(NATURAL_DOCS_PATH)/NaturalDocs -i $(BASE)/include -o HTML $(BASE)/doc -p $(BASE)/doc -s Theme
     
     .PHONY: test build doc
    diff --git a/Theme.css b/Theme.css
    deleted file mode 100644
    index 111dd6573..000000000
    --- a/Theme.css
    +++ /dev/null
    @@ -1,796 +0,0 @@
    -@import('http://fonts.googleapis.com/css?family=EB+Garamond');
    -
    -body {
    -  background-color: #FFFFFF;
    -  font-family: Georgia, sans-serif;
    -  font-size: 14px;
    -  margin: 40px;
    -}
    -
    -a:link,
    -a:visited { color: #900000; text-decoration: none }
    -a:hover { color: #900000; text-decoration: underline }
    -a:active { color: #FF0000; text-decoration: underline }
    -
    -td {
    -    vertical-align: top }
    -
    -img { border: 0;  }
    -
    -
    -/*
    -    Comment out this line to use web-style paragraphs (blank line between
    -    paragraphs, no indent) instead of print-style paragraphs (no blank line,
    -    indented.)
    -*/
    -p {
    -    text-indent: 5ex; margin: 0 }
    -
    -
    -/*  Opera doesn't break with just wbr, but will if you add this.  */
    -.Opera wbr:after {
    -	content: "\00200B";
    -	}
    -
    -
    -/*  Blockquotes are used as containers for things that may need to scroll.  */
    -blockquote {
    -    padding: 0;
    -    margin: 0;
    -    overflow: auto;
    -    }
    -
    -
    -.Firefox1 blockquote {
    -    padding-bottom: .5em;
    -    }
    -
    -/*  Turn off scrolling when printing.  */
    -@media print {
    -    blockquote {
    -        overflow: visible;
    -        }
    -    .IE blockquote {
    -        width: auto;
    -        }
    -    }
    -
    -
    -
    -#Menu {
    -    padding: 10px 0 0 0;
    -    }
    -.ContentPage #Menu,
    -.IndexPage #Menu {
    -    position: absolute;
    -    top: 0;
    -    left: 0;
    -    width: 31ex;
    -    overflow: hidden;
    -    }
    -.ContentPage .Firefox #Menu,
    -.IndexPage .Firefox #Menu {
    -    width: 27ex;
    -    }
    -
    -
    -    .MTitle {
    -        font-size: 16pt; font-weight: bold; font-variant: small-caps;
    -        text-align: center;
    -        padding: 5px 10px 15px 10px;
    -        border-bottom: 1px dotted #000000;
    -        margin-bottom: 15px }
    -
    -    .MSubTitle {
    -        font-size: 9pt; font-weight: normal; font-variant: normal;
    -        margin-top: 1ex; margin-bottom: 5px }
    -
    -
    -    .MEntry a:link,
    -    .MEntry a:hover,
    -    .MEntry a:visited { color: #606060; margin-right: 0 }
    -    .MEntry a:active { color: #A00000; margin-right: 0 }
    -
    -
    -    .MGroup {
    -        font-variant: small-caps; font-weight: bold;
    -        margin: 1em 0 1em 10px;
    -        }
    -
    -    .MGroupContent {
    -        font-variant: normal; font-weight: normal }
    -
    -    .MGroup a:link,
    -    .MGroup a:hover,
    -    .MGroup a:visited { color: #545454; margin-right: 10px }
    -    .MGroup a:active { color: #A00000; margin-right: 10px }
    -
    -
    -    .MFile,
    -    .MText,
    -    .MLink,
    -    .MIndex {
    -        padding: 1px 17px 2px 10px;
    -        margin: .25em 0 .25em 0;
    -        }
    -
    -    .MText {
    -        font-size: 8pt; font-style: italic }
    -
    -    .MLink {
    -        font-style: italic }
    -
    -    #MSelected {
    -        color: #000000; background-color: #FFFFFF;
    -        /*  Replace padding with border.  */
    -        padding: 0 10px 0 10px;
    -        border-width: 1px 2px 2px 0; border-style: solid; border-color: #000000;
    -        margin-right: 5px;
    -        }
    -
    -    /*  Close off the left side when its in a group.  */
    -    .MGroup #MSelected {
    -        padding-left: 9px; border-left-width: 1px }
    -
    -    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    -    .Firefox #MSelected {
    -        -moz-border-radius-topright: 10px;
    -        -moz-border-radius-bottomright: 10px }
    -    .Firefox .MGroup #MSelected {
    -        -moz-border-radius-topleft: 10px;
    -        -moz-border-radius-bottomleft: 10px }
    -
    -
    -    #MSearchPanel {
    -        padding: 0px 6px;
    -        margin: .25em 0;
    -        }
    -
    -
    -    #MSearchField {
    -        font: italic 9pt Verdana, sans-serif;
    -        color: #606060;
    -        background-color: #E8E8E8;
    -        border: none;
    -        padding: 2px 4px;
    -        width: 100%;
    -        }
    -    /* Only Opera gets it right. */
    -    .Firefox #MSearchField,
    -    .IE #MSearchField,
    -    .Safari #MSearchField {
    -        width: 94%;
    -        }
    -    .Opera9 #MSearchField,
    -    .Konqueror #MSearchField {
    -        width: 97%;
    -        }
    -    .FramedMenuPage .Firefox #MSearchField,
    -    .FramedMenuPage .Safari #MSearchField,
    -    .FramedMenuPage .Konqueror #MSearchField {
    -        width: 98%;
    -        }
    -
    -    /* Firefox doesn't do this right in frames without #MSearchPanel added on.
    -        It's presence doesn't hurt anything other browsers. */
    -    #MSearchPanel.MSearchPanelInactive:hover #MSearchField {
    -        background-color: #FFFFFF;
    -        border: 1px solid #C0C0C0;
    -        padding: 1px 3px;
    -        }
    -    .MSearchPanelActive #MSearchField {
    -        background-color: #FFFFFF;
    -        border: 1px solid #C0C0C0;
    -        font-style: normal;
    -        padding: 1px 3px;
    -        }
    -
    -    #MSearchType {
    -        visibility: hidden;
    -        font: 8pt Verdana, sans-serif;
    -        width: 98%;
    -        padding: 0;
    -        border: 1px solid #C0C0C0;
    -        }
    -    .MSearchPanelActive #MSearchType,
    -    /*  As mentioned above, Firefox doesn't do this right in frames without #MSearchPanel added on. */
    -    #MSearchPanel.MSearchPanelInactive:hover #MSearchType,
    -    #MSearchType:focus {
    -        visibility: visible;
    -        color: #606060;
    -        }
    -    #MSearchType option#MSearchEverything {
    -        font-weight: bold;
    -        }
    -
    -    .Opera8 .MSearchPanelInactive:hover,
    -    .Opera8 .MSearchPanelActive {
    -        margin-left: -1px;
    -        }
    -
    -
    -    iframe#MSearchResults {
    -        width: 60ex;
    -        height: 15em;
    -        }
    -    #MSearchResultsWindow {
    -        display: none;
    -        position: absolute;
    -        left: 0; top: 0;
    -        border: 1px solid #000000;
    -        background-color: #E8E8E8;
    -        }
    -    #MSearchResultsWindowClose {
    -        font-weight: bold;
    -        font-size: 8pt;
    -        display: block;
    -        padding: 2px 5px;
    -        }
    -    #MSearchResultsWindowClose:link,
    -    #MSearchResultsWindowClose:visited {
    -        color: #000000;
    -        text-decoration: none;
    -        }
    -    #MSearchResultsWindowClose:active,
    -    #MSearchResultsWindowClose:hover {
    -        color: #800000;
    -        text-decoration: none;
    -        background-color: #F4F4F4;
    -        }
    -
    -
    -
    -
    -#Content {
    -    padding-bottom: 15px;
    -    }
    -
    -.ContentPage #Content {
    -    border-width: 0 0 1px 1px;
    -    border-style: solid;
    -    border-color: #000000;
    -    background-color: #FFFFFF;
    -    font-size: 9pt;  /* To make 31ex match the menu's 31ex. */
    -    margin-left: 31ex;
    -    }
    -.ContentPage .Firefox #Content {
    -    margin-left: 27ex;
    -    }
    -
    -
    -
    -    .CTopic {
    -        font-size: 10pt;
    -        margin-bottom: 3em;
    -        }
    -
    -
    -    .CTitle {
    -        font-size: 12pt; font-weight: bold;
    -        border-width: 0 0 1px 0; border-style: solid; border-color: #A0A0A0;
    -        margin: 0 15px .5em 15px }
    -
    -    .CGroup .CTitle {
    -        font-size: 16pt; font-variant: small-caps;
    -        padding-left: 15px; padding-right: 15px;
    -        border-width: 0 0 2px 0; border-color: #000000;
    -        margin-left: 0; margin-right: 0 }
    -
    -    .CClass .CTitle,
    -    .CInterface .CTitle,
    -    .CDatabase .CTitle,
    -    .CDatabaseTable .CTitle,
    -    .CSection .CTitle {
    -        font-size: 18pt;
    -        color: #FFFFFF; background-color: #A0A0A0;
    -        padding: 10px 15px 10px 15px;
    -        border-width: 2px 0; border-color: #000000;
    -        margin-left: 0; margin-right: 0 }
    -
    -    #MainTopic .CTitle {
    -        font-size: 20pt;
    -        color: #FFFFFF; background-color: #7070C0;
    -        padding: 10px 15px 10px 15px;
    -        border-width: 0 0 3px 0; border-color: #000000;
    -        margin-left: 0; margin-right: 0 }
    -
    -    .CBody {
    -        margin-left: 15px; margin-right: 15px }
    -
    -
    -    .CToolTip {
    -        position: absolute; visibility: hidden;
    -        left: 0; top: 0;
    -        background-color: #FFFFE0;
    -        padding: 5px;
    -        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #000000;
    -        font-size: 8pt;
    -        }
    -
    -    .Opera .CToolTip {
    -        max-width: 98%;
    -        }
    -
    -    /*  Scrollbars would be useless.  */
    -    .CToolTip blockquote {
    -        overflow: hidden;
    -        }
    -    .IE6 .CToolTip blockquote {
    -        overflow: visible;
    -        }
    -
    -    .CHeading {
    -        font-weight: bold; font-size: 10pt;
    -        margin: 1.5em 0 .5em 0;
    -        }
    -
    -    .CBody pre {
    -        font: 10pt "Courier New", Courier, monospace;
    -	    background-color: #FCFCFC;
    -	    margin: 1em 35px;
    -	    padding: 10px 15px 10px 10px;
    -	    border-color: #E0E0E0 #E0E0E0 #E0E0E0 #E4E4E4;
    -	    border-width: 1px 1px 1px 6px;
    -	    border-style: dashed dashed dashed solid;
    -        }
    -
    -    .CBody ul {
    -        /*  I don't know why CBody's margin doesn't apply, but it's consistent across browsers so whatever.
    -             Reapply it here as padding.  */
    -        padding-left: 15px; padding-right: 15px;
    -        margin: .5em 5ex .5em 5ex;
    -        }
    -
    -    .CDescriptionList {
    -        margin: .5em 5ex 0 5ex }
    -
    -        .CDLEntry {
    -            font: 10pt "Courier New", Courier, monospace; color: #808080;
    -            padding-bottom: .25em;
    -            white-space: nowrap }
    -
    -        .CDLDescription {
    -            font-size: 10pt;  /*  For browsers that don't inherit correctly, like Opera 5.  */
    -            padding-bottom: .5em; padding-left: 5ex }
    -
    -
    -    .CTopic img {
    -        text-align: center;
    -        display: block;
    -        margin: 1em auto;
    -        }
    -    .CImageCaption {
    -        font-variant: small-caps;
    -        font-size: 8pt;
    -        color: #808080;
    -        text-align: center;
    -        position: relative;
    -        top: 1em;
    -        }
    -
    -    .CImageLink {
    -        color: #808080;
    -        font-style: italic;
    -        }
    -    a.CImageLink:link,
    -    a.CImageLink:visited,
    -    a.CImageLink:hover { color: #808080 }
    -
    -
    -
    -
    -
    -.Prototype {
    -    font: 10pt "Courier New", Courier, monospace;
    -    padding: 5px 3ex;
    -    border-width: 1px; border-style: solid;
    -    margin: 0 5ex 1.5em 5ex;
    -    }
    -
    -    .Prototype td {
    -        font-size: 10pt;
    -        }
    -
    -    .PDefaultValue,
    -    .PDefaultValuePrefix,
    -    .PTypePrefix {
    -        color: #8F8F8F;
    -        }
    -    .PTypePrefix {
    -        text-align: right;
    -        }
    -    .PAfterParameters {
    -        vertical-align: bottom;
    -        }
    -
    -    .IE .Prototype table {
    -        padding: 0;
    -        }
    -
    -    .CFunction .Prototype {
    -        background-color: #F4F4F4; border-color: #D0D0D0 }
    -    .CProperty .Prototype {
    -        background-color: #F4F4FF; border-color: #C0C0E8 }
    -    .CVariable .Prototype {
    -        background-color: #FFFFF0; border-color: #E0E0A0 }
    -
    -    .CClass .Prototype {
    -        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    -        background-color: #F4F4F4;
    -        }
    -    .CInterface .Prototype {
    -        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0D0;
    -        background-color: #F4F4FF;
    -        }
    -
    -    .CDatabaseIndex .Prototype,
    -    .CConstant .Prototype {
    -        background-color: #D0D0D0; border-color: #000000 }
    -    .CType .Prototype,
    -    .CEnumeration .Prototype {
    -        background-color: #FAF0F0; border-color: #E0B0B0;
    -        }
    -    .CDatabaseTrigger .Prototype,
    -    .CEvent .Prototype,
    -    .CDelegate .Prototype {
    -        background-color: #F0FCF0; border-color: #B8E4B8 }
    -
    -    .CToolTip .Prototype {
    -        margin: 0 0 .5em 0;
    -        white-space: nowrap;
    -        }
    -
    -
    -
    -
    -
    -.Summary {
    -    margin: 1.5em 5ex 0 5ex }
    -
    -    .STitle {
    -        font-size: 12pt; font-weight: bold;
    -        margin-bottom: .5em }
    -
    -
    -    .SBorder {
    -        background-color: #FFFFF0;
    -        padding: 15px;
    -        border: 1px solid #C0C060 }
    -
    -    /* In a frame IE 6 will make them too long unless you set the width to 100%.  Without frames it will be correct without a width
    -        or slightly too long (but not enough to scroll) with a width.  This arbitrary weirdness simply astounds me.  IE 7 has the same
    -        problem with frames, haven't tested it without.  */
    -    .FramedContentPage .IE .SBorder {
    -        width: 100% }
    -
    -    /*  A treat for Mozilla users.  Blatantly non-standard.  Will be replaced with CSS 3 attributes when finalized/supported.  */
    -    .Firefox .SBorder {
    -        -moz-border-radius: 20px }
    -
    -
    -    .STable {
    -        font-size: 9pt; width: 100% }
    -
    -    .SEntry {
    -        width: 30% }
    -    .SDescription {
    -        width: 70% }
    -
    -
    -    .SMarked {
    -        background-color: #F8F8D8 }
    -
    -    .SDescription { padding-left: 2ex }
    -    .SIndent1 .SEntry { padding-left: 1.5ex }   .SIndent1 .SDescription { padding-left: 3.5ex }
    -    .SIndent2 .SEntry { padding-left: 3.0ex }   .SIndent2 .SDescription { padding-left: 5.0ex }
    -    .SIndent3 .SEntry { padding-left: 4.5ex }   .SIndent3 .SDescription { padding-left: 6.5ex }
    -    .SIndent4 .SEntry { padding-left: 6.0ex }   .SIndent4 .SDescription { padding-left: 8.0ex }
    -    .SIndent5 .SEntry { padding-left: 7.5ex }   .SIndent5 .SDescription { padding-left: 9.5ex }
    -
    -    .SDescription a { color: #800000}
    -    .SDescription a:active { color: #A00000 }
    -
    -    .SGroup td {
    -        padding-top: .5em; padding-bottom: .25em }
    -
    -    .SGroup .SEntry {
    -        font-weight: bold; font-variant: small-caps }
    -
    -    .SGroup .SEntry a { color: #800000 }
    -    .SGroup .SEntry a:active { color: #F00000 }
    -
    -
    -    .SMain td,
    -    .SClass td,
    -    .SDatabase td,
    -    .SDatabaseTable td,
    -    .SSection td {
    -        font-size: 10pt;
    -        padding-bottom: .25em }
    -
    -    .SClass td,
    -    .SDatabase td,
    -    .SDatabaseTable td,
    -    .SSection td {
    -        padding-top: 1em }
    -
    -    .SMain .SEntry,
    -    .SClass .SEntry,
    -    .SDatabase .SEntry,
    -    .SDatabaseTable .SEntry,
    -    .SSection .SEntry {
    -        font-weight: bold;
    -        }
    -
    -    .SMain .SEntry a,
    -    .SClass .SEntry a,
    -    .SDatabase .SEntry a,
    -    .SDatabaseTable .SEntry a,
    -    .SSection .SEntry a { color: #000000 }
    -
    -    .SMain .SEntry a:active,
    -    .SClass .SEntry a:active,
    -    .SDatabase .SEntry a:active,
    -    .SDatabaseTable .SEntry a:active,
    -    .SSection .SEntry a:active { color: #A00000 }
    -
    -
    -
    -
    -
    -.ClassHierarchy {
    -    margin: 0 15px 1em 15px }
    -
    -    .CHEntry {
    -        border-width: 1px 2px 2px 1px; border-style: solid; border-color: #A0A0A0;
    -        margin-bottom: 3px;
    -        padding: 2px 2ex;
    -        font-size: 10pt;
    -        background-color: #F4F4F4; color: #606060;
    -        }
    -
    -    .Firefox .CHEntry {
    -        -moz-border-radius: 4px;
    -        }
    -
    -    .CHCurrent .CHEntry {
    -        font-weight: bold;
    -        border-color: #000000;
    -        color: #000000;
    -        }
    -
    -    .CHChildNote .CHEntry {
    -        font-style: italic;
    -        font-size: 8pt;
    -        }
    -
    -    .CHIndent {
    -        margin-left: 3ex;
    -        }
    -
    -    .CHEntry a:link,
    -    .CHEntry a:visited,
    -    .CHEntry a:hover {
    -        color: #606060;
    -        }
    -    .CHEntry a:active {
    -        color: #800000;
    -        }
    -
    -
    -
    -
    -
    -#Index {
    -    background-color: #FFFFFF;
    -    }
    -
    -/*  As opposed to .PopupSearchResultsPage #Index  */
    -.IndexPage #Index,
    -.FramedIndexPage #Index,
    -.FramedSearchResultsPage #Index {
    -    padding: 15px;
    -    }
    -
    -.IndexPage #Index {
    -    border-width: 0 0 1px 1px;
    -    border-style: solid;
    -    border-color: #000000;
    -    font-size: 9pt;  /* To make 27ex match the menu's 27ex. */
    -    margin-left: 27ex;
    -    }
    -
    -
    -    .IPageTitle {
    -        font-size: 20pt; font-weight: bold;
    -        color: #FFFFFF; background-color: #7070C0;
    -        padding: 10px 15px 10px 15px;
    -        border-width: 0 0 3px 0; border-color: #000000; border-style: solid;
    -        margin: -15px -15px 0 -15px }
    -
    -    .FramedSearchResultsPage .IPageTitle {
    -        margin-bottom: 15px;
    -        }
    -
    -    .INavigationBar {
    -        font-size: 10pt;
    -        text-align: center;
    -        background-color: #FFFFF0;
    -        padding: 5px;
    -        border-bottom: solid 1px black;
    -        margin: 0 -15px 15px -15px;
    -        }
    -
    -    .INavigationBar a {
    -        font-weight: bold }
    -
    -    .IHeading {
    -        font-size: 16pt; font-weight: bold;
    -        padding: 2.5em 0 .5em 0;
    -        text-align: center;
    -        width: 3.5ex;
    -        }
    -    #IFirstHeading {
    -        padding-top: 0;
    -        }
    -
    -    .IEntry {
    -        font-size: 10pt;
    -        padding-left: 1ex;
    -        }
    -    .PopupSearchResultsPage .IEntry {
    -        font-size: 8pt;
    -        padding: 1px 5px;
    -        }
    -    .PopupSearchResultsPage .Opera9 .IEntry,
    -    .FramedSearchResultsPage .Opera9 .IEntry {
    -        text-align: left;
    -        }
    -    .FramedSearchResultsPage .IEntry {
    -        padding: 0;
    -        }
    -
    -    .ISubIndex {
    -        padding-left: 3ex; padding-bottom: .5em }
    -    .PopupSearchResultsPage .ISubIndex {
    -        display: none;
    -        }
    -
    -    /*  While it may cause some entries to look like links when they aren't, I found it's much easier to read the
    -         index if everything's the same color.  */
    -    .ISymbol {
    -        font-weight: bold; color: #900000  }
    -
    -    .IndexPage .ISymbolPrefix,
    -    .FramedIndexPage .ISymbolPrefix {
    -        font-size: 10pt;
    -        text-align: right;
    -        color: #C47C7C;
    -        background-color: #F8F8F8;
    -        border-right: 3px solid #E0E0E0;
    -        border-left: 1px solid #E0E0E0;
    -        padding: 0 1px 0 2px;
    -        }
    -    .PopupSearchResultsPage .ISymbolPrefix,
    -    .FramedSearchResultsPage .ISymbolPrefix {
    -        color: #900000;
    -        }
    -    .PopupSearchResultsPage .ISymbolPrefix {
    -        font-size: 8pt;
    -        }
    -
    -    .IndexPage #IFirstSymbolPrefix,
    -    .FramedIndexPage #IFirstSymbolPrefix {
    -        border-top: 1px solid #E0E0E0;
    -        }
    -    .IndexPage #ILastSymbolPrefix,
    -    .FramedIndexPage #ILastSymbolPrefix {
    -        border-bottom: 1px solid #E0E0E0;
    -        }
    -    .IndexPage #IOnlySymbolPrefix,
    -    .FramedIndexPage #IOnlySymbolPrefix {
    -        border-top: 1px solid #E0E0E0;
    -        border-bottom: 1px solid #E0E0E0;
    -        }
    -
    -    a.IParent,
    -    a.IFile {
    -        display: block;
    -        }
    -
    -    .PopupSearchResultsPage .SRStatus {
    -        padding: 2px 5px;
    -        font-size: 8pt;
    -        font-style: italic;
    -        }
    -    .FramedSearchResultsPage .SRStatus {
    -        font-size: 10pt;
    -        font-style: italic;
    -        }
    -
    -    .SRResult {
    -        display: none;
    -        }
    -
    -
    -
    -#Footer {
    -    font-size: 8pt;
    -    color: #989898;
    -    text-align: right;
    -    }
    -
    -#Footer p {
    -    text-indent: 0;
    -    margin-bottom: .5em;
    -    }
    -
    -.ContentPage #Footer,
    -.IndexPage #Footer {
    -    text-align: right;
    -    margin: 2px;
    -    }
    -
    -.FramedMenuPage #Footer {
    -    text-align: center;
    -    margin: 5em 10px 10px 10px;
    -    padding-top: 1em;
    -    border-top: 1px solid #C8C8C8;
    -    }
    -
    -    #Footer a:link,
    -    #Footer a:hover,
    -    #Footer a:visited { color: #989898 }
    -    #Footer a:active { color: #A00000 }
    -
    -
    -
    -.prettyprint .kwd { color: #800000; }  /* keywords */
    -
    -    .prettyprint.PDefaultValue .kwd,
    -    .prettyprint.PDefaultValuePrefix .kwd,
    -    .prettyprint.PTypePrefix .kwd {
    -        color: #C88F8F;
    -        }
    -
    -.prettyprint .com { color: #008000; }  /* comments */
    -
    -    .prettyprint.PDefaultValue .com,
    -    .prettyprint.PDefaultValuePrefix .com,
    -    .prettyprint.PTypePrefix .com {
    -        color: #8FC88F;
    -        }
    -
    -.prettyprint .str { color: #0000B0; }  /* strings */
    -.prettyprint .lit { color: #0000B0; }  /* literals */
    -
    -    .prettyprint.PDefaultValue .str,
    -    .prettyprint.PDefaultValuePrefix .str,
    -    .prettyprint.PTypePrefix .str,
    -    .prettyprint.PDefaultValue .lit,
    -    .prettyprint.PDefaultValuePrefix .lit,
    -    .prettyprint.PTypePrefix .lit {
    -        color: #8F8FC0;
    -        }
    -
    -.prettyprint .typ { color: #000000; }  /* types */
    -.prettyprint .pun { color: #000000; }  /* punctuation */
    -.prettyprint .pln { color: #000000; }  /* punctuation */
    -
    -    .prettyprint.PDefaultValue .typ,
    -    .prettyprint.PDefaultValuePrefix .typ,
    -    .prettyprint.PTypePrefix .typ,
    -    .prettyprint.PDefaultValue .pun,
    -    .prettyprint.PDefaultValuePrefix .pun,
    -    .prettyprint.PTypePrefix .pun,
    -    .prettyprint.PDefaultValue .pln,
    -    .prettyprint.PDefaultValuePrefix .pln,
    -    .prettyprint.PTypePrefix .pln {
    -        color: #8F8F8F;
    -        }
    -
    -.prettyprint .tag { color: #008; }
    -.prettyprint .atn { color: #606; }
    -.prettyprint .atv { color: #080; }
    -.prettyprint .dec { color: #606; }
    -
    diff --git a/doc/Data/ClassHierarchy.nd b/doc/Data/ClassHierarchy.nd
    index 162a7f59ba7dafc3838b3409fcf6d0ae91b7181f..c9571281299df52b86a4a56030aca3f8848f2e11 100644
    GIT binary patch
    delta 14
    VcmZ3_xSnx>%fw34iB6`B3;-vC1bqMi
    
    delta 14
    VcmZ3_xSnx>%fxEqiB9H>3;-vK1b+Yk
    
    diff --git a/doc/Data/ConfigFileInfo.nd b/doc/Data/ConfigFileInfo.nd
    index ebae7c4189d0e855d2ea0e42a0a4cca47024e9d7..6db8389464a34480a5176dccc82bfd5c31574996 100644
    GIT binary patch
    literal 26
    TcmZQ$G-hC6@SR_@2^|9fZ(0hA
    
    literal 26
    XcmZQ$G-hC6@SUp7;X74E85sirLWl*_
    
    diff --git a/doc/Data/FileInfo.nd b/doc/Data/FileInfo.nd
    index f8470c7df..0a90dbd21 100644
    --- a/doc/Data/FileInfo.nd
    +++ b/doc/Data/FileInfo.nd
    @@ -1,4 +1,13 @@
     1.51
     C/C++
    -/home/tim/git/nodegit/include/blob.h	1301620615	1	GitBlob
    -/home/tim/git/nodegit/include/error.h	1301620603	1	GitError
    +/home/tim/git/nodegit/include/tree.h	1302274388	0	/home/tim/git/nodegit/include/tree.h
    +/home/tim/git/nodegit/include/revwalk.h	1302274388	0	/home/tim/git/nodegit/include/revwalk.h
    +/home/tim/git/nodegit/include/blob.h	1302274388	1	GitBlob
    +/home/tim/git/nodegit/include/tree_entry.h	1302274388	0	/home/tim/git/nodegit/include/tree_entry.h
    +/home/tim/git/nodegit/include/repo.h	1302274388	0	/home/tim/git/nodegit/include/repo.h
    +/home/tim/git/nodegit/include/sig.h	1302274388	0	/home/tim/git/nodegit/include/sig.h
    +/home/tim/git/nodegit/include/reference.h	1302274388	0	/home/tim/git/nodegit/include/reference.h
    +/home/tim/git/nodegit/include/error.h	1302206924	1	GitError
    +/home/tim/git/nodegit/include/commit.h	1302274388	0	/home/tim/git/nodegit/include/commit.h
    +/home/tim/git/nodegit/include/object.h	1302274388	0	/home/tim/git/nodegit/include/object.h
    +/home/tim/git/nodegit/include/oid.h	1302274388	0	/home/tim/git/nodegit/include/oid.h
    diff --git a/doc/Data/SymbolTable.nd b/doc/Data/SymbolTable.nd
    index 5aebce9574d6ba2fa1b56771498ed3caa5db87dc..a661101119b25881df54e19e419057ead38785aa 100644
    GIT binary patch
    delta 102
    zcmV-s0Ga=$8m1Zq1_3hwk$roS278m21EQ1V0=$!@0zi}b0idz?0RfZp1gMij1hA7|
    z3<;B(1)-Dc25yr&2C%bP2Vnw}APS0;#R*%Jp9)k72uEpjMRIa)a+5&>8I#5fqLcgz
    IsFPm|$MK3M{{R30
    
    delta 95
    zcmV-l0HFV-8m1Zs1_3hw005DHdy}9B36T+Qvp53*0h5>lpp#4jOp}}eypse0n3LE9
    zxsy2rsFRunp_8fxuai0kZ?jnkVFHsm3TBhK35t`d3Q3c$3$c^T3<{I|3c8bM48HHd
    BBKQCR
    
    diff --git a/doc/api/commit.html b/doc/api/commit.html
    deleted file mode 100644
    index e57c2ce50..000000000
    --- a/doc/api/commit.html
    +++ /dev/null
    @@ -1,61 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>commit.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               commit.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Commit</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Repo</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Commit</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">);</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Commit</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">lookup</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">oid</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">lookup</span><span class="p">(</span> <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">,</span> <span class="nx">oid</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span> <span class="nx">arguments</span> <span class="p">);</span>
    -
    -      <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">util</span><span class="p">().</span><span class="nx">error</span><span class="p">(</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
    -
    -      <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span> <span class="nx">self</span> <span class="p">)</span> <span class="p">);</span>
    -    <span class="p">});</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">msg</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">messageShort</span><span class="p">();</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">message</span><span class="p">();</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">time</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="k">return</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span> <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">time</span><span class="p">()</span> <span class="o">*</span> <span class="mi">1000</span> <span class="p">);</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">author</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="kd">var</span> <span class="nx">sig</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Sig</span><span class="p">();</span>
    -    
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">author</span><span class="p">(</span> <span class="nx">sig</span> <span class="p">);</span>
    -
    -    <span class="k">return</span> <span class="nx">git</span><span class="p">.</span><span class="nx">sig</span><span class="p">(</span> <span class="nx">sig</span> <span class="p">);</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">tree</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="kd">var</span> <span class="nx">tree</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Tree</span><span class="p">(</span> <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="p">);</span>
    -    <span class="k">if</span><span class="p">(</span> <span class="nx">tree</span><span class="p">.</span><span class="nx">error</span> <span class="p">)</span> <span class="p">{</span>
    -      <span class="k">throw</span> <span class="nx">git</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span> <span class="nx">tree</span><span class="p">.</span><span class="nx">error</span> <span class="p">);</span>
    -    <span class="p">}</span>
    -    <span class="k">else</span> <span class="p">{</span>
    -      <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span><span class="p">.</span><span class="nx">tree</span><span class="p">(</span> <span class="nx">tree</span> <span class="p">);</span>
    -    <span class="p">}</span>
    -
    -    <span class="k">return</span> <span class="nx">git</span><span class="p">.</span><span class="nx">tree</span><span class="p">(</span> <span class="nx">tree</span> <span class="p">);</span>
    -  <span class="p">};</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">commit</span> <span class="o">=</span> <span class="nx">_Commit</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/docco.css b/doc/api/docco.css
    deleted file mode 100644
    index 5aa0a8d73..000000000
    --- a/doc/api/docco.css
    +++ /dev/null
    @@ -1,186 +0,0 @@
    -/*--------------------- Layout and Typography ----------------------------*/
    -body {
    -  font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
    -  font-size: 15px;
    -  line-height: 22px;
    -  color: #252519;
    -  margin: 0; padding: 0;
    -}
    -a {
    -  color: #261a3b;
    -}
    -  a:visited {
    -    color: #261a3b;
    -  }
    -p {
    -  margin: 0 0 15px 0;
    -}
    -h1, h2, h3, h4, h5, h6 {
    -  margin: 0px 0 15px 0;
    -}
    -  h1 {
    -    margin-top: 40px;
    -  }
    -#container {
    -  position: relative;
    -}
    -#background {
    -  position: fixed;
    -  top: 0; left: 525px; right: 0; bottom: 0;
    -  background: #f5f5ff;
    -  border-left: 1px solid #e5e5ee;
    -  z-index: -1;
    -}
    -#jump_to, #jump_page {
    -  background: white;
    -  -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
    -  -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
    -  font: 10px Arial;
    -  text-transform: uppercase;
    -  cursor: pointer;
    -  text-align: right;
    -}
    -#jump_to, #jump_wrapper {
    -  position: fixed;
    -  right: 0; top: 0;
    -  padding: 5px 10px;
    -}
    -  #jump_wrapper {
    -    padding: 0;
    -    display: none;
    -  }
    -    #jump_to:hover #jump_wrapper {
    -      display: block;
    -    }
    -    #jump_page {
    -      padding: 5px 0 3px;
    -      margin: 0 0 25px 25px;
    -    }
    -      #jump_page .source {
    -        display: block;
    -        padding: 5px 10px;
    -        text-decoration: none;
    -        border-top: 1px solid #eee;
    -      }
    -        #jump_page .source:hover {
    -          background: #f5f5ff;
    -        }
    -        #jump_page .source:first-child {
    -        }
    -table td {
    -  border: 0;
    -  outline: 0;
    -}
    -  td.docs, th.docs {
    -    max-width: 450px;
    -    min-width: 450px;
    -    min-height: 5px;
    -    padding: 10px 25px 1px 50px;
    -    overflow-x: hidden;
    -    vertical-align: top;
    -    text-align: left;
    -  }
    -    .docs pre {
    -      margin: 15px 0 15px;
    -      padding-left: 15px;
    -    }
    -    .docs p tt, .docs p code {
    -      background: #f8f8ff;
    -      border: 1px solid #dedede;
    -      font-size: 12px;
    -      padding: 0 0.2em;
    -    }
    -    .pilwrap {
    -      position: relative;
    -    }
    -      .pilcrow {
    -        font: 12px Arial;
    -        text-decoration: none;
    -        color: #454545;
    -        position: absolute;
    -        top: 3px; left: -20px;
    -        padding: 1px 2px;
    -        opacity: 0;
    -        -webkit-transition: opacity 0.2s linear;
    -      }
    -        td.docs:hover .pilcrow {
    -          opacity: 1;
    -        }
    -  td.code, th.code {
    -    padding: 14px 15px 16px 25px;
    -    width: 100%;
    -    vertical-align: top;
    -    background: #f5f5ff;
    -    border-left: 1px solid #e5e5ee;
    -  }
    -    pre, tt, code {
    -      font-size: 12px; line-height: 18px;
    -      font-family: Monaco, Consolas, "Lucida Console", monospace;
    -      margin: 0; padding: 0;
    -    }
    -
    -
    -/*---------------------- Syntax Highlighting -----------------------------*/
    -td.linenos { background-color: #f0f0f0; padding-right: 10px; }
    -span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
    -body .hll { background-color: #ffffcc }
    -body .c { color: #408080; font-style: italic }  /* Comment */
    -body .err { border: 1px solid #FF0000 }         /* Error */
    -body .k { color: #954121 }                      /* Keyword */
    -body .o { color: #666666 }                      /* Operator */
    -body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
    -body .cp { color: #BC7A00 }                     /* Comment.Preproc */
    -body .c1 { color: #408080; font-style: italic } /* Comment.Single */
    -body .cs { color: #408080; font-style: italic } /* Comment.Special */
    -body .gd { color: #A00000 }                     /* Generic.Deleted */
    -body .ge { font-style: italic }                 /* Generic.Emph */
    -body .gr { color: #FF0000 }                     /* Generic.Error */
    -body .gh { color: #000080; font-weight: bold }  /* Generic.Heading */
    -body .gi { color: #00A000 }                     /* Generic.Inserted */
    -body .go { color: #808080 }                     /* Generic.Output */
    -body .gp { color: #000080; font-weight: bold }  /* Generic.Prompt */
    -body .gs { font-weight: bold }                  /* Generic.Strong */
    -body .gu { color: #800080; font-weight: bold }  /* Generic.Subheading */
    -body .gt { color: #0040D0 }                     /* Generic.Traceback */
    -body .kc { color: #954121 }                     /* Keyword.Constant */
    -body .kd { color: #954121; font-weight: bold }  /* Keyword.Declaration */
    -body .kn { color: #954121; font-weight: bold }  /* Keyword.Namespace */
    -body .kp { color: #954121 }                     /* Keyword.Pseudo */
    -body .kr { color: #954121; font-weight: bold }  /* Keyword.Reserved */
    -body .kt { color: #B00040 }                     /* Keyword.Type */
    -body .m { color: #666666 }                      /* Literal.Number */
    -body .s { color: #219161 }                      /* Literal.String */
    -body .na { color: #7D9029 }                     /* Name.Attribute */
    -body .nb { color: #954121 }                     /* Name.Builtin */
    -body .nc { color: #0000FF; font-weight: bold }  /* Name.Class */
    -body .no { color: #880000 }                     /* Name.Constant */
    -body .nd { color: #AA22FF }                     /* Name.Decorator */
    -body .ni { color: #999999; font-weight: bold }  /* Name.Entity */
    -body .ne { color: #D2413A; font-weight: bold }  /* Name.Exception */
    -body .nf { color: #0000FF }                     /* Name.Function */
    -body .nl { color: #A0A000 }                     /* Name.Label */
    -body .nn { color: #0000FF; font-weight: bold }  /* Name.Namespace */
    -body .nt { color: #954121; font-weight: bold }  /* Name.Tag */
    -body .nv { color: #19469D }                     /* Name.Variable */
    -body .ow { color: #AA22FF; font-weight: bold }  /* Operator.Word */
    -body .w { color: #bbbbbb }                      /* Text.Whitespace */
    -body .mf { color: #666666 }                     /* Literal.Number.Float */
    -body .mh { color: #666666 }                     /* Literal.Number.Hex */
    -body .mi { color: #666666 }                     /* Literal.Number.Integer */
    -body .mo { color: #666666 }                     /* Literal.Number.Oct */
    -body .sb { color: #219161 }                     /* Literal.String.Backtick */
    -body .sc { color: #219161 }                     /* Literal.String.Char */
    -body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
    -body .s2 { color: #219161 }                     /* Literal.String.Double */
    -body .se { color: #BB6622; font-weight: bold }  /* Literal.String.Escape */
    -body .sh { color: #219161 }                     /* Literal.String.Heredoc */
    -body .si { color: #BB6688; font-weight: bold }  /* Literal.String.Interpol */
    -body .sx { color: #954121 }                     /* Literal.String.Other */
    -body .sr { color: #BB6688 }                     /* Literal.String.Regex */
    -body .s1 { color: #219161 }                     /* Literal.String.Single */
    -body .ss { color: #19469D }                     /* Literal.String.Symbol */
    -body .bp { color: #954121 }                     /* Name.Builtin.Pseudo */
    -body .vc { color: #19469D }                     /* Name.Variable.Class */
    -body .vg { color: #19469D }                     /* Name.Variable.Global */
    -body .vi { color: #19469D }                     /* Name.Variable.Instance */
    -body .il { color: #666666 }                     /* Literal.Number.Integer.Long */
    \ No newline at end of file
    diff --git a/doc/api/error.html b/doc/api/error.html
    deleted file mode 100644
    index 0e5e52aac..000000000
    --- a/doc/api/error.html
    +++ /dev/null
    @@ -1,24 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>error.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               error.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Error</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nb">Error</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="p">{</span>
    -    <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="nx">self</span><span class="p">.</span><span class="nx">error</span> <span class="p">)</span> <span class="p">{</span>
    -      <span class="nx">self</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nb">Error</span><span class="p">();</span>
    -    <span class="p">}</span>
    -
    -    <span class="k">if</span><span class="p">(</span> <span class="k">typeof</span> <span class="nx">obj</span> <span class="o">===</span> <span class="s1">&#39;number&#39;</span> <span class="p">)</span> <span class="p">{</span>
    -      <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">error</span><span class="p">.</span><span class="nx">strError</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">);</span>
    -    <span class="p">}</span>
    -  <span class="p">}</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">_Error</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/index.html b/doc/api/index.html
    deleted file mode 100644
    index 1c2fc273f..000000000
    --- a/doc/api/index.html
    +++ /dev/null
    @@ -1,22 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>index.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               index.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">util</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./util.js&#39;</span> <span class="p">).</span><span class="nx">util</span><span class="p">,</span>
    -    <span class="nx">repo</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./repo.js&#39;</span> <span class="p">).</span><span class="nx">repo</span><span class="p">,</span>
    -    <span class="nx">error</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./error.js&#39;</span> <span class="p">).</span><span class="nx">error</span><span class="p">,</span>
    -    <span class="nx">sig</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./sig.js&#39;</span> <span class="p">).</span><span class="nx">sig</span><span class="p">,</span>
    -    <span class="nx">oid</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./oid.js&#39;</span> <span class="p">).</span><span class="nx">oid</span><span class="p">,</span>
    -    <span class="nx">ref</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./ref.js&#39;</span> <span class="p">).</span><span class="nx">ref</span><span class="p">,</span>
    -    <span class="nx">revwalk</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./revwalk.js&#39;</span> <span class="p">).</span><span class="nx">revwalk</span><span class="p">,</span>
    -    <span class="nx">commit</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./commit.js&#39;</span> <span class="p">).</span><span class="nx">commit</span><span class="p">,</span>
    -    <span class="nx">tree</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;./tree.js&#39;</span> <span class="p">).</span><span class="nx">tree</span><span class="p">;</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">git2</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../build/default/nodegit2.node&#39;</span> <span class="p">);</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">util</span> <span class="o">=</span> <span class="nx">util</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">repo</span> <span class="o">=</span> <span class="nx">repo</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">oid</span> <span class="o">=</span> <span class="nx">oid</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">sig</span> <span class="o">=</span> <span class="nx">sig</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="nx">error</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">revwalk</span> <span class="o">=</span> <span class="nx">revwalk</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">tree</span> <span class="o">=</span> <span class="nx">tree</span><span class="p">;</span>
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">commit</span> <span class="o">=</span> <span class="nx">commit</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/oid.html b/doc/api/oid.html
    deleted file mode 100644
    index 55e060d4f..000000000
    --- a/doc/api/oid.html
    +++ /dev/null
    @@ -1,22 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>oid.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               oid.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Oid</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Oid</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">oid</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">oid</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Oid</span><span class="p">();</span>
    -
    -    <span class="k">if</span><span class="p">(</span> <span class="k">typeof</span> <span class="nx">obj</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span> <span class="p">)</span> <span class="p">{</span>
    -      <span class="nx">self</span><span class="p">.</span><span class="nx">oid</span><span class="p">.</span><span class="nx">mkstr</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">);</span>
    -    <span class="p">}</span>
    -  <span class="p">}</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">oid</span> <span class="o">=</span> <span class="nx">_Oid</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/ref.html b/doc/api/ref.html
    deleted file mode 100644
    index a86034790..000000000
    --- a/doc/api/ref.html
    +++ /dev/null
    @@ -1,27 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>ref.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               ref.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Ref</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Repo</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Ref</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">);</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Ref</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">oid</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -    <span class="kd">var</span> <span class="nx">oid</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">oid</span><span class="p">();</span>
    -
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">ref</span><span class="p">.</span><span class="nx">oid</span><span class="p">(</span> <span class="nx">oid</span><span class="p">.</span><span class="nx">oid</span> <span class="p">);</span>
    -
    -    <span class="k">return</span> <span class="nx">oid</span><span class="p">;</span>
    -  <span class="p">};</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="nx">_Ref</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/repo.html b/doc/api/repo.html
    deleted file mode 100644
    index e8e27195b..000000000
    --- a/doc/api/repo.html
    +++ /dev/null
    @@ -1,64 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>repo.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               repo.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;path&#39;</span> <span class="p">),</span>
    -    <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Repo</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span></pre></div>             </td>           </tr>                               <tr id="section-2">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-2">&#182;</a>               </div>               <p>Public namespace</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span></pre></div>             </td>           </tr>                               <tr id="section-3">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-3">&#182;</a>               </div>               <p>Private internal use variables</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="kd">var</span> <span class="nx">_commits</span> <span class="o">=</span> <span class="p">[];</span></pre></div>             </td>           </tr>                               <tr id="section-4">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-4">&#182;</a>               </div>               <p>Internal reference to a Git repository</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Repo</span><span class="p">();</span></pre></div>             </td>           </tr>                               <tr id="section-5">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-5">&#182;</a>               </div>               <p>Work with a specific head reference</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">head</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="kd">var</span> <span class="nx">head</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">ref</span><span class="p">(</span> <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="p">);</span>
    -    
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">lookupRef</span><span class="p">(</span> <span class="nx">head</span><span class="p">.</span><span class="nx">ref</span><span class="p">,</span> <span class="s1">&#39;refs/heads/&#39;</span><span class="o">+</span> <span class="nx">name</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span> <span class="nx">arguments</span> <span class="p">);</span>
    -      <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">util</span><span class="p">().</span><span class="nx">error</span><span class="p">(</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
    -
    -      <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span> <span class="nx">head</span><span class="p">,</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span> <span class="nx">head</span> <span class="p">)</span> <span class="p">);</span>
    -    <span class="p">});</span>
    -  <span class="p">};</span></pre></div>             </td>           </tr>                               <tr id="section-6">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-6">&#182;</a>               </div>               <p>Find a single commit</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">commit</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">sha</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="kd">var</span> <span class="nx">oid</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">oid</span><span class="p">(</span> <span class="nx">sha</span> <span class="p">);</span>
    -
    -    <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
    -
    -    <span class="kd">var</span> <span class="nx">commit</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">commit</span><span class="p">(</span> <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span> <span class="p">);</span>
    -    <span class="nx">commit</span><span class="p">.</span><span class="nx">lookup</span><span class="p">(</span> <span class="nx">oid</span><span class="p">.</span><span class="nx">oid</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">);</span>
    -  <span class="p">};</span></pre></div>             </td>           </tr>                               <tr id="section-7">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-7">&#182;</a>               </div>               <p>self.find = function( name, callback ) {
    - var ref = new git.git2.Ref( repo );</p>
    -
    -<p>if( !callback ) { return; }</p>             </td>             <td class="code">               <div class="highlight"><pre></pre></div>             </td>           </tr>                               <tr id="section-8">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-8">&#182;</a>               </div>               <p>self.repo.lookupRef( ref, name, function() {
    -   var args = Array.prototype.slice.call( arguments ),
    -       ref = git.ref( ref );</p>             </td>             <td class="code">               <div class="highlight"><pre></pre></div>             </td>           </tr>                               <tr id="section-9">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-9">&#182;</a>               </div>               <p>args[0] = git.util().error( args[0] );</p>             </td>             <td class="code">               <div class="highlight"><pre></pre></div>             </td>           </tr>                               <tr id="section-10">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-10">&#182;</a>               </div>               <p>callback.apply( ref, args.concat( ref ) );
    - });
    -};</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">init</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">is_bare</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
    -
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">is_bare</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span> <span class="nx">arguments</span> <span class="p">);</span>
    -
    -      <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">util</span><span class="p">().</span><span class="nx">error</span><span class="p">(</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
    -
    -      <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span> <span class="nx">self</span> <span class="p">)</span> <span class="p">);</span>
    -    <span class="p">});</span>
    -
    -    <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -  <span class="p">};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">free</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> 
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">free</span><span class="p">();</span>
    -    <span class="k">delete</span> <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">;</span>
    -  <span class="p">};</span></pre></div>             </td>           </tr>                               <tr id="section-11">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-11">&#182;</a>               </div>               <p>Constructor use</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="k">if</span><span class="p">(</span> <span class="nx">path</span> <span class="o">&amp;&amp;</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
    -
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span> <span class="nx">path</span><span class="p">.</span><span class="nx">normalize</span><span class="p">(</span> <span class="nx">path</span> <span class="p">),</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span> <span class="nx">arguments</span> <span class="p">);</span>
    -
    -      <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">util</span><span class="p">().</span><span class="nx">error</span><span class="p">(</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
    -
    -      <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span> <span class="nx">self</span> <span class="p">)</span> <span class="p">);</span>
    -    <span class="p">});</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="nx">path</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">repo</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span> <span class="nx">path</span> <span class="p">);</span>
    -  <span class="p">}</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">repo</span> <span class="o">=</span> <span class="nx">_Repo</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/revwalk.html b/doc/api/revwalk.html
    deleted file mode 100644
    index ce7cda0b1..000000000
    --- a/doc/api/revwalk.html
    +++ /dev/null
    @@ -1,24 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>revwalk.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               revwalk.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_RevWalk</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">repo</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span></pre></div>             </td>           </tr>                               <tr id="section-2">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-2">&#182;</a>               </div>               <p>Internal reference to a Git reference</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">revwalk</span> <span class="o">=</span> <span class="nx">revwalk</span> <span class="o">||</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">RevWalk</span><span class="p">(</span> <span class="nx">repo</span> <span class="p">);</span></pre></div>             </td>           </tr>                               <tr id="section-3">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-3">&#182;</a>               </div>               <p>Walk will map to the next method</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="nx">self</span><span class="p">.</span><span class="nx">walk</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">commit</span><span class="p">,</span> <span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="k">if</span><span class="p">(</span> <span class="o">!</span><span class="nx">callback</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
    -
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">revwalk</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span> <span class="nx">commit</span> <span class="p">);</span>
    -
    -    <span class="nx">revwalk</span><span class="p">.</span><span class="nx">next</span><span class="p">(</span> <span class="nx">commit</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span> <span class="nx">arguments</span> <span class="p">);</span>
    -
    -      <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="nx">git</span><span class="p">.</span><span class="nx">util</span><span class="p">().</span><span class="nx">error</span><span class="p">(</span> <span class="nx">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">);</span>
    -
    -
    -      <span class="nx">callback</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span> <span class="nx">commit</span><span class="p">,</span> <span class="nx">args</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span> <span class="nx">commit</span> <span class="p">)</span> <span class="p">);</span>
    -    <span class="p">});</span>
    -  <span class="p">};</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">revwalk</span> <span class="o">=</span> <span class="nx">_RevWalk</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/sig.html b/doc/api/sig.html
    deleted file mode 100644
    index 9ab87d3e5..000000000
    --- a/doc/api/sig.html
    +++ /dev/null
    @@ -1,28 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>sig.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               sig.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Sig</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="p">{</span>
    -    <span class="nx">get</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">sig</span><span class="p">.</span><span class="nx">name</span><span class="p">();</span>
    -    <span class="p">},</span>
    -    <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">true</span>
    -  <span class="p">});</span>
    -
    -  <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="s1">&#39;email&#39;</span><span class="p">,</span> <span class="p">{</span>
    -    <span class="nx">get</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">sig</span><span class="p">.</span><span class="nx">email</span><span class="p">;</span>
    -    <span class="p">},</span>
    -    <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">true</span>
    -  <span class="p">});</span></pre></div>             </td>           </tr>                               <tr id="section-2">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-2">&#182;</a>               </div>               <p>Internal references to Git references</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Repo</span> <span class="p">)</span> <span class="p">{</span></pre></div>             </td>           </tr>                               <tr id="section-3">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-3">&#182;</a>               </div>               <p>TODO: Add support for creation</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="p">}</span>
    -  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Sig</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">sig</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">sig</span> <span class="o">=</span> <span class="nx">_Sig</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/tree.html b/doc/api/tree.html
    deleted file mode 100644
    index d9ad6e884..000000000
    --- a/doc/api/tree.html
    +++ /dev/null
    @@ -1,24 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>tree.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               tree.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Tree</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">obj</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span> <span class="nx">self</span><span class="p">,</span> <span class="s1">&#39;length&#39;</span><span class="p">,</span> <span class="p">{</span>
    -    <span class="nx">get</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    -      <span class="k">return</span> <span class="nx">self</span><span class="p">.</span><span class="nx">tree</span><span class="p">.</span><span class="nx">entryCount</span><span class="p">();</span>
    -    <span class="p">},</span>
    -    <span class="nx">enumerable</span><span class="o">:</span> <span class="kc">true</span>
    -  <span class="p">});</span></pre></div>             </td>           </tr>                               <tr id="section-2">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-2">&#182;</a>               </div>               <p>Internal references to Git references</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="k">if</span><span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Repo</span> <span class="p">)</span> <span class="p">{</span></pre></div>             </td>           </tr>                               <tr id="section-3">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-3">&#182;</a>               </div>               <p>TODO: Add support for creation</p>             </td>             <td class="code">               <div class="highlight"><pre>  <span class="p">}</span>
    -  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span> <span class="nx">obj</span> <span class="k">instanceof</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Tree</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">tree</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
    -  <span class="p">}</span>
    -  <span class="k">else</span> <span class="p">{</span>
    -    <span class="nx">self</span><span class="p">.</span><span class="nx">tree</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">git</span><span class="p">.</span><span class="nx">git2</span><span class="p">.</span><span class="nx">Tree</span><span class="p">();</span>
    -  <span class="p">}</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">tree</span> <span class="o">=</span> <span class="nx">_Tree</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/doc/api/util.html b/doc/api/util.html
    deleted file mode 100644
    index 0dddfeebd..000000000
    --- a/doc/api/util.html
    +++ /dev/null
    @@ -1,19 +0,0 @@
    -<!DOCTYPE html>  <html> <head>   <title>util.js</title>   <meta http-equiv="content-type" content="text/html; charset=UTF-8">   <link rel="stylesheet" media="all" href="docco.css" /> </head> <body>   <div id="container">     <div id="background"></div>            <div id="jump_to">         Jump To &hellip;         <div id="jump_wrapper">           <div id="jump_page">                                           <a class="source" href="commit.html">                 commit.js               </a>                                           <a class="source" href="error.html">                 error.js               </a>                                           <a class="source" href="index.html">                 index.js               </a>                                           <a class="source" href="oid.html">                 oid.js               </a>                                           <a class="source" href="ref.html">                 ref.js               </a>                                           <a class="source" href="repo.html">                 repo.js               </a>                                           <a class="source" href="revwalk.html">                 revwalk.js               </a>                                           <a class="source" href="sig.html">                 sig.js               </a>                                           <a class="source" href="tree.html">                 tree.js               </a>                                           <a class="source" href="util.html">                 util.js               </a>                        </div>         </div>       </div>          <table cellpadding="0" cellspacing="0">       <thead>         <tr>           <th class="docs">             <h1>               util.js             </h1>           </th>           <th class="code">           </th>         </tr>       </thead>       <tbody>                               <tr id="section-1">             <td class="docs">               <div class="pilwrap">                 <a class="pilcrow" href="#section-1">&#182;</a>               </div>                            </td>             <td class="code">               <div class="highlight"><pre><span class="kd">var</span> <span class="nx">git</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span> <span class="s1">&#39;../&#39;</span> <span class="p">);</span>
    -
    -<span class="kd">var</span> <span class="nx">_Util</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span> <span class="nx">error</span> <span class="p">)</span> <span class="p">{</span>
    -  <span class="kd">var</span> <span class="nx">self</span> <span class="o">=</span> <span class="p">{};</span>
    -
    -  <span class="nx">self</span><span class="p">.</span><span class="nx">error</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">error</span><span class="p">(</span> <span class="nx">err</span> <span class="p">)</span> <span class="p">{</span>
    -    <span class="k">if</span><span class="p">(</span><span class="nx">err</span> <span class="o">!==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    -      <span class="k">return</span> <span class="nx">git</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span> <span class="nx">err</span> <span class="p">);</span>
    -    <span class="p">}</span>
    -
    -    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    -  <span class="p">};</span>
    -
    -  <span class="k">return</span> <span class="nx">self</span><span class="p">;</span>
    -<span class="p">};</span>
    -
    -<span class="nx">exports</span><span class="p">.</span><span class="nx">util</span> <span class="o">=</span> <span class="nx">_Util</span><span class="p">;</span>
    -
    -</pre></div>             </td>           </tr>                </tbody>     </table>   </div> </body> </html> 
    \ No newline at end of file
    diff --git a/src/blob.cc b/src/blob.cc
    index 1ff6d0b2e..ce6718da2 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -65,7 +65,7 @@ Handle<Value> GitBlob::New(const Arguments& args) {
       GitBlob* blob = new GitBlob();
       blob->Wrap(args.This());
     
    -  return args.This();
    +  return scope.Close(args.This());
     }
     
     Handle<Value> GitBlob::Lookup(const Arguments& args) {
    diff --git a/util/nodejshint.js b/util/nodejshint.js
    index f27558824..384db40cc 100644
    --- a/util/nodejshint.js
    +++ b/util/nodejshint.js
    @@ -14,10 +14,10 @@ var nodejshint = function() {
     
             if( pass = JSHINT( data.toString() ), pass ) {
               counter++;
    -          console.log( '✔ Passed '+ file +'' );
    +          console.log( '✔ Passed '+ file );
             }
             else {
    -          console.log( 'x Failed '+ file +'' );
    +          console.log( 'x Failed '+ file );
               JSHINT.errors.forEach( function( err ) {
                 
                 if( err ) {
    
    From 16904bd48ecde2635615cd7dc408a72a746e7443 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 16:51:31 -0400
    Subject: [PATCH 26/37] Corrected previous commit
    
    ---
     .gitignore                         |    6 +-
     doc/Data/ClassHierarchy.nd         |  Bin 175 -> 0 bytes
     doc/Data/ConfigFileInfo.nd         |  Bin 26 -> 0 bytes
     doc/Data/FileInfo.nd               |   13 -
     doc/Data/ImageFileInfo.nd          |  Bin 8 -> 0 bytes
     doc/Data/ImageReferenceTable.nd    |  Bin 8 -> 0 bytes
     doc/Data/IndexInfo.nd              |  Bin 154 -> 0 bytes
     doc/Data/PreviousMenuState.nd      |  Bin 198 -> 0 bytes
     doc/Data/PreviousSettings.nd       |  Bin 81 -> 0 bytes
     doc/Data/SymbolTable.nd            |  Bin 3366 -> 0 bytes
     doc/Languages.txt                  |  113 --
     doc/Menu.txt                       |   59 --
     doc/{styles/main.css => Theme.css} |    0
     doc/Topics.txt                     |   81 --
     doc/files/blob-h.html              |   84 --
     doc/files/error-h.html             |   54 -
     doc/index.html                     |    1 -
     doc/index/Classes.html             |   37 -
     doc/index/Functions.html           |   61 --
     doc/index/General.html             |   73 --
     doc/index/Variables.html           |   37 -
     doc/javascript/main.js             |  841 ---------------
     doc/javascript/prettify.js         | 1526 ----------------------------
     doc/javascript/searchdata.js       |  122 ---
     doc/search/ClassesG.html           |   20 -
     doc/search/ClassesL.html           |   20 -
     doc/search/FunctionsC.html         |   20 -
     doc/search/FunctionsE.html         |   20 -
     doc/search/FunctionsG.html         |   20 -
     doc/search/FunctionsI.html         |   20 -
     doc/search/FunctionsL.html         |   20 -
     doc/search/FunctionsN.html         |   20 -
     doc/search/FunctionsR.html         |   20 -
     doc/search/FunctionsS.html         |   20 -
     doc/search/GeneralB.html           |   20 -
     doc/search/GeneralC.html           |   20 -
     doc/search/GeneralE.html           |   20 -
     doc/search/GeneralF.html           |   20 -
     doc/search/GeneralG.html           |   20 -
     doc/search/GeneralI.html           |   20 -
     doc/search/GeneralL.html           |   20 -
     doc/search/GeneralN.html           |   20 -
     doc/search/GeneralR.html           |   20 -
     doc/search/GeneralS.html           |   20 -
     doc/search/GeneralV.html           |   20 -
     doc/search/NoResults.html          |   15 -
     doc/search/VariablesB.html         |   20 -
     doc/search/VariablesC.html         |   20 -
     48 files changed, 3 insertions(+), 3580 deletions(-)
     delete mode 100644 doc/Data/ClassHierarchy.nd
     delete mode 100644 doc/Data/ConfigFileInfo.nd
     delete mode 100644 doc/Data/FileInfo.nd
     delete mode 100644 doc/Data/ImageFileInfo.nd
     delete mode 100644 doc/Data/ImageReferenceTable.nd
     delete mode 100644 doc/Data/IndexInfo.nd
     delete mode 100644 doc/Data/PreviousMenuState.nd
     delete mode 100644 doc/Data/PreviousSettings.nd
     delete mode 100644 doc/Data/SymbolTable.nd
     delete mode 100644 doc/Languages.txt
     delete mode 100644 doc/Menu.txt
     rename doc/{styles/main.css => Theme.css} (100%)
     delete mode 100644 doc/Topics.txt
     delete mode 100644 doc/files/blob-h.html
     delete mode 100644 doc/files/error-h.html
     delete mode 100644 doc/index.html
     delete mode 100644 doc/index/Classes.html
     delete mode 100644 doc/index/Functions.html
     delete mode 100644 doc/index/General.html
     delete mode 100644 doc/index/Variables.html
     delete mode 100644 doc/javascript/main.js
     delete mode 100644 doc/javascript/prettify.js
     delete mode 100644 doc/javascript/searchdata.js
     delete mode 100644 doc/search/ClassesG.html
     delete mode 100644 doc/search/ClassesL.html
     delete mode 100644 doc/search/FunctionsC.html
     delete mode 100644 doc/search/FunctionsE.html
     delete mode 100644 doc/search/FunctionsG.html
     delete mode 100644 doc/search/FunctionsI.html
     delete mode 100644 doc/search/FunctionsL.html
     delete mode 100644 doc/search/FunctionsN.html
     delete mode 100644 doc/search/FunctionsR.html
     delete mode 100644 doc/search/FunctionsS.html
     delete mode 100644 doc/search/GeneralB.html
     delete mode 100644 doc/search/GeneralC.html
     delete mode 100644 doc/search/GeneralE.html
     delete mode 100644 doc/search/GeneralF.html
     delete mode 100644 doc/search/GeneralG.html
     delete mode 100644 doc/search/GeneralI.html
     delete mode 100644 doc/search/GeneralL.html
     delete mode 100644 doc/search/GeneralN.html
     delete mode 100644 doc/search/GeneralR.html
     delete mode 100644 doc/search/GeneralS.html
     delete mode 100644 doc/search/GeneralV.html
     delete mode 100644 doc/search/NoResults.html
     delete mode 100644 doc/search/VariablesB.html
     delete mode 100644 doc/search/VariablesC.html
    
    diff --git a/.gitignore b/.gitignore
    index afc53f86c..4a7e18cf2 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -1,4 +1,4 @@
     .lock-wscript
    -build/
    -doc/
    -!doc/Theme.css
    +./build/
    +./doc
    +!./doc/Theme.css
    diff --git a/doc/Data/ClassHierarchy.nd b/doc/Data/ClassHierarchy.nd
    deleted file mode 100644
    index c9571281299df52b86a4a56030aca3f8848f2e11..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 175
    zcmZQ$G-hC6U}WIS$<NO&Er>5lEi6qfE&+?F=x5~Trs|hu=IW<smgwi@r=)`D%)I2B
    t(v(#Fq@4UDy$qmU4)@Fw*P^2QBCsx1{JK&>3c=dgf!dsaW{_YP0|219H6;K5
    
    diff --git a/doc/Data/ConfigFileInfo.nd b/doc/Data/ConfigFileInfo.nd
    deleted file mode 100644
    index 6db8389464a34480a5176dccc82bfd5c31574996..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 26
    TcmZQ$G-hC6@SR_@2^|9fZ(0hA
    
    diff --git a/doc/Data/FileInfo.nd b/doc/Data/FileInfo.nd
    deleted file mode 100644
    index 0a90dbd21..000000000
    --- a/doc/Data/FileInfo.nd
    +++ /dev/null
    @@ -1,13 +0,0 @@
    -1.51
    -C/C++
    -/home/tim/git/nodegit/include/tree.h	1302274388	0	/home/tim/git/nodegit/include/tree.h
    -/home/tim/git/nodegit/include/revwalk.h	1302274388	0	/home/tim/git/nodegit/include/revwalk.h
    -/home/tim/git/nodegit/include/blob.h	1302274388	1	GitBlob
    -/home/tim/git/nodegit/include/tree_entry.h	1302274388	0	/home/tim/git/nodegit/include/tree_entry.h
    -/home/tim/git/nodegit/include/repo.h	1302274388	0	/home/tim/git/nodegit/include/repo.h
    -/home/tim/git/nodegit/include/sig.h	1302274388	0	/home/tim/git/nodegit/include/sig.h
    -/home/tim/git/nodegit/include/reference.h	1302274388	0	/home/tim/git/nodegit/include/reference.h
    -/home/tim/git/nodegit/include/error.h	1302206924	1	GitError
    -/home/tim/git/nodegit/include/commit.h	1302274388	0	/home/tim/git/nodegit/include/commit.h
    -/home/tim/git/nodegit/include/object.h	1302274388	0	/home/tim/git/nodegit/include/object.h
    -/home/tim/git/nodegit/include/oid.h	1302274388	0	/home/tim/git/nodegit/include/oid.h
    diff --git a/doc/Data/ImageFileInfo.nd b/doc/Data/ImageFileInfo.nd
    deleted file mode 100644
    index b6cb43bc50d6a1723dadb0392ba74e8f4c83d110..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    diff --git a/doc/Data/ImageReferenceTable.nd b/doc/Data/ImageReferenceTable.nd
    deleted file mode 100644
    index b6cb43bc50d6a1723dadb0392ba74e8f4c83d110..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 8
    McmZQ$G-dz+00D6TI{*Lx
    
    diff --git a/doc/Data/IndexInfo.nd b/doc/Data/IndexInfo.nd
    deleted file mode 100644
    index 5c9e0d47bd933d2811bb5c569d1a54eacdc36058..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 154
    zcmZQ$G-dz-cK6i0)S|>3Ad`_1h=3T#0udlSm}X$$a4XGAF3HT#1L*)sqNxH216743
    c7G)+T<)i}HAidZDt8-3baWPB|tPD*p04LN7jsO4v
    
    diff --git a/doc/Data/PreviousMenuState.nd b/doc/Data/PreviousMenuState.nd
    deleted file mode 100644
    index a83adbe61fa9836006f83e7edae5618d429111f7..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 198
    zcmaKjK@Ng25JkUHq<RkB(uFtBpz#9k3ygG<GELe-^!Bz1Yghl}=e_;r0$>Z&4^CI-
    zJDjxj6Y0d09yw|81C?dz-8_))veVccs2sE*_<-~Of{N7G9jd1c^iepr3x#j8EF<cX
    mgo1Gj_05?WDWd8NC>-vy^ohMhsP~HbeVM?fb6Hf&iueIlAwA*%
    
    diff --git a/doc/Data/PreviousSettings.nd b/doc/Data/PreviousSettings.nd
    deleted file mode 100644
    index 199e3d0ef315b9d49a2f4dfa290949385545bb35..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 81
    zcmZQ$G-hC6U;$!AAe7b5$j?pHFUic+PtPpT&&y9q1<{##$vLGdsSJ#Uj0}?4m89e+
    LGq8As`1$|<3dR-{
    
    diff --git a/doc/Data/SymbolTable.nd b/doc/Data/SymbolTable.nd
    deleted file mode 100644
    index a661101119b25881df54e19e419057ead38785aa..0000000000000000000000000000000000000000
    GIT binary patch
    literal 0
    HcmV?d00001
    
    literal 3366
    zcmcguZEq7t5MGxCl8B~AfCQ?jwgrl9g;*kLg+djTgd{{lq~i5sv_6mHCF@(WyO$Jx
    z5Wka|-M!nhOVGs<_`}`FcRTaU%rno-#nw7w><4yfPr5Co`|QU)U0du<Z=}c2b8_4}
    zlCGEQ0nlCMnJNa*qp#LRZ1F@GDf$Z73cfw0f4kgUxVxV_Fu73Nzz_`N8E~h0`4!jw
    zcaXW7<DYl-4-Ov>@z67^kBdoj48tPNoYXmMTPK{%xXc}&vDOBu4-LHUBAoXfig<BP
    zx*cP*VHcjmDSEs6^@xGKrI0IkkD|MihZ`GDMLtmQH$A=)s(?+7M%$Urt>g6PV{=rD
    zA$Rr<F3gebFn^nV#Y@XP6H3()F7M~kNulINm<Q;eB~UE~Jeg|G06*|0+2jrpg6pu}
    zYYPJ^8s;-N=WYai=LF-}8B2$y;zOgy{5g_i?VYV7UV0OWVVeV9I5HaRKF!CAbF*Qc
    zXb?`fiFO7i+DfO*2O_~lukuhHdPj-r%0Y+S3kSIHERHM<Q3`)5SXgVZ73FD4q$<F=
    z#&liCl(3dvx6Txq<1<!1ZR^|#ncLJhmP$a0&kBQm<UF#~7y{Cc6ZoMvh&j_^tV^`H
    z%7wC;g8Y&}k#o;zd_Mq=nW-H^n`jF;*caJxVkN?W{~#+Itkp}s_|losNOSbitx$t(
    zButl=hT2p)MAu<|?SVrq2E^dL&eaFrFNUZl{8YM;&xsMIK0?Zi9>%gEyh_imc?Xng
    zZ4MobCrUVKqVww8KV=VLGye<5O6$<4Z}{D)Kd++oO+pmPTlaapY^MyvaZq+m{o^|D
    zs7eiSmeRq#wJ24IT_%MM-O@ar70lZ+QXLHaU!{|~q+4^i`)&$Y;aw_OK?2yUh;{u-
    z(`m|3!x@z<wNfWEh7knnH-F<eCAV;@^9?>J`U;0@m=bS|n6O)PozN6O^-;2qO8TIh
    z@O3U<dLd3<hT+vL6=}%+L|PD!x0~y8O#!AxQ8V``e>FSHh+-SX6x+UWNmCp*g3z6y
    zSa&I&XbD4_Q{!5-4W_a1#^LjORa-CIUo7sgXBF3vje2z#ww1Q$(9}u<E>kwRKaF{~
    zx@!QUg2%yI_fmCMQy!<>FGUiglc~k;tl7}8%hK}u-G{@<wx1!pxP9$P+`iABlc&^I
    z(^mYJ<oSu0Ro}>G?Mz`_PlOwF*v%I>ow0611VXD~SXK_6Y}KoGCAr%2BYY0XjQtW_
    a#VugQS<=k()VMPeTb7Y6zAcC@;rAI_LT82m
    
    diff --git a/doc/Languages.txt b/doc/Languages.txt
    deleted file mode 100644
    index 85d5fde47..000000000
    --- a/doc/Languages.txt
    +++ /dev/null
    @@ -1,113 +0,0 @@
    -Format: 1.51
    -
    -# This is the Natural Docs languages file for this project.  If you change
    -# anything here, it will apply to THIS PROJECT ONLY.  If you'd like to change
    -# something for all your projects, edit the Languages.txt in Natural Docs'
    -# Config directory instead.
    -
    -
    -# You can prevent certain file extensions from being scanned like this:
    -# Ignore Extensions: [extension] [extension] ...
    -
    -
    -#-------------------------------------------------------------------------------
    -# SYNTAX:
    -#
    -# Unlike other Natural Docs configuration files, in this file all comments
    -# MUST be alone on a line.  Some languages deal with the # character, so you
    -# cannot put comments on the same line as content.
    -#
    -# Also, all lists are separated with spaces, not commas, again because some
    -# languages may need to use them.
    -#
    -# Language: [name]
    -# Alter Language: [name]
    -#    Defines a new language or alters an existing one.  Its name can use any
    -#    characters.  If any of the properties below have an add/replace form, you
    -#    must use that when using Alter Language.
    -#
    -#    The language Shebang Script is special.  It's entry is only used for
    -#    extensions, and files with those extensions have their shebang (#!) lines
    -#    read to determine the real language of the file.  Extensionless files are
    -#    always treated this way.
    -#
    -#    The language Text File is also special.  It's treated as one big comment
    -#    so you can put Natural Docs content in them without special symbols.  Also,
    -#    if you don't specify a package separator, ignored prefixes, or enum value
    -#    behavior, it will copy those settings from the language that is used most
    -#    in the source tree.
    -#
    -# Extensions: [extension] [extension] ...
    -# [Add/Replace] Extensions: [extension] [extension] ...
    -#    Defines the file extensions of the language's source files.  You can
    -#    redefine extensions found in the main languages file.  You can use * to
    -#    mean any undefined extension.
    -#
    -# Shebang Strings: [string] [string] ...
    -# [Add/Replace] Shebang Strings: [string] [string] ...
    -#    Defines a list of strings that can appear in the shebang (#!) line to
    -#    designate that it's part of the language.  You can redefine strings found
    -#    in the main languages file.
    -#
    -# Ignore Prefixes in Index: [prefix] [prefix] ...
    -# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ...
    -#
    -# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    -# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ...
    -#    Specifies prefixes that should be ignored when sorting symbols in an
    -#    index.  Can be specified in general or for a specific topic type.
    -#
    -#------------------------------------------------------------------------------
    -# For basic language support only:
    -#
    -# Line Comments: [symbol] [symbol] ...
    -#    Defines a space-separated list of symbols that are used for line comments,
    -#    if any.
    -#
    -# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...
    -#    Defines a space-separated list of symbol pairs that are used for block
    -#    comments, if any.
    -#
    -# Package Separator: [symbol]
    -#    Defines the default package separator symbol.  The default is a dot.
    -#
    -# [Topic Type] Prototype Enders: [symbol] [symbol] ...
    -#    When defined, Natural Docs will attempt to get a prototype from the code
    -#    immediately following the topic type.  It stops when it reaches one of
    -#    these symbols.  Use \n for line breaks.
    -#
    -# Line Extender: [symbol]
    -#    Defines the symbol that allows a prototype to span multiple lines if
    -#    normally a line break would end it.
    -#
    -# Enum Values: [global|under type|under parent]
    -#    Defines how enum values are referenced.  The default is global.
    -#    global       - Values are always global, referenced as 'value'.
    -#    under type   - Values are under the enum type, referenced as
    -#               'package.enum.value'.
    -#    under parent - Values are under the enum's parent, referenced as
    -#               'package.value'.
    -#
    -# Perl Package: [perl package]
    -#    Specifies the Perl package used to fine-tune the language behavior in ways
    -#    too complex to do in this file.
    -#
    -#------------------------------------------------------------------------------
    -# For full language support only:
    -#
    -# Full Language Support: [perl package]
    -#    Specifies the Perl package that has the parsing routines necessary for full
    -#    language support.
    -#
    -#-------------------------------------------------------------------------------
    -
    -# The following languages are defined in the main file, if you'd like to alter
    -# them:
    -#
    -#    Text File, Shebang Script, C/C++, C#, Java, JavaScript, Perl, Python,
    -#    PHP, SQL, Visual Basic, Pascal, Assembly, Ada, Tcl, Ruby, Makefile,
    -#    ActionScript, ColdFusion, R, Fortran
    -
    -# If you add a language that you think would be useful to other developers
    -# and should be included in Natural Docs by default, please e-mail it to
    -# languages [at] naturaldocs [dot] org.
    diff --git a/doc/Menu.txt b/doc/Menu.txt
    deleted file mode 100644
    index 3b7b30960..000000000
    --- a/doc/Menu.txt
    +++ /dev/null
    @@ -1,59 +0,0 @@
    -Format: 1.51
    -
    -
    -# You can add a title and sub-title to your menu like this:
    -# Title: [project name]
    -# SubTitle: [subtitle]
    -
    -# You can add a footer to your documentation like this:
    -# Footer: [text]
    -# If you want to add a copyright notice, this would be the place to do it.
    -
    -# You can add a timestamp to your documentation like one of these:
    -# Timestamp: Generated on month day, year
    -# Timestamp: Updated mm/dd/yyyy
    -# Timestamp: Last updated mon day
    -#
    -#   m     - One or two digit month.  January is "1"
    -#   mm    - Always two digit month.  January is "01"
    -#   mon   - Short month word.  January is "Jan"
    -#   month - Long month word.  January is "January"
    -#   d     - One or two digit day.  1 is "1"
    -#   dd    - Always two digit day.  1 is "01"
    -#   day   - Day with letter extension.  1 is "1st"
    -#   yy    - Two digit year.  2006 is "06"
    -#   yyyy  - Four digit year.  2006 is "2006"
    -#   year  - Four digit year.  2006 is "2006"
    -
    -
    -# --------------------------------------------------------------------------
    -# 
    -# Cut and paste the lines below to change the order in which your files
    -# appear on the menu.  Don't worry about adding or removing files, Natural
    -# Docs will take care of that.
    -# 
    -# You can further organize the menu by grouping the entries.  Add a
    -# "Group: [name] {" line to start a group, and add a "}" to end it.
    -# 
    -# You can add text and web links to the menu by adding "Text: [text]" and
    -# "Link: [name] ([URL])" lines, respectively.
    -# 
    -# The formatting and comments are auto-generated, so don't worry about
    -# neatness when editing the file.  Natural Docs will clean it up the next
    -# time it is run.  When working with groups, just deal with the braces and
    -# forget about the indentation and comments.
    -# 
    -# --------------------------------------------------------------------------
    -
    -
    -File: GitBlob  (blob.h)
    -File: GitError  (error.h)
    -
    -Group: Index  {
    -
    -   Index: Everything
    -   Class Index: Classes
    -   Function Index: Functions
    -   Variable Index: Variables
    -   }  # Group: Index
    -
    diff --git a/doc/styles/main.css b/doc/Theme.css
    similarity index 100%
    rename from doc/styles/main.css
    rename to doc/Theme.css
    diff --git a/doc/Topics.txt b/doc/Topics.txt
    deleted file mode 100644
    index 21530908d..000000000
    --- a/doc/Topics.txt
    +++ /dev/null
    @@ -1,81 +0,0 @@
    -Format: 1.51
    -
    -# This is the Natural Docs topics file for this project.  If you change anything
    -# here, it will apply to THIS PROJECT ONLY.  If you'd like to change something
    -# for all your projects, edit the Topics.txt in Natural Docs' Config directory
    -# instead.
    -
    -
    -# If you'd like to prevent keywords from being recognized by Natural Docs, you
    -# can do it like this:
    -# Ignore Keywords: [keyword], [keyword], ...
    -#
    -# Or you can use the list syntax like how they are defined:
    -# Ignore Keywords:
    -#    [keyword]
    -#    [keyword], [plural keyword]
    -#    ...
    -
    -
    -#-------------------------------------------------------------------------------
    -# SYNTAX:
    -#
    -# Topic Type: [name]
    -# Alter Topic Type: [name]
    -#    Creates a new topic type or alters one from the main file.  Each type gets
    -#    its own index and behavior settings.  Its name can have letters, numbers,
    -#    spaces, and these charaters: - / . '
    -#
    -# Plural: [name]
    -#    Sets the plural name of the topic type, if different.
    -#
    -# Keywords:
    -#    [keyword]
    -#    [keyword], [plural keyword]
    -#    ...
    -#    Defines or adds to the list of keywords for the topic type.  They may only
    -#    contain letters, numbers, and spaces and are not case sensitive.  Plural
    -#    keywords are used for list topics.  You can redefine keywords found in the
    -#    main topics file.
    -#
    -# Index: [yes|no]
    -#    Whether the topics get their own index.  Defaults to yes.  Everything is
    -#    included in the general index regardless of this setting.
    -#
    -# Scope: [normal|start|end|always global]
    -#    How the topics affects scope.  Defaults to normal.
    -#    normal        - Topics stay within the current scope.
    -#    start         - Topics start a new scope for all the topics beneath it,
    -#                    like class topics.
    -#    end           - Topics reset the scope back to global for all the topics
    -#                    beneath it.
    -#    always global - Topics are defined as global, but do not change the scope
    -#                    for any other topics.
    -#
    -# Class Hierarchy: [yes|no]
    -#    Whether the topics are part of the class hierarchy.  Defaults to no.
    -#
    -# Page Title If First: [yes|no]
    -#    Whether the topic's title becomes the page title if it's the first one in
    -#    a file.  Defaults to no.
    -#
    -# Break Lists: [yes|no]
    -#    Whether list topics should be broken into individual topics in the output.
    -#    Defaults to no.
    -#
    -# Can Group With: [type], [type], ...
    -#    Defines a list of topic types that this one can possibly be grouped with.
    -#    Defaults to none.
    -#-------------------------------------------------------------------------------
    -
    -# The following topics are defined in the main file, if you'd like to alter
    -# their behavior or add keywords:
    -#
    -#    Generic, Class, Interface, Section, File, Group, Function, Variable,
    -#    Property, Type, Constant, Enumeration, Event, Delegate, Macro,
    -#    Database, Database Table, Database View, Database Index, Database
    -#    Cursor, Database Trigger, Cookie, Build Target
    -
    -# If you add something that you think would be useful to other developers
    -# and should be included in Natural Docs by default, please e-mail it to
    -# topics [at] naturaldocs [dot] org.
    diff --git a/doc/files/blob-h.html b/doc/files/blob-h.html
    deleted file mode 100644
    index b4330c3f6..000000000
    --- a/doc/files/blob-h.html
    +++ /dev/null
    @@ -1,84 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>GitBlob</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/prettify.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="ContentPage" onLoad="NDOnLoad();prettyPrint();"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Content><div class="CClass"><div class=CTopic id=MainTopic><h1 class=CTitle><a name="GitBlob"></a>GitBlob</h1><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitBlob : public ObjectWrap</td></tr></table></blockquote><p>Wrapper for libgit2 git_blob.</p><!--START_ND_SUMMARY--><div class=Summary><div class=STitle>Summary</div><div class=SBorder><table border=0 cellspacing=0 cellpadding=0 class=STable><tr class="SMain"><td class=SEntry><a href="#GitBlob" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')">GitBlob</a></td><td class=SDescription>Wrapper for libgit2 git_blob.</td></tr><tr class="SGroup SIndent1"><td class=SEntry><a href="#GitBlob.Variables" >Variables</a></td><td class=SDescription></td></tr><tr class="SVariable SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.constructor_template" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')">constructor_template</a></td><td class=SDescription>Used to create Node.js constructor.</td></tr><tr class="SGroup SIndent1"><td class=SEntry><a href="#GitBlob.Functions" >Functions</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.Initialize" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')">Initialize</a></td><td class=SDescription>Used to intialize the EventEmitter from Node.js</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.Lookup" id=link4 onMouseOver="ShowTip(event, 'tt4', 'link4')" onMouseOut="HideTip('tt4')">Lookup</a></td><td class=SDescription>Lookup a blob object from a repository.</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.RawContent" id=link5 onMouseOver="ShowTip(event, 'tt5', 'link5')" onMouseOut="HideTip('tt5')">RawContent</a></td><td class=SDescription>Get a read-only buffer with the raw content of a blob.</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.RawSize" id=link6 onMouseOver="ShowTip(event, 'tt6', 'link6')" onMouseOut="HideTip('tt6')">RawSize</a></td><td class=SDescription>Lookup a blob object from a repository.</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.Close" id=link7 onMouseOver="ShowTip(event, 'tt7', 'link7')" onMouseOut="HideTip('tt7')">Close</a></td><td class=SDescription>Free a blob object.</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.CreateFromFile" id=link8 onMouseOver="ShowTip(event, 'tt8', 'link8')" onMouseOut="HideTip('tt8')">CreateFromFile</a></td><td class=SDescription>Read a file into the ODB.</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.CreateFromBuffer" >CreateFromBuffer</a></td><td class=SDescription>Read a buffer into the ODB.</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.GitBlob" id=link9 onMouseOver="ShowTip(event, 'tt9', 'link9')" onMouseOut="HideTip('tt9')">GitBlob</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.New" id=link10 onMouseOver="ShowTip(event, 'tt10', 'link10')" onMouseOut="HideTip('tt10')">New</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.Lookup" id=link11 onMouseOver="ShowTip(event, 'tt4', 'link11')" onMouseOut="HideTip('tt4')">Lookup</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.EIO_Lookup" id=link12 onMouseOver="ShowTip(event, 'tt11', 'link12')" onMouseOut="HideTip('tt11')">EIO_Lookup</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.EIO_AfterLookup" id=link13 onMouseOver="ShowTip(event, 'tt12', 'link13')" onMouseOut="HideTip('tt12')">EIO_AfterLookup</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.RawContent" id=link14 onMouseOver="ShowTip(event, 'tt5', 'link14')" onMouseOut="HideTip('tt5')">RawContent</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.RawSize" id=link15 onMouseOver="ShowTip(event, 'tt6', 'link15')" onMouseOut="HideTip('tt6')">RawSize</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.Close" id=link16 onMouseOver="ShowTip(event, 'tt7', 'link16')" onMouseOut="HideTip('tt7')">Close</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitBlob.CreateFromFile" id=link17 onMouseOver="ShowTip(event, 'tt8', 'link17')" onMouseOut="HideTip('tt8')">CreateFromFile</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.CreateFromBuffer" id=link18 onMouseOver="ShowTip(event, 'tt13', 'link18')" onMouseOut="HideTip('tt13')">CreateFromBuffer</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SGroup SIndent1"><td class=SEntry><a href="#GitBlob.Variables" >Variables</a></td><td class=SDescription></td></tr><tr class="SVariable SIndent2 SMarked"><td class=SEntry><a href="#GitBlob.blob" id=link19 onMouseOver="ShowTip(event, 'tt14', 'link19')" onMouseOut="HideTip('tt14')">blob</a></td><td class=SDescription>Internal reference to git_blob object</td></tr><tr class="SClass"><td class=SEntry><a href="#lookup_request" id=link20 onMouseOver="ShowTip(event, 'tt15', 'link20')" onMouseOut="HideTip('tt15')">lookup_request</a></td><td class=SDescription>Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.</td></tr></table></div></div><!--END_ND_SUMMARY--></div></div></div>
    -
    -<div class="CGroup"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Variables"></a>Variables</h3></div></div>
    -
    -<div class="CVariable"><div class=CTopic><h3 class=CTitle><a name="GitBlob.constructor_template"></a>constructor_template</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote><p>Used to create Node.js constructor.</p></div></div></div>
    -
    -<div class="CGroup"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Functions"></a>Functions</h3></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Initialize"></a>Initialize</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><p>Used to intialize the EventEmitter from Node.js</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>target</td><td class=CDLDescription>v8::Object the Node.js global module object</td></tr></table></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Lookup"></a>Lookup</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int Lookup(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>id</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><p>Lookup a blob object from a repository.</p><h4 class=CHeading>Parameters</h4><p>repo the repo to use when locating the blob. id identity of the blob to locate.</p><h4 class=CHeading>Returns</h4><p>0 on success; error code otherwise</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.RawContent"></a>RawContent</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">const void* RawContent()</td></tr></table></blockquote><p>Get a read-only buffer with the raw content of a blob.</p><h4 class=CHeading>Returns</h4><p>raw content buffer; NULL if the blob has no contents</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.RawSize"></a>RawSize</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">int RawSize()</td></tr></table></blockquote><p>Lookup a blob object from a repository.</p><h4 class=CHeading>Returns</h4><p>size in bytes</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Close"></a>Close</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">void Close()</td></tr></table></blockquote><p>Free a blob object.</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.CreateFromFile"></a>CreateFromFile</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int CreateFromFile(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>oid,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>char&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>path</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><p>Read a file into the ODB.</p><h4 class=CHeading>Returns</h4><p>0 on success, error code otherwise</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.CreateFromBuffer"></a>CreateFromBuffer</h3><div class=CBody><p>Read a buffer into the ODB.</p><h4 class=CHeading>Returns</h4><p>0 on success, error code otherwise</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.GitBlob"></a>GitBlob</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitBlob()</td></tr></table></blockquote></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.New"></a>New</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Lookup"></a>Lookup</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; Lookup(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.EIO_Lookup"></a>EIO_Lookup</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_Lookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>req</td><td class=CDLDescription>an eio_req pointer</td></tr></table><h4 class=CHeading>Returns</h4><p>completion code integer</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.EIO_AfterLookup"></a>EIO_AfterLookup</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_AfterLookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>req</td><td class=CDLDescription>an eio_req pointer</td></tr></table><h4 class=CHeading>Returns</h4><p>completion code integer</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.RawContent"></a>RawContent</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; RawContent(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.RawSize"></a>RawSize</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; RawSize(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Close"></a>Close</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; Close(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.CreateFromFile"></a>CreateFromFile</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; CreateFromFile(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitBlob.CreateFromBuffer"></a>CreateFromBuffer</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; CreateFromBuffer(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CGroup"><div class=CTopic><h3 class=CTitle><a name="GitBlob.Variables"></a>Variables</h3></div></div>
    -
    -<div class="CVariable"><div class=CTopic><h3 class=CTitle><a name="GitBlob.blob"></a>blob</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">git_blob* blob</td></tr></table></blockquote><p>Internal reference to git_blob object</p></div></div></div>
    -
    -<div class="CClass"><div class=CTopic><h2 class=CTitle><a name="lookup_request"></a>lookup_request</h2><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">struct lookup_request</td></tr></table></blockquote><p>Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.</p></div></div></div>
    -
    -</div><!--Content-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile id=MSelected>GitBlob</div></div><div class=MEntry><div class=MFile><a href="error-h.html">GitError</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex><a href="../index/General.html">Everything</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Classes.html">Classes</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Functions.html">Functions</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Variables.html">Variables</a></div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitBlob : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_blob.</div></div><div class=CToolTip id="tt2"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><div class=CToolTip id="tt3"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><div class=CToolTip id="tt4"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int Lookup(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>id</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><div class=CToolTip id="tt5"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">const void* RawContent()</td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><div class=CToolTip id="tt6"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">int RawSize()</td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><div class=CToolTip id="tt7"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">void Close()</td></tr></table></blockquote>Free a blob object.</div></div><div class=CToolTip id="tt8"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int CreateFromFile(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>oid,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>char&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>path</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Read a file into the ODB.</div></div><div class=CToolTip id="tt9"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitBlob()</td></tr></table></blockquote></div></div><div class=CToolTip id="tt10"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><div class=CToolTip id="tt11"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_Lookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><div class=CToolTip id="tt12"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_AfterLookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><div class=CToolTip id="tt13"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; CreateFromBuffer(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><div class=CToolTip id="tt14"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">git_blob* blob</td></tr></table></blockquote>Internal reference to git_blob object</div></div><div class=CToolTip id="tt15"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">struct lookup_request</td></tr></table></blockquote>Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/files/error-h.html b/doc/files/error-h.html
    deleted file mode 100644
    index bbbb2667d..000000000
    --- a/doc/files/error-h.html
    +++ /dev/null
    @@ -1,54 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>GitError</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/prettify.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="ContentPage" onLoad="NDOnLoad();prettyPrint();"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Content><div class="CClass"><div class=CTopic id=MainTopic><h1 class=CTitle><a name="GitError"></a>GitError</h1><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitError : public ObjectWrap</td></tr></table></blockquote><p>Wrapper for libgit2 git_error.</p><!--START_ND_SUMMARY--><div class=Summary><div class=STitle>Summary</div><div class=SBorder><table border=0 cellspacing=0 cellpadding=0 class=STable><tr class="SMain"><td class=SEntry><a href="#GitError" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')">GitError</a></td><td class=SDescription>Wrapper for libgit2 git_error.</td></tr><tr class="SGroup SIndent1"><td class=SEntry><a href="#GitError.Variables" >Variables</a></td><td class=SDescription></td></tr><tr class="SVariable SIndent2 SMarked"><td class=SEntry><a href="#GitError.constructor_template" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')">constructor_template</a></td><td class=SDescription>Used to create Node.js constructor.</td></tr><tr class="SGroup SIndent1"><td class=SEntry><a href="#GitError.Functions" >Functions</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitError.Initialize" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')">Initialize</a></td><td class=SDescription>Used to intialize the EventEmitter from Node.js</td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitError.StrError" id=link4 onMouseOver="ShowTip(event, 'tt4', 'link4')" onMouseOut="HideTip('tt4')">StrError</a></td><td class=SDescription>Get a read-only buffer with the raw content of a blob.</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitError.GitError" id=link5 onMouseOver="ShowTip(event, 'tt5', 'link5')" onMouseOut="HideTip('tt5')">GitError</a></td><td class=SDescription></td></tr><tr class="SFunction SIndent2"><td class=SEntry><a href="#GitError.New" id=link6 onMouseOver="ShowTip(event, 'tt6', 'link6')" onMouseOut="HideTip('tt6')">New</a></td><td class=SDescription>args v8::Arguments function call</td></tr><tr class="SFunction SIndent2 SMarked"><td class=SEntry><a href="#GitError.StrError" id=link7 onMouseOver="ShowTip(event, 'tt4', 'link7')" onMouseOut="HideTip('tt4')">StrError</a></td><td class=SDescription>args v8::Arguments function call</td></tr></table></div></div><!--END_ND_SUMMARY--></div></div></div>
    -
    -<div class="CGroup"><div class=CTopic><h3 class=CTitle><a name="GitError.Variables"></a>Variables</h3></div></div>
    -
    -<div class="CVariable"><div class=CTopic><h3 class=CTitle><a name="GitError.constructor_template"></a>constructor_template</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote><p>Used to create Node.js constructor.</p></div></div></div>
    -
    -<div class="CGroup"><div class=CTopic><h3 class=CTitle><a name="GitError.Functions"></a>Functions</h3></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitError.Initialize"></a>Initialize</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><p>Used to intialize the EventEmitter from Node.js</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>target</td><td class=CDLDescription>v8::Object the Node.js global module object</td></tr></table></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitError.StrError"></a>StrError</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>const char* StrError(</td><td class="PType  prettyprint " nowrap>int&nbsp;</td><td class="PParameter  prettyprint " nowrap>err</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><p>Get a read-only buffer with the raw content of a blob.</p><h4 class=CHeading>Parameters</h4><table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList><tr><td class=CDLEntry>err</td><td class=CDLDescription>A signed int error code</td></tr></table><h4 class=CHeading>Returns</h4><p>a string explaining the error code.</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitError.GitError"></a>GitError</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitError()</td></tr></table></blockquote></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitError.New"></a>New</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -<div class="CFunction"><div class=CTopic><h3 class=CTitle><a name="GitError.StrError"></a>StrError</h3><div class=CBody><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; StrError(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote><h4 class=CHeading>Parameters</h4><p>args v8::Arguments function call</p><h4 class=CHeading>Returns</h4><p>v8::Object args.This()</p></div></div></div>
    -
    -</div><!--Content-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile><a href="blob-h.html">GitBlob</a></div></div><div class=MEntry><div class=MFile id=MSelected>GitError</div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex><a href="../index/General.html">Everything</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Classes.html">Classes</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Functions.html">Functions</a></div></div><div class=MEntry><div class=MIndex><a href="../index/Variables.html">Variables</a></div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitError : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_error.</div></div><div class=CToolTip id="tt2"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><div class=CToolTip id="tt3"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><div class=CToolTip id="tt4"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>const char* StrError(</td><td class="PType  prettyprint " nowrap>int&nbsp;</td><td class="PParameter  prettyprint " nowrap>err</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><div class=CToolTip id="tt5"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitError()</td></tr></table></blockquote></div></div><div class=CToolTip id="tt6"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/index.html b/doc/index.html
    deleted file mode 100644
    index e39a046d3..000000000
    --- a/doc/index.html
    +++ /dev/null
    @@ -1 +0,0 @@
    -<html><head><meta http-equiv="Refresh" CONTENT="0; URL=files/blob-h.html"></head></html>
    \ No newline at end of file
    diff --git a/doc/index/Classes.html b/doc/index/Classes.html
    deleted file mode 100644
    index 73f8ef707..000000000
    --- a/doc/index/Classes.html
    +++ /dev/null
    @@ -1,37 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Class Index</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="IndexPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=IPageTitle>Class Index</div><div class=INavigationBar>$#! &middot; 0-9 &middot; A &middot; B &middot; C &middot; D &middot; E &middot; F &middot; <a href="#G">G</a> &middot; H &middot; I &middot; J &middot; K &middot; <a href="#L">L</a> &middot; M &middot; N &middot; O &middot; P &middot; Q &middot; R &middot; S &middot; T &middot; U &middot; V &middot; W &middot; X &middot; Y &middot; Z</div><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading><a name="G"></a>G</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')" class=ISymbol>GitBlob</a></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/error-h.html#GitError" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')" class=ISymbol>GitError</a></td></tr><tr><td class=IHeading><a name="L"></a>L</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#lookup_request" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')" class=ISymbol>lookup_request</a></td></tr></table>
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitBlob : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_blob.</div></div><div class=CToolTip id="tt2"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitError : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_error.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt3"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">struct lookup_request</td></tr></table></blockquote>Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.</div></div><!--END_ND_TOOLTIPS-->
    -
    -</div><!--Index-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile><a href="../files/blob-h.html">GitBlob</a></div></div><div class=MEntry><div class=MFile><a href="../files/error-h.html">GitError</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex><a href="General.html">Everything</a></div></div><div class=MEntry><div class=MIndex id=MSelected>Classes</div></div><div class=MEntry><div class=MIndex><a href="Functions.html">Functions</a></div></div><div class=MEntry><div class=MIndex><a href="Variables.html">Variables</a></div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/index/Functions.html b/doc/index/Functions.html
    deleted file mode 100644
    index e62b55581..000000000
    --- a/doc/index/Functions.html
    +++ /dev/null
    @@ -1,61 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Function Index</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="IndexPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=IPageTitle>Function Index</div><div class=INavigationBar>$#! &middot; 0-9 &middot; A &middot; B &middot; <a href="#C">C</a> &middot; D &middot; <a href="#E">E</a> &middot; F &middot; <a href="#G">G</a> &middot; H &middot; <a href="#I">I</a> &middot; J &middot; K &middot; <a href="#L">L</a> &middot; M &middot; <a href="#N">N</a> &middot; O &middot; P &middot; Q &middot; <a href="#R">R</a> &middot; <a href="#S">S</a> &middot; T &middot; U &middot; V &middot; W &middot; X &middot; Y &middot; Z</div><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading><a name="C"></a>C</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.Close" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')" class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')" class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')" class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="E"></a>E</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" id=link4 onMouseOver="ShowTip(event, 'tt4', 'link4')" onMouseOut="HideTip('tt4')" class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" id=link5 onMouseOver="ShowTip(event, 'tt5', 'link5')" onMouseOut="HideTip('tt5')" class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="G"></a>G</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.GitBlob" id=link6 onMouseOver="ShowTip(event, 'tt6', 'link6')" onMouseOut="HideTip('tt6')" class=ISymbol>GitBlob</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/error-h.html#GitError.GitError" id=link7 onMouseOver="ShowTip(event, 'tt7', 'link7')" onMouseOut="HideTip('tt7')" class=ISymbol>GitError</a>, <span class=IParent>GitError</span></td></tr><tr><td class=IHeading><a name="I"></a>I</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Initialize</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" id=link8 onMouseOver="ShowTip(event, 'tt8', 'link8')" onMouseOut="HideTip('tt8')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" id=link9 onMouseOver="ShowTip(event, 'tt9', 'link9')" onMouseOut="HideTip('tt9')" class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="L"></a>L</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" id=link10 onMouseOver="ShowTip(event, 'tt10', 'link10')" onMouseOut="HideTip('tt10')" class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="N"></a>N</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>New</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" id=link11 onMouseOver="ShowTip(event, 'tt11', 'link11')" onMouseOut="HideTip('tt11')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" id=link12 onMouseOver="ShowTip(event, 'tt12', 'link12')" onMouseOut="HideTip('tt12')" class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="R"></a>R</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" id=link13 onMouseOver="ShowTip(event, 'tt13', 'link13')" onMouseOut="HideTip('tt13')" class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" id=link14 onMouseOver="ShowTip(event, 'tt14', 'link14')" onMouseOut="HideTip('tt14')" class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="S"></a>S</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/error-h.html#GitError.StrError" id=link15 onMouseOver="ShowTip(event, 'tt15', 'link15')" onMouseOut="HideTip('tt15')" class=ISymbol>StrError</a>, <span class=IParent>GitError</span></td></tr></table>
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">void Close()</td></tr></table></blockquote>Free a blob object.</div></div><div class=CToolTip id="tt2"><div class=CFunction>Read a buffer into the ODB.</div></div><div class=CToolTip id="tt3"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int CreateFromFile(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>oid,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>char&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>path</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Read a file into the ODB.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt4"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_AfterLookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><div class=CToolTip id="tt5"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_Lookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt6"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitBlob()</td></tr></table></blockquote></div></div><div class=CToolTip id="tt7"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitError()</td></tr></table></blockquote></div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt8"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><div class=CToolTip id="tt9"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt10"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int Lookup(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>id</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt11"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><div class=CToolTip id="tt12"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt13"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">const void* RawContent()</td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><div class=CToolTip id="tt14"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">int RawSize()</td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt15"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>const char* StrError(</td><td class="PType  prettyprint " nowrap>int&nbsp;</td><td class="PParameter  prettyprint " nowrap>err</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><!--END_ND_TOOLTIPS-->
    -
    -</div><!--Index-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile><a href="../files/blob-h.html">GitBlob</a></div></div><div class=MEntry><div class=MFile><a href="../files/error-h.html">GitError</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex><a href="General.html">Everything</a></div></div><div class=MEntry><div class=MIndex><a href="Classes.html">Classes</a></div></div><div class=MEntry><div class=MIndex id=MSelected>Functions</div></div><div class=MEntry><div class=MIndex><a href="Variables.html">Variables</a></div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/index/General.html b/doc/index/General.html
    deleted file mode 100644
    index c803fd514..000000000
    --- a/doc/index/General.html
    +++ /dev/null
    @@ -1,73 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Index</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="IndexPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=IPageTitle>Index</div><div class=INavigationBar>$#! &middot; 0-9 &middot; A &middot; <a href="#B">B</a> &middot; <a href="#C">C</a> &middot; D &middot; <a href="#E">E</a> &middot; <a href="#F">F</a> &middot; <a href="#G">G</a> &middot; H &middot; <a href="#I">I</a> &middot; J &middot; K &middot; <a href="#L">L</a> &middot; M &middot; <a href="#N">N</a> &middot; O &middot; P &middot; Q &middot; <a href="#R">R</a> &middot; <a href="#S">S</a> &middot; T &middot; U &middot; <a href="#V">V</a> &middot; W &middot; X &middot; Y &middot; Z</div><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading><a name="B"></a>B</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.blob" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')" class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="C"></a>C</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.Close" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')" class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>constructor_template</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" id=link4 onMouseOver="ShowTip(event, 'tt4', 'link4')" onMouseOut="HideTip('tt4')" class=IParent>GitError</a></div></td></tr><tr><td class=ISymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" id=link5 onMouseOver="ShowTip(event, 'tt5', 'link5')" onMouseOut="HideTip('tt5')" class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" id=link6 onMouseOver="ShowTip(event, 'tt6', 'link6')" onMouseOut="HideTip('tt6')" class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="E"></a>E</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" id=link7 onMouseOver="ShowTip(event, 'tt7', 'link7')" onMouseOut="HideTip('tt7')" class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" id=link8 onMouseOver="ShowTip(event, 'tt8', 'link8')" onMouseOut="HideTip('tt8')" class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="F"></a>F</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Functions</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Functions"  class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Functions"  class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="G"></a>G</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>GitBlob</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob" id=link9 onMouseOver="ShowTip(event, 'tt9', 'link9')" onMouseOut="HideTip('tt9')" class=IParent>Global</a><a href="../files/blob-h.html#GitBlob.GitBlob" id=link10 onMouseOver="ShowTip(event, 'tt10', 'link10')" onMouseOut="HideTip('tt10')" class=IParent>GitBlob</a></div></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>GitError</span><div class=ISubIndex><a href="../files/error-h.html#GitError" id=link11 onMouseOver="ShowTip(event, 'tt11', 'link11')" onMouseOut="HideTip('tt11')" class=IParent>Global</a><a href="../files/error-h.html#GitError.GitError" id=link12 onMouseOver="ShowTip(event, 'tt12', 'link12')" onMouseOut="HideTip('tt12')" class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="I"></a>I</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Initialize</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" id=link13 onMouseOver="ShowTip(event, 'tt13', 'link13')" onMouseOut="HideTip('tt13')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" id=link14 onMouseOver="ShowTip(event, 'tt14', 'link14')" onMouseOut="HideTip('tt14')" class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="L"></a>L</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" id=link15 onMouseOver="ShowTip(event, 'tt15', 'link15')" onMouseOut="HideTip('tt15')" class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#lookup_request" id=link16 onMouseOver="ShowTip(event, 'tt16', 'link16')" onMouseOut="HideTip('tt16')" class=ISymbol>lookup_request</a></td></tr><tr><td class=IHeading><a name="N"></a>N</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>New</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" id=link17 onMouseOver="ShowTip(event, 'tt17', 'link17')" onMouseOut="HideTip('tt17')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" id=link18 onMouseOver="ShowTip(event, 'tt18', 'link18')" onMouseOut="HideTip('tt18')" class=IParent>GitError</a></div></td></tr><tr><td class=IHeading><a name="R"></a>R</td><td></td></tr><tr><td class=ISymbolPrefix id=IFirstSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" id=link19 onMouseOver="ShowTip(event, 'tt19', 'link19')" onMouseOut="HideTip('tt19')" class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=ISymbolPrefix id=ILastSymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" id=link20 onMouseOver="ShowTip(event, 'tt20', 'link20')" onMouseOut="HideTip('tt20')" class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="S"></a>S</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/error-h.html#GitError.StrError" id=link21 onMouseOver="ShowTip(event, 'tt21', 'link21')" onMouseOut="HideTip('tt21')" class=ISymbol>StrError</a>, <span class=IParent>GitError</span></td></tr><tr><td class=IHeading><a name="V"></a>V</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>Variables</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Variables"  class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Variables"  class=IParent>GitError</a></div></td></tr></table>
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">git_blob* blob</td></tr></table></blockquote>Internal reference to git_blob object</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt2"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">void Close()</td></tr></table></blockquote>Free a blob object.</div></div><div class=CToolTip id="tt3"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><div class=CToolTip id="tt4"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><div class=CToolTip id="tt5"><div class=CFunction>Read a buffer into the ODB.</div></div><div class=CToolTip id="tt6"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int CreateFromFile(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>oid,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>char&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>path</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Read a file into the ODB.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt7"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_AfterLookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><div class=CToolTip id="tt8"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static int EIO_Lookup(</td><td class="PType  prettyprint " nowrap>eio_req&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>req</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote></div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt9"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitBlob : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_blob.</div></div><div class=CToolTip id="tt10"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitBlob()</td></tr></table></blockquote></div></div><div class=CToolTip id="tt11"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">class GitError : public ObjectWrap</td></tr></table></blockquote>Wrapper for libgit2 git_error.</div></div><div class=CToolTip id="tt12"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">GitError()</td></tr></table></blockquote></div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt13"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><div class=CToolTip id="tt14"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static void Initialize(</td><td class="PTypePrefix  prettyprint " nowrap>v8::</td><td class="PType  prettyprint " nowrap>Handle&lt;v8::Object&gt;&nbsp;</td><td class="PParameter  prettyprint " nowrap>target</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Used to intialize the EventEmitter from Node.js</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt15"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>int Lookup(</td><td class="PTypePrefix  prettyprint " nowrap></td><td class="PType  prettyprint " nowrap>git_repository&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>repo,</td></tr><tr><td></td><td class="PTypePrefix  prettyprint " nowrap>const&nbsp;</td><td class="PType  prettyprint " nowrap>git_oid&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>*</td><td class="PParameter  prettyprint " nowrap>id</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><div class=CToolTip id="tt16"><div class=CClass><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">struct lookup_request</td></tr></table></blockquote>Contains references to the current blob, repo, and oid for a commit lookup, also contains references to an error code post lookup, and a callback function to execute.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt17"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><div class=CToolTip id="tt18"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>static v8::Handle&lt;v8::Value&gt; New(</td><td class="PTypePrefix  prettyprint " nowrap>const v8::</td><td class="PType  prettyprint " nowrap>Arguments&nbsp;</td><td class="PParameterPrefix  prettyprint " nowrap>&amp;</td><td class="PParameter  prettyprint " nowrap>args</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>args v8::Arguments function call</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt19"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">const void* RawContent()</td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><div class=CToolTip id="tt20"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">int RawSize()</td></tr></table></blockquote>Lookup a blob object from a repository.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt21"><div class=CFunction><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td><table border=0 cellspacing=0 cellpadding=0><tr><td class="PBeforeParameters  prettyprint "nowrap>const char* StrError(</td><td class="PType  prettyprint " nowrap>int&nbsp;</td><td class="PParameter  prettyprint " nowrap>err</td><td class="PAfterParameters  prettyprint "nowrap>)</td></tr></table></td></tr></table></blockquote>Get a read-only buffer with the raw content of a blob.</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<!--END_ND_TOOLTIPS-->
    -
    -</div><!--Index-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile><a href="../files/blob-h.html">GitBlob</a></div></div><div class=MEntry><div class=MFile><a href="../files/error-h.html">GitError</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex id=MSelected>Everything</div></div><div class=MEntry><div class=MIndex><a href="Classes.html">Classes</a></div></div><div class=MEntry><div class=MIndex><a href="Functions.html">Functions</a></div></div><div class=MEntry><div class=MIndex><a href="Variables.html">Variables</a></div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/index/Variables.html b/doc/index/Variables.html
    deleted file mode 100644
    index 6a2d37b45..000000000
    --- a/doc/index/Variables.html
    +++ /dev/null
    @@ -1,37 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Variable Index</title><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script><script language=JavaScript src="../javascript/searchdata.js"></script></head><body class="IndexPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=IPageTitle>Variable Index</div><div class=INavigationBar>$#! &middot; 0-9 &middot; A &middot; <a href="#B">B</a> &middot; <a href="#C">C</a> &middot; D &middot; E &middot; F &middot; G &middot; H &middot; I &middot; J &middot; K &middot; L &middot; M &middot; N &middot; O &middot; P &middot; Q &middot; R &middot; S &middot; T &middot; U &middot; V &middot; W &middot; X &middot; Y &middot; Z</div><table border=0 cellspacing=0 cellpadding=0><tr><td class=IHeading id=IFirstHeading><a name="B"></a>B</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><a href="../files/blob-h.html#GitBlob.blob" id=link1 onMouseOver="ShowTip(event, 'tt1', 'link1')" onMouseOut="HideTip('tt1')" class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></td></tr><tr><td class=IHeading><a name="C"></a>C</td><td></td></tr><tr><td class=ISymbolPrefix id=IOnlySymbolPrefix>&nbsp;</td><td class=IEntry><span class=ISymbol>constructor_template</span><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" id=link2 onMouseOver="ShowTip(event, 'tt2', 'link2')" onMouseOut="HideTip('tt2')" class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" id=link3 onMouseOver="ShowTip(event, 'tt3', 'link3')" onMouseOut="HideTip('tt3')" class=IParent>GitError</a></div></td></tr></table>
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt1"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">git_blob* blob</td></tr></table></blockquote>Internal reference to git_blob object</div></div><!--END_ND_TOOLTIPS-->
    -
    -
    -<!--START_ND_TOOLTIPS-->
    -<div class=CToolTip id="tt2"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><div class=CToolTip id="tt3"><div class=CVariable><blockquote><table border=0 cellspacing=0 cellpadding=0 class="Prototype"><tr><td class="prettyprint">static v8::Persistent&lt;v8::FunctionTemplate&gt; constructor_template</td></tr></table></blockquote>Used to create Node.js constructor.</div></div><!--END_ND_TOOLTIPS-->
    -
    -</div><!--Index-->
    -
    -
    -<div id=Footer><a href="http://www.naturaldocs.org">Generated by Natural Docs</a></div><!--Footer-->
    -
    -
    -<div id=Menu><div class=MEntry><div class=MFile><a href="../files/blob-h.html">GitBlob</a></div></div><div class=MEntry><div class=MFile><a href="../files/error-h.html">GitError</a></div></div><div class=MEntry><div class=MGroup><a href="javascript:ToggleMenu('MGroupContent1')">Index</a><div class=MGroupContent id=MGroupContent1><div class=MEntry><div class=MIndex><a href="General.html">Everything</a></div></div><div class=MEntry><div class=MIndex><a href="Classes.html">Classes</a></div></div><div class=MEntry><div class=MIndex><a href="Functions.html">Functions</a></div></div><div class=MEntry><div class=MIndex id=MSelected>Variables</div></div></div></div></div><script type="text/javascript"><!--
    -var searchPanel = new SearchPanel("searchPanel", "HTML", "../search");
    ---></script><div id=MSearchPanel class=MSearchPanelInactive><input type=text id=MSearchField value=Search onFocus="searchPanel.OnSearchFieldFocus(true)" onBlur="searchPanel.OnSearchFieldFocus(false)" onKeyUp="searchPanel.OnSearchFieldChange()"><select id=MSearchType onFocus="searchPanel.OnSearchTypeFocus(true)" onBlur="searchPanel.OnSearchTypeFocus(false)" onChange="searchPanel.OnSearchTypeChange()"><option  id=MSearchEverything selected value="General">Everything</option><option value="Classes">Classes</option><option value="Functions">Functions</option><option value="Variables">Variables</option></select></div></div><!--Menu-->
    -
    -
    -<div id=MSearchResultsWindow><iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe><a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a></div>
    -
    -
    -<script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/javascript/main.js b/doc/javascript/main.js
    deleted file mode 100644
    index 3f42acde6..000000000
    --- a/doc/javascript/main.js
    +++ /dev/null
    @@ -1,841 +0,0 @@
    -// This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
    -// Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
    -// Refer to License.txt for the complete details
    -
    -// This file may be distributed with documentation files generated by Natural Docs.
    -// Such documentation is not covered by Natural Docs' copyright and licensing,
    -// and may have its own copyright and distribution terms as decided by its author.
    -
    -
    -//
    -//  Browser Styles
    -// ____________________________________________________________________________
    -
    -var agt=navigator.userAgent.toLowerCase();
    -var browserType;
    -var browserVer;
    -
    -if (agt.indexOf("opera") != -1)
    -    {
    -    browserType = "Opera";
    -
    -    if (agt.indexOf("opera 7") != -1 || agt.indexOf("opera/7") != -1)
    -        {  browserVer = "Opera7";  }
    -    else if (agt.indexOf("opera 8") != -1 || agt.indexOf("opera/8") != -1)
    -        {  browserVer = "Opera8";  }
    -    else if (agt.indexOf("opera 9") != -1 || agt.indexOf("opera/9") != -1)
    -        {  browserVer = "Opera9";  }
    -    }
    -
    -else if (agt.indexOf("applewebkit") != -1)
    -    {
    -    browserType = "Safari";
    -
    -    if (agt.indexOf("version/3") != -1)
    -        {  browserVer = "Safari3";  }
    -    else if (agt.indexOf("safari/4") != -1)
    -        {  browserVer = "Safari2";  }
    -    }
    -
    -else if (agt.indexOf("khtml") != -1)
    -    {
    -    browserType = "Konqueror";
    -    }
    -
    -else if (agt.indexOf("msie") != -1)
    -    {
    -    browserType = "IE";
    -
    -    if (agt.indexOf("msie 6") != -1)
    -        {  browserVer = "IE6";  }
    -    else if (agt.indexOf("msie 7") != -1)
    -        {  browserVer = "IE7";  }
    -    }
    -
    -else if (agt.indexOf("gecko") != -1)
    -    {
    -    browserType = "Firefox";
    -
    -    if (agt.indexOf("rv:1.7") != -1)
    -        {  browserVer = "Firefox1";  }
    -    else if (agt.indexOf("rv:1.8)") != -1 || agt.indexOf("rv:1.8.0") != -1)
    -        {  browserVer = "Firefox15";  }
    -    else if (agt.indexOf("rv:1.8.1") != -1)
    -        {  browserVer = "Firefox2";  }
    -    }
    -
    -
    -//
    -//  Support Functions
    -// ____________________________________________________________________________
    -
    -
    -function GetXPosition(item)
    -    {
    -    var position = 0;
    -
    -    if (item.offsetWidth != null)
    -        {
    -        while (item != document.body && item != null)
    -            {
    -            position += item.offsetLeft;
    -            item = item.offsetParent;
    -            };
    -        };
    -
    -    return position;
    -    };
    -
    -
    -function GetYPosition(item)
    -    {
    -    var position = 0;
    -
    -    if (item.offsetWidth != null)
    -        {
    -        while (item != document.body && item != null)
    -            {
    -            position += item.offsetTop;
    -            item = item.offsetParent;
    -            };
    -        };
    -
    -    return position;
    -    };
    -
    -
    -function MoveToPosition(item, x, y)
    -    {
    -    // Opera 5 chokes on the px extension, so it can use the Microsoft one instead.
    -
    -    if (item.style.left != null)
    -        {
    -        item.style.left = x + "px";
    -        item.style.top = y + "px";
    -        }
    -    else if (item.style.pixelLeft != null)
    -        {
    -        item.style.pixelLeft = x;
    -        item.style.pixelTop = y;
    -        };
    -    };
    -
    -
    -//
    -//  Menu
    -// ____________________________________________________________________________
    -
    -
    -function ToggleMenu(id)
    -    {
    -    if (!window.document.getElementById)
    -        {  return;  };
    -
    -    var display = window.document.getElementById(id).style.display;
    -
    -    if (display == "none")
    -        {  display = "block";  }
    -    else
    -        {  display = "none";  }
    -
    -    window.document.getElementById(id).style.display = display;
    -    }
    -
    -function HideAllBut(ids, max)
    -    {
    -    if (document.getElementById)
    -        {
    -        ids.sort( function(a,b) { return a - b; } );
    -        var number = 1;
    -
    -        while (number < max)
    -            {
    -            if (ids.length > 0 && number == ids[0])
    -                {  ids.shift();  }
    -            else
    -                {
    -                document.getElementById("MGroupContent" + number).style.display = "none";
    -                };
    -
    -            number++;
    -            };
    -        };
    -    }
    -
    -
    -//
    -//  Tooltips
    -// ____________________________________________________________________________
    -
    -
    -var tooltipTimer = 0;
    -
    -function ShowTip(event, tooltipID, linkID)
    -    {
    -    if (tooltipTimer)
    -        {  clearTimeout(tooltipTimer);  };
    -
    -    var docX = event.clientX + window.pageXOffset;
    -    var docY = event.clientY + window.pageYOffset;
    -
    -    var showCommand = "ReallyShowTip('" + tooltipID + "', '" + linkID + "', " + docX + ", " + docY + ")";
    -
    -    tooltipTimer = setTimeout(showCommand, 1000);
    -    }
    -
    -function ReallyShowTip(tooltipID, linkID, docX, docY)
    -    {
    -    tooltipTimer = 0;
    -
    -    var tooltip;
    -    var link;
    -
    -    if (document.getElementById)
    -        {
    -        tooltip = document.getElementById(tooltipID);
    -        link = document.getElementById(linkID);
    -        }
    -/*    else if (document.all)
    -        {
    -        tooltip = eval("document.all['" + tooltipID + "']");
    -        link = eval("document.all['" + linkID + "']");
    -        }
    -*/
    -    if (tooltip)
    -        {
    -        var left = GetXPosition(link);
    -        var top = GetYPosition(link);
    -        top += link.offsetHeight;
    -
    -
    -        // The fallback method is to use the mouse X and Y relative to the document.  We use a separate if and test if its a number
    -        // in case some browser snuck through the above if statement but didn't support everything.
    -
    -        if (!isFinite(top) || top == 0)
    -            {
    -            left = docX;
    -            top = docY;
    -            }
    -
    -        // Some spacing to get it out from under the cursor.
    -
    -        top += 10;
    -
    -        // Make sure the tooltip doesnt get smushed by being too close to the edge, or in some browsers, go off the edge of the
    -        // page.  We do it here because Konqueror does get offsetWidth right even if it doesnt get the positioning right.
    -
    -        if (tooltip.offsetWidth != null)
    -            {
    -            var width = tooltip.offsetWidth;
    -            var docWidth = document.body.clientWidth;
    -
    -            if (left + width > docWidth)
    -                {  left = docWidth - width - 1;  }
    -
    -            // If there's a horizontal scroll bar we could go past zero because it's using the page width, not the window width.
    -            if (left < 0)
    -                {  left = 0;  };
    -            }
    -
    -        MoveToPosition(tooltip, left, top);
    -        tooltip.style.visibility = "visible";
    -        }
    -    }
    -
    -function HideTip(tooltipID)
    -    {
    -    if (tooltipTimer)
    -        {
    -        clearTimeout(tooltipTimer);
    -        tooltipTimer = 0;
    -        }
    -
    -    var tooltip;
    -
    -    if (document.getElementById)
    -        {  tooltip = document.getElementById(tooltipID); }
    -    else if (document.all)
    -        {  tooltip = eval("document.all['" + tooltipID + "']");  }
    -
    -    if (tooltip)
    -        {  tooltip.style.visibility = "hidden";  }
    -    }
    -
    -
    -//
    -//  Blockquote fix for IE
    -// ____________________________________________________________________________
    -
    -
    -function NDOnLoad()
    -    {
    -    if (browserVer == "IE6")
    -        {
    -        var scrollboxes = document.getElementsByTagName('blockquote');
    -
    -        if (scrollboxes.item(0))
    -            {
    -            NDDoResize();
    -            window.onresize=NDOnResize;
    -            };
    -        };
    -    };
    -
    -
    -var resizeTimer = 0;
    -
    -function NDOnResize()
    -    {
    -    if (resizeTimer != 0)
    -        {  clearTimeout(resizeTimer);  };
    -
    -    resizeTimer = setTimeout(NDDoResize, 250);
    -    };
    -
    -
    -function NDDoResize()
    -    {
    -    var scrollboxes = document.getElementsByTagName('blockquote');
    -
    -    var i;
    -    var item;
    -
    -    i = 0;
    -    while (item = scrollboxes.item(i))
    -        {
    -        item.style.width = 100;
    -        i++;
    -        };
    -
    -    i = 0;
    -    while (item = scrollboxes.item(i))
    -        {
    -        item.style.width = item.parentNode.offsetWidth;
    -        i++;
    -        };
    -
    -    clearTimeout(resizeTimer);
    -    resizeTimer = 0;
    -    }
    -
    -
    -
    -/* ________________________________________________________________________________________________________
    -
    -    Class: SearchPanel
    -    ________________________________________________________________________________________________________
    -
    -    A class handling everything associated with the search panel.
    -
    -    Parameters:
    -
    -        name - The name of the global variable that will be storing this instance.  Is needed to be able to set timeouts.
    -        mode - The mode the search is going to work in.  Pass <NaturalDocs::Builder::Base->CommandLineOption()>, so the
    -                   value will be something like "HTML" or "FramedHTML".
    -
    -    ________________________________________________________________________________________________________
    -*/
    -
    -
    -function SearchPanel(name, mode, resultsPath)
    -    {
    -    if (!name || !mode || !resultsPath)
    -        {  alert("Incorrect parameters to SearchPanel.");  };
    -
    -
    -    // Group: Variables
    -    // ________________________________________________________________________
    -
    -    /*
    -        var: name
    -        The name of the global variable that will be storing this instance of the class.
    -    */
    -    this.name = name;
    -
    -    /*
    -        var: mode
    -        The mode the search is going to work in, such as "HTML" or "FramedHTML".
    -    */
    -    this.mode = mode;
    -
    -    /*
    -        var: resultsPath
    -        The relative path from the current HTML page to the results page directory.
    -    */
    -    this.resultsPath = resultsPath;
    -
    -    /*
    -        var: keyTimeout
    -        The timeout used between a keystroke and when a search is performed.
    -    */
    -    this.keyTimeout = 0;
    -
    -    /*
    -        var: keyTimeoutLength
    -        The length of <keyTimeout> in thousandths of a second.
    -    */
    -    this.keyTimeoutLength = 500;
    -
    -    /*
    -        var: lastSearchValue
    -        The last search string executed, or an empty string if none.
    -    */
    -    this.lastSearchValue = "";
    -
    -    /*
    -        var: lastResultsPage
    -        The last results page.  The value is only relevant if <lastSearchValue> is set.
    -    */
    -    this.lastResultsPage = "";
    -
    -    /*
    -        var: deactivateTimeout
    -
    -        The timeout used between when a control is deactivated and when the entire panel is deactivated.  Is necessary
    -        because a control may be deactivated in favor of another control in the same panel, in which case it should stay
    -        active.
    -    */
    -    this.deactivateTimout = 0;
    -
    -    /*
    -        var: deactivateTimeoutLength
    -        The length of <deactivateTimeout> in thousandths of a second.
    -    */
    -    this.deactivateTimeoutLength = 200;
    -
    -
    -
    -
    -    // Group: DOM Elements
    -    // ________________________________________________________________________
    -
    -
    -    // Function: DOMSearchField
    -    this.DOMSearchField = function()
    -        {  return document.getElementById("MSearchField");  };
    -
    -    // Function: DOMSearchType
    -    this.DOMSearchType = function()
    -        {  return document.getElementById("MSearchType");  };
    -
    -    // Function: DOMPopupSearchResults
    -    this.DOMPopupSearchResults = function()
    -        {  return document.getElementById("MSearchResults");  };
    -
    -    // Function: DOMPopupSearchResultsWindow
    -    this.DOMPopupSearchResultsWindow = function()
    -        {  return document.getElementById("MSearchResultsWindow");  };
    -
    -    // Function: DOMSearchPanel
    -    this.DOMSearchPanel = function()
    -        {  return document.getElementById("MSearchPanel");  };
    -
    -
    -
    -
    -    // Group: Event Handlers
    -    // ________________________________________________________________________
    -
    -
    -    /*
    -        Function: OnSearchFieldFocus
    -        Called when focus is added or removed from the search field.
    -    */
    -    this.OnSearchFieldFocus = function(isActive)
    -        {
    -        this.Activate(isActive);
    -        };
    -
    -
    -    /*
    -        Function: OnSearchFieldChange
    -        Called when the content of the search field is changed.
    -    */
    -    this.OnSearchFieldChange = function()
    -        {
    -        if (this.keyTimeout)
    -            {
    -            clearTimeout(this.keyTimeout);
    -            this.keyTimeout = 0;
    -            };
    -
    -        var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
    -
    -        if (searchValue != this.lastSearchValue)
    -            {
    -            if (searchValue != "")
    -                {
    -                this.keyTimeout = setTimeout(this.name + ".Search()", this.keyTimeoutLength);
    -                }
    -            else
    -                {
    -                if (this.mode == "HTML")
    -                    {  this.DOMPopupSearchResultsWindow().style.display = "none";  };
    -                this.lastSearchValue = "";
    -                };
    -            };
    -        };
    -
    -
    -    /*
    -        Function: OnSearchTypeFocus
    -        Called when focus is added or removed from the search type.
    -    */
    -    this.OnSearchTypeFocus = function(isActive)
    -        {
    -        this.Activate(isActive);
    -        };
    -
    -
    -    /*
    -        Function: OnSearchTypeChange
    -        Called when the search type is changed.
    -    */
    -    this.OnSearchTypeChange = function()
    -        {
    -        var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
    -
    -        if (searchValue != "")
    -            {
    -            this.Search();
    -            };
    -        };
    -
    -
    -
    -    // Group: Action Functions
    -    // ________________________________________________________________________
    -
    -
    -    /*
    -        Function: CloseResultsWindow
    -        Closes the results window.
    -    */
    -    this.CloseResultsWindow = function()
    -        {
    -        this.DOMPopupSearchResultsWindow().style.display = "none";
    -        this.Activate(false, true);
    -        };
    -
    -
    -    /*
    -        Function: Search
    -        Performs a search.
    -    */
    -    this.Search = function()
    -        {
    -        this.keyTimeout = 0;
    -
    -        var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
    -        var searchTopic = this.DOMSearchType().value;
    -
    -        var pageExtension = searchValue.substr(0,1);
    -
    -        if (pageExtension.match(/^[a-z]/i))
    -            {  pageExtension = pageExtension.toUpperCase();  }
    -        else if (pageExtension.match(/^[0-9]/))
    -            {  pageExtension = 'Numbers';  }
    -        else
    -            {  pageExtension = "Symbols";  };
    -
    -        var resultsPage;
    -        var resultsPageWithSearch;
    -        var hasResultsPage;
    -
    -        // indexSectionsWithContent is defined in searchdata.js
    -        if (indexSectionsWithContent[searchTopic][pageExtension] == true)
    -            {
    -            resultsPage = this.resultsPath + '/' + searchTopic + pageExtension + '.html';
    -            resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
    -            hasResultsPage = true;
    -            }
    -        else
    -            {
    -            resultsPage = this.resultsPath + '/NoResults.html';
    -            resultsPageWithSearch = resultsPage;
    -            hasResultsPage = false;
    -            };
    -
    -        var resultsFrame;
    -        if (this.mode == "HTML")
    -            {  resultsFrame = window.frames.MSearchResults;  }
    -        else if (this.mode == "FramedHTML")
    -            {  resultsFrame = window.top.frames['Content'];  };
    -
    -
    -        if (resultsPage != this.lastResultsPage ||
    -
    -            // Bug in IE.  If everything becomes hidden in a run, none of them will be able to be reshown in the next for some
    -            // reason.  It counts the right number of results, and you can even read the display as "block" after setting it, but it
    -            // just doesn't work in IE 6 or IE 7.  So if we're on the right page but the previous search had no results, reload the
    -            // page anyway to get around the bug.
    -            (browserType == "IE" && hasResultsPage &&
    -            	(!resultsFrame.searchResults || resultsFrame.searchResults.lastMatchCount == 0)) )
    -
    -            {
    -            resultsFrame.location.href = resultsPageWithSearch;
    -            }
    -
    -        // So if the results page is right and there's no IE bug, reperform the search on the existing page.  We have to check if there
    -        // are results because NoResults.html doesn't have any JavaScript, and it would be useless to do anything on that page even
    -        // if it did.
    -        else if (hasResultsPage)
    -            {
    -            // We need to check if this exists in case the frame is present but didn't finish loading.
    -            if (resultsFrame.searchResults)
    -                {  resultsFrame.searchResults.Search(searchValue);  }
    -
    -            // Otherwise just reload instead of waiting.
    -            else
    -                {  resultsFrame.location.href = resultsPageWithSearch;  };
    -            };
    -
    -
    -        var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
    -
    -        if (this.mode == "HTML" && domPopupSearchResultsWindow.style.display != "block")
    -            {
    -            var domSearchType = this.DOMSearchType();
    -
    -            var left = GetXPosition(domSearchType);
    -            var top = GetYPosition(domSearchType) + domSearchType.offsetHeight;
    -
    -            MoveToPosition(domPopupSearchResultsWindow, left, top);
    -            domPopupSearchResultsWindow.style.display = 'block';
    -            };
    -
    -
    -        this.lastSearchValue = searchValue;
    -        this.lastResultsPage = resultsPage;
    -        };
    -
    -
    -
    -    // Group: Activation Functions
    -    // Functions that handle whether the entire panel is active or not.
    -    // ________________________________________________________________________
    -
    -
    -    /*
    -        Function: Activate
    -
    -        Activates or deactivates the search panel, resetting things to their default values if necessary.  You can call this on every
    -        control's OnBlur() and it will handle not deactivating the entire panel when focus is just switching between them transparently.
    -
    -        Parameters:
    -
    -            isActive - Whether you're activating or deactivating the panel.
    -            ignoreDeactivateDelay - Set if you're positive the action will deactivate the panel and thus want to skip the delay.
    -    */
    -    this.Activate = function(isActive, ignoreDeactivateDelay)
    -        {
    -        // We want to ignore isActive being false while the results window is open.
    -        if (isActive || (this.mode == "HTML" && this.DOMPopupSearchResultsWindow().style.display == "block"))
    -            {
    -            if (this.inactivateTimeout)
    -                {
    -                clearTimeout(this.inactivateTimeout);
    -                this.inactivateTimeout = 0;
    -                };
    -
    -            this.DOMSearchPanel().className = 'MSearchPanelActive';
    -
    -            var searchField = this.DOMSearchField();
    -
    -            if (searchField.value == 'Search')
    -                 {  searchField.value = "";  }
    -            }
    -        else if (!ignoreDeactivateDelay)
    -            {
    -            this.inactivateTimeout = setTimeout(this.name + ".InactivateAfterTimeout()", this.inactivateTimeoutLength);
    -            }
    -        else
    -            {
    -            this.InactivateAfterTimeout();
    -            };
    -        };
    -
    -
    -    /*
    -        Function: InactivateAfterTimeout
    -
    -        Called by <inactivateTimeout>, which is set by <Activate()>.  Inactivation occurs on a timeout because a control may
    -        receive OnBlur() when focus is really transferring to another control in the search panel.  In this case we don't want to
    -        actually deactivate the panel because not only would that cause a visible flicker but it could also reset the search value.
    -        So by doing it on a timeout instead, there's a short period where the second control's OnFocus() can cancel the deactivation.
    -    */
    -    this.InactivateAfterTimeout = function()
    -        {
    -        this.inactivateTimeout = 0;
    -
    -        this.DOMSearchPanel().className = 'MSearchPanelInactive';
    -        this.DOMSearchField().value = "Search";
    -
    -	    this.lastSearchValue = "";
    -	    this.lastResultsPage = "";
    -        };
    -    };
    -
    -
    -
    -
    -/* ________________________________________________________________________________________________________
    -
    -   Class: SearchResults
    -   _________________________________________________________________________________________________________
    -
    -   The class that handles everything on the search results page.
    -   _________________________________________________________________________________________________________
    -*/
    -
    -
    -function SearchResults(name, mode)
    -    {
    -    /*
    -        var: mode
    -        The mode the search is going to work in, such as "HTML" or "FramedHTML".
    -    */
    -    this.mode = mode;
    -
    -    /*
    -        var: lastMatchCount
    -        The number of matches from the last run of <Search()>.
    -    */
    -    this.lastMatchCount = 0;
    -
    -
    -    /*
    -        Function: Toggle
    -        Toggles the visibility of the passed element ID.
    -    */
    -    this.Toggle = function(id)
    -        {
    -        if (this.mode == "FramedHTML")
    -            {  return;  };
    -
    -        var parentElement = document.getElementById(id);
    -
    -        var element = parentElement.firstChild;
    -
    -        while (element && element != parentElement)
    -            {
    -            if (element.nodeName == 'DIV' && element.className == 'ISubIndex')
    -                {
    -                if (element.style.display == 'block')
    -                    {  element.style.display = "none";  }
    -                else
    -                    {  element.style.display = 'block';  }
    -                };
    -
    -            if (element.nodeName == 'DIV' && element.hasChildNodes())
    -                {  element = element.firstChild;  }
    -            else if (element.nextSibling)
    -                {  element = element.nextSibling;  }
    -            else
    -                {
    -                do
    -                    {
    -                    element = element.parentNode;
    -                    }
    -                while (element && element != parentElement && !element.nextSibling);
    -
    -                if (element && element != parentElement)
    -                    {  element = element.nextSibling;  };
    -                };
    -            };
    -        };
    -
    -
    -    /*
    -        Function: Search
    -
    -        Searches for the passed string.  If there is no parameter, it takes it from the URL query.
    -
    -        Always returns true, since other documents may try to call it and that may or may not be possible.
    -    */
    -    this.Search = function(search)
    -        {
    -        if (!search)
    -            {
    -            search = window.location.search;
    -            search = search.substring(1);  // Remove the leading ?
    -            search = unescape(search);
    -            };
    -
    -        search = search.replace(/^ +/, "");
    -        search = search.replace(/ +$/, "");
    -        search = search.toLowerCase();
    -
    -        if (search.match(/[^a-z0-9]/)) // Just a little speedup so it doesn't have to go through the below unnecessarily.
    -            {
    -            search = search.replace(/\_/g, "_und");
    -            search = search.replace(/\ +/gi, "_spc");
    -            search = search.replace(/\~/g, "_til");
    -            search = search.replace(/\!/g, "_exc");
    -            search = search.replace(/\@/g, "_att");
    -            search = search.replace(/\#/g, "_num");
    -            search = search.replace(/\$/g, "_dol");
    -            search = search.replace(/\%/g, "_pct");
    -            search = search.replace(/\^/g, "_car");
    -            search = search.replace(/\&/g, "_amp");
    -            search = search.replace(/\*/g, "_ast");
    -            search = search.replace(/\(/g, "_lpa");
    -            search = search.replace(/\)/g, "_rpa");
    -            search = search.replace(/\-/g, "_min");
    -            search = search.replace(/\+/g, "_plu");
    -            search = search.replace(/\=/g, "_equ");
    -            search = search.replace(/\{/g, "_lbc");
    -            search = search.replace(/\}/g, "_rbc");
    -            search = search.replace(/\[/g, "_lbk");
    -            search = search.replace(/\]/g, "_rbk");
    -            search = search.replace(/\:/g, "_col");
    -            search = search.replace(/\;/g, "_sco");
    -            search = search.replace(/\"/g, "_quo");
    -            search = search.replace(/\'/g, "_apo");
    -            search = search.replace(/\</g, "_lan");
    -            search = search.replace(/\>/g, "_ran");
    -            search = search.replace(/\,/g, "_com");
    -            search = search.replace(/\./g, "_per");
    -            search = search.replace(/\?/g, "_que");
    -            search = search.replace(/\//g, "_sla");
    -            search = search.replace(/[^a-z0-9\_]i/gi, "_zzz");
    -            };
    -
    -        var resultRows = document.getElementsByTagName("div");
    -        var matches = 0;
    -
    -        var i = 0;
    -        while (i < resultRows.length)
    -            {
    -            var row = resultRows.item(i);
    -
    -            if (row.className == "SRResult")
    -                {
    -                var rowMatchName = row.id.toLowerCase();
    -                rowMatchName = rowMatchName.replace(/^sr\d*_/, '');
    -
    -                if (search.length <= rowMatchName.length && rowMatchName.substr(0, search.length) == search)
    -                    {
    -                    row.style.display = "block";
    -                    matches++;
    -                    }
    -                else
    -                    {  row.style.display = "none";  };
    -                };
    -
    -            i++;
    -            };
    -
    -        document.getElementById("Searching").style.display="none";
    -
    -        if (matches == 0)
    -            {  document.getElementById("NoMatches").style.display="block";  }
    -        else
    -            {  document.getElementById("NoMatches").style.display="none";  }
    -
    -        this.lastMatchCount = matches;
    -
    -        return true;
    -        };
    -    };
    -
    diff --git a/doc/javascript/prettify.js b/doc/javascript/prettify.js
    deleted file mode 100644
    index fda4bf1ed..000000000
    --- a/doc/javascript/prettify.js
    +++ /dev/null
    @@ -1,1526 +0,0 @@
    -
    -// This code comes from the December 2009 release of Google Prettify, which is Copyright © 2006 Google Inc.
    -// Minor modifications are marked with "ND Change" comments.
    -// As part of Natural Docs, this code is licensed under version 3 of the GNU Affero General Public License (AGPL.)
    -// However, it may also be obtained separately under version 2.0 of the Apache License.
    -// Refer to License.txt for the complete details
    -
    -
    -// Main code
    -// ____________________________________________________________________________
    -
    -// Copyright (C) 2006 Google Inc.
    -//
    -// Licensed under the Apache License, Version 2.0 (the "License");
    -// you may not use this file except in compliance with the License.
    -// You may obtain a copy of the License at
    -//
    -//      http://www.apache.org/licenses/LICENSE-2.0
    -//
    -// Unless required by applicable law or agreed to in writing, software
    -// distributed under the License is distributed on an "AS IS" BASIS,
    -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -// See the License for the specific language governing permissions and
    -// limitations under the License.
    -
    -
    -/**
    - * @fileoverview
    - * some functions for browser-side pretty printing of code contained in html.
    - * <p>
    - *
    - * For a fairly comprehensive set of languages see the
    - * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
    - * file that came with this source.  At a minimum, the lexer should work on a
    - * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
    - * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
    - * and a subset of Perl, but, because of commenting conventions, doesn't work on
    - * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
    - * <p>
    - * Usage: <ol>
    - * <li> include this source file in an html page via
    - *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
    - * <li> define style rules.  See the example page for examples.
    - * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
    - *    {@code class=prettyprint.}
    - *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
    - *    printer needs to do more substantial DOM manipulations to support that, so
    - *    some css styles may not be preserved.
    - * </ol>
    - * That's it.  I wanted to keep the API as simple as possible, so there's no
    - * need to specify which language the code is in, but if you wish, you can add
    - * another class to the {@code <pre>} or {@code <code>} element to specify the
    - * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    - * starts with "lang-" followed by a file extension, specifies the file type.
    - * See the "lang-*.js" files in this directory for code that implements
    - * per-language file handlers.
    - * <p>
    - * Change log:<br>
    - * cbeust, 2006/08/22
    - * <blockquote>
    - *   Java annotations (start with "@") are now captured as literals ("lit")
    - * </blockquote>
    - * @requires console
    - * @overrides window
    - */
    -
    -// JSLint declarations
    -/*global console, document, navigator, setTimeout, window */
    -
    -/**
    - * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    - * UI events.
    - * If set to {@code false}, {@code prettyPrint()} is synchronous.
    - */
    -window['PR_SHOULD_USE_CONTINUATION'] = true;
    -
    -/** the number of characters between tab columns */
    -window['PR_TAB_WIDTH'] = 8;
    -
    -/** Walks the DOM returning a properly escaped version of innerHTML.
    -  * @param {Node} node
    -  * @param {Array.<string>} out output buffer that receives chunks of HTML.
    -  */
    -window['PR_normalizedHtml']
    -
    -/** Contains functions for creating and registering new language handlers.
    -  * @type {Object}
    -  */
    -  = window['PR']
    -
    -/** Pretty print a chunk of code.
    -  *
    -  * @param {string} sourceCodeHtml code as html
    -  * @return {string} code as html, but prettier
    -  */
    -  = window['prettyPrintOne']
    -/** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    -  * {@code class=prettyprint} and prettify them.
    -  * @param {Function?} opt_whenDone if specified, called when the last entry
    -  *     has been finished.
    -  */
    -  = window['prettyPrint'] = void 0;
    -
    -/** browser detection. @extern @returns false if not IE, otherwise the major version. */
    -window['_pr_isIE6'] = function () {
    -  var ieVersion = navigator && navigator.userAgent &&
    -      navigator.userAgent.match(/\bMSIE ([678])\./);
    -  ieVersion = ieVersion ? +ieVersion[1] : false;
    -  window['_pr_isIE6'] = function () { return ieVersion; };
    -  return ieVersion;
    -};
    -
    -
    -(function () {
    -  // Keyword lists for various languages.
    -  var FLOW_CONTROL_KEYWORDS =
    -      "break continue do else for if return while ";
    -  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
    -      "double enum extern float goto int long register short signed sizeof " +
    -      "static struct switch typedef union unsigned void volatile ";
    -  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
    -      "new operator private protected public this throw true try typeof ";
    -  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
    -      "concept concept_map const_cast constexpr decltype " +
    -      "dynamic_cast explicit export friend inline late_check " +
    -      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
    -      "template typeid typename using virtual wchar_t where ";
    -  var JAVA_KEYWORDS = COMMON_KEYWORDS +
    -      "abstract boolean byte extends final finally implements import " +
    -      "instanceof null native package strictfp super synchronized throws " +
    -      "transient ";
    -  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
    -      "as base by checked decimal delegate descending event " +
    -      "fixed foreach from group implicit in interface internal into is lock " +
    -      "object out override orderby params partial readonly ref sbyte sealed " +
    -      "stackalloc string select uint ulong unchecked unsafe ushort var ";
    -  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
    -      "debugger eval export function get null set undefined var with " +
    -      "Infinity NaN ";
    -  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
    -      "goto if import last local my next no our print package redo require " +
    -      "sub undef unless until use wantarray while BEGIN END ";
    -  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
    -      "elif except exec finally from global import in is lambda " +
    -      "nonlocal not or pass print raise try with yield " +
    -      "False True None ";
    -  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
    -      " defined elsif end ensure false in module next nil not or redo rescue " +
    -      "retry self super then true undef unless until when yield BEGIN END ";
    -  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
    -      "function in local set then until ";
    -  var ALL_KEYWORDS = (
    -      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
    -      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
    -
    -  // token style names.  correspond to css classes
    -  /** token style for a string literal */
    -  var PR_STRING = 'str';
    -  /** token style for a keyword */
    -  var PR_KEYWORD = 'kwd';
    -  /** token style for a comment */
    -  var PR_COMMENT = 'com';
    -  /** token style for a type */
    -  var PR_TYPE = 'typ';
    -  /** token style for a literal value.  e.g. 1, null, true. */
    -  var PR_LITERAL = 'lit';
    -  /** token style for a punctuation string. */
    -  var PR_PUNCTUATION = 'pun';
    -  /** token style for a punctuation string. */
    -  var PR_PLAIN = 'pln';
    -
    -  /** token style for an sgml tag. */
    -  var PR_TAG = 'tag';
    -  /** token style for a markup declaration such as a DOCTYPE. */
    -  var PR_DECLARATION = 'dec';
    -  /** token style for embedded source. */
    -  var PR_SOURCE = 'src';
    -  /** token style for an sgml attribute name. */
    -  var PR_ATTRIB_NAME = 'atn';
    -  /** token style for an sgml attribute value. */
    -  var PR_ATTRIB_VALUE = 'atv';
    -
    -  /**
    -   * A class that indicates a section of markup that is not code, e.g. to allow
    -   * embedding of line numbers within code listings.
    -   */
    -  var PR_NOCODE = 'nocode';
    -
    -  /** A set of tokens that can precede a regular expression literal in
    -    * javascript.
    -    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
    -    * list, but I've removed ones that might be problematic when seen in
    -    * languages that don't support regular expression literals.
    -    *
    -    * <p>Specifically, I've removed any keywords that can't precede a regexp
    -    * literal in a syntactically legal javascript program, and I've removed the
    -    * "in" keyword since it's not a keyword in many languages, and might be used
    -    * as a count of inches.
    -    *
    -    * <p>The link a above does not accurately describe EcmaScript rules since
    -    * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    -    * very well in practice.
    -    *
    -    * @private
    -    */
    -  var REGEXP_PRECEDER_PATTERN = function () {
    -      var preceders = [
    -          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
    -          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
    -          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
    -          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
    -          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
    -          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
    -          "||=", "~" /* handles =~ and !~ */,
    -          "break", "case", "continue", "delete",
    -          "do", "else", "finally", "instanceof",
    -          "return", "throw", "try", "typeof"
    -          ];
    -      var pattern = '(?:^^|[+-]';
    -      for (var i = 0; i < preceders.length; ++i) {
    -        pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
    -      }
    -      pattern += ')\\s*';  // matches at end, and matches empty string
    -      return pattern;
    -      // CAVEAT: this does not properly handle the case where a regular
    -      // expression immediately follows another since a regular expression may
    -      // have flags for case-sensitivity and the like.  Having regexp tokens
    -      // adjacent is not valid in any language I'm aware of, so I'm punting.
    -      // TODO: maybe style special characters inside a regexp as punctuation.
    -    }();
    -
    -  // Define regexps here so that the interpreter doesn't have to create an
    -  // object each time the function containing them is called.
    -  // The language spec requires a new object created even if you don't access
    -  // the $1 members.
    -  var pr_amp = /&/g;
    -  var pr_lt = /</g;
    -  var pr_gt = />/g;
    -  var pr_quot = /\"/g;
    -  /** like textToHtml but escapes double quotes to be attribute safe. */
    -  function attribToHtml(str) {
    -    return str.replace(pr_amp, '&amp;')
    -        .replace(pr_lt, '&lt;')
    -        .replace(pr_gt, '&gt;')
    -        .replace(pr_quot, '&quot;');
    -  }
    -
    -  /** escapest html special characters to html. */
    -  function textToHtml(str) {
    -    return str.replace(pr_amp, '&amp;')
    -        .replace(pr_lt, '&lt;')
    -        .replace(pr_gt, '&gt;');
    -  }
    -
    -
    -  var pr_ltEnt = /&lt;/g;
    -  var pr_gtEnt = /&gt;/g;
    -  var pr_aposEnt = /&apos;/g;
    -  var pr_quotEnt = /&quot;/g;
    -  var pr_ampEnt = /&amp;/g;
    -  var pr_nbspEnt = /&nbsp;/g;
    -  /** unescapes html to plain text. */
    -  function htmlToText(html) {
    -    var pos = html.indexOf('&');
    -    if (pos < 0) { return html; }
    -    // Handle numeric entities specially.  We can't use functional substitution
    -    // since that doesn't work in older versions of Safari.
    -    // These should be rare since most browsers convert them to normal chars.
    -    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
    -      var end = html.indexOf(';', pos);
    -      if (end >= 0) {
    -        var num = html.substring(pos + 3, end);
    -        var radix = 10;
    -        if (num && num.charAt(0) === 'x') {
    -          num = num.substring(1);
    -          radix = 16;
    -        }
    -        var codePoint = parseInt(num, radix);
    -        if (!isNaN(codePoint)) {
    -          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
    -                  html.substring(end + 1));
    -        }
    -      }
    -    }
    -
    -    return html.replace(pr_ltEnt, '<')
    -        .replace(pr_gtEnt, '>')
    -        .replace(pr_aposEnt, "'")
    -        .replace(pr_quotEnt, '"')
    -        .replace(pr_nbspEnt, ' ')
    -        .replace(pr_ampEnt, '&');
    -  }
    -
    -  /** is the given node's innerHTML normally unescaped? */
    -  function isRawContent(node) {
    -    return 'XMP' === node.tagName;
    -  }
    -
    -  var newlineRe = /[\r\n]/g;
    -  /**
    -   * Are newlines and adjacent spaces significant in the given node's innerHTML?
    -   */
    -  function isPreformatted(node, content) {
    -    // PRE means preformatted, and is a very common case, so don't create
    -    // unnecessary computed style objects.
    -    if ('PRE' === node.tagName) { return true; }
    -    if (!newlineRe.test(content)) { return true; }  // Don't care
    -    var whitespace = '';
    -    // For disconnected nodes, IE has no currentStyle.
    -    if (node.currentStyle) {
    -      whitespace = node.currentStyle.whiteSpace;
    -    } else if (window.getComputedStyle) {
    -      // Firefox makes a best guess if node is disconnected whereas Safari
    -      // returns the empty string.
    -      whitespace = window.getComputedStyle(node, null).whiteSpace;
    -    }
    -    return !whitespace || whitespace === 'pre';
    -  }
    -
    -  function normalizedHtml(node, out) {
    -    switch (node.nodeType) {
    -      case 1:  // an element
    -        var name = node.tagName.toLowerCase();
    -        out.push('<', name);
    -        for (var i = 0; i < node.attributes.length; ++i) {
    -          var attr = node.attributes[i];
    -          if (!attr.specified) { continue; }
    -          out.push(' ');
    -          normalizedHtml(attr, out);
    -        }
    -        out.push('>');
    -        for (var child = node.firstChild; child; child = child.nextSibling) {
    -          normalizedHtml(child, out);
    -        }
    -        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
    -          out.push('<\/', name, '>');
    -        }
    -        break;
    -      case 2: // an attribute
    -        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
    -        break;
    -      case 3: case 4: // text
    -        out.push(textToHtml(node.nodeValue));
    -        break;
    -    }
    -  }
    -
    -  /**
    -   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    -   * matches the union o the sets o strings matched d by the input RegExp.
    -   * Since it matches globally, if the input strings have a start-of-input
    -   * anchor (/^.../), it is ignored for the purposes of unioning.
    -   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    -   * @return {RegExp} a global regex.
    -   */
    -  function combinePrefixPatterns(regexs) {
    -    var capturedGroupIndex = 0;
    -
    -    var needToFoldCase = false;
    -    var ignoreCase = false;
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.ignoreCase) {
    -        ignoreCase = true;
    -      } else if (/[a-z]/i.test(regex.source.replace(
    -                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    -        needToFoldCase = true;
    -        ignoreCase = false;
    -        break;
    -      }
    -    }
    -
    -    function decodeEscape(charsetPart) {
    -      if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
    -      switch (charsetPart.charAt(1)) {
    -        case 'b': return 8;
    -        case 't': return 9;
    -        case 'n': return 0xa;
    -        case 'v': return 0xb;
    -        case 'f': return 0xc;
    -        case 'r': return 0xd;
    -        case 'u': case 'x':
    -          return parseInt(charsetPart.substring(2), 16)
    -              || charsetPart.charCodeAt(1);
    -        case '0': case '1': case '2': case '3': case '4':
    -        case '5': case '6': case '7':
    -          return parseInt(charsetPart.substring(1), 8);
    -        default: return charsetPart.charCodeAt(1);
    -      }
    -    }
    -
    -    function encodeEscape(charCode) {
    -      if (charCode < 0x20) {
    -        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    -      }
    -      var ch = String.fromCharCode(charCode);
    -      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
    -        ch = '\\' + ch;
    -      }
    -      return ch;
    -    }
    -
    -    function caseFoldCharset(charSet) {
    -      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    -          new RegExp(
    -              '\\\\u[0-9A-Fa-f]{4}'
    -              + '|\\\\x[0-9A-Fa-f]{2}'
    -              + '|\\\\[0-3][0-7]{0,2}'
    -              + '|\\\\[0-7]{1,2}'
    -              + '|\\\\[\\s\\S]'
    -              + '|-'
    -              + '|[^-\\\\]',
    -              'g'));
    -      var groups = [];
    -      var ranges = [];
    -      var inverse = charsetParts[0] === '^';
    -      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    -        var p = charsetParts[i];
    -        switch (p) {
    -          case '\\B': case '\\b':
    -          case '\\D': case '\\d':
    -          case '\\S': case '\\s':
    -          case '\\W': case '\\w':
    -            groups.push(p);
    -            continue;
    -        }
    -        var start = decodeEscape(p);
    -        var end;
    -        if (i + 2 < n && '-' === charsetParts[i + 1]) {
    -          end = decodeEscape(charsetParts[i + 2]);
    -          i += 2;
    -        } else {
    -          end = start;
    -        }
    -        ranges.push([start, end]);
    -        // If the range might intersect letters, then expand it.
    -        if (!(end < 65 || start > 122)) {
    -          if (!(end < 65 || start > 90)) {
    -            ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    -          }
    -          if (!(end < 97 || start > 122)) {
    -            ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    -          }
    -        }
    -      }
    -
    -      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    -      // -> [[1, 12], [14, 14], [16, 17]]
    -      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    -      var consolidatedRanges = [];
    -      var lastRange = [NaN, NaN];
    -      for (var i = 0; i < ranges.length; ++i) {
    -        var range = ranges[i];
    -        if (range[0] <= lastRange[1] + 1) {
    -          lastRange[1] = Math.max(lastRange[1], range[1]);
    -        } else {
    -          consolidatedRanges.push(lastRange = range);
    -        }
    -      }
    -
    -      var out = ['['];
    -      if (inverse) { out.push('^'); }
    -      out.push.apply(out, groups);
    -      for (var i = 0; i < consolidatedRanges.length; ++i) {
    -        var range = consolidatedRanges[i];
    -        out.push(encodeEscape(range[0]));
    -        if (range[1] > range[0]) {
    -          if (range[1] + 1 > range[0]) { out.push('-'); }
    -          out.push(encodeEscape(range[1]));
    -        }
    -      }
    -      out.push(']');
    -      return out.join('');
    -    }
    -
    -    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    -      // Split into character sets, escape sequences, punctuation strings
    -      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    -      // include any of the above.
    -      var parts = regex.source.match(
    -          new RegExp(
    -              '(?:'
    -              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    -              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    -              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    -              + '|\\\\[0-9]+'  // a back-reference or octal escape
    -              + '|\\\\[^ux0-9]'  // other escape sequence
    -              + '|\\(\\?[:!=]'  // start of a non-capturing group
    -              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
    -              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    -              + ')',
    -              'g'));
    -      var n = parts.length;
    -
    -      // Maps captured group numbers to the number they will occupy in
    -      // the output or to -1 if that has not been determined, or to
    -      // undefined if they need not be capturing in the output.
    -      var capturedGroups = [];
    -
    -      // Walk over and identify back references to build the capturedGroups
    -      // mapping.
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          // groups are 1-indexed, so max group index is count of '('
    -          ++groupIndex;
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue && decimalValue <= groupIndex) {
    -            capturedGroups[decimalValue] = -1;
    -          }
    -        }
    -      }
    -
    -      // Renumber groups and reduce capturing groups to non-capturing groups
    -      // where possible.
    -      for (var i = 1; i < capturedGroups.length; ++i) {
    -        if (-1 === capturedGroups[i]) {
    -          capturedGroups[i] = ++capturedGroupIndex;
    -        }
    -      }
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          ++groupIndex;
    -          if (capturedGroups[groupIndex] === undefined) {
    -            parts[i] = '(?:';
    -          }
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue && decimalValue <= groupIndex) {
    -            parts[i] = '\\' + capturedGroups[groupIndex];
    -          }
    -        }
    -      }
    -
    -      // Remove any prefix anchors so that the output will match anywhere.
    -      // ^^ really does mean an anchored match though.
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    -      }
    -
    -      // Expand letters to groupts to handle mixing of case-sensitive and
    -      // case-insensitive patterns if necessary.
    -      if (regex.ignoreCase && needToFoldCase) {
    -        for (var i = 0; i < n; ++i) {
    -          var p = parts[i];
    -          var ch0 = p.charAt(0);
    -          if (p.length >= 2 && ch0 === '[') {
    -            parts[i] = caseFoldCharset(p);
    -          } else if (ch0 !== '\\') {
    -            // TODO: handle letters in numeric escapes.
    -            parts[i] = p.replace(
    -                /[a-zA-Z]/g,
    -                function (ch) {
    -                  var cc = ch.charCodeAt(0);
    -                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    -                });
    -          }
    -        }
    -      }
    -
    -      return parts.join('');
    -    }
    -
    -    var rewritten = [];
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    -      rewritten.push(
    -          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    -    }
    -
    -    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    -  }
    -
    -  var PR_innerHtmlWorks = null;
    -  function getInnerHtml(node) {
    -    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    -    // an html description of well formed XML and the containing tag is a PRE
    -    // tag, so we detect that case and emulate innerHTML.
    -    if (null === PR_innerHtmlWorks) {
    -      var testNode = document.createElement('PRE');
    -      testNode.appendChild(
    -          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
    -      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    -    }
    -
    -    if (PR_innerHtmlWorks) {
    -      var content = node.innerHTML;
    -      // XMP tags contain unescaped entities so require special handling.
    -      if (isRawContent(node)) {
    -        content = textToHtml(content);
    -      } else if (!isPreformatted(node, content)) {
    -        content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1')
    -            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
    -      }
    -      return content;
    -    }
    -
    -    var out = [];
    -    for (var child = node.firstChild; child; child = child.nextSibling) {
    -      normalizedHtml(child, out);
    -    }
    -    return out.join('');
    -  }
    -
    -  /** returns a function that expand tabs to spaces.  This function can be fed
    -    * successive chunks of text, and will maintain its own internal state to
    -    * keep track of how tabs are expanded.
    -    * @return {function (string) : string} a function that takes
    -    *   plain text and return the text with tabs expanded.
    -    * @private
    -    */
    -  function makeTabExpander(tabWidth) {
    -    var SPACES = '                ';
    -    var charInLine = 0;
    -
    -    return function (plainText) {
    -      // walk over each character looking for tabs and newlines.
    -      // On tabs, expand them.  On newlines, reset charInLine.
    -      // Otherwise increment charInLine
    -      var out = null;
    -      var pos = 0;
    -      for (var i = 0, n = plainText.length; i < n; ++i) {
    -        var ch = plainText.charAt(i);
    -
    -        switch (ch) {
    -          case '\t':
    -            if (!out) { out = []; }
    -            out.push(plainText.substring(pos, i));
    -            // calculate how much space we need in front of this part
    -            // nSpaces is the amount of padding -- the number of spaces needed
    -            // to move us to the next column, where columns occur at factors of
    -            // tabWidth.
    -            var nSpaces = tabWidth - (charInLine % tabWidth);
    -            charInLine += nSpaces;
    -            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
    -              out.push(SPACES.substring(0, nSpaces));
    -            }
    -            pos = i + 1;
    -            break;
    -          case '\n':
    -            charInLine = 0;
    -            break;
    -          default:
    -            ++charInLine;
    -        }
    -      }
    -      if (!out) { return plainText; }
    -      out.push(plainText.substring(pos));
    -      return out.join('');
    -    };
    -  }
    -
    -  var pr_chunkPattern = new RegExp(
    -      '[^<]+'  // A run of characters other than '<'
    -      + '|<\!--[\\s\\S]*?--\>'  // an HTML comment
    -      + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // a CDATA section
    -      // a probable tag that should not be highlighted
    -      + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
    -      + '|<',  // A '<' that does not begin a larger chunk
    -      'g');
    -  var pr_commentPrefix = /^<\!--/;
    -  var pr_cdataPrefix = /^<!\[CDATA\[/;
    -  var pr_brPrefix = /^<br\b/i;
    -  var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
    -
    -  /** split markup into chunks of html tags (style null) and
    -    * plain text (style {@link #PR_PLAIN}), converting tags which are
    -    * significant for tokenization (<br>) into their textual equivalent.
    -    *
    -    * @param {string} s html where whitespace is considered significant.
    -    * @return {Object} source code and extracted tags.
    -    * @private
    -    */
    -  function extractTags(s) {
    -    // since the pattern has the 'g' modifier and defines no capturing groups,
    -    // this will return a list of all chunks which we then classify and wrap as
    -    // PR_Tokens
    -    var matches = s.match(pr_chunkPattern);
    -    var sourceBuf = [];
    -    var sourceBufLen = 0;
    -    var extractedTags = [];
    -    if (matches) {
    -      for (var i = 0, n = matches.length; i < n; ++i) {
    -        var match = matches[i];
    -        if (match.length > 1 && match.charAt(0) === '<') {
    -          if (pr_commentPrefix.test(match)) { continue; }
    -          if (pr_cdataPrefix.test(match)) {
    -            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
    -            sourceBuf.push(match.substring(9, match.length - 3));
    -            sourceBufLen += match.length - 12;
    -          } else if (pr_brPrefix.test(match)) {
    -            // <br> tags are lexically significant so convert them to text.
    -            // This is undone later.
    -            sourceBuf.push('\n');
    -            ++sourceBufLen;
    -          } else {
    -            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
    -              // A <span class="nocode"> will start a section that should be
    -              // ignored.  Continue walking the list until we see a matching end
    -              // tag.
    -              var name = match.match(pr_tagNameRe)[2];
    -              var depth = 1;
    -              var j;
    -              end_tag_loop:
    -              for (j = i + 1; j < n; ++j) {
    -                var name2 = matches[j].match(pr_tagNameRe);
    -                if (name2 && name2[2] === name) {
    -                  if (name2[1] === '/') {
    -                    if (--depth === 0) { break end_tag_loop; }
    -                  } else {
    -                    ++depth;
    -                  }
    -                }
    -              }
    -              if (j < n) {
    -                extractedTags.push(
    -                    sourceBufLen, matches.slice(i, j + 1).join(''));
    -                i = j;
    -              } else {  // Ignore unclosed sections.
    -                extractedTags.push(sourceBufLen, match);
    -              }
    -            } else {
    -              extractedTags.push(sourceBufLen, match);
    -            }
    -          }
    -        } else {
    -          var literalText = htmlToText(match);
    -          sourceBuf.push(literalText);
    -          sourceBufLen += literalText.length;
    -        }
    -      }
    -    }
    -    return { source: sourceBuf.join(''), tags: extractedTags };
    -  }
    -
    -  /** True if the given tag contains a class attribute with the nocode class. */
    -  function isNoCodeTag(tag) {
    -    return !!tag
    -        // First canonicalize the representation of attributes
    -        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
    -                 ' $1="$2$3$4"')
    -        // Then look for the attribute we want.
    -        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
    -  }
    -
    -  /**
    -   * Apply the given language handler to sourceCode and add the resulting
    -   * decorations to out.
    -   * @param {number} basePos the index of sourceCode within the chunk of source
    -   *    whose decorations are already present on out.
    -   */
    -  function appendDecorations(basePos, sourceCode, langHandler, out) {
    -    if (!sourceCode) { return; }
    -    var job = {
    -      source: sourceCode,
    -      basePos: basePos
    -    };
    -    langHandler(job);
    -    out.push.apply(out, job.decorations);
    -  }
    -
    -  /** Given triples of [style, pattern, context] returns a lexing function,
    -    * The lexing function interprets the patterns to find token boundaries and
    -    * returns a decoration list of the form
    -    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    -    * where index_n is an index into the sourceCode, and style_n is a style
    -    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    -    * all characters in sourceCode[index_n-1:index_n].
    -    *
    -    * The stylePatterns is a list whose elements have the form
    -    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    -    *
    -    * Style is a style constant like PR_PLAIN, or can be a string of the
    -    * form 'lang-FOO', where FOO is a language extension describing the
    -    * language of the portion of the token in $1 after pattern executes.
    -    * E.g., if style is 'lang-lisp', and group 1 contains the text
    -    * '(hello (world))', then that portion of the token will be passed to the
    -    * registered lisp handler for formatting.
    -    * The text before and after group 1 will be restyled using this decorator
    -    * so decorators should take care that this doesn't result in infinite
    -    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    -    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    -    * '<script>foo()<\/script>', which would cause the current decorator to
    -    * be called with '<script>' which would not match the same rule since
    -    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    -    * the generic tag rule.  The handler registered for the 'js' extension would
    -    * then be called with 'foo()', and finally, the current decorator would
    -    * be called with '<\/script>' which would not match the original rule and
    -    * so the generic tag rule would identify it as a tag.
    -    *
    -    * Pattern must only match prefixes, and if it matches a prefix, then that
    -    * match is considered a token with the same style.
    -    *
    -    * Context is applied to the last non-whitespace, non-comment token
    -    * recognized.
    -    *
    -    * Shortcut is an optional string of characters, any of which, if the first
    -    * character, gurantee that this pattern and only this pattern matches.
    -    *
    -    * @param {Array} shortcutStylePatterns patterns that always start with
    -    *   a known character.  Must have a shortcut string.
    -    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    -    *   order if the shortcut ones fail.  May have shortcuts.
    -    *
    -    * @return {function (Object)} a
    -    *   function that takes source code and returns a list of decorations.
    -    */
    -  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    -    var shortcuts = {};
    -    var tokenizer;
    -    (function () {
    -      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    -      var allRegexs = [];
    -      var regexKeys = {};
    -      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    -        var patternParts = allPatterns[i];
    -        var shortcutChars = patternParts[3];
    -        if (shortcutChars) {
    -          for (var c = shortcutChars.length; --c >= 0;) {
    -            shortcuts[shortcutChars.charAt(c)] = patternParts;
    -          }
    -        }
    -        var regex = patternParts[1];
    -        var k = '' + regex;
    -        if (!regexKeys.hasOwnProperty(k)) {
    -          allRegexs.push(regex);
    -          regexKeys[k] = null;
    -        }
    -      }
    -      allRegexs.push(/[\0-\uffff]/);
    -      tokenizer = combinePrefixPatterns(allRegexs);
    -    })();
    -
    -    var nPatterns = fallthroughStylePatterns.length;
    -    var notWs = /\S/;
    -
    -    /**
    -     * Lexes job.source and produces an output array job.decorations of style
    -     * classes preceded by the position at which they start in job.source in
    -     * order.
    -     *
    -     * @param {Object} job an object like {@code
    -     *    source: {string} sourceText plain text,
    -     *    basePos: {int} position of job.source in the larger chunk of
    -     *        sourceCode.
    -     * }
    -     */
    -    var decorate = function (job) {
    -      var sourceCode = job.source, basePos = job.basePos;
    -      /** Even entries are positions in source in ascending order.  Odd enties
    -        * are style markers (e.g., PR_COMMENT) that run from that position until
    -        * the end.
    -        * @type {Array.<number|string>}
    -        */
    -      var decorations = [basePos, PR_PLAIN];
    -      var pos = 0;  // index into sourceCode
    -      var tokens = sourceCode.match(tokenizer) || [];
    -      var styleCache = {};
    -
    -      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    -        var token = tokens[ti];
    -        var style = styleCache[token];
    -        var match = void 0;
    -
    -        var isEmbedded;
    -        if (typeof style === 'string') {
    -          isEmbedded = false;
    -        } else {
    -          var patternParts = shortcuts[token.charAt(0)];
    -          if (patternParts) {
    -            match = token.match(patternParts[1]);
    -            style = patternParts[0];
    -          } else {
    -            for (var i = 0; i < nPatterns; ++i) {
    -              patternParts = fallthroughStylePatterns[i];
    -              match = token.match(patternParts[1]);
    -              if (match) {
    -                style = patternParts[0];
    -                break;
    -              }
    -            }
    -
    -            if (!match) {  // make sure that we make progress
    -              style = PR_PLAIN;
    -            }
    -          }
    -
    -          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    -          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    -            isEmbedded = false;
    -            style = PR_SOURCE;
    -          }
    -
    -          if (!isEmbedded) { styleCache[token] = style; }
    -        }
    -
    -        var tokenStart = pos;
    -        pos += token.length;
    -
    -        if (!isEmbedded) {
    -          decorations.push(basePos + tokenStart, style);
    -        } else {  // Treat group 1 as an embedded block of source code.
    -          var embeddedSource = match[1];
    -          var embeddedSourceStart = token.indexOf(embeddedSource);
    -          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    -          if (match[2]) {
    -            // If embeddedSource can be blank, then it would match at the
    -            // beginning which would cause us to infinitely recurse on the
    -            // entire token, so we catch the right context in match[2].
    -            embeddedSourceEnd = token.length - match[2].length;
    -            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    -          }
    -          var lang = style.substring(5);
    -          // Decorate the left of the embedded source
    -          appendDecorations(
    -              basePos + tokenStart,
    -              token.substring(0, embeddedSourceStart),
    -              decorate, decorations);
    -          // Decorate the embedded source
    -          appendDecorations(
    -              basePos + tokenStart + embeddedSourceStart,
    -              embeddedSource,
    -              langHandlerForExtension(lang, embeddedSource),
    -              decorations);
    -          // Decorate the right of the embedded section
    -          appendDecorations(
    -              basePos + tokenStart + embeddedSourceEnd,
    -              token.substring(embeddedSourceEnd),
    -              decorate, decorations);
    -        }
    -      }
    -      job.decorations = decorations;
    -    };
    -    return decorate;
    -  }
    -
    -  /** returns a function that produces a list of decorations from source text.
    -    *
    -    * This code treats ", ', and ` as string delimiters, and \ as a string
    -    * escape.  It does not recognize perl's qq() style strings.
    -    * It has no special handling for double delimiter escapes as in basic, or
    -    * the tripled delimiters used in python, but should work on those regardless
    -    * although in those cases a single string literal may be broken up into
    -    * multiple adjacent string literals.
    -    *
    -    * It recognizes C, C++, and shell style comments.
    -    *
    -    * @param {Object} options a set of optional parameters.
    -    * @return {function (Object)} a function that examines the source code
    -    *     in the input job and builds the decoration list.
    -    */
    -  function sourceDecorator(options) {
    -    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    -    if (options['tripleQuotedStrings']) {
    -      // '''multi-line-string''', 'single-line-string', and double-quoted
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    -           null, '\'"']);
    -    } else if (options['multiLineStrings']) {
    -      // 'multi-line-string', "multi-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    -           null, '\'"`']);
    -    } else {
    -      // 'single-line-string', "single-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,
    -           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    -           null, '"\'']);
    -    }
    -    if (options['verbatimStrings']) {
    -      // verbatim-string-literal production from the C# grammar.  See issue 93.
    -      fallthroughStylePatterns.push(
    -          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    -    }
    -    if (options['hashComments']) {
    -      if (options['cStyleComments']) {
    -        // Stop C preprocessor declarations at an unclosed open comment
    -        shortcutStylePatterns.push(
    -            [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
    -             null, '#']);
    -        fallthroughStylePatterns.push(
    -            [PR_STRING,
    -             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
    -             null]);
    -      } else {
    -        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    -      }
    -    }
    -    if (options['cStyleComments']) {
    -      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    -      fallthroughStylePatterns.push(
    -          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    -    }
    -    if (options['regexLiterals']) {
    -      var REGEX_LITERAL = (
    -          // A regular expression literal starts with a slash that is
    -          // not followed by * or / so that it is not confused with
    -          // comments.
    -          '/(?=[^/*])'
    -          // and then contains any number of raw characters,
    -          + '(?:[^/\\x5B\\x5C]'
    -          // escape sequences (\x5C),
    -          +    '|\\x5C[\\s\\S]'
    -          // or non-nesting character sets (\x5B\x5D);
    -          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
    -          // finally closed by a /.
    -          + '/');
    -      fallthroughStylePatterns.push(
    -          ['lang-regex',
    -           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    -           ]);
    -    }
    -
    -    var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
    -    if (keywords.length) {
    -      fallthroughStylePatterns.push(
    -          [PR_KEYWORD,
    -           new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
    -    }
    -
    -    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    -    fallthroughStylePatterns.push(
    -        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    -        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
    -        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_LITERAL,
    -         new RegExp(
    -             '^(?:'
    -             // A hex number
    -             + '0x[a-f0-9]+'
    -             // or an octal or decimal number,
    -             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    -             // possibly in scientific notation
    -             + '(?:e[+\\-]?\\d+)?'
    -             + ')'
    -             // with an optional modifier like UL for unsigned long
    -             + '[a-z]*', 'i'),
    -         null, '0123456789'],
    -        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
    -
    -    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    -  }
    -
    -  var decorateSource = sourceDecorator({
    -        'keywords': ALL_KEYWORDS,
    -        'hashComments': true,
    -        'cStyleComments': true,
    -        'multiLineStrings': true,
    -        'regexLiterals': true
    -      });
    -
    -  /** Breaks {@code job.source} around style boundaries in
    -    * {@code job.decorations} while re-interleaving {@code job.extractedTags},
    -    * and leaves the result in {@code job.prettyPrintedHtml}.
    -    * @param {Object} job like {
    -    *    source: {string} source as plain text,
    -    *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
    -    *                   html preceded by their position in {@code job.source}
    -    *                   in order
    -    *    decorations: {Array.<number|string} an array of style classes preceded
    -    *                 by the position at which they start in job.source in order
    -    * }
    -    * @private
    -    */
    -  function recombineTagsAndDecorations(job) {
    -    var sourceText = job.source;
    -    var extractedTags = job.extractedTags;
    -    var decorations = job.decorations;
    -
    -    var html = [];
    -    // index past the last char in sourceText written to html
    -    var outputIdx = 0;
    -
    -    var openDecoration = null;
    -    var currentDecoration = null;
    -    var tagPos = 0;  // index into extractedTags
    -    var decPos = 0;  // index into decorations
    -    var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
    -
    -    var adjacentSpaceRe = /([\r\n ]) /g;
    -    var startOrSpaceRe = /(^| ) /gm;
    -    var newlineRe = /\r\n?|\n/g;
    -    var trailingSpaceRe = /[ \r\n]$/;
    -    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
    -
    -    // A helper function that is responsible for opening sections of decoration
    -    // and outputing properly escaped chunks of source
    -    function emitTextUpTo(sourceIdx) {
    -      if (sourceIdx > outputIdx) {
    -        if (openDecoration && openDecoration !== currentDecoration) {
    -          // Close the current decoration
    -          html.push('</span>');
    -          openDecoration = null;
    -        }
    -        if (!openDecoration && currentDecoration) {
    -          openDecoration = currentDecoration;
    -          html.push('<span class="', openDecoration, '">');
    -        }
    -        // This interacts badly with some wikis which introduces paragraph tags
    -        // into pre blocks for some strange reason.
    -        // It's necessary for IE though which seems to lose the preformattedness
    -        // of <pre> tags when their innerHTML is assigned.
    -        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
    -        // and it serves to undo the conversion of <br>s to newlines done in
    -        // chunkify.
    -        var htmlChunk = textToHtml(
    -            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
    -            .replace(lastWasSpace
    -                     ? startOrSpaceRe
    -                     : adjacentSpaceRe, '$1&nbsp;');
    -        // Keep track of whether we need to escape space at the beginning of the
    -        // next chunk.
    -        lastWasSpace = trailingSpaceRe.test(htmlChunk);
    -        // IE collapses multiple adjacient <br>s into 1 line break.
    -        // Prefix every <br> with '&nbsp;' can prevent such IE's behavior.
    -        var lineBreakHtml = window['_pr_isIE6']() ? '&nbsp;<br />' : '<br />';
    -        html.push(htmlChunk.replace(newlineRe, lineBreakHtml));
    -        outputIdx = sourceIdx;
    -      }
    -    }
    -
    -    while (true) {
    -      // Determine if we're going to consume a tag this time around.  Otherwise
    -      // we consume a decoration or exit.
    -      var outputTag;
    -      if (tagPos < extractedTags.length) {
    -        if (decPos < decorations.length) {
    -          // Pick one giving preference to extractedTags since we shouldn't open
    -          // a new style that we're going to have to immediately close in order
    -          // to output a tag.
    -          outputTag = extractedTags[tagPos] <= decorations[decPos];
    -        } else {
    -          outputTag = true;
    -        }
    -      } else {
    -        outputTag = false;
    -      }
    -      // Consume either a decoration or a tag or exit.
    -      if (outputTag) {
    -        emitTextUpTo(extractedTags[tagPos]);
    -        if (openDecoration) {
    -          // Close the current decoration
    -          html.push('</span>');
    -          openDecoration = null;
    -        }
    -        html.push(extractedTags[tagPos + 1]);
    -        tagPos += 2;
    -      } else if (decPos < decorations.length) {
    -        emitTextUpTo(decorations[decPos]);
    -        currentDecoration = decorations[decPos + 1];
    -        decPos += 2;
    -      } else {
    -        break;
    -      }
    -    }
    -    emitTextUpTo(sourceText.length);
    -    if (openDecoration) {
    -      html.push('</span>');
    -    }
    -    job.prettyPrintedHtml = html.join('');
    -  }
    -
    -  /** Maps language-specific file extensions to handlers. */
    -  var langHandlerRegistry = {};
    -  /** Register a language handler for the given file extensions.
    -    * @param {function (Object)} handler a function from source code to a list
    -    *      of decorations.  Takes a single argument job which describes the
    -    *      state of the computation.   The single parameter has the form
    -    *      {@code {
    -    *        source: {string} as plain text.
    -    *        decorations: {Array.<number|string>} an array of style classes
    -    *                     preceded by the position at which they start in
    -    *                     job.source in order.
    -    *                     The language handler should assigned this field.
    -    *        basePos: {int} the position of source in the larger source chunk.
    -    *                 All positions in the output decorations array are relative
    -    *                 to the larger source chunk.
    -    *      } }
    -    * @param {Array.<string>} fileExtensions
    -    */
    -  function registerLangHandler(handler, fileExtensions) {
    -    for (var i = fileExtensions.length; --i >= 0;) {
    -      var ext = fileExtensions[i];
    -      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    -        langHandlerRegistry[ext] = handler;
    -      } else if ('console' in window) {
    -        console.warn('cannot override language handler %s', ext);
    -      }
    -    }
    -  }
    -  function langHandlerForExtension(extension, source) {
    -    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    -      // Treat it as markup if the first non whitespace character is a < and
    -      // the last non-whitespace character is a >.
    -      extension = /^\s*</.test(source)
    -          ? 'default-markup'
    -          : 'default-code';
    -    }
    -    return langHandlerRegistry[extension];
    -  }
    -  registerLangHandler(decorateSource, ['default-code']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [],
    -          [
    -           [PR_PLAIN,       /^[^<?]+/],
    -           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    -           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    -           // Unescaped content in an unknown language
    -           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    -           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    -           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    -           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    -           // Unescaped content in javascript.  (Or possibly vbscript).
    -           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    -           // Contains unescaped stylesheet content
    -           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    -           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    -          ]),
    -      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [
    -           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    -           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    -           ],
    -          [
    -           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    -           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    -           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    -           [PR_PUNCTUATION,  /^[=<>\/]+/],
    -           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    -           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    -           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    -           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    -           ]),
    -      ['in.tag']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CPP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true
    -        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': 'null true false'
    -        }), ['json']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CSHARP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true,
    -          'verbatimStrings': true
    -        }), ['cs']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JAVA_KEYWORDS,
    -          'cStyleComments': true
    -        }), ['java']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': SH_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true
    -        }), ['bsh', 'csh', 'sh']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PYTHON_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'tripleQuotedStrings': true
    -        }), ['cv', 'py']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PERL_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': true
    -        }), ['perl', 'pl', 'pm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': RUBY_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': true
    -        }), ['rb']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JSCRIPT_KEYWORDS,
    -          'cStyleComments': true,
    -          'regexLiterals': true
    -        }), ['js']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    -
    -  function applyDecorator(job) {
    -    var sourceCodeHtml = job.sourceCodeHtml;
    -    var opt_langExtension = job.langExtension;
    -
    -    // Prepopulate output in case processing fails with an exception.
    -    job.prettyPrintedHtml = sourceCodeHtml;
    -
    -    try {
    -      // Extract tags, and convert the source code to plain text.
    -      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
    -      /** Plain text. @type {string} */
    -      var source = sourceAndExtractedTags.source;
    -      job.source = source;
    -      job.basePos = 0;
    -
    -      /** Even entries are positions in source in ascending order.  Odd entries
    -        * are tags that were extracted at that position.
    -        * @type {Array.<number|string>}
    -        */
    -      job.extractedTags = sourceAndExtractedTags.tags;
    -
    -      // Apply the appropriate language handler
    -      langHandlerForExtension(opt_langExtension, source)(job);
    -      // Integrate the decorations and tags back into the source code to produce
    -      // a decorated html string which is left in job.prettyPrintedHtml.
    -      recombineTagsAndDecorations(job);
    -    } catch (e) {
    -      if ('console' in window) {
    -        console.log(e);
    -        console.trace();
    -      }
    -    }
    -  }
    -
    -  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
    -    var job = {
    -      sourceCodeHtml: sourceCodeHtml,
    -      langExtension: opt_langExtension
    -    };
    -    applyDecorator(job);
    -    return job.prettyPrintedHtml;
    -  }
    -
    -  function prettyPrint(opt_whenDone) {
    -    var isIE678 = window['_pr_isIE6']();
    -    var ieNewline = isIE678 === 6 ? '\r\n' : '\r';
    -    // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
    -
    -    // fetch a list of nodes to rewrite
    -    var codeSegments = [
    -        document.getElementsByTagName('pre'),
    -        document.getElementsByTagName('code'),
    -        document.getElementsByTagName('td'),  /* ND Change: Add tables to support prototypes. */
    -        document.getElementsByTagName('xmp') ];
    -    var elements = [];
    -    for (var i = 0; i < codeSegments.length; ++i) {
    -      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    -        elements.push(codeSegments[i][j]);
    -      }
    -    }
    -    codeSegments = null;
    -
    -    var clock = Date;
    -    if (!clock['now']) {
    -      clock = { 'now': function () { return (new Date).getTime(); } };
    -    }
    -
    -    // The loop is broken into a series of continuations to make sure that we
    -    // don't make the browser unresponsive when rewriting a large page.
    -    var k = 0;
    -    var prettyPrintingJob;
    -
    -    function doWork() {
    -      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    -                     clock.now() + 250 /* ms */ :
    -                     Infinity);
    -      for (; k < elements.length && clock.now() < endTime; k++) {
    -        var cs = elements[k];
    -        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    -          // If the classes includes a language extensions, use it.
    -          // Language extensions can be specified like
    -          //     <pre class="prettyprint lang-cpp">
    -          // the language extension "cpp" is used to find a language handler as
    -          // passed to PR_registerLangHandler.
    -          var langExtension = cs.className.match(/\blang-(\w+)\b/);
    -          if (langExtension) { langExtension = langExtension[1]; }
    -
    -          // make sure this is not nested in an already prettified element
    -          var nested = false;
    -          for (var p = cs.parentNode; p; p = p.parentNode) {
    -            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    -                 p.tagName === 'xmp' || p.tagName === 'td') &&  /* ND Change: Add tables to support prototypes */
    -                p.className && p.className.indexOf('prettyprint') >= 0) {
    -              nested = true;
    -              break;
    -            }
    -          }
    -          if (!nested) {
    -            // fetch the content as a snippet of properly escaped HTML.
    -            // Firefox adds newlines at the end.
    -            var content = getInnerHtml(cs);
    -            content = content.replace(/(?:\r\n?|\n)$/, '');
    -
    -	  		/* ND Change: we need to preserve &nbsp;s so change them to a special character instead of a space. */
    -			content = content.replace(/&nbsp;/g, '\x11');
    -
    -            // do the pretty printing
    -            prettyPrintingJob = {
    -              sourceCodeHtml: content,
    -              langExtension: langExtension,
    -              sourceNode: cs
    -            };
    -            applyDecorator(prettyPrintingJob);
    -            replaceWithPrettyPrintedHtml();
    -          }
    -        }
    -      }
    -      if (k < elements.length) {
    -        // finish up in a continuation
    -        setTimeout(doWork, 250);
    -      } else if (opt_whenDone) {
    -        opt_whenDone();
    -      }
    -    }
    -
    -    function replaceWithPrettyPrintedHtml() {
    -      var newContent = prettyPrintingJob.prettyPrintedHtml;
    -      if (!newContent) { return; }
    -
    -      /* ND Change: Restore the preserved &nbsp;s.  */
    -	  newContent = newContent.replace(/\x11/g, '&nbsp;');
    -
    -      var cs = prettyPrintingJob.sourceNode;
    -
    -      // push the prettified html back into the tag.
    -      if (!isRawContent(cs)) {
    -        // just replace the old html with the new
    -        cs.innerHTML = newContent;
    -      } else {
    -        // we need to change the tag to a <pre> since <xmp>s do not allow
    -        // embedded tags such as the span tags used to attach styles to
    -        // sections of source code.
    -        var pre = document.createElement('PRE');
    -        for (var i = 0; i < cs.attributes.length; ++i) {
    -          var a = cs.attributes[i];
    -          if (a.specified) {
    -            var aname = a.name.toLowerCase();
    -            if (aname === 'class') {
    -              pre.className = a.value;  // For IE 6
    -            } else {
    -              pre.setAttribute(a.name, a.value);
    -            }
    -          }
    -        }
    -        pre.innerHTML = newContent;
    -
    -        // remove the old
    -        cs.parentNode.replaceChild(pre, cs);
    -        cs = pre;
    -      }
    -
    -      // Replace <br>s with line-feeds so that copying and pasting works
    -      // on IE 6.
    -      // Doing this on other browsers breaks lots of stuff since \r\n is
    -      // treated as two newlines on Firefox, and doing this also slows
    -      // down rendering.
    -      if (isIE678 && cs.tagName === 'PRE') {
    -        var lineBreaks = cs.getElementsByTagName('br');
    -        for (var j = lineBreaks.length; --j >= 0;) {
    -          var lineBreak = lineBreaks[j];
    -          lineBreak.parentNode.replaceChild(
    -              document.createTextNode(ieNewline), lineBreak);
    -        }
    -      }
    -    }
    -
    -    doWork();
    -  }
    -
    -  window['PR_normalizedHtml'] = normalizedHtml;
    -  window['prettyPrintOne'] = prettyPrintOne;
    -  window['prettyPrint'] = prettyPrint;
    -  window['PR'] = {
    -        'combinePrefixPatterns': combinePrefixPatterns,
    -        'createSimpleLexer': createSimpleLexer,
    -        'registerLangHandler': registerLangHandler,
    -        'sourceDecorator': sourceDecorator,
    -        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    -        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    -        'PR_COMMENT': PR_COMMENT,
    -        'PR_DECLARATION': PR_DECLARATION,
    -        'PR_KEYWORD': PR_KEYWORD,
    -        'PR_LITERAL': PR_LITERAL,
    -        'PR_NOCODE': PR_NOCODE,
    -        'PR_PLAIN': PR_PLAIN,
    -        'PR_PUNCTUATION': PR_PUNCTUATION,
    -        'PR_SOURCE': PR_SOURCE,
    -        'PR_STRING': PR_STRING,
    -        'PR_TAG': PR_TAG,
    -        'PR_TYPE': PR_TYPE
    -      };
    -})();
    -
    -
    -// ____________________________________________________________________________
    -
    -
    -
    -// Lua extension
    -
    -PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],[PR.PR_STRING,/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],[PR.PR_KEYWORD,/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_]\w*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),['lua'])
    -
    -
    -// Haskell extension
    -
    -PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\x0B\x0C\r ]+/,null,'	\n\r '],[PR.PR_STRING,/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'\"'],[PR.PR_STRING,/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,'\''],[PR.PR_LITERAL,/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,'0123456789']],[[PR.PR_COMMENT,/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],[PR.PR_KEYWORD,/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,null],[PR.PR_PLAIN,/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],[PR.PR_PUNCTUATION,/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),['hs'])
    -
    -
    -// ML extension
    -
    -PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_COMMENT,/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,'#'],[PR.PR_STRING,/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],[PR.PR_KEYWORD,/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],[PR.PR_LITERAL,/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^(?:[a-z_]\w*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],[PR.PR_PUNCTUATION,/^[^\t\n\r \xA0\"\'\w]+/]]),['fs','ml'])
    -
    -
    -// SQL extension
    -
    -PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0]+/,null,'	\n\r \xa0'],[PR.PR_STRING,/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,'\"\'']],[[PR.PR_COMMENT,/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],[PR.PR_KEYWORD,/^(?:ADD|ALL|ALTER|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\w-]|$)/i,null],[PR.PR_LITERAL,/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],[PR.PR_PLAIN,/^[a-z_][\w-]*/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),['sql'])
    -
    -
    -// VB extension
    -
    -PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[\t\n\r \xA0\u2028\u2029]+/,null,'	\n\r \xa0\u2028\u2029'],[PR.PR_STRING,/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'\"\u201c\u201d'],[PR.PR_COMMENT,/^[\'\u2018\u2019][^\r\n\u2028\u2029]*/,null,'\'\u2018\u2019']],[[PR.PR_KEYWORD,/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,null],[PR.PR_COMMENT,/^REM[^\r\n\u2028\u2029]*/i],[PR.PR_LITERAL,/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],[PR.PR_PLAIN,/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*\])/i],[PR.PR_PUNCTUATION,/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],[PR.PR_PUNCTUATION,/^(?:\[|\])/]]),['vb','vbs'])
    diff --git a/doc/javascript/searchdata.js b/doc/javascript/searchdata.js
    deleted file mode 100644
    index ff39bc80d..000000000
    --- a/doc/javascript/searchdata.js
    +++ /dev/null
    @@ -1,122 +0,0 @@
    -var indexSectionsWithContent = {
    -   "General": {
    -      "Symbols": false,
    -      "Numbers": false,
    -      "A": false,
    -      "B": true,
    -      "C": true,
    -      "D": false,
    -      "E": true,
    -      "F": true,
    -      "G": true,
    -      "H": false,
    -      "I": true,
    -      "J": false,
    -      "K": false,
    -      "L": true,
    -      "M": false,
    -      "N": true,
    -      "O": false,
    -      "P": false,
    -      "Q": false,
    -      "R": true,
    -      "S": true,
    -      "T": false,
    -      "U": false,
    -      "V": true,
    -      "W": false,
    -      "X": false,
    -      "Y": false,
    -      "Z": false
    -      },
    -   "Variables": {
    -      "Symbols": false,
    -      "Numbers": false,
    -      "A": false,
    -      "B": true,
    -      "C": true,
    -      "D": false,
    -      "E": false,
    -      "F": false,
    -      "G": false,
    -      "H": false,
    -      "I": false,
    -      "J": false,
    -      "K": false,
    -      "L": false,
    -      "M": false,
    -      "N": false,
    -      "O": false,
    -      "P": false,
    -      "Q": false,
    -      "R": false,
    -      "S": false,
    -      "T": false,
    -      "U": false,
    -      "V": false,
    -      "W": false,
    -      "X": false,
    -      "Y": false,
    -      "Z": false
    -      },
    -   "Functions": {
    -      "Symbols": false,
    -      "Numbers": false,
    -      "A": false,
    -      "B": false,
    -      "C": true,
    -      "D": false,
    -      "E": true,
    -      "F": false,
    -      "G": true,
    -      "H": false,
    -      "I": true,
    -      "J": false,
    -      "K": false,
    -      "L": true,
    -      "M": false,
    -      "N": true,
    -      "O": false,
    -      "P": false,
    -      "Q": false,
    -      "R": true,
    -      "S": true,
    -      "T": false,
    -      "U": false,
    -      "V": false,
    -      "W": false,
    -      "X": false,
    -      "Y": false,
    -      "Z": false
    -      },
    -   "Classes": {
    -      "Symbols": false,
    -      "Numbers": false,
    -      "A": false,
    -      "B": false,
    -      "C": false,
    -      "D": false,
    -      "E": false,
    -      "F": false,
    -      "G": true,
    -      "H": false,
    -      "I": false,
    -      "J": false,
    -      "K": false,
    -      "L": true,
    -      "M": false,
    -      "N": false,
    -      "O": false,
    -      "P": false,
    -      "Q": false,
    -      "R": false,
    -      "S": false,
    -      "T": false,
    -      "U": false,
    -      "V": false,
    -      "W": false,
    -      "X": false,
    -      "Y": false,
    -      "Z": false
    -      }
    -   }
    \ No newline at end of file
    diff --git a/doc/search/ClassesG.html b/doc/search/ClassesG.html
    deleted file mode 100644
    index 7c2a0193b..000000000
    --- a/doc/search/ClassesG.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="../files/blob-h.html#GitBlob" target=_parent class=ISymbol>GitBlob</a></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="../files/error-h.html#GitError" target=_parent class=ISymbol>GitError</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/ClassesL.html b/doc/search/ClassesL.html
    deleted file mode 100644
    index beb6a60d5..000000000
    --- a/doc/search/ClassesL.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_lookup_undrequest><div class=IEntry><a href="../files/blob-h.html#lookup_request" target=_parent class=ISymbol>lookup_request</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsC.html b/doc/search/FunctionsC.html
    deleted file mode 100644
    index 03341b746..000000000
    --- a/doc/search/FunctionsC.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Close><div class=IEntry><a href="../files/blob-h.html#GitBlob.Close" target=_parent class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromBuffer><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" target=_parent class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromFile><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" target=_parent class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsE.html b/doc/search/FunctionsE.html
    deleted file mode 100644
    index eecff1530..000000000
    --- a/doc/search/FunctionsE.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_EIO_undAfterLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" target=_parent class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_EIO_undLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" target=_parent class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsG.html b/doc/search/FunctionsG.html
    deleted file mode 100644
    index b7550956c..000000000
    --- a/doc/search/FunctionsG.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="../files/blob-h.html#GitBlob.GitBlob" target=_parent class=ISymbol>GitBlob</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="../files/error-h.html#GitError.GitError" target=_parent class=ISymbol>GitError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsI.html b/doc/search/FunctionsI.html
    deleted file mode 100644
    index fc527d73c..000000000
    --- a/doc/search/FunctionsI.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Initialize><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Initialize')" class=ISymbol>Initialize</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsL.html b/doc/search/FunctionsL.html
    deleted file mode 100644
    index 11870557d..000000000
    --- a/doc/search/FunctionsL.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Lookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" target=_parent class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsN.html b/doc/search/FunctionsN.html
    deleted file mode 100644
    index 9fbc82308..000000000
    --- a/doc/search/FunctionsN.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_New><div class=IEntry><a href="javascript:searchResults.Toggle('SR_New')" class=ISymbol>New</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsR.html b/doc/search/FunctionsR.html
    deleted file mode 100644
    index b9dacba56..000000000
    --- a/doc/search/FunctionsR.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_RawContent><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" target=_parent class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_RawSize><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" target=_parent class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/FunctionsS.html b/doc/search/FunctionsS.html
    deleted file mode 100644
    index 4bfff336b..000000000
    --- a/doc/search/FunctionsS.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_StrError><div class=IEntry><a href="../files/error-h.html#GitError.StrError" target=_parent class=ISymbol>StrError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralB.html b/doc/search/GeneralB.html
    deleted file mode 100644
    index b0706df19..000000000
    --- a/doc/search/GeneralB.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_blob><div class=IEntry><a href="../files/blob-h.html#GitBlob.blob" target=_parent class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralC.html b/doc/search/GeneralC.html
    deleted file mode 100644
    index 1cb2ab199..000000000
    --- a/doc/search/GeneralC.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Close><div class=IEntry><a href="../files/blob-h.html#GitBlob.Close" target=_parent class=ISymbol>Close</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_constructor_undtemplate><div class=IEntry><a href="javascript:searchResults.Toggle('SR_constructor_undtemplate')" class=ISymbol>constructor_template</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" target=_parent class=IParent>GitError</a></div></div></div><div class=SRResult id=SR_CreateFromBuffer><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromBuffer" target=_parent class=ISymbol>CreateFromBuffer</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_CreateFromFile><div class=IEntry><a href="../files/blob-h.html#GitBlob.CreateFromFile" target=_parent class=ISymbol>CreateFromFile</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralE.html b/doc/search/GeneralE.html
    deleted file mode 100644
    index eecff1530..000000000
    --- a/doc/search/GeneralE.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_EIO_undAfterLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_AfterLookup" target=_parent class=ISymbol>EIO_AfterLookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_EIO_undLookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.EIO_Lookup" target=_parent class=ISymbol>EIO_Lookup</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralF.html b/doc/search/GeneralF.html
    deleted file mode 100644
    index c04f7272f..000000000
    --- a/doc/search/GeneralF.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Functions><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Functions')" class=ISymbol>Functions</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Functions" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Functions" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralG.html b/doc/search/GeneralG.html
    deleted file mode 100644
    index c3e5bdcb8..000000000
    --- a/doc/search/GeneralG.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_GitBlob><div class=IEntry><a href="javascript:searchResults.Toggle('SR_GitBlob')" class=ISymbol>GitBlob</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob" target=_parent class=IParent>Global</a><a href="../files/blob-h.html#GitBlob.GitBlob" target=_parent class=IParent>GitBlob</a></div></div></div><div class=SRResult id=SR_GitError><div class=IEntry><a href="javascript:searchResults.Toggle('SR_GitError')" class=ISymbol>GitError</a><div class=ISubIndex><a href="../files/error-h.html#GitError" target=_parent class=IParent>Global</a><a href="../files/error-h.html#GitError.GitError" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralI.html b/doc/search/GeneralI.html
    deleted file mode 100644
    index fc527d73c..000000000
    --- a/doc/search/GeneralI.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Initialize><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Initialize')" class=ISymbol>Initialize</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Initialize" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Initialize" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralL.html b/doc/search/GeneralL.html
    deleted file mode 100644
    index 35f682b94..000000000
    --- a/doc/search/GeneralL.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Lookup><div class=IEntry><a href="../files/blob-h.html#GitBlob.Lookup" target=_parent class=ISymbol>Lookup</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_lookup_undrequest><div class=IEntry><a href="../files/blob-h.html#lookup_request" target=_parent class=ISymbol>lookup_request</a></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralN.html b/doc/search/GeneralN.html
    deleted file mode 100644
    index 9fbc82308..000000000
    --- a/doc/search/GeneralN.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_New><div class=IEntry><a href="javascript:searchResults.Toggle('SR_New')" class=ISymbol>New</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.New" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.New" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralR.html b/doc/search/GeneralR.html
    deleted file mode 100644
    index b9dacba56..000000000
    --- a/doc/search/GeneralR.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_RawContent><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawContent" target=_parent class=ISymbol>RawContent</a>, <span class=IParent>GitBlob</span></div></div><div class=SRResult id=SR_RawSize><div class=IEntry><a href="../files/blob-h.html#GitBlob.RawSize" target=_parent class=ISymbol>RawSize</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralS.html b/doc/search/GeneralS.html
    deleted file mode 100644
    index 4bfff336b..000000000
    --- a/doc/search/GeneralS.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_StrError><div class=IEntry><a href="../files/error-h.html#GitError.StrError" target=_parent class=ISymbol>StrError</a>, <span class=IParent>GitError</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/GeneralV.html b/doc/search/GeneralV.html
    deleted file mode 100644
    index c2ed22dc4..000000000
    --- a/doc/search/GeneralV.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_Variables><div class=IEntry><a href="javascript:searchResults.Toggle('SR_Variables')" class=ISymbol>Variables</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.Variables" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.Variables" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/NoResults.html b/doc/search/NoResults.html
    deleted file mode 100644
    index 5ce771767..000000000
    --- a/doc/search/NoResults.html
    +++ /dev/null
    @@ -1,15 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=NoMatches>No Matches</div></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/VariablesB.html b/doc/search/VariablesB.html
    deleted file mode 100644
    index b0706df19..000000000
    --- a/doc/search/VariablesB.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_blob><div class=IEntry><a href="../files/blob-h.html#GitBlob.blob" target=_parent class=ISymbol>blob</a>, <span class=IParent>GitBlob</span></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    diff --git a/doc/search/VariablesC.html b/doc/search/VariablesC.html
    deleted file mode 100644
    index 6d5825810..000000000
    --- a/doc/search/VariablesC.html
    +++ /dev/null
    @@ -1,20 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
    -
    -<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="../styles/main.css"><script language=JavaScript src="../javascript/main.js"></script></head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()"><script language=JavaScript><!--
    -if (browserType) {document.write("<div class=" + browserType + ">");if (browserVer) {document.write("<div class=" + browserVer + ">"); }}// --></script>
    -
    -<!--  Generated by Natural Docs, version 1.51 -->
    -<!--  http://www.naturaldocs.org  -->
    -
    -<!-- saved from url=(0026)http://www.naturaldocs.org -->
    -
    -
    -
    -
    -<div id=Index><div class=SRStatus id=Loading>Loading...</div><table border=0 cellspacing=0 cellpadding=0><div class=SRResult id=SR_constructor_undtemplate><div class=IEntry><a href="javascript:searchResults.Toggle('SR_constructor_undtemplate')" class=ISymbol>constructor_template</a><div class=ISubIndex><a href="../files/blob-h.html#GitBlob.constructor_template" target=_parent class=IParent>GitBlob</a><a href="../files/error-h.html#GitError.constructor_template" target=_parent class=IParent>GitError</a></div></div></div></table><div class=SRStatus id=Searching>Searching...</div><div class=SRStatus id=NoMatches>No Matches</div><script type="text/javascript"><!--
    -document.getElementById("Loading").style.display="none";
    -document.getElementById("NoMatches").style.display="none";
    -var searchResults = new SearchResults("searchResults", "HTML");
    -searchResults.Search();
    ---></script></div><script language=JavaScript><!--
    -if (browserType) {if (browserVer) {document.write("</div>"); }document.write("</div>");}// --></script></body></html>
    \ No newline at end of file
    
    From 91c2282e1f773106e3ca3ee315c4852a61a74745 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 18:15:40 -0400
    Subject: [PATCH 27/37] Updated src to not send prefix to node
    
    ---
     src/oid.cc         | 4 ++--
     src/repo.cc        | 4 ++--
     src/revwalk.cc     | 4 ++--
     src/sig.cc         | 4 ++--
     test/raw-commit.js | 1 +
     5 files changed, 9 insertions(+), 8 deletions(-)
    
    diff --git a/src/oid.cc b/src/oid.cc
    index 1d4d4c8c4..955619d21 100755
    --- a/src/oid.cc
    +++ b/src/oid.cc
    @@ -20,7 +20,7 @@ void GitOid::Initialize(Handle<Object> target) {
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("GitOid"));
    +  constructor_template->SetClassName(String::NewSymbol("Oid"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "mkstr", Mkstr);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "mkraw", Mkraw);
    @@ -31,7 +31,7 @@ void GitOid::Initialize(Handle<Object> target) {
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "cpy", Cpy);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "cmp", Cmp);
     
    -  target->Set(String::NewSymbol("GitOid"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("Oid"), constructor_template->GetFunction());
     }
     
     git_oid* GitOid::GetValue() {
    diff --git a/src/repo.cc b/src/repo.cc
    index 9d8b45f5f..98fcab173 100755
    --- a/src/repo.cc
    +++ b/src/repo.cc
    @@ -23,14 +23,14 @@ void GitRepo::Initialize(Handle<Object> target) {
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("GitRepo"));
    +  constructor_template->SetClassName(String::NewSymbol("Repo"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "open", Open);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "init", Init);
     
    -  target->Set(String::NewSymbol("GitRepo"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("Repo"), constructor_template->GetFunction());
     }
     
     git_repository* GitRepo::GetValue() {
    diff --git a/src/revwalk.cc b/src/revwalk.cc
    index 27bc3bf38..0770606f0 100755
    --- a/src/revwalk.cc
    +++ b/src/revwalk.cc
    @@ -22,7 +22,7 @@ void GitRevWalk::Initialize(Handle<Object> target) {
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
    -  constructor_template->SetClassName(String::NewSymbol("GitRevWalk"));
    +  constructor_template->SetClassName(String::NewSymbol("RevWalk"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "reset", Reset);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "push", Push);
    @@ -39,7 +39,7 @@ void GitRevWalk::Initialize(Handle<Object> target) {
     
       constructor_template->Set(String::New("sort"), sort);
     
    -  target->Set(String::NewSymbol("GitRevWalk"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("RevWalk"), constructor_template->GetFunction());
     }
     
     git_revwalk* GitRevWalk::GetValue() {
    diff --git a/src/sig.cc b/src/sig.cc
    index 9d5468337..bbced516a 100755
    --- a/src/sig.cc
    +++ b/src/sig.cc
    @@ -21,7 +21,7 @@ void GitSig::Initialize (Handle<v8::Object> target) {
       
       constructor_template = Persistent<FunctionTemplate>::New(t);
       constructor_template->InstanceTemplate()->SetInternalFieldCount(3);
    -  constructor_template->SetClassName(String::NewSymbol("GitSig"));
    +  constructor_template->SetClassName(String::NewSymbol("Sig"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "dup", Dup);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free);
    @@ -30,7 +30,7 @@ void GitSig::Initialize (Handle<v8::Object> target) {
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "name", Name);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "email", Email);
     
    -  target->Set(String::NewSymbol("GitSig"), constructor_template->GetFunction());
    +  target->Set(String::NewSymbol("Sig"), constructor_template->GetFunction());
     }
     
     git_signature* GitSig::GetValue() {
    diff --git a/test/raw-commit.js b/test/raw-commit.js
    index 24846521d..8169817ad 100644
    --- a/test/raw-commit.js
    +++ b/test/raw-commit.js
    @@ -2,6 +2,7 @@ var git = require( '../' ).raw,
         rimraf = require( '../vendor/rimraf' ) || require( 'rimraf' ),
         path = require( 'path' );
     
    +console.log(git);
     var testRepo = new git.Repo();
     
     // Helper functions
    
    From fdd309470f9b6d0042857f06a751b4ab22925a6c Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 18:24:23 -0400
    Subject: [PATCH 28/37] Added in scope.close for a few methods
    
    ---
     src/blob.cc | 12 +-----------
     1 file changed, 1 insertion(+), 11 deletions(-)
    
    diff --git a/src/blob.cc b/src/blob.cc
    index ce6718da2..03b488963 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -99,7 +99,7 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
       eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, ar);
       ev_ref(EV_DEFAULT_UC);
     
    -  return Undefined();
    +  return scope.Close(Undefined());
     }
     
     int GitBlob::EIO_Lookup(eio_req* req) {
    @@ -135,8 +135,6 @@ int GitBlob::EIO_AfterLookup(eio_req* req) {
     }
     
     Handle<Value> GitBlob::RawContent(const Arguments& args) {
    -  HandleScope scope;
    -
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
       return String::New((const char*)blob->RawContent());
    @@ -145,16 +143,12 @@ Handle<Value> GitBlob::RawContent(const Arguments& args) {
     Handle<Value> GitBlob::RawSize(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  HandleScope scope;
    -
       return Integer::New(blob->RawSize());
     }
     
     Handle<Value> GitBlob::Close(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  HandleScope scope;
    -
       blob->Close();
     
       return Undefined();
    @@ -163,8 +157,6 @@ Handle<Value> GitBlob::Close(const Arguments& args) {
     Handle<Value> GitBlob::CreateFromFile(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  HandleScope scope;
    -
       blob->Close();
     
       return Undefined();
    @@ -173,8 +165,6 @@ Handle<Value> GitBlob::CreateFromFile(const Arguments& args) {
     Handle<Value> GitBlob::CreateFromBuffer(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  HandleScope scope;
    -
       return Undefined();
     }
     
    
    From 59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Fri, 8 Apr 2011 20:48:49 -0400
    Subject: [PATCH 29/37] Updated gitignore and raw-commit test
    
    ---
     .gitignore         | 7 ++++---
     test/raw-commit.js | 1 -
     2 files changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/.gitignore b/.gitignore
    index 4a7e18cf2..c85f3e820 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -1,4 +1,5 @@
     .lock-wscript
    -./build/
    -./doc
    -!./doc/Theme.css
    +build
    +doc
    +!doc/Theme.css
    +example/stress/test
    diff --git a/test/raw-commit.js b/test/raw-commit.js
    index 8169817ad..24846521d 100644
    --- a/test/raw-commit.js
    +++ b/test/raw-commit.js
    @@ -2,7 +2,6 @@ var git = require( '../' ).raw,
         rimraf = require( '../vendor/rimraf' ) || require( 'rimraf' ),
         path = require( 'path' );
     
    -console.log(git);
     var testRepo = new git.Repo();
     
     // Helper functions
    
    From 3eac224051a5e1c37ca88a4530236e9e7d24c253 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Sat, 9 Apr 2011 01:52:30 -0400
    Subject: [PATCH 30/37] using valgrind to test existing code for memory leaks,
     updating source according to findings
    
    ---
     example/raw-commit.js    |  13 +++++
     example/raw-oid.js       |  16 +++---
     example/stress/commit.js |   7 ++-
     include/commit.h         |   6 ++-
     include/index.h          |  57 +++++++++++++++++++++
     include/odb.h            |  57 +++++++++++++++++++++
     include/odb_backend.h    |  57 +++++++++++++++++++++
     include/oid.h            |   4 +-
     include/tag.h            |  57 +++++++++++++++++++++
     src/blob.cc              |   2 +-
     src/commit.cc            | 107 ++++++++++++---------------------------
     src/object.cc            |   2 +-
     src/oid.cc               |  41 +++++++--------
     src/reference.cc         |   2 +-
     src/revwalk.cc           |   8 +--
     src/tree_entry.cc        |   2 +-
     test/raw-commit.js       |  19 ++++---
     17 files changed, 332 insertions(+), 125 deletions(-)
     create mode 100644 example/raw-commit.js
     create mode 100755 include/index.h
     create mode 100755 include/odb.h
     create mode 100755 include/odb_backend.h
     create mode 100755 include/tag.h
    
    diff --git a/example/raw-commit.js b/example/raw-commit.js
    new file mode 100644
    index 000000000..6fb6fb478
    --- /dev/null
    +++ b/example/raw-commit.js
    @@ -0,0 +1,13 @@
    +var git = require( '../lib' ).raw
    +  , path = require( 'path' );
    +
    +var repo = new git.Repo();
    +repo.open( path.resolve( '../.git' ), function() {
    +  var oid = new git.Oid();
    +  oid.mkstr( '3b7670f327dc1ca66e040f0c09cc4c3f1428eb49' );
    +
    +  var commit = new git.Commit();
    +  commit.lookup( repo, oid, function( err ) {
    +    console.log( new git.Error().strError( err ) );
    +  });
    +});
    diff --git a/example/raw-oid.js b/example/raw-oid.js
    index b56f0dae7..5b57a1234 100644
    --- a/example/raw-oid.js
    +++ b/example/raw-oid.js
    @@ -1,11 +1,9 @@
    -var git2 = require( '../' ).raw;
    +var git = require( '../' ).raw;
     
    -var oid = new git2.Oid();
    -// Valid
    -console.log( oid.mkstr('1810DFF58D8A660512D4832E740F692884338CCD') );
    -// Invalid
    -console.log( oid.mkstr('1838CCD') );
    -
    -// Test formatting
    -console.log( oid.mkstr('5f2aa9407f7b3aeb531c621c3358953841ccfc98') );
    +var oid = new git.Oid();
    +oid.mkstr('1810DFF58D8A660512D4832E740F692884338CCD');
     console.log( oid.toString(40) );
    +
    +//// Test formatting
    +//console.log( oid.mkstr('5f2aa9407f7b3aeb531c621c3358953841ccfc98') );
    +//console.log( oid.toString(40) );
    diff --git a/example/stress/commit.js b/example/stress/commit.js
    index e475c4ea8..c067c9e80 100644
    --- a/example/stress/commit.js
    +++ b/example/stress/commit.js
    @@ -9,7 +9,7 @@ var git = require( '../../' ).raw;
     
             var repo = new git.Repo();
             repo.open( '/home/tim/git/nodegit/.git', function() {
    -          var commit = new git.Commit( repo );
    +          var commit = new git.Commit();
     
               console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
             });
    @@ -19,7 +19,6 @@ var git = require( '../../' ).raw;
       }, 0);
     //*/
     
    -
     //* Stress test repo open
       setInterval(function() {
         for(var i=0; i<10000; i++) {
    @@ -32,8 +31,8 @@ var git = require( '../../' ).raw;
               var oid = new git.Oid();
               oid.mkstr( 'cb76e3c030ab29db332aff3b297dc39451a84762' );
     
    -          var commit = new git.Commit( repo );
    -          commit.lookup( oid, function( err ) {
    +          var commit = new git.Commit();
    +          commit.lookup( repo, oid, function( err ) {
                 console.log( 'Time taken: ' + (+new Date-start) + 'ms' );
               });
             });
    diff --git a/include/commit.h b/include/commit.h
    index ecf3c92f0..e11c2cf4d 100755
    --- a/include/commit.h
    +++ b/include/commit.h
    @@ -39,7 +39,8 @@ class GitCommit : public EventEmitter {
     
         git_commit* GetValue();
         void SetValue(git_commit* commit);
    -    int Lookup(git_oid* oid);
    +    int Lookup(git_repository* repo, git_oid* oid);
    +    void Close();
         const git_oid* Id();
         const char* MessageShort();
         const char* Message();
    @@ -61,6 +62,7 @@ class GitCommit : public EventEmitter {
         static int EIO_Lookup(eio_req *req);
         static int EIO_AfterLookup(eio_req *req);
     
    +    static Handle<Value> Close(const Arguments& args);
         static Handle<Value> Id(const Arguments& args);
         static Handle<Value> MessageShort(const Arguments& args);
         static Handle<Value> Message(const Arguments& args);
    @@ -78,11 +80,11 @@ class GitCommit : public EventEmitter {
     
       private:
         git_commit* commit;
    -    git_repository* repo;
         git_oid* oid;
     
         struct lookup_request {
           GitCommit* commit;
    +      GitRepo* repo;
           GitOid* oid;
           int err;
           Persistent<Function> callback;
    diff --git a/include/index.h b/include/index.h
    new file mode 100755
    index 000000000..ada24ca5a
    --- /dev/null
    +++ b/include/index.h
    @@ -0,0 +1,57 @@
    +/*
    + * Copyright 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    + * Dual licensed under the MIT and GPL licenses.
    + */
    +
    +#ifndef INDEX_H
    +#define INDEX_H
    +
    +#include <node.h>
    +#include <node_events.h>
    +
    +#include "../vendor/libgit2/include/git2.h"
    +
    +using namespace node;
    +
    +/**
    + * Class: GitIndex
    + *   Wrapper for libgit2 git_error.
    + */
    +class GitIndex : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +
    +  protected:
    +    /**
    +     * Constructor: GitIndex
    +     */
    +    GitIndex() {};
    +    /**
    +     * Deconstructor: GitIndex
    +     */
    +    ~GitIndex() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +};
    + 
    +#endif
    diff --git a/include/odb.h b/include/odb.h
    new file mode 100755
    index 000000000..1bdbb14d9
    --- /dev/null
    +++ b/include/odb.h
    @@ -0,0 +1,57 @@
    +/*
    + * Copyright 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    + * Dual licensed under the MIT and GPL licenses.
    + */
    +
    +#ifndef ODB_H
    +#define ODB_H
    +
    +#include <node.h>
    +#include <node_events.h>
    +
    +#include "../vendor/libgit2/include/git2.h"
    +
    +using namespace node;
    +
    +/**
    + * Class: GitOdb
    + *   Wrapper for libgit2 git_error.
    + */
    +class GitOdb : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +
    +  protected:
    +    /**
    +     * Constructor: GitOdb
    +     */
    +    GitOdb() {};
    +    /**
    +     * Deconstructor: GitOdb
    +     */
    +    ~GitOdb() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +};
    + 
    +#endif
    diff --git a/include/odb_backend.h b/include/odb_backend.h
    new file mode 100755
    index 000000000..20a1275cc
    --- /dev/null
    +++ b/include/odb_backend.h
    @@ -0,0 +1,57 @@
    +/*
    + * Copyright 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    + * Dual licensed under the MIT and GPL licenses.
    + */
    +
    +#ifndef ODB_BACKEND_H
    +#define ODB_BACKEND_H
    +
    +#include <node.h>
    +#include <node_events.h>
    +
    +#include "../vendor/libgit2/include/git2.h"
    +
    +using namespace node;
    +
    +/**
    + * Class: GitOdbBackend
    + *   Wrapper for libgit2 git_error.
    + */
    +class GitOdbBackend : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +
    +  protected:
    +    /**
    +     * Constructor: GitOdbBackend
    +     */
    +    GitOdbBackend() {};
    +    /**
    +     * Deconstructor: GitOdbBackend
    +     */
    +    ~GitOdbBackend() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +};
    + 
    +#endif
    diff --git a/include/oid.h b/include/oid.h
    index 650062460..513e362e4 100755
    --- a/include/oid.h
    +++ b/include/oid.h
    @@ -19,8 +19,8 @@ class GitOid : public EventEmitter {
         static Persistent<FunctionTemplate> constructor_template;
         static void Initialize (Handle<v8::Object> target);
         Handle<Value> WrapObj(Local<Object> obj);
    -    git_oid* GetValue();
    -    void SetValue(git_oid* oid);
    +    git_oid GetValue();
    +    void SetValue(git_oid oid);
     
         int Mkstr(const char* str);
         void Mkraw(const unsigned char* raw);
    diff --git a/include/tag.h b/include/tag.h
    new file mode 100755
    index 000000000..6a941eedb
    --- /dev/null
    +++ b/include/tag.h
    @@ -0,0 +1,57 @@
    +/*
    + * Copyright 2011, Tim Branyen @tbranyen <tim@tabdeveloper.com>
    + * Dual licensed under the MIT and GPL licenses.
    + */
    +
    +#ifndef TAG_H
    +#define TAG_H
    +
    +#include <node.h>
    +#include <node_events.h>
    +
    +#include "../vendor/libgit2/include/git2.h"
    +
    +using namespace node;
    +
    +/**
    + * Class: GitTag
    + *   Wrapper for libgit2 git_error.
    + */
    +class GitTag : public ObjectWrap {
    +  public:
    +    /**
    +     * Variable: constructor_template
    +     *   Used to create Node.js constructor.
    +     */
    +    static v8::Persistent<v8::FunctionTemplate> constructor_template;
    +    /**
    +     * Function: Initialize
    +     *   Used to intialize the EventEmitter from Node.js
    +     *
    +     * Parameters:
    +     *   target - v8::Object the Node.js global module object
    +     */
    +    static void Initialize(v8::Handle<v8::Object> target);
    +
    +  protected:
    +    /**
    +     * Constructor: GitTag
    +     */
    +    GitTag() {};
    +    /**
    +     * Deconstructor: GitTag
    +     */
    +    ~GitTag() {};
    +    /**
    +     * Function: New
    +     *
    +     * Parameters:
    +     *   args v8::Arguments function call
    +     *
    +     * Returns:
    +     *   v8::Object args.This()
    +     */
    +    static v8::Handle<v8::Value> New(const v8::Arguments& args);
    +};
    + 
    +#endif
    diff --git a/src/blob.cc b/src/blob.cc
    index 03b488963..1ac53f57e 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -105,7 +105,7 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
     int GitBlob::EIO_Lookup(eio_req* req) {
       lookup_request* ar = static_cast<lookup_request* >(req->data);
     
    -  ar->err = ar->blob->Lookup(ar->repo->GetValue(), ar->oid->GetValue());
    +  ar->err = ar->blob->Lookup(ar->repo->GetValue(), &ar->oid->GetValue());
     
       return 0;
     }
    diff --git a/src/commit.cc b/src/commit.cc
    index 02236eaa7..f4ea9738a 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -30,6 +30,7 @@ void GitCommit::Initialize(Handle<Object> target) {
       constructor_template->SetClassName(String::NewSymbol("Commit"));
     
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup);
    +  NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "messageShort", MessageShort);
       NODE_SET_PROTOTYPE_METHOD(constructor_template, "message", Message);
    @@ -51,17 +52,17 @@ void GitCommit::SetValue(git_commit* commit) {
       this->commit = commit;
     }
     
    -int GitCommit::Lookup(git_oid* oid) {
    +int GitCommit::Lookup(git_repository* repo, git_oid* oid) {
       git_commit* commit;
     
    -  //this->oid = oid;
    +  git_oid test;
    +  int err = git_commit_lookup(&commit, repo, &test);
     
    -  //int err = git_commit_lookup(&commit, this->repo, oid);
    -
    -  //this->commit = commit;
    +  return err;
    +}
     
    -  //return err;
    -  return 0;
    +void GitCommit::Close() {
    +  git_commit_close(this->commit);
     }
     
     const git_oid* GitCommit::Id() {
    @@ -108,30 +109,36 @@ Handle<Value> GitCommit::New(const Arguments& args) {
       HandleScope scope;
     
       GitCommit *commit = new GitCommit();
    +
       commit->Wrap(args.This());
     
    -  return args.This();
    +  return scope.Close(args.This());
     }
     
     Handle<Value> GitCommit::Lookup(const Arguments& args) {
    +  HandleScope scope;
    +
       GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
       Local<Function> callback;
     
    -  HandleScope scope;
    -
       if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
    +  }
    +
    +  if(args.Length() == 1 || !args[1]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
    -  if(args.Length() == 1 || !args[1]->IsFunction()) {
    +  if(args.Length() == 2 || !args[2]->IsFunction()) {
         return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
       }
     
    -  callback = Local<Function>::Cast(args[1]);
    +  callback = Local<Function>::Cast(args[2]);
     
       lookup_request *ar = new lookup_request();
       ar->commit = commit;
    -  ar->oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    +  ar->repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
    +  ar->oid = ObjectWrap::Unwrap<GitOid>(args[1]->ToObject());
       ar->callback = Persistent<Function>::New(callback);
     
       commit->Ref();
    @@ -145,7 +152,8 @@ Handle<Value> GitCommit::Lookup(const Arguments& args) {
     int GitCommit::EIO_Lookup(eio_req *req) {
       lookup_request *ar = static_cast<lookup_request *>(req->data);
     
    -  ar->err = ar->commit->Lookup(ar->oid->GetValue());
    +  git_oid oid = ar->oid->GetValue();
    +  ar->err = ar->commit->Lookup(ar->repo->GetValue(), &oid);
     
       return 0;
     }
    @@ -157,7 +165,7 @@ int GitCommit::EIO_AfterLookup(eio_req *req) {
       ev_unref(EV_DEFAULT_UC);
       ar->commit->Unref();
     
    -  Local<Value> argv[0];
    +  Handle<Value> argv[1];
       argv[0] = Integer::New(ar->err);
     
       TryCatch try_catch;
    @@ -174,6 +182,15 @@ int GitCommit::EIO_AfterLookup(eio_req *req) {
       return 0;
     }
     
    +Handle<Value> GitCommit::Close(const Arguments& args) {
    +  HandleScope scope;
    +
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +  commit->Close();
    +  
    +  return Undefined();
    +}
    +
     Handle<Value> GitCommit::Id(const Arguments& args) {
       GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
     
    @@ -185,7 +202,7 @@ Handle<Value> GitCommit::Id(const Arguments& args) {
     
       GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
    -  oid->SetValue(const_cast<git_oid *>(commit->Id()));
    +  oid->SetValue(*const_cast<git_oid *>(commit->Id()));
       
       return Undefined();
     }
    @@ -271,64 +288,6 @@ Handle<Value> GitCommit::Tree(const Arguments& args) {
     
       return Integer::New(err);
     }
    -//Handle<Value> GitCommit::Tree(const Arguments& args) {
    -//  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -//  Local<Function> callback;
    -//
    -//  HandleScope scope;
    -//
    -//  if(args.Length() == 0 || !args[0]->IsObject()) {
    -//    return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
    -//  }
    -//
    -//  callback = Local<Function>::Cast(args[1]);
    -//
    -//  tree_request *ar = new tree_request();
    -//  ar->commit = commit;
    -//  ar->repo = ObjectWrap::Unwrap<Tree>(args[0]->ToObject());
    -//  ar->callback = Persistent<Function>::New(callback);
    -//
    -//  commit->Ref();
    -//
    -//  eio_custom(EIO_Tree, EIO_PRI_DEFAULT, EIO_AfterTree, ar);
    -//  ev_ref(EV_DEFAULT_UC);
    -//
    -//  return Undefined();
    -//}
    -//
    -//int GitCommit::EIO_Tree(eio_req *req) {
    -//  tree_request *ar = static_cast<tree_request *>(req->data);
    -//
    -//  git_tree *tree = ar->commit->Tree();
    -//
    -//  ar->tree->SetValue(tree);
    -//
    -//  return 0;
    -//}
    -//
    -//int GitCommit::EIO_AfterTree(eio_req *req) {
    -//  HandleScope scope;
    -//
    -//  tree_request *ar = static_cast<tree_request *>(req->data);
    -//  ev_unref(EV_DEFAULT_UC);
    -//  ar->commit->Unref();
    -//
    -//  Local<Value> argv[1];
    -//
    -//  TryCatch try_catch;
    -//
    -//  ar->callback->Call(Context::GetCurrent()->Global(), 1, argv);
    -//
    -//  if(try_catch.HasCaught())
    -//    FatalException(try_catch);
    -//    
    -//  ar->err.Dispose();
    -//  ar->callback.Dispose();
    -//
    -//  delete ar;
    -//
    -//  return 0;
    -//}
     
     Handle<Value> GitCommit::ParentCount(const Arguments& args) {
       GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    diff --git a/src/object.cc b/src/object.cc
    index 0fecbe6f0..9be703aa5 100755
    --- a/src/object.cc
    +++ b/src/object.cc
    @@ -107,7 +107,7 @@ Handle<Value> GitObject::Id(const Arguments& args) {
     
       GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
    -  oid->SetValue(const_cast<git_oid *>(obj->Id()));
    +  oid->SetValue(*const_cast<git_oid *>(obj->Id()));
     
       return Undefined();
     }
    diff --git a/src/oid.cc b/src/oid.cc
    index 955619d21..c0296bbe7 100755
    --- a/src/oid.cc
    +++ b/src/oid.cc
    @@ -34,12 +34,12 @@ void GitOid::Initialize(Handle<Object> target) {
       target->Set(String::NewSymbol("Oid"), constructor_template->GetFunction());
     }
     
    -git_oid* GitOid::GetValue() {
    -  return &this->oid;
    +git_oid GitOid::GetValue() {
    +  return this->oid;
     }
     
    -void GitOid::SetValue(git_oid* oid) {
    -  this->oid = *oid;
    +void GitOid::SetValue(git_oid oid) {
    +  this->oid = oid;
     }
     
     int GitOid::Mkstr(const char* id) {
    @@ -63,7 +63,7 @@ char* GitOid::AllocFmt() {
     }
     
     char* GitOid::ToString(char* buffer, size_t bufferSize) {
    -  git_oid_to_string(*&buffer, bufferSize, &this->oid);
    +  git_oid_to_string(buffer, bufferSize, &this->oid);
     }
     
     void GitOid::Cpy(git_oid* out) {
    @@ -77,28 +77,28 @@ int GitOid::Cmp(const git_oid* a, const git_oid* b) {
     Handle<Value> GitOid::New(const Arguments& args) {
       HandleScope scope;
     
    -  GitOid *oid = new GitOid();
    +  GitOid* oid = new GitOid();
       oid->Wrap(args.This());
     
    -  return args.This();
    +  return scope.Close(args.This());
     }
     
     Handle<Value> GitOid::Mkstr(const Arguments& args) {
    -  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
    -
       HandleScope scope;
     
    +  GitOid* oid = ObjectWrap::Unwrap<GitOid>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsString()) {
         return ThrowException(Exception::Error(String::New("Object id is required and must be a hex formatted String.")));
       }
     
    -  String::Utf8Value id(Local<Value>::New(args[0]));
    +  String::Utf8Value id(args[0]);
     
    -  return Integer::New(oid->Mkstr(*id));
    +  return scope.Close(Integer::New(oid->Mkstr(*id)));
     }
     
     Handle<Value> GitOid::Mkraw(const Arguments& args) {
    -  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
    +  GitOid* oid = ObjectWrap::Unwrap<GitOid>(args.This());
     
       HandleScope scope;
     
    @@ -155,9 +155,9 @@ Handle<Value> GitOid::ToString(const Arguments& args) {
     }
     
     Handle<Value> GitOid::Cpy(const Arguments& args) {
    -  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
    -
       HandleScope scope;
    +
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
       
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
    @@ -167,15 +167,15 @@ Handle<Value> GitOid::Cpy(const Arguments& args) {
       
       git_oid *out;
       oid->Cpy(out);
    -  clone->SetValue(out);
    +  clone->SetValue(*out);
     
       return Undefined();
     }
     
     Handle<Value> GitOid::Cmp(const Arguments& args) {
    -  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
    -
       HandleScope scope;
    +
    +  GitOid *oid = ObjectWrap::Unwrap<GitOid>(args.This());
       
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
    @@ -185,10 +185,11 @@ Handle<Value> GitOid::Cmp(const Arguments& args) {
         return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object.")));
       }
     
    -  GitOid *a = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    -  GitOid *b = ObjectWrap::Unwrap<GitOid>(args[1]->ToObject());
    +  GitOid* a = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    +  GitOid* b = ObjectWrap::Unwrap<GitOid>(args[1]->ToObject());
     
    -  int cmp = oid->Cmp(a->GetValue(), b->GetValue());
    +  //int cmp = oid->Cmp(&a->GetValue(), &b->GetValue());
    +  int cmp = 0;
     
       return Integer::New(cmp);
     }
    diff --git a/src/reference.cc b/src/reference.cc
    index 36459ccb8..38a10ca2a 100755
    --- a/src/reference.cc
    +++ b/src/reference.cc
    @@ -137,7 +137,7 @@ Handle<Value> GitReference::Oid(const Arguments& args) {
       }
     
       GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    -  oid->SetValue( const_cast<git_oid *>(ref->Oid()) );
    +  oid->SetValue(*const_cast<git_oid *>(ref->Oid()));
     
       return Undefined();
     }
    diff --git a/src/revwalk.cc b/src/revwalk.cc
    index 0770606f0..1b6c266fc 100755
    --- a/src/revwalk.cc
    +++ b/src/revwalk.cc
    @@ -117,7 +117,9 @@ Handle<Value> GitRevWalk::Push(const Arguments& args) {
       }
     
       GitOid *oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
    -  int err = revwalk->Push(oid->GetValue());
    +  
    +  git_oid tmp = oid->GetValue();
    +  int err = revwalk->Push(&tmp);
     
       return Integer::New(err);
     }
    @@ -153,9 +155,9 @@ Handle<Value> GitRevWalk::Next(const Arguments& args) {
     
     int GitRevWalk::EIO_Next(eio_req *req) {
       next_request *ar = static_cast<next_request *>(req->data);
    -  git_oid* oid = ar->oid->GetValue();
    +  git_oid oid = ar->oid->GetValue();
     
    -  ar->err = ar->revwalk->Next(oid);
    +  ar->err = ar->revwalk->Next(&oid);
       ar->oid->SetValue(oid);
     
       return 0;
    diff --git a/src/tree_entry.cc b/src/tree_entry.cc
    index a1095a44e..92dea96f6 100644
    --- a/src/tree_entry.cc
    +++ b/src/tree_entry.cc
    @@ -77,7 +77,7 @@ Handle<Value> GitTreeEntry::Id(const Arguments& args) {
     
       GitOid* oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
     
    -  oid->SetValue(const_cast<git_oid *>(entry->Id()));
    +  oid->SetValue(*const_cast<git_oid *>(entry->Id()));
       
       return Undefined();
     }
    diff --git a/test/raw-commit.js b/test/raw-commit.js
    index 24846521d..72b5a4bc5 100644
    --- a/test/raw-commit.js
    +++ b/test/raw-commit.js
    @@ -43,18 +43,23 @@ exports.constructor = function( test ){
     // Commit::Lookup
     exports.lookup = function( test ) {
       var testOid = new git.Oid(),
    -      testCommit = new git.Commit( testRepo );
    +      testCommit = new git.Commit();
     
       testOid.mkstr( 'cb09e99e91d41705197e0fb60823fdc7df776691' );
     
    -  test.expect( 6 );
    +  test.expect( 7 );
     
       // Test for function
       helper.testFunction( test.equals, testCommit.lookup, 'Commit::Lookup' );
     
    +  // Test repo argument existence
    +  helper.testException( test.ok, function() {
    +    testCommit.lookup();
    +  }, 'Throw an exception if no repo' );
    +
       // Test oid argument existence
       helper.testException( test.ok, function() {
    -    testCommit.lookup( );
    +    testCommit.lookup( testRepo );
       }, 'Throw an exception if no oid' );
     
       // Test callback argument existence
    @@ -62,20 +67,20 @@ exports.lookup = function( test ) {
         testCommit.lookup( testOid );
       }, 'Throw an exception if no callback' );
     
    -  // Test that both arguments result correctly
    +  // Test that all arguments result correctly
       helper.testException( test.ifError, function() {
    -    testCommit.lookup( testOid, function() {} );
    +    testCommit.lookup( testRepo, testOid, function() {} );
       }, 'No exception is thrown with proper arguments' );
     
       testRepo.open( path.resolve( '../.git' ), function() {
         // Test invalid commit
         testOid.mkstr( '100644' );
    -    testCommit.lookup( testOid, function( err ) {
    +    testCommit.lookup( testRepo, testOid, function( err ) {
           //test.notEqual( 0, err, 'Not a valid commit' );
      
           // Test valid commit
           testOid.mkstr( '3b7670f327dc1ca66e040f0c09cc4c3f1428eb49' );
    -      testCommit.lookup( testOid, function( err ) {
    +      testCommit.lookup( testRepo, testOid, function( err ) {
             test.equals( 0, err, 'Valid commit');
     
             //test.equals( 'Fixed path issues', testCommit.messageShort(), 'Commit message is valid' );
    
    From 9cf0f6ada0fb72f710bf0b009b886ed3ac801f4a Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Sat, 9 Apr 2011 02:05:29 -0400
    Subject: [PATCH 31/37] Removed fake code in commit.cc
    
    ---
     example/raw-commit.js     | 2 +-
     example/stress/revwalk.js | 2 +-
     src/commit.cc             | 3 +--
     test/raw-commit.js        | 8 ++++----
     4 files changed, 7 insertions(+), 8 deletions(-)
    
    diff --git a/example/raw-commit.js b/example/raw-commit.js
    index 6fb6fb478..885ba7f7f 100644
    --- a/example/raw-commit.js
    +++ b/example/raw-commit.js
    @@ -4,7 +4,7 @@ var git = require( '../lib' ).raw
     var repo = new git.Repo();
     repo.open( path.resolve( '../.git' ), function() {
       var oid = new git.Oid();
    -  oid.mkstr( '3b7670f327dc1ca66e040f0c09cc4c3f1428eb49' );
    +  oid.mkstr( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5' );
     
       var commit = new git.Commit();
       commit.lookup( repo, oid, function( err ) {
    diff --git a/example/stress/revwalk.js b/example/stress/revwalk.js
    index 963b3bbe9..81a57c281 100644
    --- a/example/stress/revwalk.js
    +++ b/example/stress/revwalk.js
    @@ -13,7 +13,7 @@ var git = require( '../../' ).raw;
               oid.mkstr( 'cb76e3c030ab29db332aff3b297dc39451a84762' );
     
               var commit = new git.Commit( repo );
    -          commit.lookup( oid, function( err ) {
    +          commit.lookup( repo, oid, function( err ) {
                 var revwalk = new git.RevWalk( repo );
                 revwalk.push( commit );
     
    diff --git a/src/commit.cc b/src/commit.cc
    index f4ea9738a..db3244a5a 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -55,8 +55,7 @@ void GitCommit::SetValue(git_commit* commit) {
     int GitCommit::Lookup(git_repository* repo, git_oid* oid) {
       git_commit* commit;
     
    -  git_oid test;
    -  int err = git_commit_lookup(&commit, repo, &test);
    +  int err = git_commit_lookup(&commit, repo, oid);
     
       return err;
     }
    diff --git a/test/raw-commit.js b/test/raw-commit.js
    index 72b5a4bc5..60a3e1d9d 100644
    --- a/test/raw-commit.js
    +++ b/test/raw-commit.js
    @@ -47,7 +47,7 @@ exports.lookup = function( test ) {
     
       testOid.mkstr( 'cb09e99e91d41705197e0fb60823fdc7df776691' );
     
    -  test.expect( 7 );
    +  test.expect( 8 );
     
       // Test for function
       helper.testFunction( test.equals, testCommit.lookup, 'Commit::Lookup' );
    @@ -76,14 +76,14 @@ exports.lookup = function( test ) {
         // Test invalid commit
         testOid.mkstr( '100644' );
         testCommit.lookup( testRepo, testOid, function( err ) {
    -      //test.notEqual( 0, err, 'Not a valid commit' );
    +      test.notEqual( 0, err, 'Not a valid commit' );
      
           // Test valid commit
    -      testOid.mkstr( '3b7670f327dc1ca66e040f0c09cc4c3f1428eb49' );
    +      testOid.mkstr( 'cb76e3c030ab29db332aff3b297dc39451a84762' );
           testCommit.lookup( testRepo, testOid, function( err ) {
             test.equals( 0, err, 'Valid commit');
     
    -        //test.equals( 'Fixed path issues', testCommit.messageShort(), 'Commit message is valid' );
    +        //test.equals( 'Updated gitignore and raw-commit test', testCommit.messageShort(), 'Commit message is valid' );
     
             test.done();
           });
    
    From 991dc5fa96ac36efa6b32e1457cb3f732321a2d5 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Mon, 11 Apr 2011 14:10:43 -0400
    Subject: [PATCH 32/37] Started working with node buffers, going to try
     implement fast buffer instead of slow buffer
    
    ---
     README.md                   |  2 +-
     example/convenience-repo.js |  2 +-
     example/convenience-tree.js |  2 +-
     example/raw-blob.js         | 24 +++++++++++++++++
     example/raw-commit.js       |  2 +-
     example/raw-error.js        |  2 +-
     example/raw-repo.js         | 10 +++----
     include/tree_entry.h        |  2 +-
     src/blob.cc                 |  9 +++++--
     src/commit.cc               | 54 ++++++++++++++++++-------------------
     src/tree_entry.cc           | 14 ++++++----
     test/raw-blob.js            | 24 +++++++++++++++--
     12 files changed, 99 insertions(+), 48 deletions(-)
     create mode 100644 example/raw-blob.js
     mode change 100644 => 100755 include/tree_entry.h
     mode change 100644 => 100755 src/tree_entry.cc
    
    diff --git a/README.md b/README.md
    index 55b7c1265..6594d42b0 100644
    --- a/README.md
    +++ b/README.md
    @@ -197,7 +197,7 @@ Release information
     __ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http://bit.ly/tb_methods) __
     
     ### v0.0.3: ###
    -    * Fully documented native source code
    +    * More documented native source code
         * Updated convenience api code
         * More unit tests
         * Updated libgit2 to version 0.11.0
    diff --git a/example/convenience-repo.js b/example/convenience-repo.js
    index e3e7593c3..9794bb784 100644
    --- a/example/convenience-repo.js
    +++ b/example/convenience-repo.js
    @@ -1,5 +1,5 @@
     // Load in the module
    -var git = require( 'nodegit' );
    +var git = require( '../' );
     // Open a repository for reading
     git.repo( '../.git', function( err, repo ) {
       // Success is always 0, failure is always an error string
    diff --git a/example/convenience-tree.js b/example/convenience-tree.js
    index acd771d2f..fa8ba24df 100644
    --- a/example/convenience-tree.js
    +++ b/example/convenience-tree.js
    @@ -1,4 +1,4 @@
    -var git = require( 'nodegit' );
    +var git = require( '../' );
     
     git.repo( '../.git', function( err, repo ) {
       if( err ) { throw err; }
    diff --git a/example/raw-blob.js b/example/raw-blob.js
    new file mode 100644
    index 000000000..1cab88e35
    --- /dev/null
    +++ b/example/raw-blob.js
    @@ -0,0 +1,24 @@
    +var git = require( '../' ).raw
    +  , path = require( 'path' );
    +
    +var repo = new git.Repo();
    +repo.open( path.resolve( '../.git' ), function() {
    +  var oid = new git.Oid();
    +  oid.mkstr( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5' );
    +
    +  var commit = new git.Commit();
    +  commit.lookup( repo, oid, function( err ) {
    +    var tree = new git.Tree( repo ),
    +        entry = new git.TreeEntry(),
    +        blob = new git.Blob( repo );
    +
    +    if( !commit.tree( tree ) && tree.entryCount() > 1 ) {
    +      tree.entryByIndex( entry, 1 );
    +      entry.toObject( repo, blob );
    +
    +      console.log( entry.name() + ':' );
    +      console.log( blob.rawSize() );
    +      console.log( blob.rawContent().toString() );
    +    }
    +  });
    +});
    diff --git a/example/raw-commit.js b/example/raw-commit.js
    index 885ba7f7f..bb0ff3986 100644
    --- a/example/raw-commit.js
    +++ b/example/raw-commit.js
    @@ -1,4 +1,4 @@
    -var git = require( '../lib' ).raw
    +var git = require( '../' ).raw
       , path = require( 'path' );
     
     var repo = new git.Repo();
    diff --git a/example/raw-error.js b/example/raw-error.js
    index fb78e941c..ab20568aa 100644
    --- a/example/raw-error.js
    +++ b/example/raw-error.js
    @@ -1,4 +1,4 @@
    -var git2 = require( '../lib' ).raw;
    +var git2 = require( '../' ).raw;
     
     var error = new git2.Error();
     // Valid
    diff --git a/example/raw-repo.js b/example/raw-repo.js
    index 4b101abab..bda7da00e 100644
    --- a/example/raw-repo.js
    +++ b/example/raw-repo.js
    @@ -1,16 +1,16 @@
    -var git2 = require( '../' ).raw,
    +var git = require( '../' ).raw,
         path = require( 'path' );
     
     
    -var repo = new git2.Repo(),
    -    error = new git2.Error();
    +var repo = new git.Repo(),
    +    error = new git.Error();
     
     // Access existing repository
     repo.open( path.resolve( '../.git' ), function( err ) {
    -    var master = new git2.Ref(repo);
    +    var master = new git.Ref(repo);
         repo.lookupRef( master, 'refs/heads/master', function( err, ref ) {
           console.log(err, master);
    -      var oid = new git2.Oid();
    +      var oid = new git.Oid();
           master.oid(oid);
           console.log( oid.toString(40) );
         });
    diff --git a/include/tree_entry.h b/include/tree_entry.h
    old mode 100644
    new mode 100755
    index 775ab099e..3d7379bfd
    --- a/include/tree_entry.h
    +++ b/include/tree_entry.h
    @@ -51,7 +51,7 @@ class GitTreeEntry : EventEmitter {
         void SetValue(git_tree_entry* tree);
         const char* Name();
         const git_oid* Id();
    -    int ToObject(git_object** obj);
    +    int ToObject(git_repository* repo, git_object** obj);
     
       protected:
         static Handle<Value> New(const Arguments& args);
    diff --git a/src/blob.cc b/src/blob.cc
    index 1ac53f57e..9c82195a1 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -7,6 +7,7 @@
     #include <node.h>
     #include <node_events.h>
     #include <node_buffer.h>
    +#include <string.h>
     
     #include "../vendor/libgit2/include/git2.h"
     
    @@ -105,7 +106,8 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
     int GitBlob::EIO_Lookup(eio_req* req) {
       lookup_request* ar = static_cast<lookup_request* >(req->data);
     
    -  ar->err = ar->blob->Lookup(ar->repo->GetValue(), &ar->oid->GetValue());
    +  git_oid oid = ar->oid->GetValue();
    +  ar->err = ar->blob->Lookup(ar->repo->GetValue(), &oid);
     
       return 0;
     }
    @@ -137,7 +139,10 @@ int GitBlob::EIO_AfterLookup(eio_req* req) {
     Handle<Value> GitBlob::RawContent(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  return String::New((const char*)blob->RawContent());
    +  int rawSize = blob->RawSize();
    +  const char* buffer = (const char *)const_cast<void *>(blob->RawContent());
    +
    +  return Buffer::New(const_cast<char *>(buffer), rawSize)->handle_;
     }
     
     Handle<Value> GitBlob::RawSize(const Arguments& args) {
    diff --git a/src/commit.cc b/src/commit.cc
    index db3244a5a..8985cd71e 100755
    --- a/src/commit.cc
    +++ b/src/commit.cc
    @@ -53,9 +53,7 @@ void GitCommit::SetValue(git_commit* commit) {
     }
     
     int GitCommit::Lookup(git_repository* repo, git_oid* oid) {
    -  git_commit* commit;
    -
    -  int err = git_commit_lookup(&commit, repo, oid);
    +  int err = git_commit_lookup(&this->commit, repo, oid);
     
       return err;
     }
    @@ -191,10 +189,10 @@ Handle<Value> GitCommit::Close(const Arguments& args) {
     }
     
     Handle<Value> GitCommit::Id(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
    @@ -207,42 +205,42 @@ Handle<Value> GitCommit::Id(const Arguments& args) {
     }
     
     Handle<Value> GitCommit::MessageShort(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
       
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       return String::New(commit->MessageShort());
     }
     
     Handle<Value> GitCommit::Message(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
       
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       return String::New(commit->Message());
     }
     
     Handle<Value> GitCommit::Time(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
       
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       return Integer::New(commit->Time());
     }
     
     Handle<Value> GitCommit::TimeOffset(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
    +
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
       
       return Integer::New(commit->TimeOffset());
     }
     
     Handle<Value> GitCommit::Committer(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
    @@ -255,10 +253,10 @@ Handle<Value> GitCommit::Committer(const Arguments& args) {
     }
     
     Handle<Value> GitCommit::Author(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Signature is required and must be an Object.")));
       }
    @@ -271,38 +269,38 @@ Handle<Value> GitCommit::Author(const Arguments& args) {
     }
     
     Handle<Value> GitCommit::Tree(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
       }
     
    -  git_tree* in;
    -  GitTree* tree = ObjectWrap::Unwrap<GitTree>(args[0]->ToObject());
    +  GitTree* g_tree = ObjectWrap::Unwrap<GitTree>(args[0]->ToObject());
     
    -  int err = commit->Tree(&in);
    -  tree->SetValue(in);
    +  git_tree* tree;
    +  int err = commit->Tree(&tree);
    +  g_tree->SetValue(tree);
     
    -  return Integer::New(err);
    +  return scope.Close(Integer::New(err));
     }
     
     Handle<Value> GitCommit::ParentCount(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       unsigned int count = commit->ParentCount();
     
       return Integer::New(count);
     }
     
     Handle<Value> GitCommit::Parent(const Arguments& args) {
    -  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    -
       HandleScope scope;
     
    +  GitCommit *commit = ObjectWrap::Unwrap<GitCommit>(args.This());
    +
       if(args.Length() == 0 || !args[0]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Commit is required and must be an Object.")));
       }
    diff --git a/src/tree_entry.cc b/src/tree_entry.cc
    old mode 100644
    new mode 100755
    index 92dea96f6..49d958354
    --- a/src/tree_entry.cc
    +++ b/src/tree_entry.cc
    @@ -43,9 +43,8 @@ const git_oid* GitTreeEntry::Id() {
       return git_tree_entry_id(this->entry);
     }
     
    -int GitTreeEntry::ToObject(git_object** obj) {
    -  //TODO: Implement correct arguments
    -  //return git_tree_entry_2object(obj, this->entry);
    +int GitTreeEntry::ToObject(git_repository* repo, git_object** obj) {
    +  return git_tree_entry_2object(obj, repo, this->entry);
     }
     
     Handle<Value> GitTreeEntry::New(const Arguments& args) {
    @@ -88,13 +87,18 @@ Handle<Value> GitTreeEntry::ToObject(const Arguments& args) {
       GitTreeEntry *entry = ObjectWrap::Unwrap<GitTreeEntry>(args.This());
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
    +  }
    +
    +  if(args.Length() == 1 || !args[1]->IsObject()) {
         return ThrowException(Exception::Error(String::New("Blob is required and must be an Object.")));
       }
     
    -  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args[0]->ToObject());
    +  GitRepo* repo = ObjectWrap::Unwrap<GitRepo>(args[0]->ToObject());
    +  GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args[1]->ToObject());
     
       git_object* out;
    -  entry->ToObject(&out);
    +  entry->ToObject(repo->GetValue(), &out);
       blob->SetValue((git_blob *)out);
       
       return Undefined();
    diff --git a/test/raw-blob.js b/test/raw-blob.js
    index dfdf8a49e..518832491 100644
    --- a/test/raw-blob.js
    +++ b/test/raw-blob.js
    @@ -74,13 +74,33 @@ exports.lookup = function( test ) {
     
     // Blob::RawContent
     exports.rawContent = function( test ) {
    -  var testOid = new git.Oid(),
    -      testBlob = new git.Blob();
    +  var testOid = new git.Oid()
    +    , testBlob = new git.Blob()
    +    , testCommit = new git.Commit();
     
       test.expect( 2 );
     
       // Test for function
       helper.testFunction( test.equals, testBlob.rawContent, 'Blob::RawContent' );
    +
    +  testRepo.open( path.resolve( '../.git' ), function() {
    +    testOid.mkstr( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5' );
    +
    +    commit.lookup( repo, oid, function( err ) {
    +      var tree = new git.Tree( repo ),
    +          entry = new git.TreeEntry(),
    +          blob = new git.Blob( repo );
    +
    +      if( !commit.tree( tree ) && tree.entryCount() > 1 ) {
    +        tree.entryByIndex( entry, 1 );
    +        entry.toObject( repo, blob );
    +
    +        console.log( entry.name() + ':' );
    +        console.log( blob.rawSize() );
    +        console.dir( blob.rawContent() );
    +      }
    +    });
    +  });
      
       test.done();
     };
    
    From 28b231d8b27763bf8e4666e30fb8e9bc0b04aa1e Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Tue, 12 Apr 2011 14:38:54 -0400
    Subject: [PATCH 33/37] commit updates
    
    ---
     example/convenience-commit.js | 27 +++++++++++++++++++++++++++
     lib/commit.js                 | 27 ++++++++++++++++-----------
     lib/repo.js                   |  2 +-
     3 files changed, 44 insertions(+), 12 deletions(-)
     create mode 100644 example/convenience-commit.js
    
    diff --git a/example/convenience-commit.js b/example/convenience-commit.js
    new file mode 100644
    index 000000000..4ca94c388
    --- /dev/null
    +++ b/example/convenience-commit.js
    @@ -0,0 +1,27 @@
    +var git = require( '../' );
    +
    +git.repo( '../.git', function( err, repo ) {
    +  if( err ) { throw new Error( err ); }
    +
    +  repo.commit( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5', function( err, commit ) {
    +    if( err ) { throw new Error( err ); }
    +
    +    var history = commit.history();
    +    history.on( 'commit', function() {
    +      console.log(arguments);
    +    });
    +  });
    +
    +  //repo.branch( 'master', function( err, branch ) {
    +  //  if( err ) { throw new Error( err ); }
    +
    +  //  var history = branch.history();
    +  //  console.log( history );
    +
    +  //  //branch.tree().each( function( i, entry ) {
    +  //  //  console.log( entry.name );
    +  //  //  console.log( entry.contents );
    +
    +  //  //});
    +  //});
    +});
    diff --git a/lib/commit.js b/lib/commit.js
    index 82500546e..e3195eaf8 100644
    --- a/lib/commit.js
    +++ b/lib/commit.js
    @@ -4,13 +4,20 @@ var git = require( '../' ),
     var _Commit = function( obj ) {
       var self = { _cache: {} };
     
    -  if( obj instanceof git.raw.Repo ) {
    -    self.commit = new git.raw.Commit( obj );
    -  }
    -  else if( obj instanceof git.raw.Commit ) {
    +  //if( obj instanceof git.raw.Repo ) {
    +  //  self.commit = new git.raw.Commit( obj );
    +  //}
    +  //else if( obj instanceof git.raw.Commit ) {
    +  //  self.commit = obj;
    +  //}
    +
    +  if( obj instanceof git.raw.Commit ) {
         self.commit = obj;
       }
    -
    +  else {
    +    self.commit = new git.raw.Commit();
    +  }
    + 
       Object.defineProperty( self, 'id', {
         get: function() {
           var oid = new git.raw.Oid();
    @@ -70,8 +77,9 @@ var _Commit = function( obj ) {
         enumerable: true
       });
     
    -  self.lookup = function( oid, callback ) {
    -    self.commit.lookup( oid, function() {
    +  self.lookup = function( repo, oid, callback ) {
    +    self.repo = repo;
    +    self.commit.lookup( repo, oid, function() {
           var args = Array.prototype.slice.call( arguments );
     
           args[0] = git.util().error( args[0] );
    @@ -107,12 +115,9 @@ var _Commit = function( obj ) {
           else {
             event.emit( 'commit', commit );
           }
    -
    -      if( self.stop ) {
    -        
    -      }
         });
     
    +    return event;
       };
     
     
    diff --git a/lib/repo.js b/lib/repo.js
    index e9e6a47f6..6cfc0d0d1 100644
    --- a/lib/repo.js
    +++ b/lib/repo.js
    @@ -49,7 +49,7 @@ var _Repo = function( dir, callback ) {
     
         var oid = git.oid( sha );
     
    -    git.commit( self.repo ).lookup( oid.oid, callback );
    +    git.commit().lookup( self.repo, oid.oid, callback );
       };
     
       self.init = function( dir, is_bare, callback ) {
    
    From 290338b104e2c78b3b58007dc2b236d2f4113c71 Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Tue, 12 Apr 2011 14:47:24 -0400
    Subject: [PATCH 34/37] Fixed blob issues
    
    ---
     example/raw-blob.js |  2 +-
     src/blob.cc         | 13 +++++++++----
     test/raw-blob.js    | 21 +++++++++++----------
     3 files changed, 21 insertions(+), 15 deletions(-)
    
    diff --git a/example/raw-blob.js b/example/raw-blob.js
    index 1cab88e35..2eec95049 100644
    --- a/example/raw-blob.js
    +++ b/example/raw-blob.js
    @@ -18,7 +18,7 @@ repo.open( path.resolve( '../.git' ), function() {
     
           console.log( entry.name() + ':' );
           console.log( blob.rawSize() );
    -      console.log( blob.rawContent().toString() );
    +      console.log( blob.rawContent() );
         }
       });
     });
    diff --git a/src/blob.cc b/src/blob.cc
    index 9c82195a1..80ce740c9 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -76,11 +76,11 @@ Handle<Value> GitBlob::Lookup(const Arguments& args) {
       HandleScope scope;
     
       if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Repo is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
       }
     
       if(args.Length() == 1 || !args[1]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Oid is required and must be a Object.")));
    +    return ThrowException(Exception::Error(String::New("Oid is required and must be an Object.")));
       }
     
       if(args.Length() == 2 || !args[2]->IsFunction()) {
    @@ -139,10 +139,15 @@ int GitBlob::EIO_AfterLookup(eio_req* req) {
     Handle<Value> GitBlob::RawContent(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    +  if(args.Length() == 0 || !args[0]->IsObject()) {
    +    return ThrowException(Exception::Error(String::New("Buffer is required and must be an Object.")));
    +  }
    +
       int rawSize = blob->RawSize();
    -  const char* buffer = (const char *)const_cast<void *>(blob->RawContent());
    +  const char* contents = (const char *)const_cast<void *>(blob->RawContent());
     
    -  return Buffer::New(const_cast<char *>(buffer), rawSize)->handle_;
    +  Buffer* buffer = Buffer::New(const_cast<char *>(contents), rawSize);
    +  return buffer->handle_;
     }
     
     Handle<Value> GitBlob::RawSize(const Arguments& args) {
    diff --git a/test/raw-blob.js b/test/raw-blob.js
    index 518832491..900f743c2 100644
    --- a/test/raw-blob.js
    +++ b/test/raw-blob.js
    @@ -1,5 +1,6 @@
    -var git = require( '../' ).raw,
    -    rimraf = require( '../vendor/rimraf' );
    +var git = require( '../' ).raw
    +  , path = require( 'path' )
    +  , rimraf = require( '../vendor/rimraf' );
     
     var testRepo = new git.Repo();
     
    @@ -86,18 +87,18 @@ exports.rawContent = function( test ) {
       testRepo.open( path.resolve( '../.git' ), function() {
         testOid.mkstr( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5' );
     
    -    commit.lookup( repo, oid, function( err ) {
    -      var tree = new git.Tree( repo ),
    +    testCommit.lookup( testRepo, testOid, function( err ) {
    +      var tree = new git.Tree( testRepo ),
               entry = new git.TreeEntry(),
    -          blob = new git.Blob( repo );
    +          blob = new git.Blob( testRepo );
     
    -      if( !commit.tree( tree ) && tree.entryCount() > 1 ) {
    +      if( !testCommit.tree( tree ) && tree.entryCount() > 1 ) {
             tree.entryByIndex( entry, 1 );
    -        entry.toObject( repo, blob );
    +        entry.toObject( testRepo, blob );
     
    -        console.log( entry.name() + ':' );
    -        console.log( blob.rawSize() );
    -        console.dir( blob.rawContent() );
    +        //console.log( entry.name() + ':' );
    +        //console.log( blob.rawSize() );
    +        //console.dir( blob.rawContent() );
           }
         });
       });
    
    From 3736ee138a677a35d0329beb3c0ea3576787899c Mon Sep 17 00:00:00 2001
    From: tbranyen <tim@tabdeveloper.com>
    Date: Wed, 13 Apr 2011 18:24:45 -0400
    Subject: [PATCH 35/37] fixes for 0.0.3
    
    ---
     example/convenience-commit.js | 18 ++++++++++++-
     lib/blob.js                   |  2 +-
     lib/commit.js                 | 18 +++++++------
     lib/revwalk.js                |  8 +++---
     lib/tree.js                   | 49 ++++++++++++++++++++++++++++-------
     lib/tree_entry.js             |  2 +-
     src/blob.cc                   |  6 ++---
     src/revwalk.cc                |  4 +--
     util/nodejshint.js            |  2 +-
     9 files changed, 78 insertions(+), 31 deletions(-)
    
    diff --git a/example/convenience-commit.js b/example/convenience-commit.js
    index 4ca94c388..7d72ed3e4 100644
    --- a/example/convenience-commit.js
    +++ b/example/convenience-commit.js
    @@ -8,7 +8,23 @@ git.repo( '../.git', function( err, repo ) {
     
         var history = commit.history();
         history.on( 'commit', function() {
    -      console.log(arguments);
    +      //console.log(arguments);
    +    });
    +
    +    history.on( 'end', function( commits ) {
    +      // Read first commit tree
    +      var tree = commits[0].tree();
    +
    +      // Synchronous
    +      tree.walk(function( i, entry ) {
    +        console.log( entry.content );
    +        return false;
    +      });
    +
    +      // Asynchronous - not yet implemented
    +      //tree.walk().on( 'entry', function( err, i, entry ) {
    +      //  console.log( entry );
    +      //});
         });
       });
     
    diff --git a/lib/blob.js b/lib/blob.js
    index 88c0effd0..2808a5e89 100644
    --- a/lib/blob.js
    +++ b/lib/blob.js
    @@ -13,7 +13,7 @@ var _Blob = function( obj ) {
     
       Object.defineProperty( self, 'raw', {
         get: function() {
    -      return self.blob.rawContent();
    +      return self.blob.rawContent().toString();
         },
         enumerable: true
       });
    diff --git a/lib/commit.js b/lib/commit.js
    index e3195eaf8..2708b40d1 100644
    --- a/lib/commit.js
    +++ b/lib/commit.js
    @@ -1,5 +1,5 @@
    -var git = require( '../' ),
    -    events = require( 'events' );
    +var git = require( '../' )
    +  , events = require( 'events' );
     
     var _Commit = function( obj ) {
       var self = { _cache: {} };
    @@ -91,13 +91,13 @@ var _Commit = function( obj ) {
       self.tree = function() {
         var tree = new git.raw.Tree( self.repo );
         if( tree.error ) {
    -      throw git.error( tree.error );
    +      return git.error( tree.error );
         }
         else {
           self.commit.tree( tree );
         }
     
    -    return git.tree( tree );
    +    return git.tree( self.repo, tree );
       };
     
       self.file = function( path ) {
    @@ -106,14 +106,16 @@ var _Commit = function( obj ) {
     
       self.history = function( start, end ) {
         var revwalk = git.revwalk( self.repo ),
    -        event = new events.EventEmitter();
    +        event = new events.EventEmitter(),
    +        commits = [];
     
    -    revwalk.walk( self.id, function( err, commit ) {
    +    revwalk.walk( self.id, function( err, index, commit ) {
           if( err ) {
    -        event.emit( 'error', err );
    +        event.emit( 'end', commits );
           }
           else {
    -        event.emit( 'commit', commit );
    +        event.emit( 'commit', index, commit );
    +        commits.push( commit );
           }
         });
     
    diff --git a/lib/revwalk.js b/lib/revwalk.js
    index a68c84f59..f5ef4de1d 100644
    --- a/lib/revwalk.js
    +++ b/lib/revwalk.js
    @@ -24,12 +24,12 @@ var _RevWalk = function( obj ) {
           var _tmp = git.oid();
     
           self.revwalk.next( _tmp.oid, function( err ) {
    -        if( err ) { return; }
    +        if( err ) { callback.apply( this, [ err ] ); return; }
     
    -        git.commit( self.repo ).lookup( _tmp.oid, function( err ) {
    -          if( err ) { return; }
    +        git.commit( self.repo ).lookup( self.repo, _tmp.oid, function( err ) {
    +          if( err ) { callback.apply( this, [ err, i, this ] ); }
     
    -          if( callback.apply( this, [ i, this ] ) === false ) {
    +          if( callback.apply( this, [ err, i, this ] ) === false ) {
                 cont = false;
               }
     
    diff --git a/lib/tree.js b/lib/tree.js
    index d2c9b8662..243624e85 100644
    --- a/lib/tree.js
    +++ b/lib/tree.js
    @@ -1,15 +1,15 @@
    -var git = require( '../' );
    +var git = require( '../' )
    +  , events = require( 'events' );
     
     var _Tree = function( obj, tree ) {
       var self = {};
    -
    -  if( obj instanceof git.raw.Repo ) {
    +  if ( obj instanceof git.raw.Repo && tree instanceof git.raw.Tree ) {
         self.repo = obj;
    -    self.tree = new git.raw.Tree( tree );
    +    self.tree = tree;
       }
    -  else if ( obj instanceof git.raw.Repo && tree instanceof git.raw.Tree ) {
    +  else if( obj instanceof git.raw.Repo ) {
         self.repo = obj;
    -    self.tree = tree; 
    +    self.tree = new git.raw.Tree( tree );
       }
       else if ( obj instanceof git.raw.Tree ) {
         self.tree = obj;
    @@ -25,17 +25,46 @@ var _Tree = function( obj, tree ) {
         enumerable: true
       });
     
    -  self.each = function( callback ) {
    +  // Synchronous walk
    +  self.walk = function( callback ) {
         if( !callback ) { return; }
     
    -    var entry, i;
    +    var entry
    +      , i;
    +
         for( i=0, len=self.length; i<len; i++ ) {
    -      entry = git.entry();
    +      entry = git.entry( self.repo );
    +
           self.tree.entryByIndex( entry.entry, i );
    -      callback.apply( entry, [ i, entry ] );
    +
    +      if( callback.apply( entry, [ i, entry ] ) === false ) {
    +        break;
    +      }
         }
       };
     
    +  //self.walk = function( callback ) {
    +  //  if( !callback ) { return; }
    +
    +  //  var entry
    +  //    , i
    +  //    , entries
    +  //    , event = new events.EventEmitter();
    +
    +  //  for( i=0, len=self.length; i<len; i++ ) {
    +  //    entry = git.entry();
    +
    +  //    self.tree.entryByIndex( entry.entry, i );
    +  //    event.emit( 'entry', [ err, i, entry ] );
    +
    +  //    entries.push( entry );
    +  //  }
    +
    +  //  event.emit( 'end', entries );
    +
    +  //  return event;
    +  //};
    +
       self.entry = function( name ) {
         var entry = git.entry( self.repo );
     
    diff --git a/lib/tree_entry.js b/lib/tree_entry.js
    index 5c8d23f49..36c8542d9 100644
    --- a/lib/tree_entry.js
    +++ b/lib/tree_entry.js
    @@ -22,7 +22,7 @@ var _TreeEntry = function( obj ) {
         get: function() {
           var blob = git.blob( self.repo );
     
    -      self.entry.toObject( blob.blob );
    +      self.entry.toObject( self.repo, blob.blob );
     
           return blob.raw;
         },
    diff --git a/src/blob.cc b/src/blob.cc
    index 80ce740c9..906bcac3e 100755
    --- a/src/blob.cc
    +++ b/src/blob.cc
    @@ -139,9 +139,9 @@ int GitBlob::EIO_AfterLookup(eio_req* req) {
     Handle<Value> GitBlob::RawContent(const Arguments& args) {
       GitBlob* blob = ObjectWrap::Unwrap<GitBlob>(args.This());
     
    -  if(args.Length() == 0 || !args[0]->IsObject()) {
    -    return ThrowException(Exception::Error(String::New("Buffer is required and must be an Object.")));
    -  }
    +  //if(args.Length() == 0 || !args[0]->IsObject()) {
    +  //  return ThrowException(Exception::Error(String::New("Buffer is required and must be an Object.")));
    +  //}
     
       int rawSize = blob->RawSize();
       const char* contents = (const char *)const_cast<void *>(blob->RawContent());
    diff --git a/src/revwalk.cc b/src/revwalk.cc
    index 1b6c266fc..a7b0d5f38 100755
    --- a/src/revwalk.cc
    +++ b/src/revwalk.cc
    @@ -125,7 +125,7 @@ Handle<Value> GitRevWalk::Push(const Arguments& args) {
     }
     
     Handle<Value> GitRevWalk::Next(const Arguments& args) {
    -  GitRevWalk *revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
    +  GitRevWalk* revwalk = ObjectWrap::Unwrap<GitRevWalk>(args.This());
       Local<Function> callback;
     
       HandleScope scope;
    @@ -140,7 +140,7 @@ Handle<Value> GitRevWalk::Next(const Arguments& args) {
     
       callback = Local<Function>::Cast(args[1]);
     
    -  next_request *ar = new next_request();
    +  next_request* ar = new next_request();
       ar->revwalk = revwalk;
       ar->oid = ObjectWrap::Unwrap<GitOid>(args[0]->ToObject());
       ar->callback = Persistent<Function>::New(callback);
    diff --git a/util/nodejshint.js b/util/nodejshint.js
    index 384db40cc..13a2ebcc5 100644
    --- a/util/nodejshint.js
    +++ b/util/nodejshint.js
    @@ -12,7 +12,7 @@ var nodejshint = function() {
           fs.readFile( file, function( err, data ) {
             if (err) { throw err; }
     
    -        if( pass = JSHINT( data.toString() ), pass ) {
    +        if( pass = JSHINT( data.toString() ), pass, { laxbreak: true } ) {
               counter++;
               console.log( '✔ Passed '+ file );
             }
    
    From a9c2e237237e86ad7c1783dbdb61af0751757849 Mon Sep 17 00:00:00 2001
    From: Tim Branyen <tim@tabdeveloper.com>
    Date: Wed, 13 Apr 2011 19:12:34 -0400
    Subject: [PATCH 36/37] fixed readme
    
    ---
     README.md | 12 ++++++------
     1 file changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/README.md b/README.md
    index 6594d42b0..ccf96d858 100644
    --- a/README.md
    +++ b/README.md
    @@ -20,7 +20,7 @@ This will install and configure everything you need to use `nodegit`.
     
     ### Mac OS X/Linux/Unix ###
     
    -#### Install `nodegit` by cloning source from __GitHub__ and running the `configure`, `make`, and `make install` commands: ####
    +#### Install `nodegit` by cloning source from GitHub and running the `configure`, `make`, and `make install` commands: ####
     \*Note: `nodegit` assumes your library path exists at `~/.node_libraries` you can change this by specifying a new lib path\*
         
         $ git clone git://github.com/tbranyen/nodegit.git
    @@ -170,9 +170,9 @@ API Example Usage
     Running tests
     -------------
     
    -__ `nodegit` library code is written adhering to a modified `JSHint`. Run these checks with `make lint` in the project root. __
    +__`nodegit` library code is written adhering to a modified `JSHint`. Run these checks with `make lint` in the project root.__
     
    -__ To run unit tests ensure the submodules `nodeunit` and `rimraf` are located in the `vendor/` subdirectory. __
    +__To run unit tests ensure the submodules `nodeunit` and `rimraf` are located in the `vendor/` subdirectory.__
     
     If they are not, `cd` into the `nodegit` dir and run the following `git` commands to automatically fetch them:
         $ cd nodegit
    @@ -183,7 +183,7 @@ Then simply run `make test` in the project root.
     Generating documentation
     ------------------------
     
    -__ `nodegit` native and library code is documented to be built with `Natural Docs`. __
    +__`nodegit` native and library code is documented to be built with `Natural Docs`.__
     
     To create the documentation, `cd` into the `nodegit` dir and run the following:
         $ cd nodegit
    @@ -194,7 +194,7 @@ The documentation will then generate in the `doc/` subfolder as HTML.
     Release information
     -------------------
     
    -__ Can keep track of current method coverage at: [http://bit.ly/tb_methods](http://bit.ly/tb_methods) __
    +__Can keep track of current method coverage at: [http://bit.ly/tb_methods](http://bit.ly/tb_methods)__
     
     ### v0.0.3: ###
         * More documented native source code
    @@ -228,4 +228,4 @@ Getting involved
     
     If you find this project of interest, please document all issues and fork if you feel you can provide a patch.  Testing is of huge importance; by simply running the unit tests on your system and reporting issues you can contribute!
     
    -__ Before submitting a pull request, please ensure both unit tests and lint checks pass. __
    +__Before submitting a pull request, please ensure both unit tests and lint checks pass.__
    
    From dd56e05b263b508e03cf3fae2f6478a4005a5980 Mon Sep 17 00:00:00 2001
    From: Tim Branyen <tim@tabdeveloper.com>
    Date: Wed, 13 Apr 2011 19:16:03 -0400
    Subject: [PATCH 37/37] fix github node link
    
    ---
     README.md | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/README.md b/README.md
    index ccf96d858..ca83b4fd5 100644
    --- a/README.md
    +++ b/README.md
    @@ -43,7 +43,7 @@ This will install and configure everything you need to use `nodegit`.
     #### `nodegit` has been compiled and tested to work with the setup required to build and run `Node.js` itself. ####
     
     Instructions on compiling `Node.js` on a Windows platform can be found here:
    -[https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows)](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows\))
    +[https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-(Windows)](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-%28Windows%29)
     
     API Example Usage
     -----------------