#!/bin/bash -p

# Script to process man pages output by doxygen.
# We need to use bash for its associative array facility.
# (`bash -p` prevents import of functions from the environment).

declare -A renamed_page

main(){
  set -e
  cd man/man3; rm -f _*
  count_real_pages
  rename_real_pages
  make_symlinks
  post_process
}

count_real_pages(){
  page_count=0
  #
  # Count "real" man pages (i.e. not generated by MAN_LINKS)
  # MAN_LINKS pages are 1-liners starting .so
  # Method: list files in descending order of size,
  # looking for the first 1-liner
  #
  for i in $(ls -S)
  do head -n1 $i | grep -E -q '^\.so' && break
    page_count=$(($page_count + 1))
  done
  first_link=$(($page_count + 1))
}

rename_real_pages(){
  for i in $(ls -S | head -n$page_count)
  do for j in $(ls -S | tail -n+$first_link)
    do grep -E -q $i$ $j && break
    done
    mv -f $i $j
    renamed_page[$i]=$j
  done
}

make_symlinks(){
  for j in $(ls -S | tail -n+$first_link)
  do ln -sf ${renamed_page[$(cat $j | cut -f2 -d/)]} $j
  done
}

post_process(){
  make_temp_files
  #
  # DIAGNOSTIC / DEVELOPMENT CODE
  # set -x and restrict processing to keep_me: un-comment to activate
  # Change keep_me as required
  #
  #keep_me=nfq_icmp_get_hdr.3;\
  #do_diagnostics;\
  #
  # Work through the "real" man pages
  for target in $(ls -S | head -n$page_count)
  do mygrep "^\\.SH \"Function Documentation" $target
    # Next file if this isn't a function page
    [ $linnum -ne 0 ] || continue

    del_modules
    del_bogus_synopsis
    fix_name_line
    move_synopsis
    del_empty_det_desc
    del_def_at_lines
    fix_double_blanks

    # Fix rendering of verbatim "\n" (in code snippets)
    sed -i 's/\\n/\\\\n/' $target

  done

  remove_temp_files
}

fix_double_blanks(){
  linnum=1
  #
  # Older versions of man display a blank line on encountering "\fB\fP";
  # newer versions of man do not.
  # doxygen emits "\fB\fP" on seeing "\par" on a line by itself.
  # "\par" gives us double-spacing in the web doc, which we want, but double-
  # spacing looks odd in a man page so remove "\fB\fP".
  #
  while [ $linnum -ne 0 ]
  do mygrep \\\\fB\\\\fP $target
    [ $linnum -eq 0 ] || delete_lines $linnum $linnum
  done
}

del_def_at_lines(){
  linnum=1
  while [ $linnum -ne 0 ]
  do mygrep "^Definition at line [[:digit:]]* of file" $target
    [ $linnum -eq 0 ] || delete_lines $(($linnum - 1)) $linnum
  done
}

# Only invoked if you un-comment the 2 diagnostic / development lines above
do_diagnostics(){
  mv $keep_me xxx
  rm *.3
  mv xxx $keep_me
  page_count=1
  set -x
}

del_empty_det_desc(){
  mygrep "^\\.SH \"Function Documentation" $target
  i=$linnum
  mygrep "^\\.SH \"Detailed Description" $target
  [ $linnum -ne 0  ] || return 0
  [ $(($i - $linnum)) -eq 3 ] || return 0
  # A 1-line Detailed Description is also 3 lines long,
  # but the 3rd line is not empty
  i=$(($i -1))
  [ $(tail -n+$i $target | head -n1 | wc -c) -le 2 ] || return 0
  delete_lines $linnum $i
}

move_synopsis(){
  mygrep "SH SYNOPSIS" $target
  [ $linnum -ne 0  ] || return 0
  i=$linnum
  # If this is a doxygen-created synopsis, leave it.
  # (We haven't inserted our own one in the source yet)
  mygrep "^\\.SS \"Functions" $target
  [ $i -gt $linnum ] || return 0

  mygrep "^\\.SH \"Function Documentation" $target
  j=$(($linnum - 1))
  head -n$(($j - 1)) $target | tail -n$(($linnum - $i - 1)) >$fileC
  delete_lines $i $j
  mygrep "^\\.SS \"Functions" $target
  head -n$(($linnum - 1)) $target >$fileA
  tail -n+$(($linnum + 1)) $target >$fileB
  cat $fileA $fileC $fileB >$target
}

fix_name_line(){
  all_funcs=""

  # Search a shortened version of the page in case there are .RI lines later
  mygrep "^\\.SH \"Function Documentation" $target
  head -n$linnum $target >$fileC

  while :
  do mygrep ^\\.RI $fileC
    [ $linnum -ne 0 ] || break
    # Discard this entry
    tail -n+$(($linnum + 1)) $fileC >$fileB
    cp $fileB $fileC

    func=$(cat $fileG | cut -f2 -d\\ | cut -c3-)
    [ -z "$all_funcs" ] && all_funcs=$func ||\
      all_funcs="$all_funcs, $func"
  done
  # For now, assume name is at line 5
  head -n4 $target >$fileA
  desc=$(head -n5 $target | tail -n1 | cut -f3- -d" ")
  tail -n+6 $target >$fileB
  cat $fileA >$target
  echo "$all_funcs \\- $desc" >>$target
  cat $fileB >>$target
}

del_modules(){
  mygrep "^\.SS \"Modules" $target
  [ $linnum -ne 0  ] || return 0
  i=$linnum
  mygrep "^\\.SS \"Functions" $target
  delete_lines $i $(($linnum - 1))
}

del_bogus_synopsis(){
  mygrep "SH SYNOPSIS" $target
  #
  # doxygen 1.8.20 inserts its own SYNOPSIS line but there is no mention
  # in the documentation or git log what to do with it.
  # So get rid of it
  #
  [ $linnum -ne 0  ] || return 0
  i=$linnum
  # Look for the next one
  tail -n+$(($i + 1)) $target >$fileC;\
  mygrep "SH SYNOPSIS" $fileC
  [ $linnum -ne 0  ] || return 0

  mygrep "^\\.SS \"Functions" $target
  delete_lines $i $(($linnum - 1))
}

# Delete lines $1 through $2 from $target
delete_lines(){
  head -n$(($1 - 1)) $target >$fileA
  tail -n+$(($2 +1)) $target >$fileB
  cat $fileA $fileB >$target
}

mygrep(){
  set +e
  grep -En "$1" $2 2>/dev/null >$fileH
  [ $? -ne 0 ] && linnum=0 ||\
    { head -n1 $fileH >$fileG; linnum=$(cat $fileG | cut -f1 -d:); }
  set -e
}

make_temp_files(){
  temps="A B C G H"
  for i in $temps
  do declare -g file$i=$(mktemp)
  done
}

remove_temp_files(){
  for i in $temps
  do j=file$i
    rm ${!j}
  done
}

main
