#!/bin/sh

# $Id: dexconf 2274 2005-06-01 05:00:12Z fabbione $

# dexconf: Debian X server configuration file writer
#
# This tool is a backend which uses debconf database values.  It writes an
# XFree86 X server configuration file based on the information in the database.
#
# Author: Branden Robinson

# Copyright 2000-2005 Progeny Linux Systems, Inc.
#
# This is free software; you may 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,
# or (at your option) any later version.
#
# This 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 with
# the Debian operating system, in /usr/share/common-licenses/GPL;  if
# not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307 USA

set -e

# source debconf library
. /usr/share/debconf/confmodule

# display a usage message
usage () {
  cat <<EOF
Usage: $PROGNAME [OPTION ...]
  write an XFree86 X server configuration file based on debconf database values
Options:
  -h, --help                                 display this usage message and exit
  -o FILE, --output=FILE                        write configuration file to FILE
This help message is intended only as a quick reference.  For a description of
the usage of $PROGNAME, see the $PROGNAME(1) manual page.
EOF
}

# the error-out function
bomb () {
  echo "$PROGNAME: error: $*" | fold -s -w "${COLUMNS:-80}" >&2
  exit 1
}

# wrapper around db_get to ensure that the info we try to retrieve exists; it
# is (almost) always a fatal error for the values to be null
fetch () {
  db_get "$1" || true
  if [ -z "$RET" ]; then
    ERRMSG="cannot generate configuration file; $1 not set.  Aborting."
    ERRMSG="$ERRMSG  Reconfigure the X server with \"dpkg-reconfigure"
    if [ -n "$XSERVERPKG" ]; then
      ERRMSG="$ERRMSG $XSERVERPKG"
    fi
    ERRMSG="$ERRMSG\" to correct this problem."
    bomb "$ERRMSG"
  fi
}

# convert a debconf comma-delimited list to a shell whitespace-delimited list
list_convert () {
  echo $(IFS=", "; set -- $RET; while [ $# -gt 0 ]; do echo \"$1\"; shift; done)
}

# Find out if our mouse device is multiplexed; this depends on both the mouse
# port and the kernel version.
# TODO: Change this to simply fetch a value from /var/lib/xserver-common/config,
# once we have written an init script that creates that file.
has_multiplexed_mouse () {
  # Initially assume the mouse is not multiplexed.
  _RET=1
  case $(uname -s) in
    Linux)
      case $(uname -r) in
        2.4*)
          case "$MOUSE_PORT" in
            /dev/input/mice|/dev/gpmdata)
              _RET=0
              ;;
            *)
              ;;
          esac
          ;;
        2.6*)
          case "$MOUSE_PORT" in
            /dev/input/mice|/dev/gpmdata|/dev/psaux|/dev/misc/psaux|/dev/ttyS*|/dev/tts/*)
              _RET=0
              ;;
            *)
              ;;
          esac
          ;;
        *)
          # We don't recognize any kernel versions other than 2.4 and 2.6.
          ;;
        esac
      ;;
    *)
      # We don't recognize any kernel names other than Linux.
      ;;
  esac
  return $_RET
}

PROGNAME=${0##*/}
SHOWHELP=
EARLYEXIT=

GETOPT_OUTPUT=$(getopt --options ho: \
                       --longoptions help,output: \
                       -n "$PROGNAME" -- "$@")

if [ $? -ne 0 ]; then
    bomb "error while getting options; use \"$PROGNAME --help\" for help"
fi

eval set -- "$GETOPT_OUTPUT"

while :; do
    case "$1" in
        -f|--format)
          bomb "This option, and XFree86 3.x output, are no longer supported."
          ;;
        -h|--help) SHOWHELP=yes EARLYEXIT=yes ;;
        -o|--output) XF86CONFIG="$2"; shift ;;
        --) shift; break ;;
        *)
          bomb "unrecognized option \"$1\"; use \"$PROGNAME --help\" for help"
          ;;
    esac
    shift
done

if [ -n "$SHOWHELP" ]; then
    usage
fi

if [ -n "$EARLYEXIT" ]; then
    exit 0
fi

DEXCONFTMPDIR=

trap 'if [ -e "$DEXCONFTMPDIR/backup" ] && [ -n "$XF86CONFIG" ]; then \
        cat "$DEXCONFTMPDIR/backup" >"$XF86CONFIG"; \
      fi; \
      rm -rf "$DEXCONFTMPDIR"; \
      bomb "received signal; aborting"' HUP INT QUIT TERM

# Ensure we know how to write a configuation file for the X server in use.
fetch shared/default-x-server
XSERVERPKG="$RET"
case "$XSERVERPKG" in
  xserver-xfree86|xserver-xfree86-dbg)
    : ${XF86CONFIG:=/etc/X11/XF86Config-4}
    ;;
  *)
    bomb "this program does not know how to configure the \"$XSERVERPKG\" X" \
      "server"
esac

# Set up a temporary directory for the files we'll be writing.
TDIR_PARENT="${TMPDIR:-/tmp}"
TDIR="${TMPDIR:-/tmp}/dexconf-tmp-$$"

if [ ! -d "$TDIR_PARENT" ]; then
  bomb "cannot create temporary work directory; \"$TDIR_PARENT\" does not" \
    "exist or is not a directory"
fi

if [ ! -w "$TDIR_PARENT" ]; then
  bomb "cannot create temporary work directory in \"$TDIR_PARENT\"; directory" \
    "not writable"
fi

rm -rf "$TDIR"

if mkdir -m 0700 "$TDIR"; then
  DEXCONFTMPDIR="$TDIR"
else
  bomb "creation of temporary work directory \"$TDIR\" failed"
fi

# XF86Config-4 sections:
#   Files          File pathnames
#   ServerFlags    Server flags                      NOT USED BY DEXCONF
#   Module         Dynamic module loading
#   InputDevice    Input device description
#   Device         Graphics device description
#   VideoAdaptor   Xv video adaptor description      NOT USED BY DEXCONF
#   Monitor        Monitor description
#   Modes          Video modes descriptions          NOT USED BY DEXCONF
#   Screen         Screen configuration
#   ServerLayout   Overall layout
#   DRI            DRI-specific configuration
#   Vendor         Vendor-specific configuration     NOT USED BY DEXCONF

### HEADER

# Because debconf hijacks standard output and its confmodule uses file
# descriptor 3 for its own purposes, we will write our output to file descriptor
# 4 instead of standard output.

exec 4>"$DEXCONFTMPDIR/Header"
cat >&4 <<SECTION
# XF86Config-4 (XFree86 X Window System server configuration file)
#
# This file was generated by dexconf, the Debian X Configuration tool, using
# values from the debconf database.
#
# Edit this file with caution, and see the XF86Config-4 manual page.
# (Type "man XF86Config-4" at the shell prompt.)
#
# This file is automatically updated on $XSERVERPKG package upgrades *only*
# if it has not been modified since the last upgrade of the $XSERVERPKG
# package.
#
# If you have edited this file but would like it to be automatically updated
# again, run the following commands as root:
#
#   cp /etc/X11/XF86Config-4 /etc/X11/XF86Config-4.custom
#   md5sum /etc/X11/XF86Config-4 >/var/lib/xfree86/XF86Config-4.md5sum
#   dpkg-reconfigure $XSERVERPKG
SECTION

### FILES

fetch xserver-xfree86/config/write_files_section
if [ "$RET" = "true" ]; then
  exec 4>"$DEXCONFTMPDIR/Files"
  cat >&4 <<SECTION
Section "Files"
	FontPath	"unix/:7100"			# local font server
	# if the local font server has problems, we can fall back on these
	FontPath	"/usr/lib/X11/fonts/misc"
	FontPath	"/usr/lib/X11/fonts/cyrillic"
	FontPath	"/usr/lib/X11/fonts/100dpi/:unscaled"
	FontPath	"/usr/lib/X11/fonts/75dpi/:unscaled"
	FontPath	"/usr/lib/X11/fonts/Type1"
	FontPath	"/usr/lib/X11/fonts/CID"
	FontPath	"/usr/lib/X11/fonts/Speedo"
	FontPath	"/usr/lib/X11/fonts/100dpi"
	FontPath	"/usr/lib/X11/fonts/75dpi"
EndSection
SECTION
fi

### MODULE

# The module list is permitted to be null.
db_get xserver-xfree86/config/modules || true
if [ -n "$RET" ]; then
  MODULES=$(list_convert "$RET")
  exec 4>"$DEXCONFTMPDIR/Module"
  printf "Section \"Module\"\n" >&4
  for MODULE in $MODULES; do
    printf "\tLoad\t$MODULE\n" >&4
  done
  printf "EndSection\n" >&4
fi

### KEYBOARD / INPUTDEVICE

fetch xserver-xfree86/config/inputdevice/keyboard/rules
XKB_RULES="$RET"
fetch xserver-xfree86/config/inputdevice/keyboard/model
XKB_MODEL="$RET"
fetch xserver-xfree86/config/inputdevice/keyboard/layout
XKB_LAYOUT="$RET"
# XkbVariant and XkbOptions are permitted to be null.
db_get xserver-xfree86/config/inputdevice/keyboard/variant
XKB_VARIANT="$RET"
db_get xserver-xfree86/config/inputdevice/keyboard/options
XKB_OPTIONS="$RET"

exec 4>"$DEXCONFTMPDIR/InputDeviceKeyboard"
cat >&4 <<SECTION
Section "InputDevice"
	Identifier	"Generic Keyboard"
	Driver		"keyboard"
	Option		"CoreKeyboard"
	Option		"XkbRules"	"$XKB_RULES"
	Option		"XkbModel"	"$XKB_MODEL"
	Option		"XkbLayout"	"$XKB_LAYOUT"
SECTION
if [ -n "$XKB_VARIANT" ]; then
  printf "\tOption\t\t\"XkbVariant\"\t\"$XKB_VARIANT\"\n" >&4
fi
if [ -n "$XKB_OPTIONS" ]; then
  printf "\tOption\t\t\"XkbOptions\"\t\"$XKB_OPTIONS\"\n" >&4
fi
printf "EndSection\n" >&4

### MOUSE / INPUTDEVICE

DO_EMULATE3BUTTONS=
DO_ZAXISMAPPING=

fetch xserver-xfree86/config/inputdevice/mouse/port
MOUSE_PORT="$RET"
fetch xserver-xfree86/config/inputdevice/mouse/protocol
MOUSE_PROTOCOL="$RET"
fetch xserver-xfree86/config/inputdevice/mouse/emulate3buttons
if [ "$RET" = "true" ]; then
  DO_EMULATE3BUTTONS=true
fi
fetch xserver-xfree86/config/inputdevice/mouse/zaxismapping
if [ "$RET" = "true" ]; then
  DO_ZAXISMAPPING=true
fi

exec 4>"$DEXCONFTMPDIR/InputDeviceMouse"
cat >&4 <<SECTION
Section "InputDevice"
	Identifier	"Configured Mouse"
	Driver		"mouse"
	Option		"CorePointer"
	Option		"Device"		"$MOUSE_PORT"
	Option		"Protocol"		"$MOUSE_PROTOCOL"
SECTION
if [ -n "$DO_EMULATE3BUTTONS" ]; then
  printf "\tOption\t\t\"Emulate3Buttons\"\t\"true\"\n" >&4
fi
if [ -n "$DO_ZAXISMAPPING" ]; then
  printf "\tOption\t\t\"ZAxisMapping\"\t\t\"4 5\"\n" >&4
fi
printf "EndSection\n" >&4

# Set up an additional mouse device which points at the Linux kernel's
# multiplexed input subsystem node.
#
# We only write this stanza if the Configured Mouse is *not*:
#   Linux 2.4: USB
#   Linux 2.6: USB, PS/2, serial
#
# Configuring a mouse device on /dev/input/mice isn't a problem even on systems
# without CONFIG_INPUT_MOUSEDEV (i.e., without that device node), because this
# is not the core pointer, and failure to open it is not harmful.
#
# We also don't write this stanza if the Configured Mouse is the GPM repeater.
# We have no way of knowing what device GPM is repeating for, and it might be a
# device multiplexed into /dev/input/mice.
#
# Finally, we don't write this stanza if this is not Linux.
#
# TODO: Change this if statement to reflect the 2.4 vs. 2.6 logic above; sadly,
# we probably also need an init script to rewrite XF86Config-4 on system boot
# for people who switch back and forth between these kernels, or some people
# will get "doubled" mouse events.  Sigh.
if ! has_multiplexed_mouse; then
  cat >&4 <<SECTION
Section "InputDevice"
	Identifier	"Generic Mouse"
	Driver		"mouse"
	Option		"SendCoreEvents"	"true"
	Option		"Device"		"/dev/input/mice"
	Option		"Protocol"		"ImPS/2"
SECTION
  if [ -n "$DO_EMULATE3BUTTONS" ]; then
    printf "\tOption\t\t\"Emulate3Buttons\"\t\"true\"\n" >&4
  fi
  if [ -n "$DO_ZAXISMAPPING" ]; then
    printf "\tOption\t\t\"ZAxisMapping\"\t\t\"4 5\"\n" >&4
  fi
  printf "EndSection\n" >&4
fi

### DEVICE

fetch xserver-xfree86/config/device/identifier
DEVICE_IDENTIFIER="$RET"
fetch xserver-xfree86/config/device/driver
DEVICE_DRIVER="$RET"
# BusID, VideoRam, and UseFBDev are permitted to be null.
db_get xserver-xfree86/config/device/bus_id
DEVICE_BUSID="$RET"
db_get xserver-xfree86/config/device/video_ram
DEVICE_VIDEO_RAM="$RET"
db_get xserver-xfree86/config/device/use_fbdev
DEVICE_USE_FBDEV="$RET"
exec 4>"$DEXCONFTMPDIR/Device"
cat >&4 <<SECTION
Section "Device"
	Identifier	"$DEVICE_IDENTIFIER"
	Driver		"$DEVICE_DRIVER"
SECTION
if [ -n "$DEVICE_BUSID" ]; then
  printf "\tBusID\t\t\"$DEVICE_BUSID\"\n" >&4
fi
if [ -n "$DEVICE_VIDEO_RAM" ]; then
   printf "\tVideoRam\t$DEVICE_VIDEO_RAM\n" >&4
fi
if [ "$DEVICE_USE_FBDEV" = "true" ]; then
  printf "\tOption\t\t\"UseFBDev\"\t\t\"$DEVICE_USE_FBDEV\"\n" >&4
fi
printf "EndSection\n" >&4

### MONITOR

fetch xserver-xfree86/config/monitor/identifier
MONITOR_IDENTIFIER="$RET"
fetch xserver-xfree86/config/monitor/horiz-sync
MONITOR_HORIZ_SYNC="$RET"
fetch xserver-xfree86/config/monitor/vert-refresh
MONITOR_VERT_REFRESH="$RET"
exec 4>"$DEXCONFTMPDIR/Monitor"
cat >&4 <<SECTION
Section "Monitor"
	Identifier	"$MONITOR_IDENTIFIER"
	HorizSync	$MONITOR_HORIZ_SYNC
	VertRefresh	$MONITOR_VERT_REFRESH
	Option		"DPMS"
EndSection
SECTION

### SCREEN

# The mode list is permitted to be null.
db_get xserver-xfree86/config/display/modes
if [ -n "$RET" ]; then
  DISPLAY_MODES=$(list_convert "$RET")
fi
fetch xserver-xfree86/config/display/default_depth
DISPLAY_DEFAULT_DEPTH="$RET"
exec 4>"$DEXCONFTMPDIR/Screen"
cat >&4 <<SECTION
Section "Screen"
	Identifier	"Default Screen"
	Device		"$DEVICE_IDENTIFIER"
	Monitor		"$MONITOR_IDENTIFIER"
	DefaultDepth	$DISPLAY_DEFAULT_DEPTH
SECTION
for DEPTH in 1 4 8 15 16 24; do
  printf "\tSubSection \"Display\"\n" >&4
  printf "\t\tDepth\t\t$DEPTH\n" >&4
  if [ -n "$DISPLAY_MODES" ]; then
    printf "\t\tModes\t\t$DISPLAY_MODES\n" >&4
  fi
  printf "\tEndSubSection\n" >&4
done
printf "EndSection\n" >&4

### SERVERLAYOUT

exec 4>"$DEXCONFTMPDIR/ServerLayout"
cat >&4 <<SECTION
Section "ServerLayout"
	Identifier	"Default Layout"
	Screen		"Default Screen"
	InputDevice	"Generic Keyboard"
	InputDevice	"Configured Mouse"
SECTION
if ! has_multiplexed_mouse; then
  printf "\tInputDevice\t\"Generic Mouse\"\n" >&4
fi
printf "EndSection\n" >&4

### DRI

fetch xserver-xfree86/config/write_dri_section
if [ "$RET" = "true" ]; then
  exec 4>"$DEXCONFTMPDIR/DRI"
  cat >&4 <<SECTION
Section "DRI"
	Mode	0666
EndSection
SECTION
fi

# Tell debconf to stop listening to us.
db_stop

# Write the configuration file.  Put a blank line before every section we write
# except the first.

OUTFILE="$DEXCONFTMPDIR/dexconf-out"
umask 022
: >"$OUTFILE"

SPACER=
for SECTION in Header Files Module InputDeviceKeyboard InputDeviceMouse \
               Device Monitor Screen ServerLayout DRI; do
  if [ -e "$DEXCONFTMPDIR/$SECTION" ]; then
    eval $SPACER
    cat "$DEXCONFTMPDIR/$SECTION" >>"$OUTFILE"
    SPACER='echo "" >>"$OUTFILE"'
  fi
done

# Ensure we can write to our destination if it already exits.
if [ -e "$XF86CONFIG" ]; then
  if [ ! -w "$XF86CONFIG" ]; then
    bomb "unable to write to \"$XF86CONFIG\""
  fi
fi

BACKUP=
# Create a backup of the existing configuration file if it already exists.
if [ -e "$XF86CONFIG" ]; then
  cat "$XF86CONFIG" >"$DEXCONFTMPDIR/backup"
  BACKUP=true
fi

# Move the new file into place.
if ! cat "$OUTFILE" >"$XF86CONFIG"; then
  # Failed; try to restore the backup.
  if [ -n "$BACKUP" ]; then
    cat "$DEXCONFTMPDIR/backup" >"$XF86CONFIG"
  fi
fi

rm -rf "$DEXCONFTMPDIR"

exit 0

# vim:set ai et sts=2 sw=2 tw=80:
