{ pkgs }:

pkgs.write "certaids" {
  "/bin/cert2json".link = pkgs.writeDash "cert2json" ''
    # usage: cert2json < CERT > JSON
    set -efu

    ${pkgs.openssl}/bin/openssl crl2pkcs7 -nocrl -certfile /dev/stdin |
    ${pkgs.openssl}/bin/openssl pkcs7 -print_certs -text |
    ${pkgs.gawk}/bin/awk -F, -f ${pkgs.writeText "cert2json.awk" ''
      function abort(msg) {
        print(msg) > "/dev/stderr"
        exit 1
      }

      function toJSON(x,   type, ret) {
        type = typeof(x)
        switch (type) {
          case "array":
            if (isArray(x)) return arrayToJSON(x)
            if (isObject(x)) return objectToJSON(x)
            abort("cannot render array to JSON", x)
          case "number":
            return numberToJSON(x)
          case "string":
            return stringToJSON(x)
          case "strnum":
          case "unassigned":
          case "regexp":
          case "untyped":
          default:
            abort("cannot render type: " type)
        }
      }

      function isArray(x,   i, k) {
        i = 1
        for (k in x) {
          if (k != i++) return 0
          i++
        }
        return 1
      }

      function isObject(x,   k) {
        for (k in x) {
          if (typeof(k) != "string") return 0
        }
        return 1
      }

      function arrayToJSON(x,   k, ret) {
        ret = "["
        for (k in x) {
          ret=ret toJSON(x[k]) ","
        }
        sub(/,$/,"",ret)
        ret=ret "]"
        return ret
      }

      function objectToJSON(x,   k,ret) {
        ret = "{"
        for (k in x) {
          ret = ret toJSON(k) ":" toJSON(x[k]) ","
        }
        sub(/,$/, "", ret)
        ret = ret "}"
        return ret
      }

      function numberToJSON(x) {
        return x
      }

      function stringToJSON(x) {
        gsub(/\\/, "&&",x)
        gsub(/\n/, "\\n", x)
        return "\"" x "\""
      }

      $1 ~ /^ *(Subject|Issuer):/ {
        sub(/^ */, "")
        sub(/: */, ",")
        key=tolower($1)
        sub(/[^,]*,/, "")

        # Normalize separators between relative distinguished names.
        # [1]: RFC2253, 3. Parsing a String back to a Distinguished Name
        # TODO support any distinguished name
        gsub(/ *[;,] */, ",")

        for(i = 0; i <= NF; i++) {
          split($i, a, "=")
          cache[key][a[1]] = a[2]
        }
      }

      /BEGIN CERTIFICATE/,/END CERTIFICATE/{
        cache["certificate"] = cache["certificate"] $0 "\n"
      }

      /END CERTIFICATE/{
        print toJSON(cache)
        delete cache
      }
    ''}
  '';
}