#!/usr/bin/awk -f
#
# split_cmdlist -- separate command list into single commands
#
# Created: June 16, 2013
#
# OPERATION
#
#   Commands in a command list are separated by an unquoted semicolon (`;').
#   We cut sub-strings from the command list and echo them to stdout, but
#   without a trailing newline. When an unquoted semicolon is found or the
#   end of the command list, a newline is echo'ed to stdout in order to put
#   the single command on a separate line.
#
#   Due to overlap in the regular expressions, we first try to match single
#   quoted characters (including a quoted semicolon). Next, we try to match
#   single- and double-quoted sub-strings. Then, try matching anything that's
#   not quoted and is not a semicolon. Finally, we try to match a semicolon
#   or an empty string.
#   As a precaution, the script will abort if the command list could not be
#   matched.
#


//  {

    CmdList = $0;
    fDone   = 0;

    while( fDone != 1 )
    {
        # match (single) escaped character:
        pos = match( CmdList, /^[ \t]*\\.[ \t]*/ );
        if( pos > 0 ) { output_substring(); continue; }

        # match double-quoted sub-string:
        pos = match( CmdList, /^[ \t]*"[^"]*"[ \t]*/ );
        if( pos > 0 ) { output_substring(); continue; }

        # match single-quoted sub-string:
        pos = match( CmdList, /^[ \t]*'[^']*'[ \t]*/ );
        if( pos > 0 ) { output_substring(); continue; }

        # match unquoted sub-string:
        pos = match( CmdList, /^[ \t]*[^ \t"'\\;]+[ \t]*/ );
        if( pos > 0 ) { output_substring(); continue; }

        # match end-of-command:
        pos = match( CmdList, /^[ \t]*(;|$)[ \t]*/ );
        if( pos > 0 ) { fDone = end_of_cmd(); continue; }

        # should not get here:
        unmatched();
    }
}

# echo matched sub-string and remove it from command list
function output_substring()
{
    printf "%s", substr( CmdList, RSTART, RLENGTH );
    CmdList = substr( CmdList, RSTART + RLENGTH );
}

# echo newline and return whether we're done processing command list
function end_of_cmd()
{
    print "";

    # remove matching sub-string, if any:
    CmdList = substr( CmdList, RSTART + RLENGTH );

    # no more characters on command list, then we're done:
    return ( length( CmdList ) == 0 ? 1 : 0 );
}

# unmatched stuff, script will abort
function unmatched()
{
    print "unmatched sub-string: " CmdList | "cat 1>&2";
    # (using portable way of redirecting to stderr)

    exit 1;
}
