

function generate(io::IO, libCvts::Union{Pair{<:AbstractString, Vector{Converted}}, Pair{Symbol, Vector{Converted}}, Pair{Nothing, Vector{Converted}}}...; relto::String = ".", comments::Bool = true, exports::Bool = true)
	println(io, "# This file is automatically generated.  Do not edit.")
	println(io, "using CBinding: 𝐣𝐥")
	foreach(libCvts) do (lib, cvts)
		println(io)
		lib = isnothing(lib) ? " " : " $(lib isa AbstractString ? repr(String(lib)) : String(lib)) "
		println(io, "𝐣𝐥.@cbindings", lib, "begin")
		generate(io, cvts, 1, relto = relto, comments = comments, exports = exports)
		println(io, "end")
	end
end


generate(io::IO, cvts::Vector{Converted}, indent::Int; relto::String = ".", comments::Bool = true, exports::Bool = true) = foreach(enumerate(cvts)) do (ind, cvt)
	ind > 1 && println(io, repeat('\t', indent))
	generate(io, cvt, indent, relto = relto, comments = comments, exports = exports)
end


function generate(io::IO, cvt::Converted, indent::Int; relto::String = ".", comments::Bool = true, exports::Bool = true)
	tabs = repeat('\t', indent)
	
	if comments
		for (name, comment) in cvt.comments
			isempty(name) && continue
			
			name = startswith(name, '@') ? ":("*name*")" : name
			
			comment = """
			𝐣𝐥.@doc \"\"\"
			$(rstrip(replace(replace(string(comment, expr = (startswith(cvt.expr, "𝐣𝐥.@cextern") ? cvt.expr : ""), relto = relto), '\\' => "\\\\"), '\"' => "\\\""), '\n'))
			\"\"\" $(name)
			"""
			
			println(io, tabs, rstrip(replace(comment, '\n' => "\n$(tabs)")))
		end
	end
	
	if exports
		exps = sort(collect(filter(str -> !isempty(str) && !startswith(str, '_'), keys(cvt.comments))))
		isempty(exps) || println(io, tabs, "export $(join(exps, ", "))")
	end
	
	println(io, tabs, replace(cvt.expr, '\n' => "\n$(tabs)"))
end

