#!/usr/bin/ksh93
################################################################
function usagemsg_unexpand_k93 {
  print -- "
Program: unexpand_k93

This Korn Shell emulation of the Unix \"unexpand\" command puts tabs back
into the data from the standard input or the named files and writes the
result to standard output.  By default, only leading spaces and tabs are
reconverted to maximal strings of tabs. 

Usage: ${1##*/} [-vV] [-a] [-t #] [FILE]...

    Where -v = Verbose mode
          -V = Very Verbose mode
          -a = Inserts tabs wherever their presence compresses the 
               resultant file by replacing two or more characters.
        -t # = Specifies the position of the tab stops. The default 
               value of a tab stop is 8 column positions.

Author: Dana French (dfrench@mtxia.com)    Copyright 2004

\"AutoContent\" enabled"
  return 0
}
################################################################
#### 
#### Description:
#### 
#### This Korn Shell emulation of the Unix "unexpand"
#### command puts tabs back into the data from the standard
#### input or the named files and writes the result to
#### standard output.  By default, only leading spaces and
#### tabs are reconverted to maximal strings of tabs. 
#### 
#### Assumptions:
#### 
#### It is assumed the number of columns occupied by a tab
#### character is 8.
#### 
#### Dependencies:
#### 
#### Products:
#### 
#### The output from this function is the data read from
#### files or standard input with multiple spaces replaced by
#### one or more tab characters.  The column alignment from
#### the input is retained in the output.
#### 
#### Configured Usage:
#### 
#### This script would normally be used as a function in a Korn Shell
#### function library to aid in the building and usage of 
#### multi-dimensional korn shell arrays.
#### 
#### Details:
#### 
################################################################
function unexpand_k93 {
  typeset TRUE="0"
  typeset FALSE="1"
  typeset ALLTABS="${FALSE}"
  typeset TABLEN="8"
  typeset VERBOSE="${FALSE}"
  typeset VERYVERB="${FALSE}"
  typeset FNAME

  while getopts ":at#vV" OPTION
  do
    case "${OPTION}" in
        'a') ALLTABS="${TRUE}";;
        't') TABLEN="${OPTARG}";;
        'v') VERBOSE="${TRUE}";;
        'V') VERYVERB="${TRUE}";;
        '?') usagemsg_unexpand_k93 "${0}" && return 1 ;;
    esac
  done
 
  shift $(( ${OPTIND} - 1 ))

  typeset STDIN="${1:+${FALSE}}"
  STDIN="${STDIN:-${TRUE}}"

#### Read in the data either from STDIN or one or more files
  if (( STDIN == TRUE ))
  then
      unexpandTab_k93 "${TABLEN}" "${ALLTABS}"
  else
      for FNAME in "${@}"
      do
          exec 0<"${FNAME}"
            unexpandTab_k93 "${TABLEN}" "${ALLTABS}"
          exec 0<&-
      done
  fi

  return 0
}
################################################################
function unexpandTab_k93 {
  typeset TABLEN="${1}"
  typeset ALLTABS="${2}"
  typeset TRUE="0"
  typeset FALSE="1"
  typeset NONWHITE="${FALSE}"
  typeset TABSDONE="${FALSE}"
  typeset SPCS="        "
  typeset IDX="0"
  typeset CURCOL="0"
  typeset LINE
  typeset END

#### Read each line of data from standard input or from
#### one or more files.

  while IFS="" read -r -- LINE
  do

#### Determine the number of characters in the line of data.
    END="${#LINE}"

#### Assume the first character is a non-whitespace character.
    NONWHITE="${TRUE}"

#### The beginning of each line is always unexpanded, so
#### initialize a TAB status variable to FALSE.
    TABSDONE="${FALSE}"

#### Each line begins at column 0.
    CURCOL=0

#### The last printed column also begins at
#### column 0.
    WORKCOL=0

#### At the beginning of each line, initialize the
#### number of spaces counter to 0.
    SPCS=0

#### Loop through each character of the line to convert
#### spaces to tabs.

    for (( IDX=0; IDX<=(END-1); ++IDX ))
    do

#### Check each character to determine if it is a space
#### or a tab, and that the tab status is FALSE, meaning 
#### that multiple spaces should be processed into tabs.

      if [[ "_${LINE:IDX:1}" = _[$' \t'] ]] && (( TABSDONE == FALSE ))
      then

#### 
#### If the current character is a space or a tab, set
#### the NONWHITE variable to FALSE, meaning the current
#### character is NOT a nonwhite character.
        NONWHITE="${FALSE}"

#### Increment the number of spaces counter by one character.
        (( SPCS++ ))

#### Increment the current column counter by one column.
        (( CURCOL++ ))

#### Continue with the next iteration of the loop, do
#### not print the whitespace character.  Just gathering the
#### number of concurrent whitespace characters for later
#### processing. 
        continue
      fi

#### If the TAB status is TRUE meaning that no more
#### spaces in the current line should be converted to tabs,
#### then set the NONWHITE variable to true.  This means that
#### all subsequent characters in the current line will be
#### processed normally, multiple spaces will not be
#### converted to tabs.

      (( TABSDONE == TRUE )) && NONWHITE="${TRUE}"

#### At this point if the NONWHITE variable is false,
#### this means the current character is not a whitespace
#### character, otherwise it would have been processed by the
#### previous "if" statement and would have returned for the
#### next iteration of the for loop.

      if (( NONWHITE == FALSE ))
      then

#### Since we know the current character is not a
#### whitespace character, set the NONWHITE variable to TRUE.

        NONWHITE="${TRUE}"

#### If the current column number is not equal to the
#### column number of the last non-whitespace character, this
#### means there are whitespace characters that may need to
#### be converted into tab characters.

        if (( CURCOL != WORKCOL ))
        then
    
#### Check to see if the current column is on a tab boundary,
#### if so then replace the spaces with the appropriate number
#### of tab characters to preserve the column alignment of
#### the data.

          if (( ( CURCOL % TABLEN ) == 0 ))
          then

#### Determine the number of tab characters required to fill
#### the spaces, and add 1.

            (( TABEND = ( SPCS / TABLEN ) + 1 ))

#### If the number of spaces is evenly divisible by the
#### length of a tab character, then set the number of tab
#### characters to that number.

            (( ( SPCS % TABLEN ) == 0 )) && (( TABEND = SPCS / TABLEN ))

#### Print the calculated number of tab characters 
#### without a newline character at the end.

            for (( TABIDX=0; TABIDX < TABEND; ++TABIDX ))
            do
              print -r -n -- $'\t'
            done        

#### Add the number of spaces processed to the column
#### counter associated with the last printed character.

            (( WORKCOL = WORKCOL + SPCS ))

#### If the current column is NOT on a tab boundary, then
#### replace the spaces with the appropriate number of tabs
#### and spaces to preserve the column alignment of the data.

          else

# did not end on a tab boundary - fill TABLEN spaces with tabs, leave remainder

#### Determine the number of tab characters needed to fill
#### the whitespace by dividing the current column number by
#### the length of a tab, then subtract the last printed
#### column number divided by the length of a tab.

            (( TABEND = ( CURCOL / TABLEN ) - ( WORKCOL / TABLEN ) ))

#### Initialize a variable to track the printed column
#### position, while the tab characters are printed. 

            NEWCOL="${WORKCOL}"

#### Print each tab character, but do not print a
#### newline at the end.

            for (( TABIDX=0; TABIDX < TABEND; ++TABIDX ))
            do
              print -r -n -- $'\t'
#### Recalculate the printed column position
              (( NEWCOL = ( ( NEWCOL / TABLEN ) + 1 ) * TABLEN ))
            done        

#### Determine the number of remaining spaces after the
#### tab characters have been printed, by subtracting the
#### printed column position from the current column number.

            (( SPCS = CURCOL - NEWCOL ))

#### Ensure the number of spaces that will be printed is
#### less that the length of a tab.

            (( SPCEND = SPCS % TABLEN ))

#### Print each space character, one at a time without a
#### newline character at the end.

            for (( SPCIDX=0; SPCIDX < SPCEND; ++SPCIDX ))
            do
              print -r -n -- ' '
            done        

#### When the spaces have been printed, the printed
#### column number should now be equal to the current column
#### number, so set it equal to the current column number.

            (( WORKCOL = CURCOL ))

          fi

#### After any whitespace characters were converted to
#### tabs and printed, then print the current non-whitespace
#### character from the data line. Do not print a newline at
#### the end.

          print -r -n -- "${LINE:IDX:1}"

#### Increment the current column number by one column.
          (( CURCOL++ ))

#### Increment the printed column number by one column.
          (( WORKCOL++ ))

####
#### If the "-a" command line option was used, this means
#### that all whitespace in the line should be processed for
#### tab replacement.  If this option is not used then only
#### the beginning of the line, up to the first
#### non-whitespace character is processed for tab
#### replacement.  If the option is not used, set the TAB
#### status variable to TRUE, meaning no more whitespace will
#### be processed into tabs on the current line.
#### 
          (( ALLTABS == FALSE )) && TABSDONE="${TRUE}"

       fi

#### Reset the number of spaces counter to zero since
#### all whitespace characters up to this point in the
#### current data line have been processed.

       SPCS=0

      else

#### If the NONWHITE variable is TRUE, this means the
#### current character is a not a whitespace character. 
#### Print the character, but do not print a newline
#### character at the end.
          print -r -n -- "${LINE:IDX:1}"
#### Increment the current column number by one column.
          (( CURCOL++ ))
#### Increment the printed column number by one column.
          (( WORKCOL++ ))

#### 
#### Again, if the "-a" command line option was used, this
#### means that all whitespace in the line should be
#### processed for tab replacement.  If this option is not
#### used then only the beginning of the line, up to the
#### first non-whitespace character is processed for tab
#### replacement.  If the option is not used, set the TAB
#### status variable to TRUE, meaning no more whitespace will
#### be processed into tabs on the current line.
#### 
          (( ALLTABS == FALSE )) && TABSDONE="${TRUE}"

      fi
#### Retrieve the next character in the current data line.

    done # for loop

#### After all characters in the current data line have
#### been processed, print a newline character.
    print

#### Retrieve the next data line.
  done # while loop

#### This function is complete, return to the calling function.
  return 0
}
################################################################

#### Call the unexpand function with all command line arguments.

unexpand_k93 "${@}"