#!/bin/ksh93

## Version: $Id: listings-ext.dtx 67 2010-06-29 16:38:12Z ax006ho $
## Keywords: LaTeX, listings
##-------------------------------------------------------------------
##
## This file may be distributed and/or modified under the
## conditions of the LaTeX Project Public License, either version 1.2
## of this license or (at your option) any later version.
## The latest version of this license is in:
##
##    http://www.latex-project.org/lppl.txt
##
## and version 1.2 or later is part of all distributions of LaTeX
## version 1999/12/01 or later.
##
## This file belongs to the listings-ext package.
VERSION='67 2010-06-29'
typeset -r FPROG=${.sh.file}
typeset -r PROG=${FPROG##*/}
SDIR=${FPROG%/*}

if [[ ${ uname -s ; } == 'SunOS' ]]; then
	GREP='/usr/xpg4/bin/grep'
	AWK='nawk'
else
	GREP='grep'
	AWK='awk'
fi

function showUsage {
	[[ -z $1 ]] && X='--man' || X='-?'
	getopts -a ${PROG} "${ print ${USAGE} ; }" OPT $X
}


function print_header {
	typeset FILE="$1" BN
    print "%%%% -- file $1 generated on ${ date  ; }by ${PROG}"
	BN="${FILE##*/}"
	BN="${BN%.lst}"
    BN=${BN//+([_-])}
    print "\\\\csname ${BN}LstLoaded\\\\endcsname" \
    	"\n\\\\expandafter\\\\let\\\\csname ${BN}LstLoaded\\\\endcsname\\\\endinput"
}

function doMain {
	[[ -z $1 ]] && showUsage 1 && return 1

	if [[ -n ${OUTFILE} ]]; then
		if [[ -e ${OUTFILE} ]]; then
			print "%--> file '${OUTFILE}' already exists, creating backup."
			mv "${OUTFILE}" "${OUTFILE}~"
		fi
		exec > "${OUTFILE}"				# redirect stdout to ${OUTFILE}
		print_header "${OUTFILE}"
	fi

	# now take all remaining arguments (should be all filenames) and do the job
	while [[ -n $1 ]]; do
		print "%%--> processing file '$1'"
		do_the_job "$1"
		shift
	done
}

function do_the_job {
	PATHCOMPONENT="${PWD}"
	SOURCEFILE=$1

	if [[ ! -f ${SOURCEFILE} ]]; then
        print -u2 "${SOURCEFILE} is no valid file."
		return 2
	fi
	SOURCEFILE=${SOURCEFILE##${PATHCOMPONENT}/}

	${GREP} -n "^[[:space:]]*[/%;#!][/\* ;][[:space:]]*[cbe]e:" $1 | \
	${AWK} -v pathcomponent="${PATHCOMPONENT}" -v file="${SOURCEFILE}" \
		-v header="${HEADER}" -v command="${COMMAND}" -v application="${PROG}" \
		-v debugmode="${DEBUG_MODE}" -v respect_extension="${EXTENSION}"\
		-F : \
'
BEGIN {
    is_opened = 0;
    initialised = 0;
    configured = 0;
    join = 0;
    combine = 0;

    idChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    split(idChar, idArray, "")
    idPt = 0
    linerange = "";

    linelen = 75; # length of the output line
    addchars = 8; # number of additional characters like %, > or so

    if ( debugmode ) printf("path: %s\n",  pathcomponent) > "/dev/stderr"
    if ( debugmode ) printf("file: %s\n", file) > "/dev/stderr"
    n = split(pathcomponent, partsa, "/");
    curr_dir = parts[n]
    n++;
    partsa[n] = file;
    inputfile = "/" partsa[2];
    for ( i = 3; i <= n; i++ ) {
        inputfile = inputfile "/" partsa[i];
    }
    if ( debugmode ) printf("inputfile: %s\n",  inputfile) > "/dev/stderr"
    n = split(file, partsa, "/")
    n = split(partsa[n], partsb, ".")
    identifier = partsb[1]
    for ( i = 2; i < n; i++ ) {
        identifier = identifier partsb[i]
    }
    if ( respect_extension ) {
        identifier = identifier partsb[n]
    }
    identifier = replace_characters(identifier)
    if ( debugmode ) printf("identifier: %s\n", identifier) > "/dev/stderr"
}
{
    if ( debugmode ) print $0 > "/dev/stderr"
    if ( !initialised ) {
        if ( match($2, /ce/) > 0 ) {
            n = split($(NF), text, ",");
            if ( match(text[1], "join") ) {
                join = 1;
            } else if ( match(text[1], "combine") ) {
                combine = 1;
            }
        } else if ( match($2, /be/) > 0 ) {
            opening_tag = $(NF)
            gsub(/\r/, "", opening_tag);
            is_opened = 1
            start = $1 + 1; # entry $1 is the line number
        } else if ( match($2, /ee/) > 0 ) {
            print_error($1, "missing \"be\" declaration")
        }
        initialised = 1;
    } else {
        tag = $(NF)
        if ( match($2, /be/) > 0 ) {
            if ( is_opened ) {
                print_error($1, "incorrect grouping, previous group"\
                " not closed")
            } else {
                opening_tag = $(NF)
                gsub(/\r/, "", opening_tag);
                is_opened = 1
                start = $1 + 1; # entry $1 is the line number
            }
        } else {
            if ( match($2, /ee/) > 0 ) {
                closing_tag = $(NF)
                gsub(/\r/, "", closing_tag);
                if ( !is_opened ) {
                    print_error($1, "missing \"be\" declaration")
                } else if ( opening_tag == closing_tag ) {
                    is_opened = 0
                    split($(NF), text, "*"); # omit a trailing comment
                    sub(/ /, "", text[1]); # delete leading spaces
                    gsub(/"/, "", text[1]);
                    if ( index(text[1], "_") > 0 ) gsub(/_/, "\\_", text[1]);
                    caption =  "\\lstinline|" file "|: " text[1]

                    # setup the prefixes
                    len = linelen - addchars - length(caption);
                    begin_prefix = "%%";
                    cnt = 0;
                    while ( cnt < len) {
                        begin_prefix = begin_prefix "=";
                        cnt++;
                    };
                    begin_prefix = begin_prefix ">";
                    end_prefix = begin_prefix;
                    sub(/%%/, "%%==", end_prefix);
                    if ( join ) {
                        linerange = linerange ", " start "-" $1-1;
                    } else if ( combine ) {
                        sub(/ /, "", closing_tag)
                        if ( combine_array[closing_tag] == "" ) {
                            combine_array[closing_tag] = start "-" $1-1
                        } else {
                            combine_array[closing_tag] = \
                            combine_array[closing_tag] ", " start "-" $1-1
                        }
                        if ( debugmode ) printf("combine_array: >%s<\n",\
                                combine_array[closing_tag]) > "/dev/stderr"
                    } else {
                        linerange = start "-" $1-1;
                        if ( command ) {
                            print_command(\
                                    (identifier toB26(idPt++)), \
                                    linerange);
                        } else {
                            print_linerange(\
                                    (identifier toB26(idPt++)), \
                                    caption, linerange);
                        }
                    }
                } else if ( opening_tag != closing_tag ) {
                    print_error($1, "opening and closing tags differ")
                } else {
                    print_error($1, "unknown error")
                }
            }
        }
    }
}
END {
    if ( is_opened ) {
        print_error($1, "missing \"ee\" declaration")
    }
    caption = "\\lstinline|" file "|";
    if ( join ) {
        sub(/, /, "", linerange);
        if ( command ) {
            print_command(identifier, linerange);
        } else {
            caption = "\\lstinline|" file "|";
            print_linerange(identifier, caption, linerange);
        }
    } else if ( combine ) {
        for ( range in combine_array ) {
            if ( debugmode ) printf("range: %s, combine_array[range]: >%s<\n", \
                    range, combine_array[range]) > "/dev/stderr"
            printf("%%%%-->> %s <<--\n", range)
            if ( command ) {
                print_command((identifier toB26(idPt++)), \
                        combine_array[range]);
            } else {
                print_linerange((identifier toB26(idPt++)), caption, \
                        combine_array[range]);
            }
        }
    }
}
function replace_characters(identifier) {
    tmp = ""
    toUppercase = 0
    n = split(identifier, sequence, "") # split the string into an array
                                        # of single characters
    for ( i = 1; i <= n; i++ )
    {
        if ( (sequence[i] == "_") || (sequence[i] == "-") ) {
            toUppercase = 1
        } else {
            if ( toUppercase ) {
                cTmp = toupper(sequence[i])
                tmp = (tmp cTmp)
                toUppercase = 0
            } else {
                tmp = (tmp sequence[i])
            }
        }
    }
    return tmp
}
function print_command(identifier, linerange) {
    if ( debugmode ) printf("print_command.linerange: >%s<\n", linerange) > "/dev/stderr"
    print break_line("\\lstdef{" identifier "}{" inputfile "}{" \
            linerange "}", linelen)
}
function print_linerange(identifier, caption, linerange) {
    print break_line(begin_prefix "begin{" \
        identifier"}\n{%\n  \\def\\inputfile{%\n    " inputfile "%\n  }");
    local_style = "";
    if ( header )
    {
        print "  \\ifLecture{%\n    \\lstdefinestyle{localStyle}{%\n      " \
            "caption={" caption"}\n    }%\n  }\n  {%\n" \
            "    \\lstdefinestyle{localStyle}{%\n" \
            "      title={" caption "}\n    }%\n  }";
        local_style="style=localStyle, "
    }
    print "  \\lstinputlisting[" local_style "linerange={" linerange "}]" \
        "{%\n    \\inputfile\n  }\n" \
        "}%\n" end_prefix "end{"identifier"}";
}
function print_error(linenumber, error_message)
{
    printf "%--> error (line number %d): %s\n", \
        linenumber, error_message > "/dev/stderr"
}
function break_line(input_line, line_len) {
    n = split(input_line, parts, "/");
    output_line = parts[1];
    len_curr = length(output_line)
    for ( i = 2; i <= n; i++ ) {
        len_next = length(parts[i])
        if ( len_curr + len_next + 1 < linelen ) {
            output_line = output_line "/" parts[i];
            len_curr += len_next + 1 # continue current line
        } else {
            output_line = output_line "%\n      /" parts[i];
            len_curr = len_next + 7 # start a new line
        }
    }
    return output_line
}
function toB26(n10) {
    v26 = ""
    do {
      remainder = n10%26
      v26 = idArray[remainder+1] v26
      n10 = int(n10/26)
    } while ( n10 != 0 )
    return v26
}
'
  return $?
}

USAGE='[-?'"${VERSION}"' ]
[-copyright?(C) 2008-2010 Jobst Hoffmann, <j.hoffmann@fh-aachen.de>]
[-copyright?Copyright (c) 2015 Jens Elkner (ksh93 port).]
[-license?LaTeX Project Public License 1.2]
[+NAME?'"${PROG}"' - source file integration helper]
[+DESCRIPTION?'"${PROG}"' is a script to simplify the insertion of parts of source code files into a LaTeX document(ation).]
[h:help?Print this help and exit.]
[F:functions?Print a list of all functions available.]
[T:trace]:[fnList?A comma separated list of functions of this script to trace (convinience for troubleshooting).]
[+?]
[c:command?Generate commands, which can be input and later used by \\\\lstuse.]
[e:ext?Include file name extension into the identifier.]
[g:debug?Debug mode, create some additional output.]
[n:no-header?Write no header into the LaTeX code. Valid only, if -c is not used.]
[o:output-file]:[file?Redirect the standard output to the given \afile\a.]
[+SEE ALSO?https://www.ctan.org/tex-archive/macros/latex/contrib/listings-ext]
'
integer DEBUG_MODE=0 EXTENSION=0 HEADER=1 COMMAND=0
unset OUTFILE; OUTFILE=
X="${ print ${USAGE} ; }"
while getopts "${X}" OPT ; do
	case ${OPT} in
		T)	if [[ ${OPTARG} == 'ALL' ]]; then
				typeset -ft ${ typeset +f ; }
			else
				typeset -ft ${OPTARG//,/ }
			fi
			;;
		F)
			typeset +f && exit 0
			;;
		h) showUsage ; exit 0 ;;
		c) COMMAND=1; HEADER=0 ;;
		e) EXTENSION=1 ;;
		g) DEBUG_MODE=1 ;;
		n) HEADER=0 ;;
		o) OUTFILE="${OPTARG}" ;;
		*) showUsage 1 ; exit 1 ;;
	esac
done
X=$((OPTIND-1))
shift $X && OPTIND=1
unset X

doMain "$@"
