setext-headings (Source)

#!/usr/bin/awk -f
##---------------------------------------------------------------------------##
#   Program: setext-headings
#   Date:    New Year's Day 2020
#   Purpose: Promote or demote setext headings
#
#   * "Promote" means to move level 3 headings to level 2, level 3 headings to
#     level 2, etc. Any level 1 headings will be left alone.
#   * "Demote" means the opposite: level 1 headings become level 2, level 2
#     headings become level 3, etc.
#
#   * Unlike genTOC, which dynamically determines heading levels as it encounters
#     new underlines, this program uses a fixed set:
#
#       Level 1     Level 2     Level 3
#       *******     =======     -------
#
#       Level 4     Level 5     Level 6
#       ~~~~~~~     .......     '''''''
#
#   This program is intended to be symlinked to "setext-headings-promote" and
#   "setext-headings-demote"; it can't be run simply as "setext-headings".
##---------------------------------------------------------------------------##
# BUUS: This script is part of Brian's Useful Utilities Set

BEGIN {
    if (! match(PROCINFO["argv"][2], /-(pro|de)mote$/, a)) {
        print "Error: cannot run as \"setext-headings\". Symlink to setext-headings-promote" >"/dev/stderr"
        print "and setext-headings-demote, and run as one of those names." >"/dev/stderr"
        exit 1
    }
    h_chars = "*=-~.\047"
    direction = a[1] == "de" ? 1 : -1
}

match($0, /^([[:space:]]*)([\*=\~\.\047-]+)$/, a) {
    s1 = length(a[1])   # Number of leading spaces on this line
    match(prev_line, /^([[:space:]]*)(.*)/, b)
    s2 = length(b[1])   # Number of leading spaces on previous line
    i = length(a[2]) - length(b[2])
    i = i < 0 ? -i : i  # abs() value of difference in text lengths
    if (s1 == s2 && i <= 2) {
        i = index(h_chars, substr(a[2], 1, 1)) + direction
        if (i < 1) i = 1; if (i > 6) i = 6
        x = ""; for (j=0; j<length(a[2]); j++) x = x substr(h_chars, i, 1)
        $0 = a[1] x
    }
}

{ print; prev_line = $0 }

# vim: tabstop=4