# typed: false # frozen_string_literal: true SOURCE_DIR = File.join Dir.home, 'src', 'github.com', 'openbsd', 'src', 'lib', 'libc', 'gen' FILENAMES = %w[authenticate.c auth_subr.c login_cap.c].freeze FUNCTION_REGEX = /^\w.*?$\n(?!DEF)(\w*)\(.*?\)\n\{(.*?)^\}/m.freeze ONELINE_FUNCTION_REFGEX = /^\w.*?(\w*)\(.*?\).*?\{(.*?)\}/.freeze CALL_REGEX = /[^\n](\w+)\(.*?\)/.freeze class FunctionDigraph attr_accessor :pairs, :subgraphs class Subgraph attr_accessor :name, :label, :functions def initialize(name, label) @name = name @label = label @functions = [] end def emit puts "subgraph cluster_#{name} {" puts "label = \"#{label}\"" functions.each { |f| puts f unless f == 'DEF_WEAK' } puts '}' end end class Pair attr_accessor :to, :from def initialize(from, to) @from = from @to = to end def emit puts "#{from} -> #{to}" if to =~ /auth|login|^_/ end end def initialize @pairs = [] @subgraphs = [] end def emit puts 'digraph G {' puts 'rankdir=LR' puts 'splines=ortho' puts 'graph [pad="0.5", nodesep="0.5", ranksep="1.5"]' @subgraphs.each(&:emit) @pairs.uniq { |p| [p.to, p.from] }.each(&:emit) puts '}' end def parse_files(filenames) filenames.each do |filename| contents = File.read(filename) basename = File.basename filename subgraph = Subgraph.new(basename.gsub(/\..*/, ''), basename) functions = contents.scan(FUNCTION_REGEX) oneliners = contents.scan(ONELINE_FUNCTION_REFGEX) functions.concat(oneliners) unless oneliners.empty? functions.each do |function| function_name = function[0] function_body = function[1] subgraph.functions << function_name function_body.scan(CALL_REGEX) do |call| @pairs << Pair.new(function_name, call[0]) end end @subgraphs << subgraph end end end fg = FunctionDigraph.new files = FILENAMES.map { |f| File.join(SOURCE_DIR, f) } fg.parse_files files fg.emit