diff options
Diffstat (limited to 'content/posts/how-bsd-authentication-works/gen_dot.rb')
-rwxr-xr-x | content/posts/how-bsd-authentication-works/gen_dot.rb | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/content/posts/how-bsd-authentication-works/gen_dot.rb b/content/posts/how-bsd-authentication-works/gen_dot.rb new file mode 100755 index 0000000..9f71876 --- /dev/null +++ b/content/posts/how-bsd-authentication-works/gen_dot.rb @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Copyright (C) 2021 Dante Catalfamo +# SPDX-License-Identifier: MIT + +require 'digest' +require 'set' + +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} [color = \"##{color}\"]" + end + + def color + Digest::MD5.hexdigest(from)[..5] + 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"]' + all_functions = Set.new + @subgraphs.each { |s| all_functions.merge(s.functions) } + @subgraphs.each(&:emit) + @pairs.uniq { |p| [p.to, p.from] }.each do |p| + p.emit if all_functions.include?(p.to) + end + 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 |