ok, most features can be vias

This refactors most features out of ok's core into vias:
   -w    --> a .png dumping via, "png", opening the door to other types
   -m/-s --> a filtering via "filter"

Everything now can print a brief help message too.

Change-Id: I9e653aab98fd57182a6d458c7a80052130980284
Reviewed-on: https://skia-review.googlesource.com/10509
Reviewed-by: Mike Klein <mtklein@chromium.org>
Commit-Queue: Mike Klein <mtklein@chromium.org>
diff --git a/tools/ok.cpp b/tools/ok.cpp
index 2d4a5df..de5727b 100644
--- a/tools/ok.cpp
+++ b/tools/ok.cpp
@@ -10,21 +10,20 @@
 //   * ok is entirely opt-in.  No more maintaining huge --blacklists.
 
 #include "SkGraphics.h"
-#include "SkOSFile.h"
 #include "ok.h"
 #include <chrono>
 #include <future>
 #include <list>
-#include <regex>
 #include <stdio.h>
 #include <stdlib.h>
 #include <thread>
+#include <vector>
 
 #if !defined(__has_include)
     #define  __has_include(x) 0
 #endif
 
-static thread_local const char* tls_name = "";
+static thread_local const char* tls_currently_running = "";
 
 #if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
     #include <execinfo.h>
@@ -53,7 +52,7 @@
                     #undef CASE
                     }
                     log(" while running '");
-                    log(tls_name);
+                    log(tls_currently_running);
                     log("'\n");
 
                     void* stack[128];
@@ -81,7 +80,7 @@
     void ok_log(const char* msg) {
         lockf(log_fd, F_LOCK, 0);
             log("[");
-            log(tls_name);
+            log(tls_currently_running);
             log("]\t");
             log(msg);
             log("\n");
@@ -97,8 +96,6 @@
     }
 #endif
 
-enum class Status { OK, Failed, Crashed, Skipped, None };
-
 struct Engine {
     virtual ~Engine() {}
     virtual bool spawn(std::function<Status(void)>) = 0;
@@ -174,55 +171,54 @@
 #endif
 
 struct StreamType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Stream> (*factory)(Options);
 };
 static std::vector<StreamType> stream_types;
 
 struct DstType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Dst> (*factory)(Options);
 };
 static std::vector<DstType> dst_types;
 
 struct ViaType {
-    const char* name;
+    const char *name, *help;
     std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>);
 };
 static std::vector<ViaType> via_types;
 
+template <typename T>
+static std::string help_for(std::vector<T> registered) {
+    std::string help;
+    for (auto r : registered) {
+        help += "\n    ";
+        help += r.name;
+        help += ": ";
+        help += r.help;
+    }
+    return help;
+}
+
 int main(int argc, char** argv) {
     SkGraphics::Init();
     setup_crash_handler();
 
-    int         jobs        {1};
-    std::regex  match       {".*"};
-    std::regex  search      {".*"};
-    std::string write_dir   {""};
-
+    int                                       jobs{1};
     std::unique_ptr<Stream>                   stream;
-    std::function<std::unique_ptr<Dst>(void)> dst_factory;
+    std::function<std::unique_ptr<Dst>(void)> dst_factory = []{
+        // A default Dst that's enough for unit tests and not much else.
+        struct : Dst {
+            Status draw(Src* src)  override { return src->draw(nullptr); }
+            sk_sp<SkImage> image() override { return nullptr; }
+        } dst;
+        return move_unique(dst);
+    };
 
     auto help = [&] {
-        std::string stream_names, dst_names, via_names;
-        for (auto s : stream_types) {
-            if (!stream_names.empty()) {
-                stream_names += ", ";
-            }
-            stream_names += s.name;
-        }
-        for (auto d : dst_types) {
-            if (!dst_names.empty()) {
-                dst_names += ", ";
-            }
-            dst_names += d.name;
-        }
-        for (auto v : via_types) {
-            if (!via_names.empty()) {
-                via_names += ", ";
-            }
-            via_names += v.name;
-        }
+        std::string stream_help = help_for(stream_types),
+                       dst_help = help_for(   dst_types),
+                       via_help = help_for(   via_types);
 
         printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h]                        \n"
                 "  src[:k=v,...] dst[:k=v,...] [via[:k=v,...] ...]                   \n"
@@ -230,23 +226,17 @@
                 "      If <0, use -N threads instead.                                \n"
                 "      If 0, use one thread in one process.                          \n"
                 "      If 1 (default) or -1, auto-detect N.                          \n"
-                "  -m: Run only names matching regex exactly.                        \n"
-                "  -s: Run only names matching regex anywhere.                       \n"
-                "  -w: If set, write .pngs into dir.                                 \n"
                 "  -h: Print this message and exit.                                  \n"
-                " src: content to draw: %s                                           \n"
-                " dst: how to draw that content: %s                                  \n"
-                " via: front-patches to the dst: %s                                  \n"
-                " Some srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
-                argv[0], stream_names.c_str(), dst_names.c_str(), via_names.c_str());
+                " src: content to draw%s                                             \n"
+                " dst: how to draw that content%s                                    \n"
+                " via: wrappers around dst%s                                         \n"
+                " Most srcs, dsts and vias have options, e.g. skp:dir=skps sw:ct=565 \n",
+                argv[0], stream_help.c_str(), dst_help.c_str(), via_help.c_str());
         return 1;
     };
 
     for (int i = 1; i < argc; i++) {
-        if (0 == strcmp("-j", argv[i])) { jobs      = atoi(argv[++i]); }
-        if (0 == strcmp("-m", argv[i])) { match     =      argv[++i] ; }
-        if (0 == strcmp("-s", argv[i])) { search    =      argv[++i] ; }
-        if (0 == strcmp("-w", argv[i])) { write_dir =      argv[++i] ; }
+        if (0 == strcmp("-j", argv[i])) { jobs = atoi(argv[++i]); }
         if (0 == strcmp("-h", argv[i])) { return help(); }
 
         for (auto s : stream_types) {
@@ -283,16 +273,6 @@
         }
     }
     if (!stream) { return help(); }
-    if (!dst_factory) {
-        // A default Dst that's enough for unit tests and not much else.
-        dst_factory = []{
-            struct : Dst {
-                bool draw(Src* src) override { return src->draw(nullptr); }
-                sk_sp<SkImage> image() override { return nullptr; }
-            } dst;
-            return move_unique(dst);
-        };
-    }
 
     std::unique_ptr<Engine> engine;
     if (jobs == 0) { engine.reset(new SerialEngine);                  }
@@ -301,10 +281,6 @@
 
     if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }
 
-    if (!write_dir.empty()) {
-        sk_mkdir(write_dir.c_str());
-    }
-
     int ok = 0, failed = 0, crashed = 0, skipped = 0;
 
     auto update_stats = [&](Status s) {
@@ -343,25 +319,10 @@
         spawn([=] {
             std::unique_ptr<Src> src{raw};
 
-            auto name = src->name();
-            tls_name = name.c_str();
-            if (!std::regex_match (name, match) ||
-                !std::regex_search(name, search)) {
-                return Status::Skipped;
-            }
+            std::string name = src->name();
+            tls_currently_running = name.c_str();
 
-            auto dst = dst_factory();
-            if (!dst->draw(src.get())) {
-                return Status::Failed;
-            }
-
-            if (!write_dir.empty()) {
-                auto image = dst->image();
-                sk_sp<SkData> png{image->encode()};
-                SkFILEWStream{(write_dir + "/" + name + ".png").c_str()}
-                    .write(png->data(), png->size());
-            }
-            return Status::OK;
+            return dst_factory()->draw(src.get());
         });
     }
 
@@ -374,15 +335,17 @@
 }
 
 
-Register::Register(const char* name, std::unique_ptr<Stream> (*factory)(Options)) {
-    stream_types.push_back(StreamType{name, factory});
+Register::Register(const char* name, const char* help,
+                   std::unique_ptr<Stream> (*factory)(Options)) {
+    stream_types.push_back(StreamType{name, help, factory});
 }
-Register::Register(const char* name, std::unique_ptr<Dst> (*factory)(Options)) {
-    dst_types.push_back(DstType{name, factory});
+Register::Register(const char* name, const char* help,
+                   std::unique_ptr<Dst> (*factory)(Options)) {
+    dst_types.push_back(DstType{name, help, factory});
 }
-Register::Register(const char* name,
+Register::Register(const char* name, const char* help,
                    std::unique_ptr<Dst> (*factory)(Options, std::unique_ptr<Dst>)) {
-    via_types.push_back(ViaType{name, factory});
+    via_types.push_back(ViaType{name, help, factory});
 }
 
 Options::Options(std::string str) {