Skip to content

Instantly share code, notes, and snippets.

@bak1an
Created December 14, 2012 18:12
Show Gist options
  • Save bak1an/4287380 to your computer and use it in GitHub Desktop.
Save bak1an/4287380 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# encoding: utf-8
#
# This file, gist, is generated code.
# Please DO NOT EDIT or send patches for it.
#
# Please take a look at the source from
# http://github.com/defunkt/gist
# and submit patches against the individual files
# that build gist.
#
require 'strscan'
module JSON
module Pure
class Parser < StringScanner
STRING = /" ((?:[^\x0-\x1f"\\] |
\\["\\\/bfnrt] |
\\u[0-9a-fA-F]{4} |
\\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
"/nx
INTEGER = /(-?0|-?[1-9]\d*)/
FLOAT = /(-?
(?:0|[1-9]\d*)
(?:
\.\d+(?i:e[+-]?\d+) |
\.\d+ |
(?i:e[+-]?\d+)
)
)/x
NAN = /NaN/
INFINITY = /Infinity/
MINUS_INFINITY = /-Infinity/
OBJECT_OPEN = /\{/
OBJECT_CLOSE = /\}/
ARRAY_OPEN = /\[/
ARRAY_CLOSE = /\]/
PAIR_DELIMITER = /:/
COLLECTION_DELIMITER = /,/
TRUE = /true/
FALSE = /false/
NULL = /null/
IGNORE = %r(
(?:
//[^\n\r]*[\n\r]| # line comments
/\* # c-style comments
(?:
[^*/]| # normal chars
/[^*]| # slashes that do not start a nested comment
\*[^/]| # asterisks that do not end this comment
/(?=\*/) # single slash before this comment's end
)*
\*/ # the End of this comment
|[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
)+
)mx
UNPARSED = Object.new
def initialize(source, opts = {})
opts ||= {}
unless @quirks_mode = opts[:quirks_mode]
source = convert_encoding source
end
super source
if !opts.key?(:max_nesting) # defaults to 19
@max_nesting = 19
elsif opts[:max_nesting]
@max_nesting = opts[:max_nesting]
else
@max_nesting = 0
end
@allow_nan = !!opts[:allow_nan]
@symbolize_names = !!opts[:symbolize_names]
if opts.key?(:create_additions)
@create_additions = !!opts[:create_additions]
else
@create_additions = true
end
@create_id = @create_additions ? JSON.create_id : nil
@object_class = opts[:object_class] || Hash
@array_class = opts[:array_class] || Array
@match_string = opts[:match_string]
end
alias source string
def quirks_mode?
!!@quirks_mode
end
def reset
super
@current_nesting = 0
end
def parse
reset
obj = nil
if @quirks_mode
while !eos? && skip(IGNORE)
end
if eos?
raise ParserError, "source did not contain any JSON!"
else
obj = parse_value
obj == UNPARSED and raise ParserError, "source did not contain any JSON!"
end
else
until eos?
case
when scan(OBJECT_OPEN)
obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
@current_nesting = 1
obj = parse_object
when scan(ARRAY_OPEN)
obj and raise ParserError, "source '#{peek(20)}' not in JSON!"
@current_nesting = 1
obj = parse_array
when skip(IGNORE)
;
else
raise ParserError, "source '#{peek(20)}' not in JSON!"
end
end
obj or raise ParserError, "source did not contain any JSON!"
end
obj
end
private
def convert_encoding(source)
if source.respond_to?(:to_str)
source = source.to_str
else
raise TypeError, "#{source.inspect} is not like a string"
end
if defined?(::Encoding)
if source.encoding == ::Encoding::ASCII_8BIT
b = source[0, 4].bytes.to_a
source =
case
when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
source.dup.force_encoding(::Encoding::UTF_32BE).encode!(::Encoding::UTF_8)
when b.size >= 4 && b[0] == 0 && b[2] == 0
source.dup.force_encoding(::Encoding::UTF_16BE).encode!(::Encoding::UTF_8)
when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
source.dup.force_encoding(::Encoding::UTF_32LE).encode!(::Encoding::UTF_8)
when b.size >= 4 && b[1] == 0 && b[3] == 0
source.dup.force_encoding(::Encoding::UTF_16LE).encode!(::Encoding::UTF_8)
else
source.dup
end
else
source = source.encode(::Encoding::UTF_8)
end
source.force_encoding(::Encoding::ASCII_8BIT)
else
b = source
source =
case
when b.size >= 4 && b[0] == 0 && b[1] == 0 && b[2] == 0
JSON.iconv('utf-8', 'utf-32be', b)
when b.size >= 4 && b[0] == 0 && b[2] == 0
JSON.iconv('utf-8', 'utf-16be', b)
when b.size >= 4 && b[1] == 0 && b[2] == 0 && b[3] == 0
JSON.iconv('utf-8', 'utf-32le', b)
when b.size >= 4 && b[1] == 0 && b[3] == 0
JSON.iconv('utf-8', 'utf-16le', b)
else
b
end
end
source
end
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
UNESCAPE_MAP.update({
?" => '"',
?\\ => '\\',
?/ => '/',
?b => "\b",
?f => "\f",
?n => "\n",
?r => "\r",
?t => "\t",
?u => nil,
})
EMPTY_8BIT_STRING = ''
if ::String.method_defined?(:encode)
EMPTY_8BIT_STRING.force_encoding Encoding::ASCII_8BIT
end
def parse_string
if scan(STRING)
return '' if self[1].empty?
string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
if u = UNESCAPE_MAP[$&[1]]
u
else # \uXXXX
bytes = EMPTY_8BIT_STRING.dup
i = 0
while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
i += 1
end
JSON.iconv('utf-8', 'utf-16be', bytes)
end
end
if string.respond_to?(:force_encoding)
string.force_encoding(::Encoding::UTF_8)
end
if @create_additions and @match_string
for (regexp, klass) in @match_string
klass.json_creatable? or next
string =~ regexp and return klass.json_create(string)
end
end
string
else
UNPARSED
end
rescue => e
raise ParserError, "Caught #{e.class} at '#{peek(20)}': #{e}"
end
def parse_value
case
when scan(FLOAT)
Float(self[1])
when scan(INTEGER)
Integer(self[1])
when scan(TRUE)
true
when scan(FALSE)
false
when scan(NULL)
nil
when (string = parse_string) != UNPARSED
string
when scan(ARRAY_OPEN)
@current_nesting += 1
ary = parse_array
@current_nesting -= 1
ary
when scan(OBJECT_OPEN)
@current_nesting += 1
obj = parse_object
@current_nesting -= 1
obj
when @allow_nan && scan(NAN)
NaN
when @allow_nan && scan(INFINITY)
Infinity
when @allow_nan && scan(MINUS_INFINITY)
MinusInfinity
else
UNPARSED
end
end
def parse_array
raise NestingError, "nesting of #@current_nesting is too deep" if
@max_nesting.nonzero? && @current_nesting > @max_nesting
result = @array_class.new
delim = false
until eos?
case
when (value = parse_value) != UNPARSED
delim = false
result << value
skip(IGNORE)
if scan(COLLECTION_DELIMITER)
delim = true
elsif match?(ARRAY_CLOSE)
;
else
raise ParserError, "expected ',' or ']' in array at '#{peek(20)}'!"
end
when scan(ARRAY_CLOSE)
if delim
raise ParserError, "expected next element in array at '#{peek(20)}'!"
end
break
when skip(IGNORE)
;
else
raise ParserError, "unexpected token in array at '#{peek(20)}'!"
end
end
result
end
def parse_object
raise NestingError, "nesting of #@current_nesting is too deep" if
@max_nesting.nonzero? && @current_nesting > @max_nesting
result = @object_class.new
delim = false
until eos?
case
when (string = parse_string) != UNPARSED
skip(IGNORE)
unless scan(PAIR_DELIMITER)
raise ParserError, "expected ':' in object at '#{peek(20)}'!"
end
skip(IGNORE)
unless (value = parse_value).equal? UNPARSED
result[@symbolize_names ? string.to_sym : string] = value
delim = false
skip(IGNORE)
if scan(COLLECTION_DELIMITER)
delim = true
elsif match?(OBJECT_CLOSE)
;
else
raise ParserError, "expected ',' or '}' in object at '#{peek(20)}'!"
end
else
raise ParserError, "expected value in object at '#{peek(20)}'!"
end
when scan(OBJECT_CLOSE)
if delim
raise ParserError, "expected next name, value pair in object at '#{peek(20)}'!"
end
if @create_additions and klassname = result[@create_id]
klass = JSON.deep_const_get klassname
break unless klass and klass.json_creatable?
result = klass.json_create(result)
end
break
when skip(IGNORE)
;
else
raise ParserError, "unexpected token in object at '#{peek(20)}'!"
end
end
result
end
end
end
end
module JSON
MAP = {
"\x0" => '\u0000',
"\x1" => '\u0001',
"\x2" => '\u0002',
"\x3" => '\u0003',
"\x4" => '\u0004',
"\x5" => '\u0005',
"\x6" => '\u0006',
"\x7" => '\u0007',
"\b" => '\b',
"\t" => '\t',
"\n" => '\n',
"\xb" => '\u000b',
"\f" => '\f',
"\r" => '\r',
"\xe" => '\u000e',
"\xf" => '\u000f',
"\x10" => '\u0010',
"\x11" => '\u0011',
"\x12" => '\u0012',
"\x13" => '\u0013',
"\x14" => '\u0014',
"\x15" => '\u0015',
"\x16" => '\u0016',
"\x17" => '\u0017',
"\x18" => '\u0018',
"\x19" => '\u0019',
"\x1a" => '\u001a',
"\x1b" => '\u001b',
"\x1c" => '\u001c',
"\x1d" => '\u001d',
"\x1e" => '\u001e',
"\x1f" => '\u001f',
'"' => '\"',
'\\' => '\\\\',
} # :nodoc:
if defined?(::Encoding)
def utf8_to_json(string) # :nodoc:
string = string.dup
string << '' # XXX workaround: avoid buffer sharing
string.force_encoding(::Encoding::ASCII_8BIT)
string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
string.force_encoding(::Encoding::UTF_8)
string
end
def utf8_to_json_ascii(string) # :nodoc:
string = string.dup
string << '' # XXX workaround: avoid buffer sharing
string.force_encoding(::Encoding::ASCII_8BIT)
string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
string.gsub!(/(
(?:
[\xc2-\xdf][\x80-\xbf] |
[\xe0-\xef][\x80-\xbf]{2} |
[\xf0-\xf4][\x80-\xbf]{3}
)+ |
[\x80-\xc1\xf5-\xff] # invalid
)/nx) { |c|
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
s.gsub!(/.{4}/n, '\\\\u\&')
}
string.force_encoding(::Encoding::UTF_8)
string
rescue => e
raise GeneratorError, "Caught #{e.class}: #{e}"
end
else
def utf8_to_json(string) # :nodoc:
string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
end
def utf8_to_json_ascii(string) # :nodoc:
string = string.gsub(/["\\\x0-\x1f]/) { MAP[$&] }
string.gsub!(/(
(?:
[\xc2-\xdf][\x80-\xbf] |
[\xe0-\xef][\x80-\xbf]{2} |
[\xf0-\xf4][\x80-\xbf]{3}
)+ |
[\x80-\xc1\xf5-\xff] # invalid
)/nx) { |c|
c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
s = JSON.iconv('utf-16be', 'utf-8', c).unpack('H*')[0]
s.gsub!(/.{4}/n, '\\\\u\&')
}
string
rescue => e
raise GeneratorError, "Caught #{e.class}: #{e}"
end
end
module_function :utf8_to_json, :utf8_to_json_ascii
module Pure
module Generator
class State
def self.from_state(opts)
case
when self === opts
opts
when opts.respond_to?(:to_hash)
new(opts.to_hash)
when opts.respond_to?(:to_h)
new(opts.to_h)
else
SAFE_STATE_PROTOTYPE.dup
end
end
def initialize(opts = {})
@indent = ''
@space = ''
@space_before = ''
@object_nl = ''
@array_nl = ''
@allow_nan = false
@ascii_only = false
@quirks_mode = false
@buffer_initial_length = 1024
configure opts
end
attr_accessor :indent
attr_accessor :space
attr_accessor :space_before
attr_accessor :object_nl
attr_accessor :array_nl
attr_accessor :max_nesting
attr_accessor :quirks_mode
attr_reader :buffer_initial_length
def buffer_initial_length=(length)
if length > 0
@buffer_initial_length = length
end
end
attr_accessor :depth
def check_max_nesting # :nodoc:
return if @max_nesting.zero?
current_nesting = depth + 1
current_nesting > @max_nesting and
raise NestingError, "nesting of #{current_nesting} is too deep"
end
def check_circular?
!@max_nesting.zero?
end
def allow_nan?
@allow_nan
end
def ascii_only?
@ascii_only
end
def quirks_mode?
@quirks_mode
end
def configure(opts)
@indent = opts[:indent] if opts.key?(:indent)
@space = opts[:space] if opts.key?(:space)
@space_before = opts[:space_before] if opts.key?(:space_before)
@object_nl = opts[:object_nl] if opts.key?(:object_nl)
@array_nl = opts[:array_nl] if opts.key?(:array_nl)
@allow_nan = !!opts[:allow_nan] if opts.key?(:allow_nan)
@ascii_only = opts[:ascii_only] if opts.key?(:ascii_only)
@depth = opts[:depth] || 0
@quirks_mode = opts[:quirks_mode] if opts.key?(:quirks_mode)
if !opts.key?(:max_nesting) # defaults to 19
@max_nesting = 19
elsif opts[:max_nesting]
@max_nesting = opts[:max_nesting]
else
@max_nesting = 0
end
self
end
alias merge configure
def to_h
result = {}
for iv in %w[indent space space_before object_nl array_nl allow_nan max_nesting ascii_only quirks_mode buffer_initial_length depth]
result[iv.intern] = instance_variable_get("@#{iv}")
end
result
end
def generate(obj)
result = obj.to_json(self)
unless @quirks_mode
unless result =~ /\A\s*\[/ && result =~ /\]\s*\Z/ ||
result =~ /\A\s*\{/ && result =~ /\}\s*\Z/
then
raise GeneratorError, "only generation of JSON objects or arrays allowed"
end
end
result
end
def [](name)
__send__ name
end
end
module GeneratorMethods
module Object
def to_json(*) to_s.to_json end
end
module Hash
def to_json(state = nil, *)
state = State.from_state(state)
state.check_max_nesting
json_transform(state)
end
private
def json_shift(state)
state.object_nl.empty? or return ''
state.indent * state.depth
end
def json_transform(state)
delim = ','
delim << state.object_nl
result = '{'
result << state.object_nl
depth = state.depth += 1
first = true
indent = !state.object_nl.empty?
each { |key,value|
result << delim unless first
result << state.indent * depth if indent
result << key.to_s.to_json(state)
result << state.space_before
result << ':'
result << state.space
result << value.to_json(state)
first = false
}
depth = state.depth -= 1
result << state.object_nl
result << state.indent * depth if indent if indent
result << '}'
result
end
end
module Array
def to_json(state = nil, *)
state = State.from_state(state)
state.check_max_nesting
json_transform(state)
end
private
def json_transform(state)
delim = ','
delim << state.array_nl
result = '['
result << state.array_nl
depth = state.depth += 1
first = true
indent = !state.array_nl.empty?
each { |value|
result << delim unless first
result << state.indent * depth if indent
result << value.to_json(state)
first = false
}
depth = state.depth -= 1
result << state.array_nl
result << state.indent * depth if indent
result << ']'
end
end
module Integer
def to_json(*) to_s end
end
module Float
def to_json(state = nil, *)
state = State.from_state(state)
case
when infinite?
if state.allow_nan?
to_s
else
raise GeneratorError, "#{self} not allowed in JSON"
end
when nan?
if state.allow_nan?
to_s
else
raise GeneratorError, "#{self} not allowed in JSON"
end
else
to_s
end
end
end
module String
if defined?(::Encoding)
def to_json(state = nil, *args)
state = State.from_state(state)
if encoding == ::Encoding::UTF_8
string = self
else
string = encode(::Encoding::UTF_8)
end
if state.ascii_only?
'"' << JSON.utf8_to_json_ascii(string) << '"'
else
'"' << JSON.utf8_to_json(string) << '"'
end
end
else
def to_json(state = nil, *args)
state = State.from_state(state)
if state.ascii_only?
'"' << JSON.utf8_to_json_ascii(self) << '"'
else
'"' << JSON.utf8_to_json(self) << '"'
end
end
end
module Extend
def json_create(o)
o['raw'].pack('C*')
end
end
def self.included(modul)
modul.extend Extend
end
def to_json_raw_object
{
JSON.create_id => self.class.name,
'raw' => self.unpack('C*'),
}
end
def to_json_raw(*args)
to_json_raw_object.to_json(*args)
end
end
module TrueClass
def to_json(*) 'true' end
end
module FalseClass
def to_json(*) 'false' end
end
module NilClass
def to_json(*) 'null' end
end
end
end
end
end
module JSON
class << self
def [](object, opts = {})
if object.respond_to? :to_str
JSON.parse(object.to_str, opts)
else
JSON.generate(object, opts)
end
end
attr_reader :parser
def parser=(parser) # :nodoc:
@parser = parser
remove_const :Parser if JSON.const_defined_in?(self, :Parser)
const_set :Parser, parser
end
def deep_const_get(path) # :nodoc:
path.to_s.split(/::/).inject(Object) do |p, c|
case
when c.empty? then p
when JSON.const_defined_in?(p, c) then p.const_get(c)
else
begin
p.const_missing(c)
rescue NameError => e
raise ArgumentError, "can't get const #{path}: #{e}"
end
end
end
end
def generator=(generator) # :nodoc:
old, $VERBOSE = $VERBOSE, nil
@generator = generator
generator_methods = generator::GeneratorMethods
for const in generator_methods.constants
klass = deep_const_get(const)
modul = generator_methods.const_get(const)
klass.class_eval do
instance_methods(false).each do |m|
m.to_s == 'to_json' and remove_method m
end
include modul
end
end
self.state = generator::State
const_set :State, self.state
const_set :SAFE_STATE_PROTOTYPE, State.new
const_set :FAST_STATE_PROTOTYPE, State.new(
:indent => '',
:space => '',
:object_nl => "",
:array_nl => "",
:max_nesting => false
)
const_set :PRETTY_STATE_PROTOTYPE, State.new(
:indent => ' ',
:space => ' ',
:object_nl => "\n",
:array_nl => "\n"
)
ensure
$VERBOSE = old
end
attr_reader :generator
attr_accessor :state
attr_accessor :create_id
end
self.create_id = 'json_class'
NaN = 0.0/0
Infinity = 1.0/0
MinusInfinity = -Infinity
class JSONError < StandardError; end
class ParserError < JSONError; end
class NestingError < ParserError; end
class CircularDatastructure < NestingError; end
class GeneratorError < JSONError; end
UnparserError = GeneratorError
class MissingUnicodeSupport < JSONError; end
module_function
def parse(source, opts = {})
Parser.new(source, opts).parse
end
def parse!(source, opts = {})
opts = {
:max_nesting => false,
:allow_nan => true
}.update(opts)
Parser.new(source, opts).parse
end
def generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = SAFE_STATE_PROTOTYPE.dup
end
if opts
if opts.respond_to? :to_hash
opts = opts.to_hash
elsif opts.respond_to? :to_h
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
state = state.configure(opts)
end
state.generate(obj)
end
alias unparse generate
module_function :unparse
def fast_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = FAST_STATE_PROTOTYPE.dup
end
if opts
if opts.respond_to? :to_hash
opts = opts.to_hash
elsif opts.respond_to? :to_h
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
state.configure(opts)
end
state.generate(obj)
end
alias fast_unparse fast_generate
module_function :fast_unparse
def pretty_generate(obj, opts = nil)
if State === opts
state, opts = opts, nil
else
state = PRETTY_STATE_PROTOTYPE.dup
end
if opts
if opts.respond_to? :to_hash
opts = opts.to_hash
elsif opts.respond_to? :to_h
opts = opts.to_h
else
raise TypeError, "can't convert #{opts.class} into Hash"
end
state.configure(opts)
end
state.generate(obj)
end
alias pretty_unparse pretty_generate
module_function :pretty_unparse
class << self
attr_accessor :load_default_options
end
self.load_default_options = {
:max_nesting => false,
:allow_nan => true,
:quirks_mode => true,
}
def load(source, proc = nil)
opts = load_default_options
if source.respond_to? :to_str
source = source.to_str
elsif source.respond_to? :to_io
source = source.to_io.read
elsif source.respond_to?(:read)
source = source.read
end
if opts[:quirks_mode] && (source.nil? || source.empty?)
source = 'null'
end
result = parse(source, opts)
recurse_proc(result, &proc) if proc
result
end
def recurse_proc(result, &proc)
case result
when Array
result.each { |x| recurse_proc x, &proc }
proc.call result
when Hash
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
proc.call result
else
proc.call result
end
end
alias restore load
module_function :restore
class << self
attr_accessor :dump_default_options
end
self.dump_default_options = {
:max_nesting => false,
:allow_nan => true,
:quirks_mode => true,
}
def dump(obj, anIO = nil, limit = nil)
if anIO and limit.nil?
anIO = anIO.to_io if anIO.respond_to?(:to_io)
unless anIO.respond_to?(:write)
limit = anIO
anIO = nil
end
end
opts = JSON.dump_default_options
limit and opts.update(:max_nesting => limit)
result = generate(obj, opts)
if anIO
anIO.write result
anIO
else
result
end
rescue JSON::NestingError
raise ArgumentError, "exceed depth limit"
end
def self.swap!(string) # :nodoc:
0.upto(string.size / 2) do |i|
break unless string[2 * i + 1]
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
end
string
end
if ::String.method_defined?(:encode)
def self.iconv(to, from, string)
string.encode(to, from)
end
else
require 'iconv'
def self.iconv(to, from, string)
Iconv.conv(to, from, string)
end
end
if ::Object.method(:const_defined?).arity == 1
def self.const_defined_in?(modul, constant)
modul.const_defined?(constant)
end
else
def self.const_defined_in?(modul, constant)
modul.const_defined?(constant, false)
end
end
end
module ::Kernel
private
def j(*objs)
objs.each do |obj|
puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
end
nil
end
def jj(*objs)
objs.each do |obj|
puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
end
nil
end
def JSON(object, *args)
if object.respond_to? :to_str
JSON.parse(object.to_str, args.first)
else
JSON.generate(object, args.first)
end
end
end
class ::Class
def json_creatable?
respond_to?(:json_create)
end
end
JSON.generator = JSON::Pure::Generator
JSON.parser = JSON::Pure::Parser
module Gist
module Manpage
extend self
def display(name)
puts manpage(name)
end
def manpage(name)
return "** Can't find groff(1)" unless groff?
require 'open3'
out = nil
Open3.popen3(groff_command) do |stdin, stdout, _|
stdin.puts raw_manpage(name)
stdin.close
out = stdout.read.strip
end
out
end
def raw_manpage(name)
if File.exists? file = File.dirname(__FILE__) + "/../../man/#{name}.1"
File.read(file)
else
DATA.read.split("__CACERT__").first
end
end
def groff?
system("which groff > /dev/null")
end
def groff_command
"groff -Wall -mtty-char -mandoc -Tascii"
end
def puts(*args)
page_stdout
super
end
def page_stdout
return unless $stdout.tty?
read, write = IO.pipe
if Kernel.fork
$stdin.reopen(read)
read.close
write.close
ENV['LESS'] = 'FSRX'
Kernel.select [STDIN]
pager = ENV['PAGER'] || 'less -isr'
pager = 'cat' if pager.empty?
exec pager rescue exec "/bin/sh", "-c", pager
else
$stdout.reopen(write)
$stderr.reopen(write) if $stderr.tty?
read.close
write.close
end
end
end
end
module Gist
VERSION = Version = '3.1.0'
end
require 'open-uri'
require 'net/https'
require 'optparse'
require 'base64'
require 'gist/json' unless defined?(JSON)
require 'gist/manpage' unless defined?(Gist::Manpage)
require 'gist/version' unless defined?(Gist::Version)
module Gist
extend self
GIST_URL = 'https://api.github.com/gists/%s'
CREATE_URL = 'https://api.github.com/gists'
if ENV['HTTPS_PROXY']
PROXY = URI(ENV['HTTPS_PROXY'])
elsif ENV['HTTP_PROXY']
PROXY = URI(ENV['HTTP_PROXY'])
else
PROXY = nil
end
PROXY_HOST = PROXY ? PROXY.host : nil
PROXY_PORT = PROXY ? PROXY.port : nil
def execute(*args)
private_gist = defaults["private"]
gist_filename = nil
gist_extension = defaults["extension"]
browse_enabled = defaults["browse"]
description = nil
opts = OptionParser.new do |opts|
opts.banner = "Usage: gist [options] [filename or stdin] [filename] ...\n" +
"Filename '-' forces gist to read from stdin."
opts.on('-p', '--[no-]private', 'Make the gist private') do |priv|
private_gist = priv
end
t_desc = 'Set syntax highlighting of the Gist by file extension'
opts.on('-t', '--type [EXTENSION]', t_desc) do |extension|
gist_extension = '.' + extension
end
opts.on('-d','--description DESCRIPTION', 'Set description of the new gist') do |d|
description = d
end
opts.on('-o','--[no-]open', 'Open gist in browser') do |o|
browse_enabled = o
end
opts.on('-m', '--man', 'Print manual') do
Gist::Manpage.display("gist")
end
opts.on('-v', '--version', 'Print version') do
puts Gist::Version
exit
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
opts.parse!(args)
if $stdin.tty? && args[0] != '-'
if args.empty?
puts opts
exit
end
files = args.inject([]) do |files, file|
abort "Can't find #{file}" unless File.exists?(file)
files.push({
:input => File.read(file),
:filename => file,
:extension => (File.extname(file) if file.include?('.'))
})
end
else
input = $stdin.read
files = [{:input => input, :extension => gist_extension}]
end
url = write(files, private_gist, description)
browse(url) if browse_enabled
puts copy(url)
rescue => e
warn e
puts opts
end
end
def write(files, private_gist = false, description = nil)
url = URI.parse(CREATE_URL)
if PROXY_HOST
proxy = Net::HTTP::Proxy(PROXY_HOST, PROXY_PORT)
http = proxy.new(url.host, url.port)
else
http = Net::HTTP.new(url.host, url.port)
end
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = ca_cert
req = Net::HTTP::Post.new(url.path)
req.body = JSON.generate(data(files, private_gist, description))
user, password = auth()
if user && password
req.basic_auth(user, password)
end
response = http.start{|h| h.request(req) }
case response
when Net::HTTPCreated
JSON.parse(response.body)['html_url']
else
puts "Creating gist failed: #{response.code} #{response.message}"
exit(false)
end
end
def read(gist_id)
data = JSON.parse(open(GIST_URL % gist_id).read)
data["files"].map{|name, content| content['content'] }.join("\n\n")
end
def browse(url)
if RUBY_PLATFORM =~ /darwin/
`open #{url}`
elsif RUBY_PLATFORM =~ /linux/
`#{ENV['BROWSER']} #{url}`
elsif ENV['OS'] == 'Windows_NT' or
RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw|wince/i
`start "" "#{url}"`
end
end
def copy(content)
cmd = case true
when system("type pbcopy > /dev/null 2>&1")
:pbcopy
when system("type xclip > /dev/null 2>&1")
:xclip
when system("type putclip > /dev/null 2>&1")
:putclip
end
if cmd
IO.popen(cmd.to_s, 'r+') { |clip| clip.print content }
end
content
end
private
def data(files, private_gist, description)
i = 0
file_data = {}
files.each do |file|
i = i + 1
filename = file[:filename] ? file[:filename] : "gistfile#{i}"
file_data[filename] = {:content => file[:input]}
end
data = {"files" => file_data}
data.merge!({ 'description' => description }) unless description.nil?
data.merge!({ 'public' => !private_gist })
data
end
def auth
user = config("github.user")
password = config("github.password")
token = config("github.token")
if password.to_s.empty? && !token.to_s.empty?
abort "Please set GITHUB_PASSWORD or github.password instead of using a token."
end
if user.to_s.empty? || password.to_s.empty?
nil
else
[ user, password ]
end
end
def defaults
extension = config("gist.extension")
return {
"private" => config("gist.private"),
"browse" => config("gist.browse"),
"extension" => extension
}
end
def config(key)
env_key = ENV[key.upcase.gsub(/\./, '_')]
return env_key if env_key and not env_key.strip.empty?
str_to_bool `git config --global #{key}`.strip
end
def str_to_bool(str)
if str.size > 0 and str[0].chr == '!'
command = str[1, str.length]
value = `#{command}`
else
value = str
end
case value.downcase.strip
when "false", "0", "nil", "", "no", "off"
nil
when "true", "1", "yes", "on"
true
else
value
end
end
def ca_cert
cert_file = [
File.expand_path("../gist/cacert.pem", __FILE__),
"/tmp/gist_cacert.pem"
].find{|l| File.exist?(l) }
if cert_file
cert_file
else
File.open("/tmp/gist_cacert.pem", "w") do |f|
f.write(DATA.read.split("__CACERT__").last)
end
"/tmp/gist_cacert.pem"
end
end
end
Gist.execute(*ARGV)
__END__
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "GIST" "1" "March 2012" "GITHUB" "Gist Manual"
.
.SH "NAME"
\fBgist\fR \- gist on the command line
.
.SH "SYNOPSIS"
\fBgist\fR [\fB\-p\fR] [\fB\-t extension\fR] \fIFILE|\-\fR
.
.SH "DESCRIPTION"
\fBgist\fR can be used to create gists on gist\.github\.com from the command line\. There are two primary methods of creating gists\.
.
.P
If standard input is supplied, it will be used as the content of the new gist\. If \fIFILE\fR is provided, the content of that file will be used to create the gist\. If \fIFILE\fR is \'\-\' then gist will wait for content from standard input\.
.
.P
Once your gist is successfully created, the URL will be copied to your clipboard\. If you are on OS X, \fBgist\fR will open the gist in your browser, too\.
.
.SH "OPTIONS"
\fBgist\fR\'s default mode of operation is to read content from standard input and create a public, text gist without description from it, tied to your GitHub account if you user and passwordare provided (see \fBCONFIGURATION\fR)\.
.
.P
These options can be used to change this behavior:
.
.TP
\fB\-p\fR, \fB\-\-private\fR
Create a private gist instead of a public gist\.
.
.TP
\fB\-t\fR, \fB\-\-type\fR
Set the file extension explicitly\. Passing a type of \fBrb\fR ensures the gist is created as a Ruby file\.
.
.TP
\fB\-d\fR, \fB\-\-description\fR
Set a description\.
.
.TP
\fB\-o\fR, \fB\-\-[no\-]open\fR
Open the gist in your browser after creation\. Or don\'t\. Defaults to \-\-open
.
.P
You may additionally ask for help:
.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help\.
.
.TP
\fB\-m\fR, \fB\-\-man\fR
Display this man page\.
.
.SH "AUTHENTICATION"
There are two ways to set GitHub user and password info:
.
.IP "\(bu" 4
Using environment vars GITHUB_USER and GITHUB_PASSWORD
.
.IP
$ export GITHUB_USER=johndoe
.
.br
$ export GITHUB_PASSWORD=mysecretgithubpassword
.
.br
$ gist ~/example
.
.IP "\(bu" 4
Using git\-config(1)
.
.IP "" 0
.
.P
Use git\-config(1) to display the currently configured GitHub username:
.
.IP "" 4
.
.nf
$ git config \-\-global github\.user
.
.fi
.
.IP "" 0
.
.P
Or, set the GitHub username with:
.
.IP "" 4
.
.nf
$ git config \-\-global github\.user <username>
.
.fi
.
.IP "" 0
.
.P
See \fIhttp://github\.com/guides/local\-github\-config\fR for more information\.
.
.SH "CONFIGURATION"
You can set a few options in your git config (using git\-config(1)) to control the default behavior of gist(1)\.
.
.IP "\(bu" 4
gist\.private \- boolean (yes or no) \- Determines whether to make a gist private by default
.
.IP "\(bu" 4
gist\.extension \- string \- Default extension for gists you create\.
.
.IP "\(bu" 4
gist\.browse \- boolean (yes or no) \- Whether to open the gist in your browser after creation\. Default: yes
.
.IP "" 0
.
.SH "ENVIRONMENT"
The following environment variables affect the execution of \fBgist\fR:
.
.IP "\(bu" 4
HTTP_PROXY \- Proxy to use when Gisting\. Should be "http://host:port/"
.
.IP "" 0
.
.SH "EXAMPLES"
.
.nf
$ gist < file\.txt
$ echo secret | gist \-\-private
$ echo "puts :hi" | gist \-t rb
$ gist script\.py
$ gist \-
the quick brown fox jumps over the lazy dog
^D
.
.fi
.
.SH "BUGS"
\fIhttp://github\.com/defunkt/gist/issues\fR
.
.SH "AUTHOR"
Chris Wanstrath :: chris@ozmm\.org
.
.SH "SEE ALSO"
hub(1), git(1), git\-clone(1), \fIhttp://github\.com\fR, \fIhttp://github\.com/defunkt/gist\fR
__CACERT__
---
Certificate chain
0 s:/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=*.github.com
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
-----BEGIN CERTIFICATE-----
MIIGpjCCBY6gAwIBAgIQCc4q8gtIK3iiFj+bqb1z9jANBgkqhkiG9w0BAQUFADBm
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBDQS0zMB4XDTEyMDQzMDAwMDAwMFoXDTE0MDcwOTEyMDAwMFowaDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEVMBMGA1UEAwwMKi5naXRodWIuY29t
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA70XN+uwT7z4M04aFUwEJ
ypo6Tlr5gMvBvVFQnJlEqM3rYeqVHU8wU8af1tNV7dE6OkPpbdQ3yYgT2kWOXJnw
gR16Bzbqsd8OPGAFkKIS2zVm1uB3qKN5UaZTEE83vEbjjtwQE5OZDWqzEB2HFN14
YHtUezSp8unjO19RtWqnYiC/neEqdX2UJFJKjc0tm1yWL9jf6P04vYCsEWBh57O3
v4GugyHJ63SI8n0RZgNCX6dV9OwAq/Eju1qr+8p8E6sojA7BIvmUJMoGpNKoRtbU
RhjlzyG2udbZUYY5UGYEqQZgDx1vqKCbgq9xQ2RVd6ZWsW017HyvSK0BLnYtFubX
wQIDAQABo4IDTDCCA0gwHwYDVR0jBBgwFoAUUOpzidsp+xCPnuUBINTeeZlIg/cw
HQYDVR0OBBYEFIFnymFWE2hKxPmD1vRNyJ+hiKP6MCMGA1UdEQQcMBqCDCouZ2l0
aHViLmNvbYIKZ2l0aHViLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMF8GA1UdHwRYMFYwKaAnoCWGI2h0dHA6Ly9jcmwz
LmRpZ2ljZXJ0LmNvbS9jYTMtZzguY3JsMCmgJ6AlhiNodHRwOi8vY3JsNC5kaWdp
Y2VydC5jb20vY2EzLWc4LmNybDCCAcQGA1UdIASCAbswggG3MIIBswYJYIZIAYb9
bAEBMIIBpDA6BggrBgEFBQcCARYuaHR0cDovL3d3dy5kaWdpY2VydC5jb20vc3Ns
LWNwcy1yZXBvc2l0b3J5Lmh0bTCCAWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkA
IAB1AHMAZQAgAG8AZgAgAHQAaABpAHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUA
IABjAG8AbgBzAHQAaQB0AHUAdABlAHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAA
bwBmACAAdABoAGUAIABEAGkAZwBpAEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEA
bgBkACAAdABoAGUAIABSAGUAbAB5AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIA
ZQBlAG0AZQBuAHQAIAB3AGgAaQBjAGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkA
bABpAHQAeQAgAGEAbgBkACAAYQByAGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUA
ZAAgAGgAZQByAGUAaQBuACAAYgB5ACAAcgBlAGYAZQByAGUAbgBjAGUALjB7Bggr
BgEFBQcBAQRvMG0wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
bTBFBggrBgEFBQcwAoY5aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
ZXJ0SGlnaEFzc3VyYW5jZUNBLTMuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
AQEFBQADggEBABArBePXElSsd5Ec5IIqfb7MBAafKK1jbFstNTyf175xDJAUPh3p
5fsB72UOpm1rdfn5/aDc1rgTKiL3t+1ALcHvyIhaxkf2pe39W9zN2bAy0Yx+sSOf
GEi0LHMfzeDkmaeHrQXUjWBQoE4QOIon7AoBsylNK6gbsLryiab0rJ565/BEhC16
BfZt0XgSRJHF18enz5vjf2fK6CgtM1poVVEPdxfr9qxdJ7A9MBBw+OcB+JwWdE89
IzV94FOVPClMm1G87wcFx0cIqYvA3j8g6UvIOeG63PWLmXhAeHCGmVEoIjQz96S/
Ox6cL5JR0RfxxBvKCzONM8XS2pU1dvUlcpw=
-----END CERTIFICATE-----
1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
-----BEGIN CERTIFICATE-----
MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR
CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv
KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5
BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf
1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs
zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d
32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w
ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3
LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH
AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm
MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB
hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln
aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl
cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME
GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB
INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a
vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j
CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X
dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE
JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY
Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E=
-----END CERTIFICATE-----
DigiCert High Assurance EV Root CA
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment