http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/_config.yml
----------------------------------------------------------------------
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..26c27e9
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,71 @@
+# Hexo Configuration
+## Docs: https://hexo.io/docs/configuration.html
+## Source: https://github.com/hexojs/hexo/
+
+# Site
+title: Hexo
+subtitle:
+description:
+author: John Doe
+language:
+timezone:
+
+# URL
+## If your site is put in a subdirectory, set url as 
'http://yoursite.com/child' and root as '/child/'
+url: http://yoursite.com
+root: /
+permalink: :year/:month/:day/:title/
+permalink_defaults:
+
+# Directory
+source_dir: source
+public_dir: public
+tag_dir: tags
+archive_dir: archives
+category_dir: categories
+code_dir: downloads/code
+i18n_dir: :lang
+skip_render:
+
+# Writing
+new_post_name: :title.md # File name of new posts
+default_layout: post
+titlecase: false # Transform title into titlecase
+external_link: true # Open external links in new tab
+filename_case: 0
+render_drafts: false
+post_asset_folder: false
+relative_link: false
+future: true
+highlight:
+  enable: true
+  line_number: true
+  auto_detect: false
+  tab_replace:
+
+# Category & Tag
+default_category: uncategorized
+category_map:
+tag_map:
+
+# Date / Time format
+## Hexo uses Moment.js to parse and display date
+## You can customize the date format as defined in
+## http://momentjs.com/docs/#/displaying/format/
+date_format: YYYY-MM-DD
+time_format: HH:mm:ss
+
+# Pagination
+## Set per_page to 0 to disable pagination
+per_page: 10
+pagination_dir: page
+
+# Extensions
+## Plugins: https://hexo.io/plugins/
+## Themes: https://hexo.io/themes/
+theme: landscape
+
+# Deployment
+## Docs: https://hexo.io/docs/deployment.html
+deploy:
+  type:

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/db.json
----------------------------------------------------------------------
diff --git a/db.json b/db.json
new file mode 100644
index 0000000..675a1dc
--- /dev/null
+++ b/db.json
@@ -0,0 +1 @@
+{"meta":{"version":1,"warehouse":"2.2.0"},"models":{"Asset":[{"_id":"themes/landscape/source/css/style.styl","path":"css/style.styl","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/blank.gif","path":"fancybox/blank.gif","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/fancybox_loading.gif","path":"fancybox/fancybox_loading.gif","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/[email protected]","path":"fancybox/[email protected]","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/fancybox_overlay.png","path":"fancybox/fancybox_overlay.png","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/[email protected]","path":"fancybox/[email protected]","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/fancybox_sprite.png","path":"fancybox/fancybox_sprite.png","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.css","
 
path":"fancybox/jquery.fancybox.css","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.pack.js","path":"fancybox/jquery.fancybox.pack.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.js","path":"fancybox/jquery.fancybox.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/js/script.js","path":"js/script.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/fonts/FontAwesome.otf","path":"css/fonts/FontAwesome.otf","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.eot","path":"css/fonts/fontawesome-webfont.eot","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.woff","path":"css/fonts/fontawesome-webfont.woff","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/helpers/fancybox_buttons.png","path":"fancybox/helpers/fancybox_buttons.png","modified":0,"renderable":1},{"_id":"themes/lands
 
cape/source/fancybox/helpers/jquery.fancybox-media.js","path":"fancybox/helpers/jquery.fancybox-media.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css","path":"fancybox/helpers/jquery.fancybox-thumbs.css","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js","path":"fancybox/helpers/jquery.fancybox-thumbs.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css","path":"fancybox/helpers/jquery.fancybox-buttons.css","modified":0,"renderable":1},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js","path":"fancybox/helpers/jquery.fancybox-buttons.js","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.ttf","path":"css/fonts/fontawesome-webfont.ttf","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.svg","path":"css/fonts/fo
 
ntawesome-webfont.svg","modified":0,"renderable":1},{"_id":"themes/landscape/source/css/images/banner.jpg","path":"css/images/banner.jpg","modified":0,"renderable":1}],"Cache":[{"_id":"themes/landscape/.gitignore","hash":"58d26d4b5f2f94c2d02a4e4a448088e4a2527c77","modified":1490029954000},{"_id":"themes/landscape/LICENSE","hash":"c480fce396b23997ee23cc535518ffaaf7f458f8","modified":1490029954000},{"_id":"themes/landscape/README.md","hash":"c7e83cfe8f2c724fc9cac32bd71bb5faf9ceeddb","modified":1490029954000},{"_id":"themes/landscape/_config.yml","hash":"fb8c98a0f6ff9f962637f329c22699721854cd73","modified":1490029954000},{"_id":"themes/landscape/package.json","hash":"85358dc34311c6662e841584e206a4679183943f","modified":1490029954000},{"_id":"themes/landscape/Gruntfile.js","hash":"71adaeaac1f3cc56e36c49d549b8d8a72235c9b9","modified":1490029954000},{"_id":"source/_posts/hello.md","hash":"ee65bc00faf8e1730426d221f5ddc5d9ac0eaeae","modified":1490030414000},{"_id":"source/_posts/hello-world
 
.md","hash":"8a02477044e2b77f1b262da2c48c01429e4a32e4","modified":1490029946000},{"_id":"themes/landscape/layout/archive.ejs","hash":"2703b07cc8ac64ae46d1d263f4653013c7e1666b","modified":1490029954000},{"_id":"themes/landscape/layout/category.ejs","hash":"765426a9c8236828dc34759e604cc2c52292835a","modified":1490029954000},{"_id":"themes/landscape/layout/index.ejs","hash":"aa1b4456907bdb43e629be3931547e2d29ac58c8","modified":1490029954000},{"_id":"themes/landscape/layout/layout.ejs","hash":"f155824ca6130080bb057fa3e868a743c69c4cf5","modified":1490029954000},{"_id":"themes/landscape/layout/page.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1490029954000},{"_id":"themes/landscape/layout/post.ejs","hash":"7d80e4e36b14d30a7cd2ac1f61376d9ebf264e8b","modified":1490029954000},{"_id":"themes/landscape/layout/tag.ejs","hash":"eaa7b4ccb2ca7befb90142e4e68995fb1ea68b2e","modified":1490029954000},{"_id":"themes/landscape/languages/fr.yml","hash":"84ab164b37c6abf625473e9a0c18f6
 
f815dd5fd9","modified":1490029954000},{"_id":"themes/landscape/languages/nl.yml","hash":"12ed59faba1fc4e8cdd1d42ab55ef518dde8039c","modified":1490029954000},{"_id":"themes/landscape/languages/no.yml","hash":"965a171e70347215ec726952e63f5b47930931ef","modified":1490029954000},{"_id":"themes/landscape/languages/ru.yml","hash":"4fda301bbd8b39f2c714e2c934eccc4b27c0a2b0","modified":1490029954000},{"_id":"themes/landscape/languages/zh-CN.yml","hash":"ca40697097ab0b3672a80b455d3f4081292d1eed","modified":1490029954000},{"_id":"themes/landscape/languages/zh-TW.yml","hash":"53ce3000c5f767759c7d2c4efcaa9049788599c3","modified":1490029954000},{"_id":"themes/landscape/languages/default.yml","hash":"3083f319b352d21d80fc5e20113ddf27889c9d11","modified":1490029954000},{"_id":"themes/landscape/scripts/fancybox.js","hash":"aa411cd072399df1ddc8e2181a3204678a5177d9","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/article.ejs","hash":"c4c835615d96a950d51fa2c3b5d64d0596534fed","modifie
 
d":1490029954000},{"_id":"themes/landscape/layout/_partial/archive.ejs","hash":"931aaaffa0910a48199388ede576184ff15793ee","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/google-analytics.ejs","hash":"f921e7f9223d7c95165e0f835f353b2938e40c45","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/head.ejs","hash":"4fe8853e864d192701c03e5cd3a5390287b90612","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/header.ejs","hash":"c21ca56f419d01a9f49c27b6be9f4a98402b2aa3","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/sidebar.ejs","hash":"930da35cc2d447a92e5ee8f835735e6fd2232469","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/after-footer.ejs","hash":"82a30f81c0e8ba4a8af17acd6cc99e93834e4d5e","modified":1490029954000},{"_id":"themes/landscape/layout/_widget/category.ejs","hash":"dd1e5af3c6af3f5d6c85dfd5ca1766faed6a0b05","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/archive-post.ejs",
 
"hash":"c7a71425a946d05414c069ec91811b5c09a92c47","modified":1490029954000},{"_id":"themes/landscape/layout/_widget/tag.ejs","hash":"2de380865df9ab5f577f7d3bcadf44261eb5faae","modified":1490029954000},{"_id":"themes/landscape/layout/_widget/tagcloud.ejs","hash":"b4a2079101643f63993dcdb32925c9b071763b46","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/footer.ejs","hash":"93518893cf91287e797ebac543c560e2a63b8d0e","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/mobile-nav.ejs","hash":"e952a532dfc583930a666b9d4479c32d4a84b44e","modified":1490029954000},{"_id":"themes/landscape/layout/_widget/archive.ejs","hash":"beb4a86fcc82a9bdda9289b59db5a1988918bec3","modified":1490029954000},{"_id":"themes/landscape/layout/_widget/recent_posts.ejs","hash":"0d4f064733f8b9e45c0ce131fe4a689d570c883a","modified":1490029954000},{"_id":"themes/landscape/source/css/_extend.styl","hash":"222fbe6d222531d61c1ef0f868c90f747b1c2ced","modified":1490029954000},{"_id":"themes/
 
landscape/source/css/_variables.styl","hash":"5e37a6571caf87149af83ac1cc0cdef99f117350","modified":1490029954000},{"_id":"themes/landscape/source/css/style.styl","hash":"a70d9c44dac348d742702f6ba87e5bb3084d65db","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/blank.gif","hash":"2daeaa8b5f19f0bc209d976c02bd6acb51b00b0a","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/fancybox_loading.gif","hash":"1a755fb2599f3a313cc6cfdb14df043f8c14a99c","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/[email protected]","hash":"273b123496a42ba45c3416adb027cd99745058b0","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/fancybox_overlay.png","hash":"b3a4ee645ba494f52840ef8412015ba0f465dbe0","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/[email protected]","hash":"30c58913f327e28f466a00f4c1ac8001b560aed8","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/fancybox_sprite.png","hash":"
 
17df19f97628e77be09c352bf27425faea248251","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.css","hash":"aaa582fb9eb4b7092dc69fcb2d5b1c20cca58ab6","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.pack.js","hash":"9e0d51ca1dbe66f6c0c7aefd552dc8122e694a6e","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/jquery.fancybox.js","hash":"d08b03a42d5c4ba456ef8ba33116fdbb7a9cabed","modified":1490029954000},{"_id":"themes/landscape/source/js/script.js","hash":"2876e0b19ce557fca38d7c6f49ca55922ab666a1","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/post/gallery.ejs","hash":"3d9d81a3c693ff2378ef06ddb6810254e509de5b","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/post/nav.ejs","hash":"16a904de7bceccbb36b4267565f2215704db2880","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/post/tag.ejs","hash":"2fcb0bf9c8847a644167a27824c9bb19ac74dd14","modified":14900299
 
54000},{"_id":"themes/landscape/layout/_partial/post/title.ejs","hash":"2f275739b6f1193c123646a5a31f37d48644c667","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/post/category.ejs","hash":"c6bcd0e04271ffca81da25bcff5adf3d46f02fc0","modified":1490029954000},{"_id":"themes/landscape/layout/_partial/post/date.ejs","hash":"6197802873157656e3077c5099a7dda3d3b01c29","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/archive.styl","hash":"db15f5677dc68f1730e82190bab69c24611ca292","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/article.styl","hash":"10685f8787a79f79c9a26c2f943253450c498e3e","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/comment.styl","hash":"79d280d8d203abb3bd933ca9b8e38c78ec684987","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/footer.styl","hash":"e35a060b8512031048919709a8e7b1ec0e40bc1b","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/heade
 
r.styl","hash":"85ab11e082f4dd86dde72bed653d57ec5381f30c","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/highlight.styl","hash":"bf4e7be1968dad495b04e83c95eac14c4d0ad7c0","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/mobile.styl","hash":"a399cf9e1e1cec3e4269066e2948d7ae5854d745","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/sidebar-bottom.styl","hash":"8fd4f30d319542babfd31f087ddbac550f000a8a","modified":1490029954000},{"_id":"themes/landscape/source/css/_util/grid.styl","hash":"0bf55ee5d09f193e249083602ac5fcdb1e571aed","modified":1490029954000},{"_id":"themes/landscape/source/css/fonts/FontAwesome.otf","hash":"b5b4f9be85f91f10799e87a083da1d050f842734","modified":1490029954000},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.eot","hash":"7619748fe34c64fb157a57f6d4ef3678f63a8f5e","modified":1490029954000},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.woff","hash":"04c3bf56d87a08289
 
35bd6b4aee859995f321693","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/sidebar-aside.styl","hash":"890349df5145abf46ce7712010c89237900b3713","modified":1490029954000},{"_id":"themes/landscape/source/css/_partial/sidebar.styl","hash":"404ec059dc674a48b9ab89cd83f258dec4dcb24d","modified":1490029954000},{"_id":"themes/landscape/source/css/_util/mixin.styl","hash":"44f32767d9fd3c1c08a60d91f181ee53c8f0dbb3","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/fancybox_buttons.png","hash":"e385b139516c6813dcd64b8fc431c364ceafe5f3","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-media.js","hash":"294420f9ff20f4e3584d212b0c262a00a96ecdb3","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.css","hash":"4ac329c16a5277592fc12a37cca3d72ca4ec292f","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-thumbs.js","hash":"47da1
 
ae5401c24b5c17cc18e2730780f5c1a7a0c","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.css","hash":"1a9d8e5c22b371fcc69d4dbbb823d9c39f04c0c8","modified":1490029954000},{"_id":"themes/landscape/source/fancybox/helpers/jquery.fancybox-buttons.js","hash":"dc3645529a4bf72983a39fa34c1eb9146e082019","modified":1490029954000},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.ttf","hash":"7f09c97f333917034ad08fa7295e916c9f72fd3f","modified":1490029954000},{"_id":"themes/landscape/source/css/fonts/fontawesome-webfont.svg","hash":"46fcc0194d75a0ddac0a038aee41b23456784814","modified":1490029954000},{"_id":"themes/landscape/source/css/images/banner.jpg","hash":"f44aa591089fcb3ec79770a1e102fd3289a7c6a6","modified":1490029954000}],"Category":[],"Data":[],"Page":[],"Post":[{"title":"hello","date":"2017-03-20T17:20:14.000Z","_content":"","source":"_posts/hello.md","raw":"---\ntitle:
 hello\ndate: 2017-03-20 10:20:14\ntags:\n---\n","slug":"
 
hello","published":1,"updated":"2017-03-20T17:20:14.000Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cj0ief9mp0000i3pozs4qmmyx","content":"","excerpt":"","more":""},{"title":"Hello
 World","_content":"Welcome to [Hexo](https://hexo.io/)! This is your very 
first post. Check [documentation](https://hexo.io/docs/) for more info. If you 
get any problems when using Hexo, you can find the answer in 
[troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me 
on [GitHub](https://github.com/hexojs/hexo/issues).\n\n## Quick Start\n\n### 
Create a new post\n\n``` bash\n$ hexo new \"My New Post\"\n```\n\nMore info: 
[Writing](https://hexo.io/docs/writing.html)\n\n### Run server\n\n``` bash\n$ 
hexo server\n```\n\nMore info: 
[Server](https://hexo.io/docs/server.html)\n\n### Generate static files\n\n``` 
bash\n$ hexo generate\n```\n\nMore info: 
[Generating](https://hexo.io/docs/generating.html)\n\n### Deploy to remote 
sites\n\n``` bash\n$ hexo deploy\n```\n\nMore info
 : 
[Deployment](https://hexo.io/docs/deployment.html)\n","source":"_posts/hello-world.md","raw":"---\ntitle:
 Hello World\n---\nWelcome to [Hexo](https://hexo.io/)! This is your very first 
post. Check [documentation](https://hexo.io/docs/) for more info. If you get 
any problems when using Hexo, you can find the answer in 
[troubleshooting](https://hexo.io/docs/troubleshooting.html) or you can ask me 
on [GitHub](https://github.com/hexojs/hexo/issues).\n\n## Quick Start\n\n### 
Create a new post\n\n``` bash\n$ hexo new \"My New Post\"\n```\n\nMore info: 
[Writing](https://hexo.io/docs/writing.html)\n\n### Run server\n\n``` bash\n$ 
hexo server\n```\n\nMore info: 
[Server](https://hexo.io/docs/server.html)\n\n### Generate static files\n\n``` 
bash\n$ hexo generate\n```\n\nMore info: 
[Generating](https://hexo.io/docs/generating.html)\n\n### Deploy to remote 
sites\n\n``` bash\n$ hexo deploy\n```\n\nMore info: 
[Deployment](https://hexo.io/docs/deployment.html)\n","slug":"hello-world","published":
 
1,"date":"2017-03-20T17:12:26.000Z","updated":"2017-03-20T17:12:26.000Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cj0ief9mt0001i3po2si4b8t3","content":"<p>Welcome
 to <a href=\"https://hexo.io/\"; target=\"_blank\" rel=\"external\">Hexo</a>! 
This is your very first post. Check <a href=\"https://hexo.io/docs/\"; 
target=\"_blank\" rel=\"external\">documentation</a> for more info. If you get 
any problems when using Hexo, you can find the answer in <a 
href=\"https://hexo.io/docs/troubleshooting.html\"; target=\"_blank\" 
rel=\"external\">troubleshooting</a> or you can ask me on <a 
href=\"https://github.com/hexojs/hexo/issues\"; target=\"_blank\" 
rel=\"external\">GitHub</a>.</p>\n<h2 id=\"Quick-Start\"><a 
href=\"#Quick-Start\" class=\"headerlink\" title=\"Quick Start\"></a>Quick 
Start</h2><h3 id=\"Create-a-new-post\"><a href=\"#Create-a-new-post\" 
class=\"headerlink\" title=\"Create a new post\"></a>Create a new 
post</h3><figure class=\"highlight bash\"><table><tr><td class=\"
 gutter\"><pre><div class=\"line\">1</div></pre></td><td 
class=\"code\"><pre><div class=\"line\">$ hexo new <span class=\"string\">\"My 
New Post\"</span></div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/writing.html\"; target=\"_blank\" 
rel=\"external\">Writing</a></p>\n<h3 id=\"Run-server\"><a href=\"#Run-server\" 
class=\"headerlink\" title=\"Run server\"></a>Run server</h3><figure 
class=\"highlight bash\"><table><tr><td class=\"gutter\"><pre><div 
class=\"line\">1</div></pre></td><td class=\"code\"><pre><div class=\"line\">$ 
hexo server</div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/server.html\"; target=\"_blank\" 
rel=\"external\">Server</a></p>\n<h3 id=\"Generate-static-files\"><a 
href=\"#Generate-static-files\" class=\"headerlink\" title=\"Generate static 
files\"></a>Generate static files</h3><figure class=\"highlight 
bash\"><table><tr><td class=\"gutter\"><pre><div 
class=\"line\">1</div></pre></td><td class=
 \"code\"><pre><div class=\"line\">$ hexo 
generate</div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/generating.html\"; target=\"_blank\" 
rel=\"external\">Generating</a></p>\n<h3 id=\"Deploy-to-remote-sites\"><a 
href=\"#Deploy-to-remote-sites\" class=\"headerlink\" title=\"Deploy to remote 
sites\"></a>Deploy to remote sites</h3><figure class=\"highlight 
bash\"><table><tr><td class=\"gutter\"><pre><div 
class=\"line\">1</div></pre></td><td class=\"code\"><pre><div class=\"line\">$ 
hexo deploy</div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/deployment.html\"; target=\"_blank\" 
rel=\"external\">Deployment</a></p>\n","excerpt":"","more":"<p>Welcome to <a 
href=\"https://hexo.io/\";>Hexo</a>! This is your very first post. Check <a 
href=\"https://hexo.io/docs/\";>documentation</a> for more info. If you get any 
problems when using Hexo, you can find the answer in <a 
href=\"https://hexo.io/docs/troubleshooting.html\";>troublesh
 ooting</a> or you can ask me on <a 
href=\"https://github.com/hexojs/hexo/issues\";>GitHub</a>.</p>\n<h2 
id=\"Quick-Start\"><a href=\"#Quick-Start\" class=\"headerlink\" title=\"Quick 
Start\"></a>Quick Start</h2><h3 id=\"Create-a-new-post\"><a 
href=\"#Create-a-new-post\" class=\"headerlink\" title=\"Create a new 
post\"></a>Create a new post</h3><figure class=\"highlight 
bash\"><table><tr><td class=\"gutter\"><pre><div 
class=\"line\">1</div></pre></td><td class=\"code\"><pre><div class=\"line\">$ 
hexo new <span class=\"string\">\"My New 
Post\"</span></div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/writing.html\";>Writing</a></p>\n<h3 
id=\"Run-server\"><a href=\"#Run-server\" class=\"headerlink\" title=\"Run 
server\"></a>Run server</h3><figure class=\"highlight bash\"><table><tr><td 
class=\"gutter\"><pre><div class=\"line\">1</div></pre></td><td 
class=\"code\"><pre><div class=\"line\">$ hexo 
server</div></pre></td></tr></table></figure>\n<p>More info:
  <a href=\"https://hexo.io/docs/server.html\";>Server</a></p>\n<h3 
id=\"Generate-static-files\"><a href=\"#Generate-static-files\" 
class=\"headerlink\" title=\"Generate static files\"></a>Generate static 
files</h3><figure class=\"highlight bash\"><table><tr><td 
class=\"gutter\"><pre><div class=\"line\">1</div></pre></td><td 
class=\"code\"><pre><div class=\"line\">$ hexo 
generate</div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/generating.html\";>Generating</a></p>\n<h3 
id=\"Deploy-to-remote-sites\"><a href=\"#Deploy-to-remote-sites\" 
class=\"headerlink\" title=\"Deploy to remote sites\"></a>Deploy to remote 
sites</h3><figure class=\"highlight bash\"><table><tr><td 
class=\"gutter\"><pre><div class=\"line\">1</div></pre></td><td 
class=\"code\"><pre><div class=\"line\">$ hexo 
deploy</div></pre></td></tr></table></figure>\n<p>More info: <a 
href=\"https://hexo.io/docs/deployment.html\";>Deployment</a></p>\n"}],"PostAsset":[],"PostCategory":[],"PostTag":[]
 ,"Tag":[]}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/.bin/JSONStream
----------------------------------------------------------------------
diff --git a/node_modules/.bin/JSONStream b/node_modules/.bin/JSONStream
new file mode 100755
index 0000000..6d68a19
--- /dev/null
+++ b/node_modules/.bin/JSONStream
@@ -0,0 +1,253 @@
+#! /usr/bin/env node
+
+'use strict'
+
+var Parser = require('jsonparse')
+  , through = require('through')
+
+/*
+
+  the value of this.stack that creationix's jsonparse has is weird.
+
+  it makes this code ugly, but his problem is way harder that mine,
+  so i'll forgive him.
+
+*/
+
+exports.parse = function (path, map) {
+  var header, footer
+  var parser = new Parser()
+  var stream = through(function (chunk) {
+    if('string' === typeof chunk)
+      chunk = new Buffer(chunk)
+    parser.write(chunk)
+  },
+  function (data) {
+    if(data)
+      stream.write(data)
+    if (header)
+        stream.emit('header', header)
+    if (footer)
+      stream.emit('footer', footer)
+    stream.queue(null)
+  })
+
+  if('string' === typeof path)
+    path = path.split('.').map(function (e) {
+      if (e === '$*')
+        return {emitKey: true}
+      else if (e === '*')
+        return true
+      else if (e === '') // '..'.split('.') returns an empty string
+        return {recurse: true}
+      else
+        return e
+    })
+
+
+  var count = 0, _key
+  if(!path || !path.length)
+    path = null
+
+  parser.onValue = function (value) {
+    if (!this.root)
+      stream.root = value
+
+    if(! path) return
+
+    var i = 0 // iterates on path
+    var j  = 0 // iterates on stack
+    var emitKey = false;
+    var emitPath = false;
+    while (i < path.length) {
+      var key = path[i]
+      var c
+      j++
+
+      if (key && !key.recurse) {
+        c = (j === this.stack.length) ? this : this.stack[j]
+        if (!c) return
+        if (! check(key, c.key)) {
+          setHeaderFooter(c.key, value)
+          return
+        }
+        emitKey = !!key.emitKey;
+        emitPath = !!key.emitPath;
+        i++
+      } else {
+        i++
+        var nextKey = path[i]
+        if (! nextKey) return
+        while (true) {
+          c = (j === this.stack.length) ? this : this.stack[j]
+          if (!c) return
+          if (check(nextKey, c.key)) {
+            i++;
+            if (!Object.isFrozen(this.stack[j]))
+              this.stack[j].value = null
+            break
+          } else {
+            setHeaderFooter(c.key, value)
+          }
+          j++
+        }
+      }
+
+    }
+
+    // emit header
+    if (header) {
+      stream.emit('header', header);
+      header = false;
+    }
+    if (j !== this.stack.length) return
+
+    count ++
+    var actualPath = this.stack.slice(1).map(function(element) { return 
element.key }).concat([this.key])
+    var data = this.value[this.key]
+    if(null != data)
+      if(null != (data = map ? map(data, actualPath) : data)) {
+        if (emitKey || emitPath) {
+          data = { value: data };
+          if (emitKey)
+            data["key"] = this.key;
+          if (emitPath)
+            data["path"] = actualPath;
+        }
+
+        stream.queue(data)
+      }
+    delete this.value[this.key]
+    for(var k in this.stack)
+      if (!Object.isFrozen(this.stack[k]))
+        this.stack[k].value = null
+  }
+  parser._onToken = parser.onToken;
+
+  parser.onToken = function (token, value) {
+    parser._onToken(token, value);
+    if (this.stack.length === 0) {
+      if (stream.root) {
+        if(!path)
+          stream.queue(stream.root)
+        count = 0;
+        stream.root = null;
+      }
+    }
+  }
+
+  parser.onError = function (err) {
+    if(err.message.indexOf("at position") > -1)
+      err.message = "Invalid JSON (" + err.message + ")";
+    stream.emit('error', err)
+  }
+
+  return stream
+
+  function setHeaderFooter(key, value) {
+    // header has not been emitted yet
+    if (header !== false) {
+      header = header || {}
+      header[key] = value
+    }
+
+    // footer has not been emitted yet but header has
+    if (footer !== false && header === false) {
+      footer = footer || {}
+      footer[key] = value
+    }
+  }
+}
+
+function check (x, y) {
+  if ('string' === typeof x)
+    return y == x
+  else if (x && 'function' === typeof x.exec)
+    return x.exec(y)
+  else if ('boolean' === typeof x || 'object' === typeof x)
+    return x
+  else if ('function' === typeof x)
+    return x(y)
+  return false
+}
+
+exports.stringify = function (op, sep, cl, indent) {
+  indent = indent || 0
+  if (op === false){
+    op = ''
+    sep = '\n'
+    cl = ''
+  } else if (op == null) {
+
+    op = '[\n'
+    sep = '\n,\n'
+    cl = '\n]\n'
+
+  }
+
+  //else, what ever you like
+
+  var stream
+    , first = true
+    , anyData = false
+  stream = through(function (data) {
+    anyData = true
+    try {
+      var json = JSON.stringify(data, null, indent)
+    } catch (err) {
+      return stream.emit('error', err)
+    }
+    if(first) { first = false ; stream.queue(op + json)}
+    else stream.queue(sep + json)
+  },
+  function (data) {
+    if(!anyData)
+      stream.queue(op)
+    stream.queue(cl)
+    stream.queue(null)
+  })
+
+  return stream
+}
+
+exports.stringifyObject = function (op, sep, cl, indent) {
+  indent = indent || 0
+  if (op === false){
+    op = ''
+    sep = '\n'
+    cl = ''
+  } else if (op == null) {
+
+    op = '{\n'
+    sep = '\n,\n'
+    cl = '\n}\n'
+
+  }
+
+  //else, what ever you like
+
+  var first = true
+  var anyData = false
+  var stream = through(function (data) {
+    anyData = true
+    var json = JSON.stringify(data[0]) + ':' + JSON.stringify(data[1], null, 
indent)
+    if(first) { first = false ; this.queue(op + json)}
+    else this.queue(sep + json)
+  },
+  function (data) {
+    if(!anyData) this.queue(op)
+    this.queue(cl)
+
+    this.queue(null)
+  })
+
+  return stream
+}
+
+if(!module.parent && process.title !== 'browser') {
+  process.stdin
+    .pipe(exports.parse(process.argv[2]))
+    .pipe(exports.stringify('[', ',\n', ']\n', 2))
+    .pipe(process.stdout)
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/.bin/acorn
----------------------------------------------------------------------
diff --git a/node_modules/.bin/acorn b/node_modules/.bin/acorn
new file mode 100755
index 0000000..db07909
--- /dev/null
+++ b/node_modules/.bin/acorn
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+"use strict";
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return 
obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if 
(Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } 
newObj["default"] = obj; return newObj; } }
+
+var _path = require("path");
+
+var _fs = require("fs");
+
+var _distAcornJs = require("../dist/acorn.js");
+
+var acorn = _interopRequireWildcard(_distAcornJs);
+
+var infile = undefined,
+    forceFile = undefined,
+    silent = false,
+    compact = false,
+    tokenize = false;
+var options = {};
+
+function help(status) {
+  var print = status == 0 ? console.log : console.error;
+  print("usage: " + (0, _path.basename)(process.argv[1]) + " 
[--ecma3|--ecma5|--ecma6]");
+  print("        [--tokenize] [--locations] [---allow-hash-bang] [--compact] 
[--silent] [--module] [--help] [--] [infile]");
+  process.exit(status);
+}
+
+for (var i = 2; i < process.argv.length; ++i) {
+  var arg = process.argv[i];
+  if ((arg == "-" || arg[0] != "-") && !infile) infile = arg;else if (arg == 
"--" && !infile && i + 2 == process.argv.length) forceFile = infile = 
process.argv[++i];else if (arg == "--ecma3") options.ecmaVersion = 3;else if 
(arg == "--ecma5") options.ecmaVersion = 5;else if (arg == "--ecma6") 
options.ecmaVersion = 6;else if (arg == "--locations") options.locations = 
true;else if (arg == "--allow-hash-bang") options.allowHashBang = true;else if 
(arg == "--silent") silent = true;else if (arg == "--compact") compact = 
true;else if (arg == "--help") help(0);else if (arg == "--tokenize") tokenize = 
true;else if (arg == "--module") options.sourceType = 'module';else help(1);
+}
+
+function run(code) {
+  var result = undefined;
+  if (!tokenize) {
+    try {
+      result = acorn.parse(code, options);
+    } catch (e) {
+      console.error(e.message);process.exit(1);
+    }
+  } else {
+    result = [];
+    var tokenizer = acorn.tokenizer(code, options),
+        token = undefined;
+    while (true) {
+      try {
+        token = tokenizer.getToken();
+      } catch (e) {
+        console.error(e.message);process.exit(1);
+      }
+      result.push(token);
+      if (token.type == acorn.tokTypes.eof) break;
+    }
+  }
+  if (!silent) console.log(JSON.stringify(result, null, compact ? null : 2));
+}
+
+if (forceFile || infile && infile != "-") {
+  run((0, _fs.readFileSync)(infile, "utf8"));
+} else {
+  (function () {
+    var code = "";
+    process.stdin.resume();
+    process.stdin.on("data", function (chunk) {
+      return code += chunk;
+    });
+    process.stdin.on("end", function () {
+      return run(code);
+    });
+  })();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/.bin/bunyan
----------------------------------------------------------------------
diff --git a/node_modules/.bin/bunyan b/node_modules/.bin/bunyan
new file mode 100755
index 0000000..73cf458
--- /dev/null
+++ b/node_modules/.bin/bunyan
@@ -0,0 +1,1665 @@
+#!/usr/bin/env node
+/**
+ * Copyright 2016 Trent Mick
+ * Copyright 2016 Joyent Inc.
+ *
+ * bunyan -- filter and pretty-print Bunyan log files (line-delimited JSON)
+ *
+ * See <https://github.com/trentm/node-bunyan>.
+ *
+ * -*- mode: js -*-
+ * vim: expandtab:ts=4:sw=4
+ */
+
+var VERSION = '1.8.8';
+
+var p = console.log;
+var util = require('util');
+var pathlib = require('path');
+var vm = require('vm');
+var http = require('http');
+var fs = require('fs');
+var warn = console.warn;
+var child_process = require('child_process'),
+    spawn = child_process.spawn,
+    exec = child_process.exec,
+    execFile = child_process.execFile;
+var assert = require('assert');
+
+try {
+    var moment = require('moment');
+} catch (e) {
+    moment = null;
+}
+
+
+//---- globals and constants
+
+var nodeVer = process.versions.node.split('.').map(Number);
+var nodeSpawnSupportsStdio = (nodeVer[0] > 0 || nodeVer[1] >= 8);
+
+// Internal debug logging via `console.warn`.
+var _DEBUG = false;
+
+// Output modes.
+var OM_LONG = 1;
+var OM_JSON = 2;
+var OM_INSPECT = 3;
+var OM_SIMPLE = 4;
+var OM_SHORT = 5;
+var OM_BUNYAN = 6;
+var OM_FROM_NAME = {
+    'long': OM_LONG,
+    'paul': OM_LONG,  /* backward compat */
+    'json': OM_JSON,
+    'inspect': OM_INSPECT,
+    'simple': OM_SIMPLE,
+    'short': OM_SHORT,
+    'bunyan': OM_BUNYAN
+};
+
+
+// Levels
+var TRACE = 10;
+var DEBUG = 20;
+var INFO = 30;
+var WARN = 40;
+var ERROR = 50;
+var FATAL = 60;
+
+var levelFromName = {
+    'trace': TRACE,
+    'debug': DEBUG,
+    'info': INFO,
+    'warn': WARN,
+    'error': ERROR,
+    'fatal': FATAL
+};
+var nameFromLevel = {};
+var upperNameFromLevel = {};
+var upperPaddedNameFromLevel = {};
+Object.keys(levelFromName).forEach(function (name) {
+    var lvl = levelFromName[name];
+    nameFromLevel[lvl] = name;
+    upperNameFromLevel[lvl] = name.toUpperCase();
+    upperPaddedNameFromLevel[lvl] = (
+        name.length === 4 ? ' ' : '') + name.toUpperCase();
+});
+
+
+// Display time formats.
+var TIME_UTC = 1;  // the default, bunyan's native format
+var TIME_LOCAL = 2;
+
+// Timezone formats: output format -> momentjs format string
+var TIMEZONE_UTC_FORMATS = {
+    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSS[Z][]]',
+    short: 'HH:mm:ss.SSS[Z]'
+};
+var TIMEZONE_LOCAL_FORMATS = {
+    long:  '[[]YYYY-MM-DD[T]HH:mm:ss.SSSZ[]]',
+    short: 'HH:mm:ss.SSS'
+};
+
+
+// The current raw input line being processed. Used for `uncaughtException`.
+var currLine = null;
+
+// Child dtrace process, if any. Used for signal-handling.
+var child = null;
+
+// Whether ANSI codes are being used. Used for signal-handling.
+var usingAnsiCodes = false;
+
+// Used to tell the 'uncaughtException' handler that '-c CODE' is being used.
+var gUsingConditionOpts = false;
+
+// Pager child process, and output stream to which to write.
+var pager = null;
+var stdout = process.stdout;
+
+// Whether we are reading from stdin.
+var readingStdin = false;
+
+
+
+//---- support functions
+
+function getVersion() {
+    return VERSION;
+}
+
+
+var format = util.format;
+if (!format) {
+    /* BEGIN JSSTYLED */
+    // If not node 0.6, then use its `util.format`:
+    // <https://github.com/joyent/node/blob/master/lib/util.js#L22>:
+    var inspect = util.inspect;
+    var formatRegExp = /%[sdj%]/g;
+    format = function format(f) {
+        if (typeof f !== 'string') {
+            var objects = [];
+            for (var i = 0; i < arguments.length; i++) {
+                objects.push(inspect(arguments[i]));
+            }
+            return objects.join(' ');
+        }
+
+        var i = 1;
+        var args = arguments;
+        var len = args.length;
+        var str = String(f).replace(formatRegExp, function (x) {
+            if (i >= len)
+                return x;
+            switch (x) {
+                case '%s': return String(args[i++]);
+                case '%d': return Number(args[i++]);
+                case '%j': return JSON.stringify(args[i++]);
+                case '%%': return '%';
+                default:
+                    return x;
+            }
+        });
+        for (var x = args[i]; i < len; x = args[++i]) {
+            if (x === null || typeof x !== 'object') {
+                str += ' ' + x;
+            } else {
+                str += ' ' + inspect(x);
+            }
+        }
+        return str;
+    };
+    /* END JSSTYLED */
+}
+
+function indent(s) {
+    return '    ' + s.split(/\r?\n/).join('\n    ');
+}
+
+function objCopy(obj) {
+    if (obj === null) {
+        return null;
+    } else if (Array.isArray(obj)) {
+        return obj.slice();
+    } else {
+        var copy = {};
+        Object.keys(obj).forEach(function (k) {
+            copy[k] = obj[k];
+        });
+        return copy;
+    }
+}
+
+function printHelp() {
+    /* BEGIN JSSTYLED */
+    p('Usage:');
+    p('  bunyan [OPTIONS] [FILE ...]');
+    p('  ... | bunyan [OPTIONS]');
+    p('  bunyan [OPTIONS] -p PID');
+    p('');
+    p('Filter and pretty-print Bunyan log file content.');
+    p('');
+    p('General options:');
+    p('  -h, --help    print this help info and exit');
+    p('  --version     print version of this command and exit');
+    p('');
+    p('Runtime log snooping (via DTrace, only on supported platforms):');
+    p('  -p PID        Process bunyan:log-* probes from the process');
+    p('                with the given PID. Can be used multiple times,');
+    p('                or specify all processes with "*", or a set of');
+    p('                processes whose command & args match a pattern');
+    p('                with "-p NAME".');
+    p('');
+    p('Filtering options:');
+    p('  -l, --level LEVEL');
+    p('                Only show messages at or above the specified level.');
+    p('                You can specify level *names* or the internal numeric');
+    p('                values.');
+    p('  -c, --condition CONDITION');
+    p('                Run each log message through the condition and');
+    p('                only show those that return truish. E.g.:');
+    p('                    -c \'this.pid == 123\'');
+    p('                    -c \'this.level == DEBUG\'');
+    p('                    -c \'this.msg.indexOf("boom") != -1\'');
+    p('                "CONDITION" must be legal JS code. `this` holds');
+    p('                the log record. The TRACE, DEBUG, ... FATAL values');
+    p('                are defined to help with comparing `this.level`.');
+    p('  --strict      Suppress all but legal Bunyan JSON log lines. By 
default');
+    p('                non-JSON, and non-Bunyan lines are passed through.');
+    p('');
+    p('Output options:');
+    p('  --pager       Pipe output into `less` (or $PAGER if set), if');
+    p('                stdout is a TTY. This overrides $BUNYAN_NO_PAGER.');
+    p('                Note: Paging is only supported on node >=0.8.');
+    p('  --no-pager    Do not pipe output into a pager.');
+    p('  --color       Colorize output. Defaults to try if output');
+    p('                stream is a TTY.');
+    p('  --no-color    Force no coloring (e.g. terminal doesn\'t support it)');
+    p('  -o, --output MODE');
+    p('                Specify an output mode/format. One of');
+    p('                  long: (the default) pretty');
+    p('                  json: JSON output, 2-space indent');
+    p('                  json-N: JSON output, N-space indent, e.g. "json-4"');
+    p('                  bunyan: 0 indented JSON, bunyan\'s native format');
+    p('                  inspect: node.js `util.inspect` output');
+    p('                  short: like "long", but more concise');
+    p('                  simple: level, followed by "-" and then the message');
+    p('  -j            shortcut for `-o json`');
+    p('  -0            shortcut for `-o bunyan`');
+    p('  -L, --time local');
+    p('                Display time field in local time, rather than UTC.');
+    p('');
+    p('Environment Variables:');
+    p('  BUNYAN_NO_COLOR    Set to a non-empty value to force no output ');
+    p('                     coloring. See "--no-color".');
+    p('  BUNYAN_NO_PAGER    Disable piping output to a pager. ');
+    p('                     See "--no-pager".');
+    p('');
+    p('See <https://github.com/trentm/node-bunyan> for more complete docs.');
+    p('Please report bugs to <https://github.com/trentm/node-bunyan/issues>.');
+    /* END JSSTYLED */
+}
+
+/*
+ * If the user specifies multiple input sources, we want to print out records
+ * from all sources in a single, chronologically ordered stream.  To do this
+ * efficiently, we first assume that all records within each source are ordered
+ * already, so we need only keep track of the next record in each source and
+ * the time of the last record emitted.  To avoid excess memory usage, we
+ * pause() streams that are ahead of others.
+ *
+ * 'streams' is an object indexed by source name (file name) which specifies:
+ *
+ *    stream        Actual stream object, so that we can pause and resume it.
+ *
+ *    records       Array of log records we've read, but not yet emitted.  Each
+ *                  record includes 'line' (the raw line), 'rec' (the JSON
+ *                  record), and 'time' (the parsed time value).
+ *
+ *    done          Whether the stream has any more records to emit.
+ */
+var streams = {};
+
+function gotRecord(file, line, rec, opts, stylize)
+{
+    var time = new Date(rec.time);
+
+    streams[file]['records'].push({ line: line, rec: rec, time: time });
+    emitNextRecord(opts, stylize);
+}
+
+function filterRecord(rec, opts)
+{
+    if (opts.level && rec.level < opts.level) {
+        return false;
+    }
+
+    if (opts.condFuncs) {
+        var recCopy = objCopy(rec);
+        for (var i = 0; i < opts.condFuncs.length; i++) {
+            var pass = opts.condFuncs[i].call(recCopy);
+            if (!pass)
+                return false;
+        }
+    } else if (opts.condVm) {
+        for (var i = 0; i < opts.condVm.length; i++) {
+            var pass = opts.condVm[i].runInNewContext(rec);
+            if (!pass)
+                return false;
+        }
+    }
+
+    return true;
+}
+
+function emitNextRecord(opts, stylize)
+{
+    var ofile, ready, minfile, rec;
+
+    for (;;) {
+        /*
+         * Take a first pass through the input streams to see if we have a
+         * record from all of them.  If not, we'll pause any streams for
+         * which we do already have a record (to avoid consuming excess
+         * memory) and then wait until we have records from the others
+         * before emitting the next record.
+         *
+         * As part of the same pass, we look for the earliest record
+         * we have not yet emitted.
+         */
+        minfile = undefined;
+        ready = true;
+        for (ofile in streams) {
+
+            if (streams[ofile].stream === null ||
+                (!streams[ofile].done && streams[ofile].records.length === 0)) 
{
+                ready = false;
+                break;
+            }
+
+            if (streams[ofile].records.length > 0 &&
+                (minfile === undefined ||
+                    streams[minfile].records[0].time >
+                        streams[ofile].records[0].time)) {
+                minfile = ofile;
+            }
+        }
+
+        if (!ready || minfile === undefined) {
+            for (ofile in streams) {
+                if (!streams[ofile].stream || streams[ofile].done)
+                    continue;
+
+                if (streams[ofile].records.length > 0) {
+                    if (!streams[ofile].paused) {
+                        streams[ofile].paused = true;
+                        streams[ofile].stream.pause();
+                    }
+                } else if (streams[ofile].paused) {
+                    streams[ofile].paused = false;
+                    streams[ofile].stream.resume();
+                }
+            }
+
+            return;
+        }
+
+        /*
+         * Emit the next record for 'minfile', and invoke ourselves again to
+         * make sure we emit as many records as we can right now.
+         */
+        rec = streams[minfile].records.shift();
+        emitRecord(rec.rec, rec.line, opts, stylize);
+    }
+}
+
+/**
+ * Return a function for the given JS code that returns.
+ *
+ * If no 'return' in the given javascript snippet, then assume we are a single
+ * statement and wrap in 'return (...)'. This is for convenience for short
+ * '-c ...' snippets.
+ */
+function funcWithReturnFromSnippet(js) {
+    // auto-"return"
+    if (js.indexOf('return') === -1) {
+        if (js.substring(js.length - 1) === ';') {
+            js = js.substring(0, js.length - 1);
+        }
+        js = 'return (' + js + ')';
+    }
+
+    // Expose level definitions to condition func context
+    var varDefs = [];
+    Object.keys(upperNameFromLevel).forEach(function (lvl) {
+        varDefs.push(format('var %s = %d;',
+                upperNameFromLevel[lvl], lvl));
+    });
+    varDefs = varDefs.join('\n') + '\n';
+
+    return (new Function(varDefs + js));
+}
+
+/**
+ * Parse the command-line options and arguments into an object.
+ *
+ *    {
+ *      'args': [...]       // arguments
+ *      'help': true,       // true if '-h' option given
+ *       // etc.
+ *    }
+ *
+ * @return {Object} The parsed options. `.args` is the argument list.
+ * @throws {Error} If there is an error parsing argv.
+ */
+function parseArgv(argv) {
+    var parsed = {
+        args: [],
+        help: false,
+        color: null,
+        paginate: null,
+        outputMode: OM_LONG,
+        jsonIndent: 2,
+        level: null,
+        strict: false,
+        pids: null,
+        pidsType: null,
+        timeFormat: TIME_UTC  // one of the TIME_ constants
+    };
+
+    // Turn '-iH' into '-i -H', except for argument-accepting options.
+    var args = argv.slice(2);  // drop ['node', 'scriptname']
+    var newArgs = [];
+    var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true, 'p': true};
+    for (var i = 0; i < args.length; i++) {
+        if (args[i].charAt(0) === '-' && args[i].charAt(1) !== '-' &&
+            args[i].length > 2)
+        {
+            var splitOpts = args[i].slice(1).split('');
+            for (var j = 0; j < splitOpts.length; j++) {
+                newArgs.push('-' + splitOpts[j]);
+                if (optTakesArg[splitOpts[j]]) {
+                    var optArg = splitOpts.slice(j+1).join('');
+                    if (optArg.length) {
+                        newArgs.push(optArg);
+                    }
+                    break;
+                }
+            }
+        } else {
+            newArgs.push(args[i]);
+        }
+    }
+    args = newArgs;
+
+    // Expose level definitions to condition vm context
+    var condDefines = [];
+    Object.keys(upperNameFromLevel).forEach(function (lvl) {
+        condDefines.push(
+            format('Object.prototype.%s = %s;', upperNameFromLevel[lvl], lvl));
+    });
+    condDefines = condDefines.join('\n') + '\n';
+
+    var endOfOptions = false;
+    while (args.length > 0) {
+        var arg = args.shift();
+        switch (arg) {
+            case '--':
+                endOfOptions = true;
+                break;
+            case '-h': // display help and exit
+            case '--help':
+                parsed.help = true;
+                break;
+            case '--version':
+                parsed.version = true;
+                break;
+            case '--strict':
+                parsed.strict = true;
+                break;
+            case '--color':
+                parsed.color = true;
+                break;
+            case '--no-color':
+                parsed.color = false;
+                break;
+            case '--pager':
+                parsed.paginate = true;
+                break;
+            case '--no-pager':
+                parsed.paginate = false;
+                break;
+            case '-o':
+            case '--output':
+                var name = args.shift();
+                var idx = name.lastIndexOf('-');
+                if (idx !== -1) {
+                    var indentation = Number(name.slice(idx+1));
+                    if (! isNaN(indentation)) {
+                        parsed.jsonIndent = indentation;
+                        name = name.slice(0, idx);
+                    }
+                }
+                parsed.outputMode = OM_FROM_NAME[name];
+                if (parsed.outputMode === undefined) {
+                    throw new Error('unknown output mode: "'+name+'"');
+                }
+                break;
+            case '-j': // output with JSON.stringify
+                parsed.outputMode = OM_JSON;
+                break;
+            case '-0':
+                parsed.outputMode = OM_BUNYAN;
+                break;
+            case '-L':
+                parsed.timeFormat = TIME_LOCAL;
+                if (!moment) {
+                    throw new Error(
+                        'could not find moment package required for "-L"');
+                }
+                break;
+            case '--time':
+                var timeArg = args.shift();
+                switch (timeArg) {
+                case 'utc':
+                    parsed.timeFormat = TIME_UTC;
+                    break
+                case 'local':
+                    parsed.timeFormat = TIME_LOCAL;
+                    if (!moment) {
+                        throw new Error('could not find moment package '
+                            + 'required for "--time=local"');
+                    }
+                    break
+                case undefined:
+                    throw new Error('missing argument to "--time"');
+                default:
+                    throw new Error(format('invalid time format: "%s"',
+                        timeArg));
+                }
+                break;
+            case '-p':
+                if (!parsed.pids) {
+                    parsed.pids = [];
+                }
+                var pidArg = args.shift();
+                var pid = +(pidArg);
+                if (!isNaN(pid) || pidArg === '*') {
+                    if (parsed.pidsType && parsed.pidsType !== 'num') {
+                        throw new Error(format('cannot mix PID name and '
+                            + 'number arguments: "%s"', pidArg));
+                    }
+                    parsed.pidsType = 'num';
+                    if (!parsed.pids) {
+                        parsed.pids = [];
+                    }
+                    parsed.pids.push(isNaN(pid) ? pidArg : pid);
+                } else {
+                    if (parsed.pidsType && parsed.pidsType !== 'name') {
+                        throw new Error(format('cannot mix PID name and '
+                            + 'number arguments: "%s"', pidArg));
+                    }
+                    parsed.pidsType = 'name';
+                    parsed.pids = pidArg;
+                }
+                break;
+            case '-l':
+            case '--level':
+                var levelArg = args.shift();
+                var level = +(levelArg);
+                if (isNaN(level)) {
+                    level = +levelFromName[levelArg.toLowerCase()];
+                }
+                if (isNaN(level)) {
+                    throw new Error('unknown level value: "'+levelArg+'"');
+                }
+                parsed.level = level;
+                break;
+            case '-c':
+            case '--condition':
+                gUsingConditionOpts = true;
+                var condition = args.shift();
+                if (Boolean(process.env.BUNYAN_EXEC &&
+                    process.env.BUNYAN_EXEC === 'vm'))
+                {
+                    parsed.condVm = parsed.condVm || [];
+                    var scriptName = 'bunyan-condition-'+parsed.condVm.length;
+                    var code = condDefines + condition;
+                    var script;
+                    try {
+                        script = vm.createScript(code, scriptName);
+                    } catch (complErr) {
+                        throw new Error(format('illegal CONDITION code: %s\n'
+                            + '  CONDITION script:\n'
+                            + '%s\n'
+                            + '  Error:\n'
+                            + '%s',
+                            complErr, indent(code), indent(complErr.stack)));
+                    }
+
+                    // Ensure this is a reasonably safe CONDITION.
+                    try {
+                        script.runInNewContext(minValidRecord);
+                    } catch (condErr) {
+                        throw new Error(format(
+                            /* JSSTYLED */
+                            'CONDITION code cannot safely filter a minimal 
Bunyan log record\n'
+                            + '  CONDITION script:\n'
+                            + '%s\n'
+                            + '  Minimal Bunyan log record:\n'
+                            + '%s\n'
+                            + '  Filter error:\n'
+                            + '%s',
+                            indent(code),
+                            indent(JSON.stringify(minValidRecord, null, 2)),
+                            indent(condErr.stack)
+                            ));
+                    }
+                    parsed.condVm.push(script);
+                } else  {
+                    parsed.condFuncs = parsed.condFuncs || [];
+                    
parsed.condFuncs.push(funcWithReturnFromSnippet(condition));
+                }
+                break;
+            default: // arguments
+                if (!endOfOptions && arg.length > 0 && arg[0] === '-') {
+                    throw new Error('unknown option "'+arg+'"');
+                }
+                parsed.args.push(arg);
+                break;
+        }
+    }
+    //TODO: '--' handling and error on a first arg that looks like an option.
+
+    return parsed;
+}
+
+
+function isInteger(s) {
+    return (s.search(/^-?[0-9]+$/) == 0);
+}
+
+
+// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+// Suggested colors (some are unreadable in common cases):
+// - Good: cyan, yellow (limited use), bold, green, magenta, red
+// - Bad: blue (not visible on cmd.exe), grey (same color as background on
+//   Solarized Dark theme from <https://github.com/altercation/solarized>, see
+//   issue #160)
+var colors = {
+    'bold' : [1, 22],
+    'italic' : [3, 23],
+    'underline' : [4, 24],
+    'inverse' : [7, 27],
+    'white' : [37, 39],
+    'grey' : [90, 39],
+    'black' : [30, 39],
+    'blue' : [34, 39],
+    'cyan' : [36, 39],
+    'green' : [32, 39],
+    'magenta' : [35, 39],
+    'red' : [31, 39],
+    'yellow' : [33, 39]
+};
+
+function stylizeWithColor(str, color) {
+    if (!str)
+        return '';
+    var codes = colors[color];
+    if (codes) {
+        return '\033[' + codes[0] + 'm' + str +
+                     '\033[' + codes[1] + 'm';
+    } else {
+        return str;
+    }
+}
+
+function stylizeWithoutColor(str, color) {
+    return str;
+}
+
+
+/**
+ * Is this a valid Bunyan log record.
+ */
+function isValidRecord(rec) {
+    if (rec.v == null ||
+            rec.level == null ||
+            rec.name == null ||
+            rec.hostname == null ||
+            rec.pid == null ||
+            rec.time == null ||
+            rec.msg == null) {
+        // Not valid Bunyan log.
+        return false;
+    } else {
+        return true;
+    }
+}
+var minValidRecord = {
+    v: 0,   //TODO: get this from bunyan.LOG_VERSION
+    level: INFO,
+    name: 'name',
+    hostname: 'hostname',
+    pid: 123,
+    time: Date.now(),
+    msg: 'msg'
+};
+
+
+/**
+ * Parses the given log line and either emits it right away (for invalid
+ * records) or enqueues it for emitting later when it's the next line to show.
+ */
+function handleLogLine(file, line, opts, stylize) {
+    currLine = line; // intentionally global
+
+    // Emit non-JSON lines immediately.
+    var rec;
+    if (!line) {
+        if (!opts.strict) emit(line + '\n');
+        return;
+    } else if (line[0] !== '{') {
+        if (!opts.strict) emit(line + '\n');  // not JSON
+        return;
+    } else {
+        try {
+            rec = JSON.parse(line);
+        } catch (e) {
+            if (!opts.strict) emit(line + '\n');
+            return;
+        }
+    }
+
+    if (!isValidRecord(rec)) {
+        if (!opts.strict) emit(line + '\n');
+        return;
+    }
+
+    if (!filterRecord(rec, opts))
+        return;
+
+    if (file === null)
+        return emitRecord(rec, line, opts, stylize);
+
+    return gotRecord(file, line, rec, opts, stylize);
+}
+
+/**
+ * Print out a single result, considering input options.
+ */
+function emitRecord(rec, line, opts, stylize) {
+    var short = false;
+
+    switch (opts.outputMode) {
+    case OM_SHORT:
+        short = true;
+        /* jsl:fall-thru */
+
+    case OM_LONG:
+        //    [time] LEVEL: name[/comp]/pid on hostname (src): msg* (extras...)
+        //        msg*
+        //        --
+        //        long and multi-line extras
+        //        ...
+        // If 'msg' is single-line, then it goes in the top line.
+        // If 'req', show the request.
+        // If 'res', show the response.
+        // If 'err' and 'err.stack' then show that.
+        if (!isValidRecord(rec)) {
+            return emit(line + '\n');
+        }
+
+        delete rec.v;
+
+        // Time.
+        var time;
+        if (!short && opts.timeFormat === TIME_UTC) {
+            // Fast default path: We assume the raw `rec.time` is a UTC time
+            // in ISO 8601 format (per spec).
+            time = '[' + rec.time + ']';
+        } else if (!moment && opts.timeFormat === TIME_UTC) {
+            // Don't require momentjs install, as long as not using TIME_LOCAL.
+            time = rec.time.substr(11);
+        } else {
+            var tzFormat;
+            var moTime = moment(rec.time);
+            switch (opts.timeFormat) {
+            case TIME_UTC:
+                tzFormat = TIMEZONE_UTC_FORMATS[short ? 'short' : 'long'];
+                moTime.utc();
+                break;
+            case TIME_LOCAL:
+                tzFormat = TIMEZONE_LOCAL_FORMATS[short ? 'short' : 'long'];
+                break;
+            default:
+                throw new Error('unexpected timeFormat: ' + opts.timeFormat);
+            };
+            time = moTime.format(tzFormat);
+        }
+        time = stylize(time, 'XXX');
+        delete rec.time;
+
+        var nameStr = rec.name;
+        delete rec.name;
+
+        if (rec.component) {
+            nameStr += '/' + rec.component;
+        }
+        delete rec.component;
+
+        if (!short)
+            nameStr += '/' + rec.pid;
+        delete rec.pid;
+
+        var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
+        if (opts.color) {
+            var colorFromLevel = {
+                10: 'white',    // TRACE
+                20: 'yellow',   // DEBUG
+                30: 'cyan',     // INFO
+                40: 'magenta',  // WARN
+                50: 'red',      // ERROR
+                60: 'inverse',  // FATAL
+            };
+            level = stylize(level, colorFromLevel[rec.level]);
+        }
+        delete rec.level;
+
+        var src = '';
+        if (rec.src && rec.src.file) {
+            var s = rec.src;
+            if (s.func) {
+                src = format(' (%s:%d in %s)', s.file, s.line, s.func);
+            } else {
+                src = format(' (%s:%d)', s.file, s.line);
+            }
+            src = stylize(src, 'green');
+        }
+        delete rec.src;
+
+        var hostname = rec.hostname;
+        delete rec.hostname;
+
+        var extras = [];
+        var details = [];
+
+        if (rec.req_id) {
+            extras.push('req_id=' + rec.req_id);
+        }
+        delete rec.req_id;
+
+        var onelineMsg;
+        if (rec.msg.indexOf('\n') !== -1) {
+            onelineMsg = '';
+            details.push(indent(stylize(rec.msg, 'cyan')));
+        } else {
+            onelineMsg = ' ' + stylize(rec.msg, 'cyan');
+        }
+        delete rec.msg;
+
+        if (rec.req && typeof (rec.req) === 'object') {
+            var req = rec.req;
+            delete rec.req;
+            var headers = req.headers;
+            if (!headers) {
+                headers = '';
+            } else if (typeof (headers) === 'string') {
+                headers = '\n' + headers;
+            } else if (typeof (headers) === 'object') {
+                headers = '\n' + Object.keys(headers).map(function (h) {
+                    return h + ': ' + headers[h];
+                }).join('\n');
+            }
+            var s = format('%s %s HTTP/%s%s', req.method,
+                req.url,
+                req.httpVersion || '1.1',
+                headers
+            );
+            delete req.url;
+            delete req.method;
+            delete req.httpVersion;
+            delete req.headers;
+            if (req.body) {
+                s += '\n\n' + (typeof (req.body) === 'object'
+                    ? JSON.stringify(req.body, null, 2) : req.body);
+                delete req.body;
+            }
+            if (req.trailers && Object.keys(req.trailers) > 0) {
+                s += '\n' + Object.keys(req.trailers).map(function (t) {
+                    return t + ': ' + req.trailers[t];
+                }).join('\n');
+            }
+            delete req.trailers;
+            details.push(indent(s));
+            // E.g. for extra 'foo' field on 'req', add 'req.foo' at
+            // top-level. This *does* have the potential to stomp on a
+            // literal 'req.foo' key.
+            Object.keys(req).forEach(function (k) {
+                rec['req.' + k] = req[k];
+            })
+        }
+
+        if (rec.client_req && typeof (rec.client_req) === 'object') {
+            var client_req = rec.client_req;
+            delete rec.client_req;
+            var headers = client_req.headers;
+            var hostHeaderLine = '';
+            var s = '';
+            if (client_req.address) {
+                hostHeaderLine = '\nHost: ' + client_req.address;
+                if (client_req.port)
+                    hostHeaderLine += ':' + client_req.port;
+            }
+            delete client_req.headers;
+            delete client_req.address;
+            delete client_req.port;
+            s += format('%s %s HTTP/%s%s%s', client_req.method,
+                client_req.url,
+                client_req.httpVersion || '1.1',
+                hostHeaderLine,
+                (headers ?
+                    '\n' + Object.keys(headers).map(
+                        function (h) {
+                            return h + ': ' + headers[h];
+                        }).join('\n') :
+                    ''));
+            delete client_req.method;
+            delete client_req.url;
+            delete client_req.httpVersion;
+            if (client_req.body) {
+                s += '\n\n' + (typeof (client_req.body) === 'object' ?
+                    JSON.stringify(client_req.body, null, 2) :
+                    client_req.body);
+                delete client_req.body;
+            }
+            // E.g. for extra 'foo' field on 'client_req', add
+            // 'client_req.foo' at top-level. This *does* have the potential
+            // to stomp on a literal 'client_req.foo' key.
+            Object.keys(client_req).forEach(function (k) {
+                rec['client_req.' + k] = client_req[k];
+            })
+            details.push(indent(s));
+        }
+
+        function _res(res) {
+            var s = '';
+            if (res.statusCode !== undefined) {
+                s += format('HTTP/1.1 %s %s\n', res.statusCode,
+                    http.STATUS_CODES[res.statusCode]);
+                delete res.statusCode;
+            }
+            // Handle `res.header` or `res.headers` as either a string or
+            // and object of header key/value pairs. Prefer `res.header` if set
+            // (TODO: Why? I don't recall. Typical of restify serializer?
+            // Typical JSON.stringify of a core node HttpResponse?)
+            var headerTypes = {string: true, object: true};
+            var headers;
+            if (res.header && headerTypes[typeof (res.header)]) {
+                headers = res.header;
+                delete res.header;
+            } else if (res.headers && headerTypes[typeof (res.headers)]) {
+                headers = res.headers;
+                delete res.headers;
+            }
+            if (headers === undefined) {
+                /* pass through */
+            } else if (typeof (headers) === 'string') {
+                s += headers.trimRight();
+            } else {
+                s += Object.keys(headers).map(
+                    function (h) { return h + ': ' + headers[h]; }).join('\n');
+            }
+            if (res.body !== undefined) {
+                var body = (typeof (res.body) === 'object'
+                    ? JSON.stringify(res.body, null, 2) : res.body);
+                if (body.length > 0) { s += '\n\n' + body };
+                delete res.body;
+            } else {
+                s = s.trimRight();
+            }
+            if (res.trailer) {
+                s += '\n' + res.trailer;
+            }
+            delete res.trailer;
+            if (s) {
+                details.push(indent(s));
+            }
+            // E.g. for extra 'foo' field on 'res', add 'res.foo' at
+            // top-level. This *does* have the potential to stomp on a
+            // literal 'res.foo' key.
+            Object.keys(res).forEach(function (k) {
+                rec['res.' + k] = res[k];
+            });
+        }
+
+        if (rec.res && typeof (rec.res) === 'object') {
+            _res(rec.res);
+            delete rec.res;
+        }
+        if (rec.client_res && typeof (rec.client_res) === 'object') {
+            _res(rec.client_res);
+            delete rec.client_res;
+        }
+
+        if (rec.err && rec.err.stack) {
+            var err = rec.err
+            if (typeof (err.stack) !== 'string') {
+                details.push(indent(err.stack.toString()));
+            } else {
+                details.push(indent(err.stack));
+            }
+            delete err.message;
+            delete err.name;
+            delete err.stack;
+            // E.g. for extra 'foo' field on 'err', add 'err.foo' at
+            // top-level. This *does* have the potential to stomp on a
+            // literal 'err.foo' key.
+            Object.keys(err).forEach(function (k) {
+                rec['err.' + k] = err[k];
+            })
+            delete rec.err;
+        }
+
+        var leftover = Object.keys(rec);
+        for (var i = 0; i < leftover.length; i++) {
+            var key = leftover[i];
+            var value = rec[key];
+            var stringified = false;
+            if (typeof (value) !== 'string') {
+                value = JSON.stringify(value, null, 2);
+                stringified = true;
+            }
+            if (value.indexOf('\n') !== -1 || value.length > 50) {
+                details.push(indent(key + ': ' + value));
+            } else if (!stringified && (value.indexOf(' ') != -1 ||
+                value.length === 0))
+            {
+                extras.push(key + '=' + JSON.stringify(value));
+            } else {
+                extras.push(key + '=' + value);
+            }
+        }
+
+        extras = stylize(
+            (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'XXX');
+        details = stylize(
+            (details.length ? details.join('\n    --\n') + '\n' : ''), 'XXX');
+        if (!short)
+            emit(format('%s %s: %s on %s%s:%s%s\n%s',
+                time,
+                level,
+                nameStr,
+                hostname || '<no-hostname>',
+                src,
+                onelineMsg,
+                extras,
+                details));
+        else
+            emit(format('%s %s %s:%s%s\n%s',
+                time,
+                level,
+                nameStr,
+                onelineMsg,
+                extras,
+                details));
+        break;
+
+    case OM_INSPECT:
+        emit(util.inspect(rec, false, Infinity, true) + '\n');
+        break;
+
+    case OM_BUNYAN:
+        emit(JSON.stringify(rec, null, 0) + '\n');
+        break;
+
+    case OM_JSON:
+        emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n');
+        break;
+
+    case OM_SIMPLE:
+        /* JSSTYLED */
+        // 
<http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html>
+        if (!isValidRecord(rec)) {
+            return emit(line + '\n');
+        }
+        emit(format('%s - %s\n',
+            upperNameFromLevel[rec.level] || 'LVL' + rec.level,
+            rec.msg));
+        break;
+    default:
+        throw new Error('unknown output mode: '+opts.outputMode);
+    }
+}
+
+
+var stdoutFlushed = true;
+function emit(s) {
+    try {
+        stdoutFlushed = stdout.write(s);
+    } catch (e) {
+        // Handle any exceptions in stdout writing in `stdout.on('error', 
...)`.
+    }
+}
+
+
+/**
+ * A hacked up version of 'process.exit' that will first drain stdout
+ * before exiting. *WARNING: This doesn't stop event processing.* IOW,
+ * callers have to be careful that code following this call isn't
+ * accidentally executed.
+ *
+ * In node v0.6 "process.stdout and process.stderr are blocking when they
+ * refer to regular files or TTY file descriptors." However, this hack might
+ * still be necessary in a shell pipeline.
+ */
+function drainStdoutAndExit(code) {
+    if (_DEBUG) warn('(drainStdoutAndExit(%d))', code);
+    stdout.on('drain', function () {
+        cleanupAndExit(code);
+    });
+    if (stdoutFlushed) {
+        cleanupAndExit(code);
+    }
+}
+
+
+/**
+ * Process all input from stdin.
+ *
+ * @params opts {Object} Bunyan options object.
+ * @param stylize {Function} Output stylize function to use.
+ * @param callback {Function} `function ()`
+ */
+function processStdin(opts, stylize, callback) {
+    readingStdin = true;
+    var leftover = '';  // Left-over partial line from last chunk.
+    var stdin = process.stdin;
+    stdin.resume();
+    stdin.setEncoding('utf8');
+    stdin.on('data', function (chunk) {
+        var lines = chunk.split(/\r\n|\n/);
+        var length = lines.length;
+        if (length === 1) {
+            leftover += lines[0];
+            return;
+        }
+
+        if (length > 1) {
+            handleLogLine(null, leftover + lines[0], opts, stylize);
+        }
+        leftover = lines.pop();
+        length -= 1;
+        for (var i = 1; i < length; i++) {
+            handleLogLine(null, lines[i], opts, stylize);
+        }
+    });
+    stdin.on('end', function () {
+        if (leftover) {
+            handleLogLine(null, leftover, opts, stylize);
+            leftover = '';
+        }
+        callback();
+    });
+}
+
+
+/**
+ * Process bunyan:log-* probes from the given pid.
+ *
+ * @params opts {Object} Bunyan options object.
+ * @param stylize {Function} Output stylize function to use.
+ * @param callback {Function} `function (code)`
+ */
+function processPids(opts, stylize, callback) {
+    var leftover = '';  // Left-over partial line from last chunk.
+
+    /**
+     * Get the PIDs to dtrace.
+     *
+     * @param cb {Function} `function (errCode, pids)`
+     */
+    function getPids(cb) {
+        if (opts.pidsType === 'num') {
+            return cb(null, opts.pids);
+        }
+        if (process.platform === 'sunos') {
+            execFile('/bin/pgrep', ['-lf', opts.pids],
+                function (pidsErr, stdout, stderr) {
+                    if (pidsErr) {
+                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
+                            opts.pids, pidsErr.message, stdout, stderr);
+                        return cb(1);
+                    }
+                    var pids = stdout.trim().split('\n')
+                        .map(function (line) {
+                            return line.trim().split(/\s+/)[0]
+                        })
+                        .filter(function (pid) {
+                            return Number(pid) !== process.pid
+                        });
+                    if (pids.length === 0) {
+                        warn('bunyan: error: no matching PIDs found for "%s"',
+                            opts.pids);
+                        return cb(2);
+                    }
+                    cb(null, pids);
+                }
+            );
+        } else {
+            var regex = opts.pids;
+            if (regex && /[a-zA-Z0-9_]/.test(regex[0])) {
+                // 'foo' -> '[f]oo' trick to exclude the 'grep' PID from its
+                // own search.
+                regex = '[' + regex[0] + ']' + regex.slice(1);
+            }
+            exec(format('ps -A -o pid,command | grep \'%s\'', regex),
+                function (pidsErr, stdout, stderr) {
+                    if (pidsErr) {
+                        warn('bunyan: error getting PIDs for "%s": %s\n%s\n%s',
+                            opts.pids, pidsErr.message, stdout, stderr);
+                        return cb(1);
+                    }
+                    var pids = stdout.trim().split('\n')
+                        .map(function (line) {
+                            return line.trim().split(/\s+/)[0];
+                        })
+                        .filter(function (pid) {
+                            return Number(pid) !== process.pid;
+                        });
+                    if (pids.length === 0) {
+                        warn('bunyan: error: no matching PIDs found for "%s"',
+                            opts.pids);
+                        return cb(2);
+                    }
+                    cb(null, pids);
+                }
+            );
+        }
+    }
+
+    getPids(function (errCode, pids) {
+        if (errCode) {
+            return callback(errCode);
+        }
+
+        var probes = pids.map(function (pid) {
+            if (!opts.level)
+                return format('bunyan%s:::log-*', pid);
+
+            var rval = [], l;
+
+            for (l in levelFromName) {
+                if (levelFromName[l] >= opts.level)
+                    rval.push(format('bunyan%s:::log-%s', pid, l));
+            }
+
+            if (rval.length != 0)
+                return rval.join(',');
+
+            warn('bunyan: error: level (%d) exceeds maximum logging level',
+                opts.level);
+            return drainStdoutAndExit(1);
+        }).join(',');
+        var argv = ['dtrace', '-Z', '-x', 'strsize=4k',
+            '-x', 'switchrate=10hz', '-qn',
+            format('%s{printf("%s", copyinstr(arg0))}', probes)];
+        //console.log('dtrace argv: %s', argv);
+        var dtrace = spawn(argv[0], argv.slice(1),
+            // Share the stderr handle to have error output come
+            // straight through. Only supported in v0.8+.
+            {stdio: ['pipe', 'pipe', process.stderr]});
+        dtrace.on('error', function (e) {
+            if (e.syscall === 'spawn' && e.errno === 'ENOENT') {
+                console.error('bunyan: error: could not spawn "dtrace" ' +
+                    '("bunyan -p" is only supported on platforms with 
dtrace)');
+            } else {
+                console.error('bunyan: error: unexpected dtrace error: %s', e);
+            }
+            callback(1);
+        })
+        child = dtrace; // intentionally global
+
+        function finish(code) {
+            if (leftover) {
+                handleLogLine(null, leftover, opts, stylize);
+                leftover = '';
+            }
+            callback(code);
+        }
+
+        dtrace.stdout.setEncoding('utf8');
+        dtrace.stdout.on('data', function (chunk) {
+            var lines = chunk.split(/\r\n|\n/);
+            var length = lines.length;
+            if (length === 1) {
+                leftover += lines[0];
+                return;
+            }
+            if (length > 1) {
+                handleLogLine(null, leftover + lines[0], opts, stylize);
+            }
+            leftover = lines.pop();
+            length -= 1;
+            for (var i = 1; i < length; i++) {
+                handleLogLine(null, lines[i], opts, stylize);
+            }
+        });
+
+        if (nodeSpawnSupportsStdio) {
+            dtrace.on('exit', finish);
+        } else {
+            // Fallback (for < v0.8) to pipe the dtrace process' stderr to
+            // this stderr. Wait for all of (1) process 'exit', (2) stderr
+            // 'end', and (2) stdout 'end' before returning to ensure all
+            // stderr is flushed (issue #54).
+            var returnCode = null;
+            var eventsRemaining = 3;
+            function countdownToFinish(code) {
+                returnCode = code;
+                eventsRemaining--;
+                if (eventsRemaining == 0) {
+                    finish(returnCode);
+                }
+            }
+            dtrace.stderr.pipe(process.stderr);
+            dtrace.stderr.on('end', countdownToFinish);
+            dtrace.stderr.on('end', countdownToFinish);
+            dtrace.on('exit', countdownToFinish);
+        }
+    });
+}
+
+
+/**
+ * Process all input from the given log file.
+ *
+ * @param file {String} Log file path to process.
+ * @params opts {Object} Bunyan options object.
+ * @param stylize {Function} Output stylize function to use.
+ * @param callback {Function} `function ()`
+ */
+function processFile(file, opts, stylize, callback) {
+    var stream = fs.createReadStream(file);
+    if (/\.gz$/.test(file)) {
+        stream = stream.pipe(require('zlib').createGunzip());
+    }
+    // Manually decode streams - lazy load here as per node/lib/fs.js
+    var decoder = new (require('string_decoder').StringDecoder)('utf8');
+
+    streams[file].stream = stream;
+
+    stream.on('error', function (err) {
+        streams[file].done = true;
+        callback(err);
+    });
+
+    var leftover = '';  // Left-over partial line from last chunk.
+    stream.on('data', function (data) {
+        var chunk = decoder.write(data);
+        if (!chunk.length) {
+            return;
+        }
+        var lines = chunk.split(/\r\n|\n/);
+        var length = lines.length;
+        if (length === 1) {
+            leftover += lines[0];
+            return;
+        }
+
+        if (length > 1) {
+            handleLogLine(file, leftover + lines[0], opts, stylize);
+        }
+        leftover = lines.pop();
+        length -= 1;
+        for (var i = 1; i < length; i++) {
+            handleLogLine(file, lines[i], opts, stylize);
+        }
+    });
+
+    stream.on('end', function () {
+        streams[file].done = true;
+        if (leftover) {
+            handleLogLine(file, leftover, opts, stylize);
+            leftover = '';
+        } else {
+            emitNextRecord(opts, stylize);
+        }
+        callback();
+    });
+}
+
+
+/**
+ * From node async module.
+ */
+/* BEGIN JSSTYLED */
+function asyncForEach(arr, iterator, callback) {
+    callback = callback || function () {};
+    if (!arr.length) {
+        return callback();
+    }
+    var completed = 0;
+    arr.forEach(function (x) {
+        iterator(x, function (err) {
+            if (err) {
+                callback(err);
+                callback = function () {};
+            }
+            else {
+                completed += 1;
+                if (completed === arr.length) {
+                    callback();
+                }
+            }
+        });
+    });
+};
+/* END JSSTYLED */
+
+
+
+/**
+ * Cleanup and exit properly.
+ *
+ * Warning: this doesn't stop processing, i.e. process exit might be delayed.
+ * It is up to the caller to ensure that no subsequent bunyan processing
+ * is done after calling this.
+ *
+ * @param code {Number} exit code.
+ * @param signal {String} Optional signal name, if this was exitting because
+ *    of a signal.
+ */
+var cleanedUp = false;
+function cleanupAndExit(code, signal) {
+    // Guard one call.
+    if (cleanedUp) {
+        return;
+    }
+    cleanedUp = true;
+    if (_DEBUG) warn('(bunyan: cleanupAndExit)');
+
+    // Clear possibly interrupted ANSI code (issue #59).
+    if (usingAnsiCodes) {
+        stdout.write('\033[0m');
+    }
+
+    // Kill possible dtrace child.
+    if (child) {
+        child.kill(signal);
+    }
+
+    if (pager) {
+        // Let pager know that output is done, then wait for pager to exit.
+        stdout.end();
+        pager.on('exit', function (pagerCode) {
+            if (_DEBUG)
+                warn('(bunyan: pager exit -> process.exit(%s))',
+                    pagerCode || code);
+            process.exit(pagerCode || code);
+        });
+    } else {
+        if (_DEBUG) warn('(bunyan: process.exit(%s))', code);
+        process.exit(code);
+    }
+}
+
+
+
+//---- mainline
+
+process.on('SIGINT', function () {
+    /**
+     * Ignore SIGINT (Ctrl+C) if processing stdin -- we should process
+     * remaining output from preceding process in the pipeline and
+     * except *it* to close.
+     */
+    if (!readingStdin) {
+        cleanupAndExit(1, 'SIGINT');
+    }
+});
+process.on('SIGQUIT', function () { cleanupAndExit(1, 'SIGQUIT'); });
+process.on('SIGTERM', function () { cleanupAndExit(1, 'SIGTERM'); });
+process.on('SIGHUP', function () { cleanupAndExit(1, 'SIGHUP'); });
+
+process.on('uncaughtException', function (err) {
+    function _indent(s) {
+        var lines = s.split(/\r?\n/);
+        for (var i = 0; i < lines.length; i++) {
+            lines[i] = '*     ' + lines[i];
+        }
+        return lines.join('\n');
+    }
+
+    var title = encodeURIComponent(format(
+        'Bunyan %s crashed: %s', getVersion(), String(err)));
+    var e = console.error;
+    e('```');
+    e('* The Bunyan CLI crashed!');
+    e('*');
+    if (err.name === 'ReferenceError' && gUsingConditionOpts) {
+        /* BEGIN JSSTYLED */
+        e('* This crash was due to a "ReferenceError", which is often the 
result of given');
+        e('* `-c CONDITION` code that doesn\'t guard against undefined values. 
If that is');
+        /* END JSSTYLED */
+        e('* not the problem:');
+        e('*');
+    }
+    e('* Please report this issue and include the details below:');
+    e('*');
+    e('*    https://github.com/trentm/node-bunyan/issues/new?title=%s', title);
+    e('*');
+    e('* * *');
+    e('* platform:', process.platform);
+    e('* node version:', process.version);
+    e('* bunyan version:', getVersion());
+    e('* argv: %j', process.argv);
+    e('* log line: %j', currLine);
+    e('* stack:');
+    e(_indent(err.stack));
+    e('```');
+    process.exit(1);
+});
+
+
+function main(argv) {
+    try {
+        var opts = parseArgv(argv);
+    } catch (e) {
+        warn('bunyan: error: %s', e.message);
+        return drainStdoutAndExit(1);
+    }
+    if (opts.help) {
+        printHelp();
+        return;
+    }
+    if (opts.version) {
+        console.log('bunyan ' + getVersion());
+        return;
+    }
+    if (opts.pid && opts.args.length > 0) {
+        warn('bunyan: error: can\'t use both "-p PID" (%s) and file (%s) args',
+            opts.pid, opts.args.join(' '));
+        return drainStdoutAndExit(1);
+    }
+    if (opts.color === null) {
+        if (process.env.BUNYAN_NO_COLOR &&
+                process.env.BUNYAN_NO_COLOR.length > 0) {
+            opts.color = false;
+        } else {
+            opts.color = process.stdout.isTTY;
+        }
+    }
+    usingAnsiCodes = opts.color; // intentionally global
+    var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor);
+
+    // Pager.
+    var paginate = (
+        process.stdout.isTTY &&
+        process.stdin.isTTY &&
+        !opts.pids && // Don't page if following process output.
+        opts.args.length > 0 && // Don't page if no file args to process.
+        process.platform !== 'win32' &&
+        (nodeVer[0] > 0 || nodeVer[1] >= 8) &&
+        (opts.paginate === true ||
+            (opts.paginate !== false &&
+                (!process.env.BUNYAN_NO_PAGER ||
+                    process.env.BUNYAN_NO_PAGER.length === 0))));
+    if (paginate) {
+        var pagerCmd = process.env.PAGER || 'less';
+        /* JSSTYLED */
+        assert.ok(pagerCmd.indexOf('"') === -1 && pagerCmd.indexOf("'") === -1,
+            'cannot parse PAGER quotes yet');
+        var argv = pagerCmd.split(/\s+/g);
+        var env = objCopy(process.env);
+        if (env.LESS === undefined) {
+            // git's default is LESS=FRSX. I don't like the 'S' here because
+            // lines are *typically* wide with bunyan output and scrolling
+            // horizontally is a royal pain. Note a bug in Mac's `less -F`,
+            // such that SIGWINCH can kill it. If that rears too much then
+            // I'll remove 'F' from here.
+            env.LESS = 'FRX';
+        }
+        if (_DEBUG) warn('(pager: argv=%j, env.LESS=%j)', argv, env.LESS);
+        // `pager` and `stdout` intentionally global.
+        pager = spawn(argv[0], argv.slice(1),
+            // Share the stderr handle to have error output come
+            // straight through. Only supported in v0.8+.
+            {env: env, stdio: ['pipe', 1, 2]});
+        stdout = pager.stdin;
+
+        // Early termination of the pager: just stop.
+        pager.on('exit', function (pagerCode) {
+            if (_DEBUG) warn('(bunyan: pager exit)');
+            pager = null;
+            stdout.end()
+            stdout = process.stdout;
+            cleanupAndExit(pagerCode);
+        });
+    }
+
+    // Stdout error handling. (Couldn't setup until `stdout` was determined.)
+    stdout.on('error', function (err) {
+        if (_DEBUG) warn('(stdout error event: %s)', err);
+        if (err.code === 'EPIPE') {
+            drainStdoutAndExit(0);
+        } else if (err.toString() === 'Error: This socket is closed.') {
+            // Could get this if the pager closes its stdin, but hasn't
+            // exited yet.
+            drainStdoutAndExit(1);
+        } else {
+            warn(err);
+            drainStdoutAndExit(1);
+        }
+    });
+
+    var retval = 0;
+    if (opts.pids) {
+        processPids(opts, stylize, function (code) {
+            cleanupAndExit(code);
+        });
+    } else if (opts.args.length > 0) {
+        var files = opts.args;
+        files.forEach(function (file) {
+            streams[file] = { stream: null, records: [], done: false }
+        });
+        asyncForEach(files,
+            function (file, next) {
+                processFile(file, opts, stylize, function (err) {
+                    if (err) {
+                        warn('bunyan: %s', err.message);
+                        retval += 1;
+                    }
+                    next();
+                });
+            },
+            function (err) {
+                if (err) {
+                    warn('bunyan: unexpected error: %s', err.stack || err);
+                    return drainStdoutAndExit(1);
+                }
+                cleanupAndExit(retval);
+            }
+        );
+    } else {
+        processStdin(opts, stylize, function () {
+            cleanupAndExit(retval);
+        });
+    }
+}
+
+if (require.main === module) {
+    // HACK guard for <https://github.com/trentm/json/issues/24>.
+    // We override the `process.stdout.end` guard that core node.js puts in
+    // place. The real fix is that `.end()` shouldn't be called on stdout
+    // in node core. Node v0.6.9 fixes that. Only guard for v0.6.0..v0.6.8.
+    if ([0, 6, 0] <= nodeVer && nodeVer <= [0, 6, 8]) {
+        var stdout = process.stdout;
+        stdout.end = stdout.destroy = stdout.destroySoon = function () {
+            /* pass */
+        };
+    }
+
+    main(process.argv);
+}


Reply via email to