#!/bin/sh
###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
#          text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2025 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################

# Convert the boot parameter to configuration file directives.
# If /proc is mounted then use the brltty= boot parameter in /proc/cmdline.
# If /proc is not mounted then use the brltty environment variable.
# Invoke with -h for usage information.

programName="${0##*/}"
programMessage()
{
   echo 2>&1 "${programName}: ${1}"
}
syntaxError()
{
   programMessage "${1}"
   exit 2
}
internalError()
{
   programMessage "${1}"
   exit 3
}

configurationFile=""
requestedAction=create
deviceTranslation=none
requestedParameter=""
OPTIND=1
while getopts ":f:cundop:h" option
do
   case "${option}"
   in
      f) configurationFile="${OPTARG}";;
      c) requestedAction=create;;
      u) requestedAction=update;;
      n) deviceTranslation=none;;
      d) deviceTranslation=devfs;;
      o) deviceTranslation=old;;
      p) requestedParameter="${OPTARG}";;
      h)
         cat <<EOF
Usage: ${programName} [option ...]
-f file  The configuration file to create/update.
-c       Create the configuration file (write to stdout if no -f).
-u       Update the configuration file (copying from stdin to stdout if no -f).
-n       Do not translate device paths.
-d       Do old-style to devfs device path translation.
-o       Do devfs to old-style device path translation.
-p [driver][,[device][,[table]]]
         Explicitly specify the boot parameter.
-h       Display this usage summary.
EOF
         exit 0
         ;;
     \?) syntaxError "unknown option: -${OPTARG}";;
      :) syntaxError "missing value: -${OPTARG}";;
      *) internalError "unimplemented option: -${option}";;
   esac
done
shift "`expr $OPTIND - 1`"
[ "${#}" -eq 0 ] || syntaxError "too many parameters."

case "${requestedAction}"
in
   create)
      putConfigurationLine()
      {
         echo "${1}" || exit 4
      }
      startConfigurationFile()
      {
         [ -n "${configurationFile}" ] && exec >"${configurationFile}"
         putConfigurationLine "`makeHeaderLine Created`"
         putConfigurationLine "`makeParameterLine`"
         putConfigurationLine ""
      }
      putConfigurationDirective()
      {
         putConfigurationLine "${1} ${2}"
      }
      finalizeConfigurationFile()
      {
         :
      }
      ;;
   update)
      putSedCommand()
      {
         sedScript="${sedScript}
${1}"
      }
      startConfigurationFile()
      {
         if [ -n "${configurationFile}" ]
         then
            [ -e "${configurationFile}" ] || syntaxError "file not found: ${configurationFile}"
            [ -f "${configurationFile}" ] || syntaxError "not a file: ${configurationFile}"
            [ -r "${configurationFile}" ] || syntaxError "file not readable: ${configurationFile}"
            [ -w "${configurationFile}" ] || syntaxError "file not writable: ${configurationFile}"
            outputFile="${configurationFile}.new"
            exec <"${configurationFile}" >"${outputFile}"
         fi
         sedScript=""
         putSedCommand "\$a\\
\\
`makeHeaderLine Updated`\\
`makeParameterLine`\\
"
      }
      putConfigurationDirective()
      {
         value="`echo "${2}" | sed -e 's%\\([/\\]\\)%\\\\\\1%g'`"
         putSedCommand "/^[[:space:]]*${1}[[:space:]]/s/^/#/"
         putSedCommand "\$a${1} ${value}"
      }
      finalizeConfigurationFile()
      {
         sed -e "${sedScript}"
         [ -n "${outputFile}" ] && mv -f "${outputFile}" "${configurationFile}"
      }
      ;;
   *) internalError "unimplemented action: ${requestedAction}";;
esac

translateDevice_none()
{
   :
}
translateDevice_devfs()
{
   minor="${device#ttyS}"
   if [ "${minor}" != "${device}" ]
   then
      device="tts/${minor}"
      return 0
   fi
   minor="${device#lp}"
   if [ "${minor}" != "${device}" ]
   then
      device="printers/${minor}"
      return 0
   fi
   programMessage "unsupported old-style device: ${device}"
}
translateDevice_old()
{
   major="${device%%/*}" 
   if [ "${major}" != "${device}" ]
   then
      minor="${device#*/}"
      case "${major}"
      in
         tts) devfs="ttyS${minor}";;
         printers) devfs="lp${minor}";;
      esac
   fi
   if [ -n "${devfs}" ]
   then
      device="${devfs}"
   else
      programMessage "unsupported devfs device: ${device}"
   fi
}

makeHeaderLine()
{
   echo "# ${1} by brltty-bp2cf`date +' on %Y-%m-%d at %H:%M:%S %Z (UTC%z)'`."
}
makeParameterLine()
{
   echo "# Boot Parameter:${bootParameter}"
}
putConfigurationFile()
{
   startConfigurationFile
   [ -n "${brailleDriver}" ] && putConfigurationDirective "braille-driver" "${brailleDriver}"
   [ -n "${brailleDevice}" ] && {
      device="`echo "${brailleDevice}" | sed -e 's%//*%/%g' -e 's%^/dev/%%'`"
      if [ "${device#/}" = "${device}" ]
      then
         translateDevice_${deviceTranslation}
      fi
      putConfigurationDirective "braille-device" "${device}"
   }
   [ -n "${textTable}" ] && putConfigurationDirective "text-table" "${textTable}"
   finalizeConfigurationFile
}
parseBootParameter()
{
   bootParameter="${bootParameter} ${1}"
   number=1
   while [ "${number}" -le 3 ]
   do
      cut="cut -d, -f${number}"
      [ "${number}" -gt 1 ] && cut="${cut} -s"
      operand="`echo ${1} | ${cut}`"
      if [ -n "${operand}" ]
      then
         case "${number}"
         in
            1) brailleDriver="${operand}";;
            2) brailleDevice="${operand}";;
            3) textTable="${operand}";;
         esac
      fi
      number="`expr ${number} + 1`"
   done
}
putBootParameter()
{
   parseBootParameter "${1}"
   putConfigurationFile
}
parseBootCommand()
{
   found=false
   while [ "${#}" -gt 0 ]
   do
      case "${1}"
      in
         "brltty="*)
            found=true
            parseBootParameter "${1#*=}"
            ;;
      esac
      shift
   done
   "${found}" && putConfigurationFile
}

brailleDriver=""
brailleDevice=""
textTable=""
bootCommandFile="/proc/cmdline"
if [ -n "${requestedParameter}" ]
then
   putBootParameter "${requestedParameter}"
elif [ -f "${bootCommandFile}" ]
then
   parseBootCommand `cat "${bootCommandFile}"`
elif [ -n "${brltty}" ]
then
   putBootParameter "${brltty}"
fi
exit 0
