Add a helper function to split strings in tool/

This function matches both Go in Python in saying that
SplitString("", ",") is [""] instead of [].

(Better would be if this returned an iterator, but that's a bit too much
effort.)

Change-Id: I65c0044ca2d8cfecefe3e57185ac7c10325603ef
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/84268
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Lily Chen <chlily@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
diff --git a/tool/args.cc b/tool/args.cc
index 5537042..3e4cd88 100644
--- a/tool/args.cc
+++ b/tool/args.cc
@@ -15,6 +15,7 @@
 #include <string>
 #include <vector>
 
+#include <assert.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
@@ -106,3 +107,19 @@
 
   return true;
 }
+
+std::vector<std::string_view> SplitString(std::string_view s,
+                                          std::string_view sep) {
+  assert(!sep.empty());
+  std::vector<std::string_view> ret;
+  while (true) {
+    size_t idx = s.find(sep);
+    if (idx == std::string_view::npos) {
+      ret.push_back(s);
+      break;
+    }
+    ret.push_back(s.substr(0, idx));
+    s = s.substr(idx + sep.size());
+  }
+  return ret;
+}
diff --git a/tool/client.cc b/tool/client.cc
index 61384b9..605b486 100644
--- a/tool/client.cc
+++ b/tool/client.cc
@@ -493,22 +493,13 @@
   if (args_map.count("-alpn-protos") != 0) {
     const std::string &alpn_protos = args_map["-alpn-protos"];
     std::vector<uint8_t> wire;
-    size_t i = 0;
-    while (i <= alpn_protos.size()) {
-      size_t j = alpn_protos.find(',', i);
-      if (j == std::string::npos) {
-        j = alpn_protos.size();
-      }
-      size_t len = j - i;
-      if (len > 255) {
+    for (std::string_view proto : SplitString(alpn_protos, ",")) {
+      if (proto.empty() || proto.size() > 255) {
         fprintf(stderr, "Invalid ALPN protocols: '%s'\n", alpn_protos.c_str());
         return false;
       }
-      wire.push_back(static_cast<uint8_t>(len));
-      wire.resize(wire.size() + len);
-      OPENSSL_memcpy(wire.data() + wire.size() - len, alpn_protos.data() + i,
-                     len);
-      i = j + 1;
+      wire.push_back(static_cast<uint8_t>(proto.size()));
+      wire.insert(wire.end(), proto.begin(), proto.end());
     }
     if (SSL_CTX_set_alpn_protos(ctx.get(), wire.data(), wire.size()) != 0) {
       return false;
diff --git a/tool/internal.h b/tool/internal.h
index 739b518..78fb1fc 100644
--- a/tool/internal.h
+++ b/tool/internal.h
@@ -20,6 +20,7 @@
 
 #include <map>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -113,6 +114,11 @@
                  unsigned default_value,
                  const std::map<std::string, std::string> &args);
 
+// SplitString returns |s| split on copies of |sep|. If |sep| does not appear in
+// |s|, it returns a single-element array containing |s|, even if |s| is empty.
+std::vector<std::string_view> SplitString(std::string_view s,
+                                          std::string_view sep);
+
 bool ReadAll(std::vector<uint8_t> *out, FILE *in);
 bool WriteToFile(const std::string &path, bssl::Span<const uint8_t> in);