#!/bin/bash

ver="4.0.2003081219";
copy="1995-2003"
mail="PointedEars@gmx.de"
mail_feedback="bug-ascii@PointedEars.de"
# ----------------------------------------------------------------------------
#     ASCII-Tool for GNU/Linux 4.0+
#     Copyright (C) 1995-2003  Thomas Lahn <PointedEars@gmx.de>
#
#     This program is free software; you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation; either version 2 of the License, or
#     (at your option) any later version.
#
#     This program is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with this program; if not, write to the Free Software
#     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
## Standard shell script disclaimer blurb thing:
##
## This script is a hack.  It's brute force.  It's horrible.
## It doesn't use Artificial Intelligence.  It doesn't use Virtual Reality.
## It's not perl.  It's not python.  It probably won't work unchanged on
## the "other" thousands of unices.  But it worksforme.  --ramiro
# (from /usr/local/mozilla/run-mozilla.sh)
#
#     This is work in progress. If you have an improvement, patch,
#     idea, whatever, on how to make this script better, please
#     send it to <bug-ascii@PointedEars.de>
# 
# ----------------------------------------------------------------------------

# from /etc/rc.status
# check for $COLUMNS
if test -z "$LINES" -o -z "$COLUMNS" ; then
  eval `stty size 2>/dev/null | (read L C; echo LINES=${L:-24} \
COLUMNS=${C:-80})`
fi
test $LINES   -eq 0 && LINES=24
test $COLUMNS -eq 0 && COLUMNS=80

if test "$TERM" != "raw" && stty size > /dev/null 2>&1 ; then
  esc=`echo -en "\033"`
  extd="${esc}[1m"
  norm=`echo -en "${esc}[m\017"`
else
	esc=""
  extd=""
  norm=""
fi


Title () {
  echo "
${extd}ASCII-Tool $ver$norm
Copyright (C) $copy  Thomas Lahn <$mail>
Distributed under the terms of the GNU General Public License (GPL),
see COPYING file or http://www.fsf.org/copyleft/gpl.html for details."
}

help () {
  echo "
${extd}`basename "$1"`${norm}
  [ ( ${extd}-?${norm} | ${extd}--version${norm} | \
[${extd}-n${norm}] [${extd}-v${norm}] [ ( ${extd}-o${norm} | ${extd}-x${norm} )\ ] CODE | ${extd}-c${norm} CHAR ) ]

  ${extd}-?${norm}, ${extd}--help${norm}         Display this help and exit.
      ${extd}--version${norm}      Display version information and exit.
  ${extd}-n${norm}, ${extd}--no-newline${norm}   Suppress the trailing newline.
  ${extd}-v${norm}, ${extd}--verbose${norm}      Return decimal code, \
hexadecimal code (preceded
                       by \`0x'), octal code (preceded by \`0'),
                       character and optionally a description
                       separated by spaces.
  ${extd}-o${norm}, ${extd}--octal${norm}        Use octal value for CODE.
  ${extd}-x${norm}, ${extd}--hex${norm}          Use hexadecimal value for CODE.
  CODE               Value of the ASCII character to return. If no
                       further format options are provided, a decimal
                       value is expected.
  ${extd}-c${norm}, ${extd}--char${norm}         Display ASCII code for the \
character CHAR. The
                       output is the same as if the program was called
                       with ${extd}-v${norm} and the respective decimal ASCII \
code.
    CHAR               Character which ASCII code should be retrieved.
                       Note that you must escape special characters
                       like \`*'.

If no options and arguments are provided, the current ASCII
code table from character #32 to #255 is displayed.
  "
}

LZero () {
  result=$1
  while [ ${#result} -lt $2 ]; do
    result=0$result
  done
  echo $result
}

seq () {
  local i=$1
  while [ $i -le $2 ]; do
    echo -n $i
    if [ $i -lt $2 ]; then
      echo -n ' ' 
    fi
    let i++
  done
}

base2base () {
# base2base 0.2
# Copyright (C) 2003  Thomas Lahn <PointedEars@gmx.de>
# Distributed under the terms of the GNU General Public License (GPL),
# see COPYING file or http://www.fsf.org/copyleft/gpl.html for details.
#
# Converts a number from one positional number system to another.
# 
# Parameters
# ===========
# 
# $1   Number to be converted. Required.
# $2   Source base. Optional. If not provided, 10 is assumed.
# $3   Target base. Optional. If not provided, 2 is assumed.
# 
# Allowed as digits are characters from `0' to `9' and `A' to `Z',
# therefore bases from 2 to 36, depending on the source base. The
# number string is case-sensitive.
# You MUST NOT use any characters that are strange to the number
# system converted from, otherwise the conversion will fail. So
# you MUST strip prefixes like `0x' for hexadecimal values (of
# base 16) before using them with this function.
#
# Options
# ========
#
# -   Don't check for valid digits when the source base is not 10.
#     This will speed-up conversion but may produce wrong results
#     if you pass an invalid number. Must be the first argument.
#
# Return value
# =============
# 
# The function prints the converted value to the standard output.
# If the conversion fails, nothing is printed.
# 
# The exit code (return value) indicates whether the conversion
# was successful or not:
# 
# Exit Code     Meaning
# ------------------------------------------
#         0     Success
#         1     No value provided
#         2     Invalid value provided
#         3     Invalid source base provided
#         4     Invalid target base provided

  local check=1
  test "$1" = "-" && check=0 && shift

  local value=$1
  test -z "$value" && return 1

  local src=$(( ${2:-10} ))
  local target=$(( ${3:-2} ))
  test $src -lt 2 -o $src -gt 36 -o $target -lt 2 -o $target -gt 36 && return 4

  test $src -ne $target && { # Convert only if required

    digits=( \
      "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" \
      "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" \
      "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" \
      "U" "V" "W" "X" "Y" "Z" \
    )

# Convert from source base to decimal if required

    if [ $src -ne 10 ] || [ $src -eq 10 ] && [ $check -eq 1 ]; then
      # check only if not decimal or if decimal and check==1
      local n=0
      for i in `seq 1 ${#value}`; do          # for all positions, left-to-right
        digit=`echo $value | cut -c $i`        # get digit
        for j in `seq 0 $(( $src-1 ))`; do     # for all valid digits in the sys
          test "$digit" = "${digits[$j]}" && { # valid digit
            digit=$j
            break
          }
          test $j -eq $(( $src-1 )) && return 2 # no valid digit found
        done
        local pot; let pot=${#value}-$i
        let n=$n+$digit*$src**$pot
      done
    else
      local n=$value
    fi

# Convert from decimal to target base

    local remd
    local result
    value=""
  
    while [ $(( $n )) -ne 0 ]; do
      let remd=$n%$target
    # test $? -ne 0 && return 2
      value=${digits[$remd]}$value
      let n=$n/$target
    done
  }

  echo $value
  return 0
}

getASCIICode () {
  for i in `seq 0 255`; do
    test "$1" = "$( echo -e "\x`base2base - $i 10 16`" )" && echo $i && return 0
  done
  return 1
}

ASCIITable () {
  echo "\
${extd}ASCII Code Table For The Current Character Set${norm}

Characters with decimal values from 0 to 31 are control characters and
therefore not printable. Retrieve them with \`${extd}`basename $0`${norm} \
[...] CODE' instead.
See \`${extd}`basename $0` -?${norm}' for details.
  "
  for i in `seq 32 56`; do
    echo -en "\
`LZero $((i    )) 3`: \\x`base2base - $((i    )) 10 16` | \
`LZero $((i+ 25)) 3`: \\x`base2base - $((i+ 25)) 10 16` | \
`LZero $((i+ 50)) 3`: \\x`base2base - $((i+ 50)) 10 16` | \
`LZero $((i+ 75)) 3`: \\x`base2base - $((i+ 75)) 10 16` | \
`LZero $((i+100)) 3`: \\x`base2base - $((i+100)) 10 16` | \
`LZero $((i+125)) 3`: \\x`base2base - $((i+125)) 10 16` | \
`LZero $((i+150)) 3`: \\x`base2base - $((i+150)) 10 16` | \
`LZero $((i+175)) 3`: \\x`base2base - $((i+175)) 10 16`"
    if [ $i -lt 56 ]; then
      echo -e " | \
`LZero $((i+200)) 3`: \\x`base2base - $((i+200)) 10 16`"
    else
      echo
    fi
  done
  echo
}

test -z "$1" -o "$1" = "-?" -o "$1" = "--help" -o "$1" = "--version" && Title

help=0
if [ "$1" = "-?" ] || [ "$1" = "--help" ]; then
  help=1
  test -n "$1" && shift
fi
test $help -eq 1 && help "$0"

test -z "$1" -o "$1" = "-?" -o "$1" = "--help" -o "$1" = "--version" && \
  echo "Send suggestions and bug reports to <$mail_feedback>
  "

test "$1" = "--version" && exit 0
test $help -eq 1 && exit 255

if [ -z "$1" ]; then
  ASCIITable
else
  newline=1
  verbose=0 
  format=10

  while [ -n "$1" ]; do
    case "$1" in
      "-n" | "--no-newline")
        newline=0;;
      "-c" | "--char")
        shift
        $0 -v "`getASCIICode "$1"`"
        test -z "$2" && exit 0;;
      "-o" | "--octal")
        format=8;;
      "-x" | "--hex")
        format=16;;
      "-v" | "--verbose")
        verbose=1;;
      *)
        test -z "$value" && value="$1"
    esac
    shift
  done

_XPG=0
  test $verbose -eq 1 && {
    dec=`base2base $value $format 10`
    echo -n "$dec "
    echo -n "0x$(LZero "`base2base "$value" $format 16`" 2) "
    echo -n "0$(LZero "`base2base "$value" $format 8`" 3) "
  }
  case $format in
     8)
      echo -en "\\$value";;
    10)
      echo -en "\\x`base2base "$value" 10 16`";;
    16)
      echo -en "\\x$value";;
  esac
  test $verbose -eq 1 && {
    case $dec in
       0) echo -n " NUL";; # leading space: most control characters are N/P
       1) echo -n "  SOH";;
       2) echo -n "  STX";;
       3) echo -n "  ETX";;
       4) echo -n " EOT";;
       5) echo -n " ENQ";;
       8) echo -n "  BkSp";;
       9) echo -n "Tab";;
      27) echo -n "EEsc";; # EEsc because of the escape character
    esac
  }
  test $newline -eq 1 && echo
fi

exit 0
