#!/usr/bin/perl # # man-page-table-check - workaround for go-md2man bug that screws up tables # package Podman::ManPage::TableCheck; use v5.14; use utf8; use strict; use warnings; (our $ME = $0) =~ s|.*/||; ############################################################################### # BEGIN boilerplate args checking, usage messages sub usage { print <<"END_USAGE"; Usage: $ME [OPTIONS] $ME checks man pages (the *roff files produced by go-md2man) for empty table cells. Reason: go-md2man cannot handle markdown characters (e.g. asterisk) in tables. It produces horribly broken *roff which in turn makes unreadable man pages. If $ME finds broken tables, it will highlight them and display hints on how to resolve the problem. OPTIONS: --help display this message END_USAGE exit; } # Command-line options. Note that this operates directly on @ARGV ! our $debug = 0; our $force = 0; our $verbose = 0; our $NOT = ''; # print "blahing the blah$NOT\n" if $debug sub handle_opts { use Getopt::Long; GetOptions( 'debug!' => \$debug, help => \&usage, ) or die "Try `$ME --help' for help\n"; } # END boilerplate args checking, usage messages ############################################################################### ############################## CODE BEGINS HERE ############################### # The term is "modulino". __PACKAGE__->main() unless caller(); # Main code. sub main { # Note that we operate directly on @ARGV, not on function parameters. # This is deliberate: it's because Getopt::Long only operates on @ARGV # and there's no clean way to make it use @_. handle_opts(); # will set package globals die "$ME: Too many arguments; try $ME --help\n" if @ARGV; my $manpage_dir = 'docs/build/man'; # FIXME-hardcoding opendir my $dir_fh, $manpage_dir or die "$ME: Cannot opendir $manpage_dir: $!\n"; my @manpages; for my $ent (sort readdir $dir_fh) { next unless $ent =~ /^[a-z].*\.[1-8][a-z]?$/; # groff files only next if -l "$manpage_dir/$ent"; # skip links push @manpages, $ent; } closedir $dir_fh; @manpages or die "$ME: did not find any .[1-8] files under $manpage_dir\n"; my $errs = 0; for my $file (@manpages) { $errs += check_tables("$manpage_dir/$file"); } exit 0 if !$errs; die "\n$ME: found empty cells in the above man page(s) This is a bug in go-md2man: it gets really confused when it sees misaligned vertical-bar signs ('|') in tables, or a left-hand column with more than 31 characters. WORKAROUND: find the above line(s) in the docs/source/markdown file, then fix the issue (left as exercise for the reader). Keep regenerating docs until it passes: \$ make -C docs clean;make docs;$0 " } sub check_tables { my $path = shift; my $status = 0; my @cmd = ('man', '-l', '--no-hyphenation', '-Tlatin1', '-'); pipe my $fh_read, my $fh_write; my $kidpid = fork; if ($kidpid) { # we are the parent close $fh_write; } elsif (defined $kidpid) { # we are the child close $fh_read; open my $fh_in, '<:utf8', $path or die "$ME: Could not read $path: $!\n"; # groff spits out nasty useless warnings close STDERR; open STDOUT, '>&', $fh_write; open my $fh_man, '|-', @cmd or die "$ME: Could not fork: $! (message will never be seen)\n"; while (my $line = <$fh_in>) { $line =~ s/✅/OK/g; print { $fh_man } $line; } close $fh_in or die; close $fh_man or die; exit 0; } else { # fork failed die "$ME: could not fork: $!"; } my $linecount = 0; my $want = 0; while (my $line = <$fh_read>) { ++$linecount; chomp $line; # Table borders (+----------+------------+) if ($line =~ /^\s*\+-+\+-+/) { $want = 1; next; } # Row immediately after table borders elsif ($want) { # print $line, "\n"; # *Two* blank cells is OK, go-md2man always does this # on the last row of each table. if ($line !~ /^\s*\|\s+\|\s+\|/) { if ($line =~ /\|\s+\|/) { warn "\n$ME: $path:\n" if $status == 0; warn " $line\n"; $status = 1; } } } $want = 0; } close $fh_read; die "$ME: $path: command failed: @cmd\n" if $?; waitpid $kidpid, 0; if ($linecount < 10) { die "$ME: $path: nothing seen!\n"; } return $status; } 1;