diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /scripts | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'scripts')
29 files changed, 8469 insertions, 0 deletions
diff --git a/scripts/Configure b/scripts/Configure new file mode 100644 index 000000000..9bea73153 --- /dev/null +++ b/scripts/Configure @@ -0,0 +1,472 @@ +#! /bin/sh +# +# This script is used to configure the linux kernel. +# +# It was inspired by the challenge in the original Configure script +# to ``do something better'', combined with the actual need to ``do +# something better'' because the old configure script wasn't flexible +# enough. +# +# Please send comments / questions / bug fixes to raymondc@microsoft.com. +# +# ***** IMPORTANT COMPATIBILITY NOTE **** +# If configuration changes are made which might adversely effect +# Menuconfig or xconfig, please notify the respective authors so that +# those utilities can be updated in parallel. +# +# Menuconfig: <roadcapw@cfw.com> +# xconfig: <apenwarr@foxnet.net> <eric@aib.com> +# **************************************** +# +# Each line in the config file is a command. +# +# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13 +# with an empty IFS. +# +# 030995 (storner@osiris.ping.dk) - added support for tri-state answers, +# for selecting modules to compile. +# +# 180995 Bernhard Kaindl (bkaindl@ping.at) - added dummy functions for +# use with a config.in modified for make menuconfig. +# +# 301195 (boldt@math.ucsb.edu) - added help text support +# +# 281295 Paul Gortmaker - make tri_state functions collapse to boolean +# if module support is not enabled. +# +# 010296 Aaron Ucko (ucko@vax1.rockhurst.edu) - fix int and hex to accept +# arbitrary ranges +# +# 150296 Dick Streefland (dicks@tasking.nl) - report new configuration +# items and ask for a value even when doing a "make oldconfig" +# +# 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is +# chosen for an item, define the macro <option_name>_MODULE + +# +# Make sure we're really running bash. +# +# I would really have preferred to write this script in a language with +# better string handling, but alas, bash is the only scripting language +# that I can be reasonable sure everybody has on their linux machine. +# +[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; } + +# Disable filename globbing once and for all. +# Enable function cacheing. +set -f -h + +# +# Dummy functions for use with a config.in modified for menuconf +# +function mainmenu_option () { + : +} +function mainmenu_name () { + : +} +function endmenu () { + : +} + +# +# help prints the corresponding help text from Configure.help to stdout +# +# help variable +# +function help () { + if [ -f Documentation/Configure.help ] + then + #first escape regexp special characters in the argument: + var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g') + #now pick out the right help text: + text=$(sed -n "/^$var[ ]*\$/,\${ + /^$var[ ]*\$/b + /^#.*/b + /^[ ]*\$/q + p + }" Documentation/Configure.help) + if [ -z "$text" ] + then + echo; echo " Sorry, no help available for this option yet.";echo + else + (echo; echo "$text"; echo) | ${PAGER:-more} + fi + else + echo; + echo " Can't access the file Documentation/Configure.help which" + echo " should contain the help texts." + echo + fi +} + + +# +# readln reads a line into $ans. +# +# readln prompt default oldval +# +function readln () { + if [ "$DEFAULT" = "-d" -a -n "$3" ]; then + echo "$1" + ans=$2 + else + echo -n "$1" + [ -z "$3" ] && echo -n "(NEW) " + IFS='@' read ans </dev/tty || exit 1 + [ -z "$ans" ] && ans=$2 + fi +} + +# +# comment does some pretty-printing +# +# comment 'xxx' +# +function comment () { + echo "*"; echo "* $1" ; echo "*" + (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG + (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H +} + +# +# define_bool sets the value of a boolean argument +# +# define_bool define value +# +function define_bool () { + case "$2" in + "y") + echo "$1=y" >>$CONFIG + echo "#define $1 1" >>$CONFIG_H + ;; + + "m") + echo "$1=m" >>$CONFIG + echo "#undef $1" >>$CONFIG_H + echo "#define $1_MODULE 1" >>$CONFIG_H + ;; + + "n") + echo "# $1 is not set" >>$CONFIG + echo "#undef $1" >>$CONFIG_H + ;; + esac + eval "$1=$2" +} + +# +# bool processes a boolean argument +# +# bool question define +# +function bool () { + old=$(eval echo "\${$2}") + def=${old:-'n'} + case "$def" in + "y" | "m") defprompt="Y/n/?" + def="y" + ;; + "n") defprompt="N/y/?" + ;; + esac + while :; do + readln "$1 ($2) [$defprompt] " "$def" "$old" + case "$ans" in + [yY] | [yY]es ) define_bool "$2" "y" + break;; + [nN] | [nN]o ) define_bool "$2" "n" + break;; + * ) help "$2" + ;; + esac + done +} + +# +# tristate processes a tristate argument +# +# tristate question define +# +function tristate () { + if [ "$CONFIG_MODULES" != "y" ]; then + bool "$1" "$2" + else + old=$(eval echo "\${$2}") + def=${old:-'n'} + case "$def" in + "y") defprompt="Y/m/n/?" + ;; + "m") defprompt="M/n/y/?" + ;; + "n") defprompt="N/y/m/?" + ;; + esac + while :; do + readln "$1 ($2) [$defprompt] " "$def" "$old" + case "$ans" in + [yY] | [yY]es ) define_bool "$2" "y" + break ;; + [nN] | [nN]o ) define_bool "$2" "n" + break ;; + [mM] ) define_bool "$2" "m" + break ;; + * ) help "$2" + ;; + esac + done + fi +} + +# +# dep_tristate processes a tristate argument that depends upon +# another option. If the option we depend upon is a module, +# then the only allowable options are M or N. If Y, then +# this is a normal tristate. This is used in cases where modules +# are nested, and one module requires the presence of something +# else in the kernel. +# +# tristate question define default +# +function dep_tristate () { + old=$(eval echo "\${$2}") + def=${old:-'n'} + if [ "$3" = "n" ]; then + define_bool "$2" "n" + elif [ "$3" = "y" ]; then + tristate "$1" "$2" + else + if [ "$CONFIG_MODULES" = "y" ]; then + case "$def" in + "y" | "m") defprompt="M/n/?" + def="m" + ;; + "n") defprompt="N/m/?" + ;; + esac + while :; do + readln "$1 ($2) [$defprompt] " "$def" "$old" + case "$ans" in + [nN] | [nN]o ) define_bool "$2" "n" + break ;; + [mM] ) define_bool "$2" "m" + break ;; + [yY] | [yY]es ) echo + echo " This answer is not allowed, because it is not consistent with" + echo " your other choices." + echo " This driver depends on another one which you chose to compile" + echo " as a module. This means that you can either compile this one" + echo " as a module as well (with M) or leave it out altogether (N)." + echo + ;; + * ) help "$2" + ;; + esac + done + fi + fi +} + +# +# define_int sets the value of a integer argument +# +# define_int define value +# +function define_int () { + echo "$1=$2" >>$CONFIG + echo "#define $1 ($2)" >>$CONFIG_H + eval "$1=$2" +} + +# +# int processes an integer argument +# +# int question define default +# +function int () { + old=$(eval echo "\${$2}") + def=${old:-$3} + while :; do + readln "$1 ($2) [$def] " "$def" "$old" + if expr "$ans" : '0$\|-?[1-9][0-9]*$' > /dev/null; then + define_int "$2" "$ans" + break + else + help "$2" + fi + done +} +# +# define_hex sets the value of a hexadecimal argument +# +# define_hex define value +# +function define_hex () { + echo "$1=$2" >>$CONFIG + echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H + eval "$1=$2" +} + +# +# hex processes an hexadecimal argument +# +# hex question define default +# +function hex () { + old=$(eval echo "\${$2}") + def=${old:-$3} + def=${def#*[x,X]} + while :; do + readln "$1 ($2) [$def] " "$def" "$old" + ans=${ans#*[x,X]} + if expr "$ans" : '[0-9a-fA-F]+$' > /dev/null; then + define_hex "$2" "$ans" + break + else + help "$2" + fi + done +} + +# +# choice processes a choice list (1-out-of-n) +# +# choice question choice-list default +# +# The choice list has a syntax of: +# NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE } +# The user may enter any unique prefix of one of the NAMEs and +# choice will define VALUE as if it were a boolean option. +# VALUE must be in all uppercase. Normally, VALUE is of the +# form CONFIG_<something>. Thus, if the user selects <something>, +# the CPP symbol CONFIG_<something> will be defined and the +# shell variable CONFIG_<something> will be set to "y". +# +function choice () { + question="$1" + choices="$2" + old= + def=$3 + + # determine default answer: + names="" + set -- $choices + firstvar=$2 + while [ -n "$2" ]; do + if [ -n "$names" ]; then + names="$names, $1" + else + names="$1" + fi + if [ "$(eval echo \"\${$2}\")" = "y" ]; then + old=$1 + def=$1 + fi + shift; shift + done + + val="" + while [ -z "$val" ]; do + ambg=n + readln "$question ($names) [$def] " "$def" "$old" + ans=$(echo $ans | tr a-z A-Z) + set -- $choices + while [ -n "$1" ]; do + name=$(echo $1 | tr a-z A-Z) + case "$name" in + "$ans"* ) + if [ "$name" = "$ans" ]; then + val="$2" + break # stop on exact match + fi + if [ -n "$val" ]; then + echo;echo \ + " Sorry, \"$ans\" is ambiguous; please enter a longer string." + echo + val="" + ambg=y + break + else + val="$2" + fi;; + esac + shift; shift + done + if [ "$val" = "" -a "$ambg" = "n" ]; then + help "$firstvar" + fi + done + set -- $choices + while [ -n "$2" ]; do + if [ "$2" = "$val" ]; then + echo " defined $val" + define_bool "$2" "y" + else + define_bool "$2" "n" + fi + shift; shift + done +} + +CONFIG=.tmpconfig +CONFIG_H=.tmpconfig.h +trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2 + +# +# Make sure we start out with a clean slate. +# +echo "#" > $CONFIG +echo "# Automatically generated make config: don't edit" >> $CONFIG +echo "#" >> $CONFIG + +echo "/*" > $CONFIG_H +echo " * Automatically generated C config: don't edit" >> $CONFIG_H +echo " */" >> $CONFIG_H + +DEFAULT="" +if [ "$1" = "-d" ] ; then + DEFAULT="-d" + shift +fi + +CONFIG_IN=./config.in +if [ "$1" != "" ] ; then + CONFIG_IN=$1 +fi + +DEFAULTS=arch/$ARCH/defconfig +if [ -f .config ]; then + DEFAULTS=.config +fi + +if [ -f $DEFAULTS ]; then + echo "#" + echo "# Using defaults found in" $DEFAULTS + echo "#" + . $DEFAULTS + sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$ + . /tmp/conf.$$ + rm /tmp/conf.$$ +else + echo "#" + echo "# No defaults found" + echo "#" +fi + +. $CONFIG_IN + +rm -f .config.old +if [ -f .config ]; then + mv .config .config.old +fi +mv .tmpconfig .config +mv .tmpconfig.h include/linux/autoconf.h + +echo +echo "The linux kernel is now hopefully configured for your setup." +echo "Check the top-level Makefile for additional configuration," +echo "and do a 'make dep ; make clean' if you want to be sure all" +echo "the files are correctly re-made" +echo + +exit 0 diff --git a/scripts/MAKEDEV.ide b/scripts/MAKEDEV.ide new file mode 100644 index 000000000..6476860b7 --- /dev/null +++ b/scripts/MAKEDEV.ide @@ -0,0 +1,37 @@ +#!/bin/sh +# +# This script creates the proper /dev/ entries for IDE devices +# on the primary, secondary, tertiary, and quaternary interfaces. +# See ../Documentation/ide.txt for more information. +# +makedev () { + rm -f /dev/$1 + echo mknod /dev/$1 $2 $3 $4 + mknod /dev/$1 $2 $3 $4 + chown root:disk /dev/$1 + chmod 660 /dev/$1 +} + +makedevs () { + rm -f /dev/$1* + makedev $1 b $2 $3 + for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + do + makedev $1$part b $2 `expr $3 + $part` + done +} + +makedevs hda 3 0 +makedevs hdb 3 64 +makedevs hdc 22 0 +makedevs hdd 22 64 +makedevs hde 33 0 +makedevs hdf 33 64 +makedevs hdg 34 0 +makedevs hdh 34 64 + +for tape in 0 1 2 3 4 5 6 7 +do + makedev ht$tape c 37 $tape + makedev nht$tape c 37 `expr $tape + 128` +done diff --git a/scripts/Makefile b/scripts/Makefile new file mode 100644 index 000000000..a214e709e --- /dev/null +++ b/scripts/Makefile @@ -0,0 +1,47 @@ +HOSTCFLAGS:=$(HOSTCFLAGS) -g -Wall +HEADER=header.tk +TAIL=tail.tk + +# We allow the Makefile in drivers/sound to decide when to rebuild its +# files, rather than trying to second-guess it like we did before. +# +soundscript: + make -C ${TOPDIR}/drivers/sound mkscript + @echo + +# There is probably a better way to decide when to rebuild kconfig.tk; this +# one won't catch every last change to the various Config.in files. However, +# the reliance on ${TOPDIR}/Makefile makes sure we at least rebuild when the +# kernel version number changes. +# +kconfig.tk: soundscript ${TOPDIR}/Makefile ${TOPDIR}/arch/${ARCH}/config.in \ + tkparse ${HEADER} ${TAIL} + ./tkparse < ../arch/${ARCH}/config.in > kconfig.tmp + @if [ -f /usr/local/bin/wish ]; then \ + echo '#!'"/usr/local/bin/wish -f" > kconfig.tk; \ + else \ + echo '#!'"/usr/bin/wish -f" > kconfig.tk; \ + fi + cat ${HEADER} >> ./kconfig.tk + cat kconfig.tmp >> kconfig.tk + rm -f kconfig.tmp + echo "set defaults \"arch/${ARCH}/defconfig\"" >> kconfig.tk + cat ${TAIL} >> kconfig.tk + chmod 755 kconfig.tk + +tkparse: tkparse.o tkcond.o tkgen.o + ${HOSTCC} -o tkparse tkparse.o tkcond.o tkgen.o + +tkparse.o: tkparse.c tkparse.h + $(HOSTCC) $(HOSTCFLAGS) -c -o tkparse.o tkparse.c + +tkcond.o: tkcond.c tkparse.h + $(HOSTCC) $(HOSTCFLAGS) -c -o tkcond.o tkcond.c + +tkgen.o: tkgen.c tkparse.h + $(HOSTCC) $(HOSTCFLAGS) -c -o tkgen.o tkgen.c + +clean: + rm -f *~ kconfig.tk *.o tkparse + +include $(TOPDIR)/Rules.make diff --git a/scripts/Menuconfig b/scripts/Menuconfig new file mode 100644 index 000000000..2f0f8c765 --- /dev/null +++ b/scripts/Menuconfig @@ -0,0 +1,1232 @@ +#! /bin/sh +# +# This script is used to configure the linux kernel. +# +# It was inspired by a desire to not have to hit <enter> 9 million times +# or startup the X server just to change a single kernel parameter. +# +# This script attempts to parse the configuration files, which are +# scattered throughout the kernel source tree, and creates a temporary +# set of mini scripts which are in turn used to create nested menus and +# radiolists. +# +# It uses a very modified/mutilated version of the "dialog" utility +# written by Savio Lam (lam836@cs.cuhk.hk). Savio is not responsible +# for this script or the version of dialog used by this script. +# Please do not contact him with questions. The official version of +# dialog is available at sunsite.unc.edu or a sunsite mirror. +# +# Portions of this script were borrowed from the original Configure +# script. +# +# Please send comments / questions / bug fixes to roadcapw@cfw.com +# +#---------------------------------------------------------------------------- + + +# +# Change this to TRUE if you prefer all kernel options listed +# in a single menu rather than the standard menu hierarchy. +# +single_menu_mode= + +# +# Make sure we're really running bash. +# +[ -z "$BASH" ] && { echo "Menuconfig requires bash" 1>&2; exit 1; } + +# +# Cache function definitions, turn off posix compliance +# +set -h +o posix + + + + + +# +# Load the functions used by the config.in files. +# +# I do this because these functions must be redefined depending +# on whether they are being called for interactive use or for +# saving a configuration to a file. +# +# Thank the heavens bash supports nesting function definitions. +# +load_functions () { + +# +# Additional comments +# +function comment () { + comment_ctr=$[ comment_ctr + 1 ] + echo -ne "': $comment_ctr' '--- $1' " >>MCmenu +} + +# +# Don't need this yet, but we don't want to puke either. +# +function define_bool () { + : +} + +# +# Create a boolean (Yes/No) function for our current menu +# which calls our local bool function. +# +function bool () { + eval $2=\${$2:-'n'} x=\$$2 + + case $x in + y|m) flag="*" ;; + n) flag=" " ;; + esac + + echo -ne "'$2' '[$flag] $1' " >>MCmenu + + echo -e "function $2 () { l_bool '$2' \"\$1\" ;}\n" >>MCradiolists +} + +# +# Create a tristate (Yes/No/Module) radiolist function +# which calls our local tristate function. +# +# Collapses to a boolean (Yes/No) if module support is disabled. +# +function tristate () { + if [ "$CONFIG_MODULES" != "y" ] + then + bool "$1" "$2" + else + eval $2=\${$2:-'n'} x=\$$2 + + case $x in + y) flag="*" ;; + m) flag="M" ;; + *) flag=" " ;; + esac + + echo -ne "'$2' '<$flag> $1' " >>MCmenu + + echo -e " + function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists + fi +} + +# +# Create a tristate radiolist function which is dependent on +# another kernel configuration option. +# +# Quote from the original configure script: +# +# If the option we depend upon is a module, +# then the only allowable options are M or N. If Y, then +# this is a normal tristate. This is used in cases where modules +# are nested, and one module requires the presence of something +# else in the kernel. +# +function dep_tristate () { + if [ "$CONFIG_MODULES" != "y" ] + then + bool "$1" "$2" + else + if eval [ "_$3" != "_m" ] + then + tristate "$1" "$2" $3 + else + mod_bool "$1" "$2" + fi + fi +} + +# +# Add a menu item which will call our local int function. +# +function int () { + eval $2=\${$2:-"$3"} x=\$$2 + + echo -ne "'$2' '($x) $1' " >>MCmenu + + echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' ;}" >>MCradiolists +} + +# +# Add a menu item which will call our local int function. +# +function hex () { + eval $2=\${$2:-"$3"} x=\${$2##*[x,X]} + + echo -ne "'$2' '($x) $1' " >>MCmenu + + echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' ;}" >>MCradiolists +} + +# +# Add a menu item which will call our local One-of-Many choice list. +# +function choice () { + # + # Need to remember params cause they're gonna get reset. + # + title=$1 + choices=$2 + default=$3 + current= + + # + # Find out if one of the choices is already set. + # If it's not then make it the default. + # + set -- $choices + firstchoice=$2 + + while [ -n "$2" ] + do + if eval [ "_\$$2" = "_y" ] + then + current=$1 + break + fi + shift ; shift + done + + : ${current:=$default} + + echo -ne "'$firstchoice' '($current) $title' " >>MCmenu + + echo -e " + function $firstchoice () \ + { l_choice '$title' \"$choices\" $current ;}" >>MCradiolists +} + +} # END load_functions() + + + + + +# +# Extract available help for an option from Configure.help +# and send it to standard output. +# +# Most of this function was borrowed from the original kernel +# Configure script. +# +function extract_help () { + if [ -f Documentation/Configure.help ] + then + #first escape regexp special characters in the argument: + var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g') + #now pick out the right help text: + text=$(sed -n "/^$var[ ]*\$/,\${ + /^$var[ ]*\$/d + /^#.*/d + /^[ ]*\$/q + s/^ // + p + }" Documentation/Configure.help) + + if [ -z "$text" ] + then + echo "There is no help available for this kernel option." + return 1 + else + echo "$text" + fi + else + echo "There is no help available for this kernel option." + return 1 + fi +} + +# +# Activate a help dialog. +# +function help () { + if extract_help $1 >help.out + then + $DIALOG --backtitle "$backtitle" --title "$2"\ + --textbox help.out $ROWS $COLS + else + $DIALOG --backtitle "$backtitle" \ + --textbox help.out $ROWS $COLS + fi + rm help.out +} + +# +# Show the README file. +# +function show_readme () { + $DIALOG --backtitle "$backtitle" \ + --textbox scripts/README.Menuconfig $ROWS $COLS +} + +# +# Begin building the dialog menu command and Initialize the +# Radiolist function file. +# +function menu_name () { + echo -ne "$DIALOG --title '$1'\ + --backtitle '$backtitle' \ + --menu '$menu_instructions' \ + $ROWS $COLS $((ROWS-10)) \ + '$default' " >MCmenu + >MCradiolists +} + +# +# Add a submenu option to the menu currently under construction. +# +function submenu () { + echo -ne "'activate_menu $2' '$1 --->' " >>MCmenu +} + +# +# Create a menu entry to handle the traditional sound configuration. +# +function soundcfg () { + echo -ne "'l_soundcfg' "\ + "'Old configuration script "\ + "(For: SM Wave, PSS & AudioTrix Pro) -->' " >>MCmenu +} + +# +# Startup the traditional sound configuration program. +# +function l_soundcfg () { + clear + $MAKE -C drivers/sound config +} + +# +# Handle a boolean (Yes/No) option. +# +function l_bool () { + if [ -n "$2" ] + then + case "$2" in + y|m) eval $1=y ;; + c) eval x=\$$1 + case $x in + y) eval $1=n ;; + n) eval $1=y ;; + esac ;; + *) eval $1=n ;; + esac + else + echo -ne "\007" + fi +} + +# +# Same as bool() except options are (Module/No) +# +function mod_bool () { + eval $2=\${$2:-'n'} x=\$$2 + + case $x in + y|m) flag='M' ;; + *) flag=' ' ;; + esac + + echo -ne "'$2' '<$flag> $1' " >>MCmenu + + echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists +} + +# +# Same as l_bool() except options are (Module/No) +# +function l_mod_bool() { + if [ -n "$2" ] + then + case "$2" in + y) echo -en "\007" + ${DIALOG} --backtitle "$backtitle" \ + --infobox "\ +This feature depends on another which has been configured as a module. \ +As a result, this feature will be built as a module." 4 70 + sleep 5 + eval $1=m ;; + m) eval $1=m ;; + c) eval x=\$$1 + case $x in + m) eval $1=n ;; + n) eval $1=m ;; + esac ;; + *) eval $1=n ;; + esac + else + echo -ne "\007" + fi +} + +# +# Handle a tristate (Yes/No/Module) option. +# +function l_tristate () { + if [ -n "$2" ] + then + eval x=\$$1 + + case "$2" in + y) eval $1=y ;; + m) eval $1=m ;; + c) eval x=\$$1 + case $x in + y) eval $1=n ;; + n) eval $1=m ;; + m) eval $1=y ;; + esac ;; + *) eval $1=n ;; + esac + else + echo -ne "\007" + fi +} + +# +# Create a dialog for entering an integer into a kernel option. +# +function l_int () { + while true + do + if $DIALOG --title "$1" \ + --backtitle "$backtitle" \ + --inputbox "$inputbox_instructions_int" \ + 10 75 "$4" 2>MCdialog.out + then + answer="`cat MCdialog.out`" + answer="${answer:-$3}" + + if expr $answer : '0$\|-?[1-9][0-9]*$' >/dev/null + then + eval $2="$answer" + else + eval $2="$3" + echo -en "\007" + ${DIALOG} --backtitle "$backtitle" \ + --infobox "You have made an invalid entry." 3 43 + sleep 2 + fi + + break + fi + + help "$2" "$1" + done +} + +# +# Create a dialog for entering a hexadecimal into a kernel option. +# +function l_hex () { + while true + do + if $DIALOG --title "$1" \ + --backtitle "$backtitle" \ + --inputbox "$inputbox_instructions_hex" \ + 10 75 "$4" 2>MCdialog.out + then + answer="`cat MCdialog.out`" + answer="${answer:-$3}" + answer="${answer##*[x,X]}" + + if expr $answer : '[0-9a-fA-F]+$' >/dev/null + then + eval $2="$answer" + else + eval $2="$3" + echo -en "\007" + ${DIALOG} --backtitle "$backtitle" \ + --infobox "You have made an invalid entry." 3 43 + sleep 2 + fi + + break + fi + + help "$2" "$1" + done +} + +# +# Handle a on-of-many choice list. +# +function l_choice () { + # + # Need to remember params cause they're gonna get reset. + # + title="$1" + choices="$2" + current="$3" + + # + # Scan current value of choices and set radiolist switches. + # + list= + set -- $choices + firstchoice=$2 + while [ -n "$2" ] + do + case "$1" in + "$current") list="$list $2 $1 ON " ;; + *) list="$list $2 $1 OFF " ;; + esac + + shift ; shift + done + + while true + do + if $DIALOG --title "$title" \ + --backtitle "$backtitle" \ + --radiolist "$radiolist_instructions" \ + 15 70 6 $list 2>MCdialog.out + then + choice=`cat MCdialog.out` + break + fi + + help "$firstchoice" "$title" + done + + # + # Now set the boolean value of each option base on + # the selection made from the radiolist. + # + set -- $choices + while [ -n "$2" ] + do + if [ "$2" = "$choice" ] + then + eval $2="y" + else + eval $2="n" + fi + + shift ; shift + done +} + + +# +# A faster awk based recursive parser. (I hope) +# +function parser1 () { +awk ' +BEGIN { + menu_no = 0 + comment_is_option = 0 + parser("'$CONFIG_IN'","MCmenu0") +} + +function parser(ifile,menu) { + + while (getline <ifile) { + if ($1 == "mainmenu_option") { + comment_is_option = "1" + } + else if ($1 == "comment" && comment_is_option == "1") { + comment_is_option= "0" + sub($1,"",$0) + ++menu_no + + printf("submenu %s MCmenu%s\n", $0, menu_no) >>menu + + printf( "function MCmenu%s () {\n"\ + "default=$1\n"\ + "menu_name %s\n",\ + menu_no, $0) >"MCmenu"menu_no + + parser(ifile, "MCmenu"menu_no) + } + else if ($1 ~ "endmenu") { + printf("}\n") >>menu + return + } + else if ($0 ~ /^#|\$MAKE|mainmenu_name/) { + printf("") >>menu + } + else if ($1 == "source") { + # Yuk! Blah! Phooey! + if ($2 ~ "drivers/sound") { + printf("soundcfg\n") >>menu + } + + parser($2,menu) + } + else { + print >>menu + } + } +}' +} + +# +# Secondary parser for single menu mode. +# +function parser2 () { +awk ' +BEGIN { + parser("'$CONFIG_IN'","MCmenu0") +} + +function parser(ifile,menu) { + + while (getline <ifile) { + if ($1 ~ /mainmenu_option|endmenu/) { + printf("") >>menu + } + else if ($0 ~ /^#|$MAKE|mainmenu_name/) { + printf("") >>menu + } + else if ($1 == "source") { + if ($2 ~ "drivers/sound") { + printf("soundcfg\n") >>menu + } + parser($2,menu) + } + else { + print >>menu + } + } +}' +} + +# +# Parse all the config.in files into mini scripts. +# +function parse_config_files () { + rm -f MCmenu* + + echo "function MCmenu0 () {" >MCmenu0 + echo 'default=$1' >>MCmenu0 + echo "menu_name 'Main Menu'" >>MCmenu0 + + if [ "_$single_menu_mode" = "_TRUE" ] + then + parser2 + else + parser1 + fi + + echo "comment ''" >>MCmenu0 + echo "g_alt_config" >>MCmenu0 + echo "s_alt_config" >>MCmenu0 + + echo "}" >>MCmenu0 + + # + # These mini scripts must be sourced into the current + # environment in order for all of this to work. Leaving + # them on the disk as executables screws up the recursion + # in activate_menu(), among other things. Once they are + # sourced we can discard them. + # + for i in MCmenu* + do + source ./$i + done + + rm -f MCmenu* +} + +# +# This is the menu tree's bootstrap. +# +# Executes the parsed menus on demand and creates a set of functions, +# one per configuration option. These functions will in turn execute +# dialog commands or recursively call other menus. +# +function activate_menu () { + while true + do + comment_ctr=0 #So comment lines get unique tags + + $1 "$default" #Create the lxdialog menu & functions + + if [ "$?" != "0" ] + then + clear + cat <<EOM + +Menuconfig has encountered a possible error in one of the kernel's +configuration files and is unable to continue. + +Please report this to the author <roadcapw@cfw.com>. You may also +send a problem report to linux-kernel@vger.rutgers.edu or post a +message to the linux.dev.kernel news group. + +Please indicate the kernel version you are trying to configure and +which menu you were trying to enter when this error occurred. + +EOM + cleanup + exit 1 + fi + + . ./MCradiolists #Source the menu's functions + + . ./MCmenu 2>MCdialog.out #Activate the lxdialog menu + ret=$? + + read selection <MCdialog.out + + case "$ret" in + 0|3|4|5|6) + defaults="$selection$defaults" #pseudo stack + case "$ret" in + 0) eval $selection ;; + 3) eval $selection y ;; + 4) eval $selection n ;; + 5) eval $selection m ;; + 6) eval $selection c ;; + esac + default="${defaults%%*}" defaults="${defaults#*}" + ;; + 2) + default="${selection%%\ *}" + + case "$selection" in + *"-->"*|*"alt_config"*) + show_readme ;; + *) + eval help $selection ;; + esac + ;; + 255|1) + break + ;; + 139) + stty sane + clear + cat <<EOM + +There seems to be a problem with the lxdialog companion utility which is +built prior to running Menuconfig. Usually this is an indicator that you +have upgraded/downgraded your ncurses libraries and did not remove the +old ncurses header file(s) in /usr/include or /usr/include/ncurses. + +It is VERY important that you have only one set of ncurses header files +and that those files are properly version matched to the ncurses libraries +installed on your machine. + +You may also need to rebuild lxdialog. This can be done by moving to +the /usr/src/linux/scripts/lxdialog directory and issuing the +"make clean all" command. + +If you have verified that your ncurses install is correct, you may email +the author <roadcapw@cfw.com> or post a message on the linux.dev.kernel +news group for additional assistance. + +EOM + cleanup + exit 139 + ;; + esac + done +} + +# +# Create a menu item to load an alternate configuration file. +# +g_alt_config () { + echo -n "get_alt_config 'Load an Alternate Configuration File' "\ + >>MCmenu +} + +# +# Get alternate config file name and load the +# configuration from it. +# +get_alt_config () { + set -f ## Switch file expansion OFF + + while true + do + ALT_CONFIG="${ALT_CONFIG:-$DEFAULTS}" + + $DIALOG --backtitle "$backtitle" \ + --inputbox "\ +Enter the name of the configuration file you wish to load. \ +Accept the name shown to restore the configuration you \ +last retrieved. Leave blank to abort."\ + 11 55 "$ALT_CONFIG" 2>MCdialog.out + + if [ "$?" = "0" ] + then + ALT_CONFIG=`cat MCdialog.out` + + [ "_" = "_$ALT_CONFIG" ] && break + + if eval [ -r "$ALT_CONFIG" ] + then + eval load_config_file "$ALT_CONFIG" + break + else + echo -ne "\007" + $DIALOG --backtitle "$backtitle" \ + --infobox "File does not exist!" 3 38 + sleep 2 + fi + else + cat <<EOM >help.out + +For various reasons, one may wish to keep several different kernel +configurations available on a single machine. + +If you have saved a previous configuration in a file other than the +kernel's default, entering the name of the file here will allow you +to modify that configuration. + +If you are uncertain, then you have probably never used alternate +configuration files. You should therefor leave this blank to abort. + +EOM + $DIALOG --backtitle "$backtitle"\ + --title "Load Alternate Configuration"\ + --textbox help.out $ROWS $COLS + fi + done + + set +f ## Switch file expansion ON + rm -f help.out MCdialog.out +} + +# +# Create a menu item to store an alternate config file. +# +s_alt_config () { + echo -n "save_alt_config 'Save Configuration to an Alternate File' "\ + >>MCmenu +} + +# +# Get an alternate config file name and save the current +# configuration to it. +# +save_alt_config () { + set -f ## Switch file expansion OFF + + while true + do + $DIALOG --backtitle "$backtitle" \ + --inputbox "\ +Enter a filename to which this configuration should be saved \ +as an alternate. Leave blank to abort."\ + 10 55 "$ALT_CONFIG" 2>MCdialog.out + + if [ "$?" = "0" ] + then + ALT_CONFIG=`cat MCdialog.out` + + [ "_" = "_$ALT_CONFIG" ] && break + + if eval touch $ALT_CONFIG 2>/dev/null + then + eval save_configuration $ALT_CONFIG + load_functions ## RELOAD + break + else + echo -ne "\007" + $DIALOG --backtitle "$backtitle" \ + --infobox "Can't create file! Probably a nonexistent directory." 3 60 + sleep 2 + fi + else + cat <<EOM >help.out + +For various reasons, one may wish to keep different kernel +configurations available on a single machine. + +Entering a file name here will allow you to later retrieve, modify +and use the current configuration as an alternate to whatever +configuration options you have selected at that time. + +If you are uncertain what all this means then you should probably +leave this blank. +EOM + $DIALOG --backtitle "$backtitle"\ + --title "Save Alternate Configuration"\ + --textbox help.out $ROWS $COLS + fi + done + + set +f ## Switch file expansion ON + rm -f help.out MCdialog.out +} + +# +# Load config options from a file. +# Converts all "# OPTION is not set" lines to "OPTION=n" lines +# +function load_config_file () { + awk ' + /# .* is not set.*/ { printf("%s=n\n", $2) } + ! /# .* is not set.*/ { print } + ' $1 >.tmpconfig + + source ./.tmpconfig + rm -f .tmpconfig +} + +# +# Just what it says. +# +save_configuration () { + ${DIALOG} --backtitle "$backtitle" \ + --infobox "Saving your kernel configuration..." 3 40 + + # + # Now, let's redefine the configuration functions for final + # output to the config files. + # + # Nested function definitions, YIPEE! + # + function bool () { + eval define_bool "$2" "\${$2:-n}" + } + + function tristate () { + eval define_bool "$2" "\${$2:-n}" + } + + function dep_tristate () { + eval x=\${$2:-n} + + if eval [ "_$3" = "_m" ] + then + if [ "$x" = "y" ] + then + x="m" + fi + fi + + define_bool "$2" "$x" + } + + function int () { + eval x=\${$2:-"$3"} + echo "$2=$x" >>$CONFIG + echo "#define $2 ($x)" >>$CONFIG_H + } + + function hex () { + eval x=\${$2:-"$3"} + echo "$2=$x" >>$CONFIG + echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H + } + + function define_bool () { + eval $1="$2" + + case "$2" in + y) + echo "$1=y" >>$CONFIG + echo "#define $1 1" >>$CONFIG_H + ;; + + m) + if [ "$CONFIG_MODULES" = "y" ] + then + echo "$1=m" >>$CONFIG + echo "#undef $1" >>$CONFIG_H + echo "#define $1_MODULE 1" >>$CONFIG_H + else + echo "$1=y" >>$CONFIG + echo "#define $1 1" >>$CONFIG_H + fi + ;; + + n) + echo "# $1 is not set" >>$CONFIG + echo "#undef $1" >>$CONFIG_H + ;; + esac + } + + function choice () { + # + # Find the first choice that's already set to 'y' + # + choices="$2" + default="$3" + current= + + set -- $choices + while [ -n "$2" ] + do + if eval [ "_\$$2" = "_y" ] + then + current=$1 + break + fi + shift ; shift + done + + # + # Use the default if none were set. + # + : ${current:=$default} + + # + # Then extract the actual option from the list of choices. + # + current=${choices#*$current} ; set $current + + define_bool "$1" "y" + } + + function mainmenu_name () { + : + } + + function mainmenu_option () { + comment_is_option=TRUE + } + + function endmenu () { + : + } + + function comment () { + if [ "$comment_is_option" ] + then + comment_is_option= + echo >>$CONFIG + echo "#" >>$CONFIG + echo "# $1" >>$CONFIG + echo "#" >>$CONFIG + + echo >>$CONFIG_H + echo "/*" >>$CONFIG_H + echo " * $1" >>$CONFIG_H + echo " */" >>$CONFIG_H + fi + } + + DEF_CONFIG="${1:-.config}" + DEF_CONFIG_H="include/linux/autoconf.h" + + CONFIG=.tmpconfig + CONFIG_H=.tmpconfig.h + + echo "#" >$CONFIG + echo "# Automatically generated by make menuconfig: don't edit" >>$CONFIG + echo "#" >>$CONFIG + + echo "/*" >$CONFIG_H + echo " * Automatically generated by make menuconfig: don't edit" >>$CONFIG_H + echo " */" >>$CONFIG_H + + MAKE=: #To prevent sound Makefile from running. + + if . $CONFIG_IN >>.menuconfig.log 2>&1 + then + if [ "$DEF_CONFIG" = ".config" ] + then + # + # Create the sound driver's config files for cards + # Which are compatible with the new config method. + # + if [ "_$CONFIG_TRIX" != "_y" -a\ + "_$CONFIG_PSS" != "_y" -a\ + "_$CONFIG_SMWAVE" != "_y" ] + then + make -C drivers/sound kernelconfig >>.menuconfig.log 2>&1 + fi + + mv $CONFIG_H $DEF_CONFIG_H + fi + + if [ -f "$DEF_CONFIG" ] + then + rm -f ${DEF_CONFIG}.old + mv $DEF_CONFIG ${DEF_CONFIG}.old + fi + + mv $CONFIG $DEF_CONFIG + + return 0 + else + return 1 + fi +} + +# +# Remove temporary files +# +cleanup () { + cleanup1 + cleanup2 +} + +cleanup1 () { + rm -f MCmenu* MCradiolists MCdialog.out help.out +} + +cleanup2 () { + rm -f .tmpconfig .tmpconfig.h +} + +set_geometry () { + # Some distributions export these with incorrect values + # which can really screw up some ncurses programs. + LINES= COLUMNS= + + ROWS=${1:-24} COLS=${2:-80} + + # Just in case the nasty rlogin bug returns. + # + [ $ROWS = 0 ] && ROWS=24 + [ $COLS = 0 ] && COLS=80 + + if [ $ROWS -lt 19 -o $COLS -lt 80 ] + then + echo -e "\n\007Your display is too small to run Menuconfig!" + echo "It must be at least 19 lines by 80 columns." + exit 0 + fi + + ROWS=$((ROWS-4)) COLS=$((COLS-5)) +} + + +set_geometry `stty size 2>/dev/null` + +menu_instructions="\ +Arrow keys navigate the menu. \ +<Enter> selects submenus --->. \ +Highlighted letters are hotkeys. \ +Pressing <Y> includes, <N> excludes, <M> modularizes features. \ +Press <Esc><Esc> to exit, <?> for Help. \ +Legend: [*] built-in [ ] excluded <M> module < > module capable" + +radiolist_instructions="\ +Use the arrow keys to navigate this window or \ +press the hotkey of the item you wish to select \ +followed by the <SPACE BAR>. +Press <?> for additional information about this option." + +inputbox_instructions_int="\ +Please enter a decimal value between 1 and 9999. \ +Fractions will not be accepted. \ +Use the <TAB> key to move from the input field to the buttons below it." + +inputbox_instructions_hex="\ +Please enter a hexadecimal value. \ +Use the <TAB> key to move from the input field to the buttons below it." + +DIALOG="./scripts/lxdialog/lxdialog" + +kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}" + +backtitle="Linux Kernel v$kernel_version Configuration" + +trap "cleanup ; rm -f .menuconfig ; exit 1" 1 2 15 + + +# +# Locate default files. +# +CONFIG_IN=./config.in +if [ "$1" != "" ] ; then + CONFIG_IN=$1 +fi + +DEFAULTS=arch/$ARCH/defconfig +if [ -f .config ]; then + DEFAULTS=.config +fi + +if [ -f $DEFAULTS ] +then + echo "#" + echo "# Using defaults found in" $DEFAULTS + echo "#" + load_config_file $DEFAULTS +else + echo "#" + echo "# No defaults found" + echo "#" +fi + + +# Fresh new log. +>.menuconfig.log + +$DIALOG --backtitle "$backtitle" \ + --infobox "Preparing configuration scripts..." 3 40 + +# +# Check kernel version of previous menuconfig build. +# If it's different then we should tell the sound driver +# to rebuild its Config.in file. +# +rebuildsound=TRUE +if [ -e .menuconfig ] +then + read x <.menuconfig + if [ "$x" = "# $kernel_version" ] + then + rebuildsound= + fi +fi + +if [ "$rebuildsound" ] +then + # Activate the Linux compatible sound configuration. + # This may not work for all sound cards. (See sound docs) + # + make -C drivers/sound mkscript kernelconfig >>.menuconfig.log 2>&1 + + echo "# $kernel_version" >.menuconfig +fi + +# Load the functions used by the config.in files. +load_functions + +# +# Read config.in files and parse them into one shell function per menu. +# +parse_config_files $CONFIG_IN + +# +# Start the ball rolling from the top. +# +activate_menu MCmenu0 + +# +# All done! +# +cleanup1 + +# +# Confirm and Save +# +if $DIALOG --backtitle "$backtitle" \ + --yesno "Do you wish to save your new kernel configuration?" 5 60 + +then + save_configuration + + clear + cat <<EOM + + +The linux kernel is now hopefully configured for your setup. +Check the top-level Makefile for additional configuration, +and do a 'make dep ; make clean' if you want to be sure all +the files are correctly re-made. + +EOM +else + clear + echo -e "Your kernel configuration changes were NOT saved.\n" +fi + + +exit 0 + diff --git a/scripts/README.Menuconfig b/scripts/README.Menuconfig new file mode 100644 index 000000000..b3ec5aaaf --- /dev/null +++ b/scripts/README.Menuconfig @@ -0,0 +1,194 @@ +Menuconfig gives the Linux kernel configuration a long needed face +lift. Featuring text based color menus and dialogs, it does not +require X Windows. With this utility you can easily select a kernel +option to modify without sifting through 100 other options. + +Overview +-------- +Some kernel features may be built directly into the kernel. +Some may be made into loadable runtime modules. Some features +may be completely removed altogether. There are also certain +kernel parameters which are not really features, but must be +entered in as decimal or hexadecimal numbers or possibly text. + +Menu items beginning with [*], <M> or [ ] represent features +configured to be built in, modularized or removed respectively. +Pointed brackets <> represent module capable features. + more... + +To change any of these features, highlight it with the cursor +keys and press <Y> to build it in, <M> to make it a module or +<N> to removed it. You may also press the <Space Bar> to cycle +through the available options (ie. Y->N->M->Y). + +Items beginning with numbers or other text within parenthesis can +be changed by highlighting the item and pressing <Enter>. Then +enter the new parameter into the dialog box that pops up. + + +Some additional keyboard hints: + +Menus +---------- +o Use the Up/Down arrow keys (cursor keys) to highlight the item + you wish to change or submenu wish to select and press <Enter>. + Submenus are designated by "--->". + + Shortcut: Press the option's highlighted letter (hotkey). + Pressing a hotkey more than once will sequence + through all visible items which use that hotkey. + + You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll + unseen options into view. + +o To exit a menu use the cursor keys to highlight the <Exit> button + and press <ENTER>. + + Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey + using those letters. You may press a single <ESC>, but + there is a delayed response which you may find annoying. + + Also, the <TAB> and cursor keys will cycle between <Select>, + <Exit> and <Help> + +o To get help with an item, use the cursor keys to highlight <Help> + and Press <ENTER>. + + Shortcut: Press <H> or <?>. + + +Radiolists (Choice lists) +----------- +o Use the cursor keys to select the option you wish to set and press + <S> or the <SPACE BAR>. + + Shortcut: Press the first letter of the option you wish to set then + press <S> or <SPACE BAR>. + +o To see available help for the item, use the cursor keys to highlight + <Help> and Press <ENTER>. + + Shortcut: Press <H> or <?>. + + Also, the <TAB> and cursor keys will cycle between <Select> and + <Help> + + +Data Entry +----------- +o Enter the requested information and press <ENTER> + If you are entering hexadecimal values, it is not necessary to + add the '0x' prefix to the entry. + +o For help, use the <TAB> or cursor keys to highlight the help option + and press <ENTER>. You can try <TAB><H> as well. + + +Text Box (Help Window) +-------- +o Use the cursor keys to scroll up/down/left/right. The VI editor + keys h,j,k,l function here as do <SPACE BAR> and <B> for those + who are familiar with less and lynx. + +o Press <E>, <X>, <Enter> or <Esc><Esc> to exit. + + +Final Acceptance +---------------- +With the exception of the old style sound configuration, +YOUR CHANGES ARE NOT FINAL. You will be given a last chance to +confirm them prior to exiting Menuconfig. + +If Menuconfig quits with an error while saving your configuration, +you may look in the file /usr/src/linux/.menuconfig.log for +information which may help you determine the cause. + +Alternate Configuration Files +----------------------------- +Menuconfig supports the use of alternate configuration files for +those who, for various reasons, find it necessary to switch +between different kernel configurations. + +At the end of the main menu you will find two options. One is +for saving the current configuration to a file of your choosing. +The other option is for loading a previously saved alternate +configuration. + +Even if you don't use alternate configuration files, but you +find during a Menuconfig session that you have completely messed +up your settings, you may use the "Load Alternate..." option to +restore your previously saved settings from ".config" without +restarting Menuconfig. + +Other information +----------------- +The windowing utility, lxdialog, will only be rebuilt if your kernel +source tree is fresh, or changes are patched into it via a kernel +patch or you do 'make mrproper'. If changes to lxdialog are patched +in, most likely the rebuild time will be short. You may force a +complete rebuild of lxdialog by changing to it's directory and doing +'make clean all' + +If you use Menuconfig in an XTERM window make sure you have your +$TERM variable set to point to a xterm definition which supports color. +Otherwise, Menuconfig will look rather bad. Menuconfig will not +display correctly in a RXVT window because rxvt displays only one +intensity of color, bright. + +Menuconfig will display larger menus on screens or xterms which are +set to display more than the standard 25 row by 80 column geometry. +In order for this to work, the "stty size" command must be able to +display the screen's current row and column geometry. I STRONGLY +RECOMMEND that you make sure you do NOT have the shell variables +LINES and COLUMNS exported into your environment. Some distributions +export those variables via /etc/profile. Some ncurses programs can +become confused when those variables (LINES & COLUMNS) don't reflect +the true screen size. + + +NOTICE: lxdialog requires the ncurses libraries to compile. If you + don't already have ncurses you really should get it. + + The makefile for lxdialog attempts to find your ncurses + header file. Although it should find the header for older + versions of ncurses, it is probably a good idea to get the + latest ncurses anyway. + + If you have upgraded your ncurses libraries, MAKE SURE you + remove the old ncurses header files. If you don't you + will most certainly get a segmentation fault. + +WARNING: It is not recommended that you change any defines in + lxdialog's header files. If you have a grayscale display and + are brave, you may tinker with color.h to tune the colors to + your preference. + +COMPATIBILITY ISSUE: + There have been some compatibility problems reported with + older versions of bash and sed. I am trying to work these + out but it is preferable that you upgrade those utilities. + + +******** IMPORTANT, OPTIONAL ALTERNATE PERSONALITY AVAILABLE ******** +******** ******** +If you prefer to have all of the kernel options listed in a single +menu, rather than the default multimenu hierarchy, you may edit the +Menuconfig script and change the line "single_menu_mode=" to +"single_menu_mode=TRUE". + +This mode is not recommended unless you have a fairly fast machine. +********************************************************************* + + +Propaganda +---------- +The windowing support utility (lxdialog) is a VERY modified version of +the dialog utility by Savio Lam <lam836@cs.cuhk.hk>. Although lxdialog +is significantly different from dialog, I have left Savio's copyrights +intact. Please DO NOT contact Savio with questions about lxdialog. +He will not be able to assist. + +Please feel free to send any questions, comments or suggestions to +William Roadcap <roadcapw@cfw.com>. + +<END OF FILE> diff --git a/scripts/depend.awk b/scripts/depend.awk new file mode 100644 index 000000000..93cb0ebbc --- /dev/null +++ b/scripts/depend.awk @@ -0,0 +1,154 @@ +# This is an awk script which does dependencies. We do NOT want it to +# recursively follow #include directives. +# +# The HPATH environment variable should be set to indicate where to look +# for include files. The -I in front of the path is optional. + +# +# Surely there is a more elegant way to see if a file exists. Anyone know +# what it is? +# +function fileExists(f, TMP, dummy, result) { + if(result=FILEHASH[f]) { + if(result=="Yes") { + return "Yes" + } else {return ""} + } + ERRNO = getline dummy < f + if(ERRNO >= 0) { + close(f) + return FILEHASH[f]="Yes" + } else { + FILEHASH[f]="No" + return "" + } +} + +function endfile(f) { + if (hasconfig && !needsconfig) { + printf "%s doesn't need config\n",f > "/dev/stderr" + } + if (hasdep) { + print cmd + } +} + +BEGIN{ + hasdep=0 + hasconfig=0 + needsconfig=0 + incomment=0 + if(!(TOPDIR=ENVIRON["TOPDIR"])) { + print "Environment variable TOPDIR is not set" + exit 1 + } + split(ENVIRON["HPATH"],parray," ") + for(path in parray) { + sub("^-I","",parray[path]) + sub("[/ ]*$","",parray[path]) + } +} + +# eliminate comments +{ + # remove all comments fully contained on a single line + gsub("\\/\\*.*\\*\\/", "") + if (incomment) { + if ($0 ~ /\*\//) { + incomment = 0; + gsub(".*\\*\\/", "") + } else { + next + } + } else { + # start of multi-line comment + if ($0 ~ /\/\*/) + { + incomment = 1; + sub("\\/\\*.*", "") + } else if ($0 ~ /\*\//) { + incomment = 0; + sub(".*\\*\\/", "") + } + } +} + +/^[ ]*#[ ]*if.*[^A-Za-z_]CONFIG_/ { + needsconfig=1 + if (!hasconfig) { + printf "%s needs config but has not included config file\n",FILENAME > "/dev/stderr" + # only say it once per file.. + hasconfig = 1 + } +} + +/^[ ]*#[ ]*include[ ]*[<"][^ ]*[>"]/{ + found=0 + if(LASTFILE!=FILENAME) { + endfile(LASTFILE) + hasdep=0 + hasconfig=0 + needsconfig=0 + incomment=0 + cmd="" + LASTFILE=FILENAME + depname=FILENAME + relpath=FILENAME + sub("\\.c$",".o: ",depname) + sub("\\.S$",".o: ",depname) + if (depname==FILENAME) { + cmd="\n\t@touch "depname + } + sub("\\.h$",".h: ",depname) + if(relpath ~ "^\\." ) { + sub("[^/]*$","", relpath) + relpath=relpath"/" + sub("//","/", relpath) + } else { + relpath="" + } + } + fname=$0 + sub("^#[ ]*include[ ]*[<\"]","",fname) + sub("[>\"].*","",fname) + if (fname=="linux/config.h") { + hasconfig=1 + } + rfname=relpath""fname + if(fileExists(rfname)) { + found=1 + if (!hasdep) { + printf "%s", depname + } + hasdep=1 + printf " \\\n %s", rfname + if(fname ~ "^\\." ) { + fnd=0; + for(i in ARGV) { + if(ARGV[i]==rfname) { + fnd=1 + } + } + if(fnd==0) { + ARGV[ARGC]=rfname + ++ARGC + } + } + } else { + for(path in parray) { + if(fileExists(parray[path]"/"fname)) { + shortp=parray[path] + found=1 + if (!hasdep) { + printf "%s", depname + } + hasdep=1 + printf " \\\n %s", parray[path]"/"fname + } + } + } +} + +END{ + endfile(FILENAME) +} diff --git a/scripts/header.tk b/scripts/header.tk new file mode 100644 index 000000000..763eca6db --- /dev/null +++ b/scripts/header.tk @@ -0,0 +1,440 @@ +# +# This is a handy replacement for ".widget cget" that requires neither tk4 +# nor additional source code uglification. +# +proc cget { w option } { + return "[lindex [$w configure $option] 4]" +} + +# +# Function to compensate for broken config.in scripts like the sound driver, +# which make dependencies on variables that are never even conditionally +# defined. +# +proc vfix { var } { + global $var + if [ catch {eval concat $$var} ] { + puts stdout "WARNING - broken Config.in! $var was not declared!" + set $var 0 + } +} + +# +# Create a "reference" object to steal colors from. +# +button .ref +# +# On monochrome displays, -disabledforeground is blank by default; that's +# bad. Fill it with -foreground instead. +# +if { [cget .ref -disabledforeground] == "" } { + .ref configure -disabledforeground [cget .ref -foreground] +} + + +# +# Define some macros we will need to parse the config.in file. +# +proc mainmenu_name { text } { + message .header.message -width 400 -relief raised -text "$text" + pack .header.label .header.message -side left -padx 15 + wm title . "$text" +} + +proc menu_option { w menu_num text } { + button .f0.x$menu_num -text "$text" -width 50 -command "$w .$w \"$text\"" + pack .f0.x$menu_num -pady 1 -expand on +} + +# +# Not used at the moment, but this runs a command in a subprocess and +# displays the result in a window with a scrollbar. +# +# For now, we just do external "make" commands to stdout with do_make, so +# this function is never called. +# +proc do_cmd { w command } { + catch {destroy $w} + toplevel $w -class Dialog + frame $w.tb + text $w.tb.text -relief raised -bd 2 -yscrollcommand "$w.tb.scroll set" + scrollbar $w.tb.scroll -command "$w.tb.text yview" + pack $w.tb.scroll -side right -fill y + pack $w.tb.text -side left + + set oldFocus [focus] + frame $w.back + button $w.back.ok -text "OK" -width 20 \ + -command "destroy $w; focus $oldFocus" -state disabled + button $w.back.ccl -text "Cancel" -width 20 \ + -command "destroy $w; focus $oldFocus" + pack $w.tb -side top + pack $w.back.ok $w.back.ccl -side left + pack $w.back -side bottom -pady 10 + + focus $w + wm geometry $w +30+35 + + $w.tb.text delete 1.0 end + set f [open |$command] + while {![eof $f]} { + $w.tb.text insert end [read $f 256] + } + close $f + $w.back.ok configure -state normal +} + +proc load_configfile { w title func } { + catch {destroy $w} + toplevel $w -class Dialog + global loadfile + frame $w.x + label $w.bm -bitmap questhead + pack $w.bm -pady 10 -side top -padx 10 + label $w.x.l -text "Enter filename:" -relief raised + entry $w.x.x -width 35 -relief sunken -borderwidth 2 \ + -textvariable loadfile + pack $w.x.l $w.x.x -anchor w -side left + pack $w.x -side top -pady 10 + wm title $w "$title" + + set oldFocus [focus] + frame $w.f + button $w.f.back -text "OK" -width 20 \ + -command "destroy $w; focus $oldFocus;$func .fileio" + button $w.f.canc -text "Cancel" \ + -width 20 -command "destroy $w; focus $oldFocus" + pack $w.f.back $w.f.canc -side left -pady 10 -padx 45 + pack $w.f -pady 10 -side bottom -padx 10 -anchor w + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy +} + +proc maybe_exit { w } { + catch {destroy $w} + toplevel $w -class Dialog + label $w.bm -bitmap questhead + pack $w.bm -pady 10 -side top -padx 10 + message $w.m -width 400 -aspect 300 \ + -text "Changes will be lost. Are you sure?" -relief flat + pack $w.m -pady 10 -side top -padx 10 + wm title $w "Are you sure?" + + set oldFocus [focus] + frame $w.f + button $w.f.back -text "OK" -width 20 \ + -command "exit" + button $w.f.canc -text "Cancel" \ + -width 20 -command "destroy $w; focus $oldFocus" + pack $w.f.back $w.f.canc -side left -pady 10 -padx 45 + pack $w.f -pady 10 -side bottom -padx 10 -anchor w + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy +} + +proc read_config_file { w } { + global loadfile + if { [string length $loadfile] != 0 && [file readable $loadfile] == 1 } then { + read_config $loadfile + } else { + catch {destroy $w} + toplevel $w -class Dialog + message $w.m -width 400 -aspect 300 -text \ + "Unable to read file $loadfile" \ + -relief raised + label $w.bm -bitmap error + pack $w.bm $w.m -pady 10 -side top -padx 10 + wm title $w "Oops" + + set oldFocus [focus] + frame $w.f + button $w.f.back -text "Bummer" \ + -width 10 -command "destroy $w; focus $oldFocus" + pack $w.f.back -side bottom -pady 10 -anchor s + pack $w.f -pady 10 -side top -padx 10 -anchor s + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy + } +} + +proc write_config_file { w } { + global loadfile + if { [string length $loadfile] != 0 + && ([file writable $loadfile] == 1 || ([file exists $loadfile] == 0 && [file writable [file dirname $loadfile]] == 1)) } then { + writeconfig $loadfile /dev/null + } else { + catch {destroy $w} + toplevel $w -class Dialog + message $w.m -width 400 -aspect 300 -text \ + "Unable to write file $loadfile" \ + -relief raised + label $w.bm -bitmap error + pack $w.bm $w.m -pady 10 -side top -padx 10 + wm title $w "Oops" + + set oldFocus [focus] + frame $w.f + button $w.f.back -text "OK" \ + -width 10 -command "destroy $w; focus $oldFocus" + pack $w.f.back -side bottom -pady 10 -anchor s + pack $w.f -pady 10 -side top -padx 10 -anchor s + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy + } +} + +proc read_config { filename } { + set file1 [open $filename r] + clear_choices + while { [gets $file1 line] >= 0} { + if [regexp {([0-9A-Za-z_]+)=([ynm])} $line foo var value] { + if { $value == "y" } then { set cmd "global $var; set $var 1" } + if { $value == "n" } then { set cmd "global $var; set $var 0" } + if { $value == "m" } then { set cmd "global $var; set $var 2" } + eval $cmd + } + if [regexp {# ([0-9A-Za-z_]+) is not set} $line foo var] { + set cmd "global $var; set $var 0" + eval $cmd + } + if [regexp {([0-9A-Za-z_]+)=([0-9A-Fa-f]+)} $line foo var value] { + set cmd "global $var; set $var $value" + eval $cmd + } + } + close $file1 + update_choices + update_mainmenu .rdupd +} +proc write_comment { file1 file2 text } { + puts $file1 "" + puts $file1 "#" + puts $file1 "# $text" + puts $file1 "#" + puts $file2 "/*" + puts $file2 " * $text" + puts $file2 " */" +} + +proc write_tristate { file1 file2 varname variable dep } { + if { $variable == 0 } \ + then { puts $file1 "# $varname is not set"; \ + puts $file2 "#undef $varname"} \ + elseif { $variable == 2 || ($dep == 2 && $variable == 1) } \ + then { puts $file1 "$varname=m"; \ + puts $file2 "#undef $varname"; \ + puts $file2 "#define ${varname}_MODULE 1" } \ + elseif { $variable == 1 && $dep != 2 } \ + then { puts $file1 "$varname=y"; \ + puts $file2 "#define $varname 1" } \ + else { \ + error "Attempting to write value for variable that is not configured ($varname)." \ + } +} + +proc write_int { file1 file2 varname variable dep } { + if { $dep == 0 } \ + then { puts $file1 "# $varname is not set"; \ + puts $file2 "#undef $varname"} \ + else { + puts $file1 "$varname=$variable"; \ + puts $file2 "#define $varname $variable"; \ + } +} + +proc write_hex { file1 file2 varname variable dep } { + if { $dep == 0 } \ + then { puts $file1 "# $varname is not set"; \ + puts $file2 "#undef $varname"} \ + else { + puts $file1 "$varname=$variable"; \ + puts $file2 "#define $varname 0x$variable"; \ + } +} + +proc option_name {w mnum line text helpidx} { + button $w.x$line.l -text "$text" -relief groove -anchor w + $w.x$line.l configure -activefore [cget $w.x$line.l -fg] \ + -activeback [cget $w.x$line.l -bg] + button $w.x$line.help -text "Help" -relief raised \ + -command "dohelp .dohelp $helpidx" + pack $w.x$line.help -side right -fill y + pack $w.x$line.l -side right -fill both -expand on +} + +proc toggle_switch {w mnum line text variable} { + frame $w.x$line -relief sunken + radiobutton $w.x$line.y -text "y" -variable $variable -value 1 \ + -relief groove -width 2 -command "update_menu$mnum .menu$mnum" + radiobutton $w.x$line.m -text "m" -variable $variable -value 2 \ + -relief groove -width 2 -command "update_menu$mnum .menu$mnum" + radiobutton $w.x$line.n -text "n" -variable $variable -value 0 \ + -relief groove -width 2 -command "update_menu$mnum .menu$mnum" + + option_name $w $mnum $line $text $variable + + pack $w.x$line.n $w.x$line.m $w.x$line.y -side right -fill y +} + +proc bool {w mnum line text variable} { + toggle_switch $w $mnum $line $text $variable + $w.x$line.m configure -state disabled + pack $w.x$line -anchor w -fill both -expand on +} + +proc tristate {w mnum line text variable } { + toggle_switch $w $mnum $line $text $variable + pack $w.x$line -anchor w -fill both -expand on +} + +proc dep_tristate {w mnum line text variable depend } { + tristate $w $mnum $line $text $variable +} + +proc int { w mnum line text variable } { + frame $w.x$line + entry $w.x$line.x -width 18 -relief sunken -borderwidth 2 \ + -textvariable $variable + option_name $w $mnum $line $text $variable + pack $w.x$line.x -anchor w -side right -fill y + pack $w.x$line -anchor w -fill both -expand on +} + +proc hex { w mnum line text variable } { + int $w $mnum $line $text $variable +} + +proc minimenu { w mnum line text variable helpidx } { + frame $w.x$line + menubutton $w.x$line.x -textvariable $variable -menu \ + $w.x$line.x.menu -relief raised \ + -width 15 -anchor w + option_name $w $mnum $line $text $helpidx + pack $w.x$line.x -anchor w -side right -fill y + pack $w.x$line -anchor w -fill both -expand on +} + +proc comment {w line text } { +#nothing done for comments now. +} + +proc do_make { command } { + exec sh -c $command <@stdin >@stdout 2>@stderr +# do_cmd .make_window "sh -c $command" +} + +proc dohelp {w var } { + catch {destroy $w} + toplevel $w -class Dialog + + set filefound 0 + set found 0 + set lineno 0 + + if { [file readable Documentation/Configure.help] == 1} then { + set filefound 1 + set message [exec sed -n " + /^$var\[ \]*\$/,\${ + /^$var\[ \]*\$/c\\ +${var}:\\ + + /^#.*/d + /^\[ \]*\$/bL + H + } + d + :L x + s/\\n // + s/\\n / /g + p + q + " Documentation/Configure.help] + set found [expr [string length "$message"] > 0] + } + + frame $w.f1 + + if { $found == 0 } then { + if { $filefound == 0 } then { + message $w.f1.m -width 750 -aspect 300 -relief flat -text \ + "No help available - unable to open file Documentation/Configure.help. This file should have come with your kernel." + } else { + message $w.f1.m -width 400 -aspect 300 -relief flat -text \ + "No help available for $var" + } + label $w.f1.bm -bitmap error + wm title $w "RTFM" + } else { + message $w.f1.m -width 400 -aspect 300 -text $message \ + -relief flat + label $w.f1.bm -bitmap info + wm title $w "Configuration help" + } + pack $w.f1.bm $w.f1.m -side left -padx 10 + pack $w.f1 -side top + set oldFocus [focus] + + # Do the OK button + # + frame $w.f2 + button $w.f2.ok -text "OK" \ + -width 10 -command "destroy $w; focus $oldFocus" + pack $w.f2.ok -side bottom -pady 10 -anchor s + pack $w.f2 -side bottom -padx 10 -anchor s + + # Finish off the window + # + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy +} + +proc wrapup {w } { + catch {destroy $w} + toplevel $w -class Dialog + message $w.m -width 400 -aspect 300 -text \ + "The linux kernel is now hopefully configured for your setup. Check the top-level Makefile for additional configuration, and do a 'make dep ; make clean' if you want to be sure all the files are correctly re-made." -relief raised + label $w.bm -bitmap info + pack $w.bm $w.m -pady 10 -side top -padx 10 + wm title $w "Kernel build instructions" + + set oldFocus [focus] + frame $w.f + button $w.f.back -text "OK" \ + -width 10 -command "exit" + pack $w.f.back -side bottom -pady 10 -anchor s + pack $w.f -pady 10 -side top -padx 10 -anchor s + focus $w + global winx; global winy + set winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30] + wm geometry $w +$winx+$winy + +} + +proc check_sound_config { num } { +#nothing for now. +} + +proc do_sound {w mnum line} { + message $w.x$line -width 400 -aspect 300 -text "Note: The sound drivers cannot as of yet be configured via the X-based interface" -relief raised + pack $w.x$line -side top -pady 10 +} + +# +# Next set up the particulars for the top level menu, and define a few +# buttons which we will stick down at the bottom. +# +frame .header +label .header.label + +frame .f0 + diff --git a/scripts/hfiles.sh b/scripts/hfiles.sh new file mode 100644 index 000000000..4643194c8 --- /dev/null +++ b/scripts/hfiles.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# This script looks to see if a directory contains .h files +# +for dir in $@; do + for hfile in $dir/*.h; do + if [ -f $hfile ]; then echo $dir; fi + break + done +done +exit 0 diff --git a/scripts/ksymoops.cc b/scripts/ksymoops.cc new file mode 100644 index 000000000..28d93f56f --- /dev/null +++ b/scripts/ksymoops.cc @@ -0,0 +1,325 @@ +// ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs +// Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com> +// compile like so: g++ -o ksymoops ksymoops.cc -liostream + +////////////////////////////////////////////////////////////////////////////// + +// 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, 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; see the file COPYING. If not, write to the +// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +// This is a simple filter to resolve EIP and call-trace symbols from +// a Linux kernel "Oops" log. Supply the symbol-map file name as a +// command-line argument, and redirect the oops-log into stdin. Out +// will come the EIP and call-trace in symbolic form. + +////////////////////////////////////////////////////////////////////////////// + +// BUGS: +// * Doesn't deal with line-prefixes prepended by syslog--strip +// these off first, before submitting to ksymoops. +// * Only resolves operands of jump and call instructions. + +#include <fstream.h> +#include <iomanip.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + +inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); } +inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); } + +const int code_size = 20; + +////////////////////////////////////////////////////////////////////////////// + +class KSym +{ + friend class NameList; + + private: + unsigned long address_; + char* name_; + long offset_; + long extent_; + + private: + istream& scan(istream&); + ostream& print(ostream&) const; + void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; } + + public: + friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); } + friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); } +}; + +istream& +KSym::scan(istream& is) +{ + is >> ::hex >> address_; + char type; + is >> type; + char name[128]; + is >> name; + name_ = new char [strlen(name)+1]; + strcpy(name_, name); + offset_ = 0; + return is; +} + +ostream& +KSym::print(ostream& os) const +{ + os << ::hex << address_ + offset_ << ' ' << '<' << name_; + if (offset_) + os << '+' << ::hex << offset_ << '/' << ::hex << extent_; + return os << '>'; +} + +////////////////////////////////////////////////////////////////////////////// + +class NameList +{ + private: + // Caution: Fixed Allocation! + // This should suffice for awhile since 1.1.86 has only 2482 symbols. + KSym ksyms_0_[8000]; + int cardinality_; + + public: + NameList() : cardinality_(0) { } + + private: + istream& scan(istream&); + + public: + int valid() { return (cardinality_ > 0); } + + KSym* find(unsigned long address); + void decode(unsigned char* code, long eip_addr); + + public: + friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); } +}; + +KSym* +NameList::find(unsigned long address) +{ + if (!valid()) + return 0; + KSym* start = ksyms_0_; + KSym* end = &ksyms_0_[cardinality_ - 1]; + if (address < start->address_ || address >= end->address_) + return 0; + + KSym* mid; + while (start <= end) { + mid = &start[(end - start) / 2]; + if (mid->address_ < address) + start = mid + 1; + else if (mid->address_ > address) + end = mid - 1; + else + return mid; + } + while (mid->address_ > address) + --mid; + mid->offset_ = address - mid->address_; + if (mid->offset_ > mid->extent_) + clog << "Oops! " << *mid << endl; + return mid; +} + +void +NameList::decode(unsigned char* code, long eip_addr) +{ + /* This is a hack to avoid using gcc. We create an object file by + concatenating objfile_head, the twenty bytes of code, and + objfile_tail. */ + unsigned char objfile_head[] = { + 0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char objfile_tail[] = { + 0x00, 0x90, 0x90, 0x90, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 'g', 'c', 'c', '2', '_', 'c', 'o', 'm', + 'p', 'i', 'l', 'e', 'd', '.', '\0', '_', + 'E', 'I', 'P', '\0', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', '\0', '\0' + }; + char const* objdump_command = "objdump -d oops_decode.o"; + char const* objfile_name = &objdump_command[11]; + ofstream objfile_stream(objfile_name); + + objfile_stream.write(objfile_head, sizeof(objfile_head)); + objfile_stream.write(code, code_size); + objfile_stream.write(objfile_tail, sizeof(objfile_tail)); + objfile_stream.close(); + + FILE* objdump_FILE = popen(objdump_command, "r"); + if (objdump_FILE == 0) { + clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl; + return; + } + + char buf[1024]; + int lines = 0; + while (fgets(buf, sizeof(buf), objdump_FILE)) { + if (!strnequ(&buf[9], "<_EIP", 5)) + continue; + if (strstr(buf, " is out of bounds")) + break; + lines++; + cout << "Code: "; + if (!valid()) { + cout << buf; + continue; + } + long offset = strtol(buf, 0, 16); + char* bp_0 = strchr(buf, '>') + 2; + KSym* ksym = find(eip_addr + offset); + if (ksym) + cout << *ksym << ' '; + char* bp = bp_0; + while (!isspace(*bp)) + bp++; + while (isspace(*bp)) + bp++; + if (*bp != '0') { + cout << bp_0; + } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn + long rel_addr = strtol(bp, 0, 16); + ksym = find(eip_addr + rel_addr); + if (ksym) { + *bp++ = '\0'; + cout << bp_0 << *ksym << endl; + } else + cout << bp_0; + } else { + cout << bp_0; + } + } + if (!lines) + clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl; + pclose(objdump_FILE); + unlink(objfile_name); +} + +istream& +NameList::scan(istream& is) +{ + KSym* ksyms = ksyms_0_; + int cardinality = 0; + while (!is.eof()) { + is >> *ksyms; + if (cardinality++ > 0) + ksyms[-1].set_extent(*ksyms); + ksyms++; + } + cardinality_ = --cardinality; + return is; +} + +////////////////////////////////////////////////////////////////////////////// + +char const* program_name; + +void +usage() +{ + clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl; + exit(1); +} + +int +main(int argc, char** argv) +{ + char c; + program_name = (argc--, *argv++); + + NameList names; + if (argc > 1) + usage(); + else if (argc == 1) { + char const* map_file_name = (argc--, *argv++); + ifstream map(map_file_name); + if (map.bad()) + clog << program_name << ": Can't open `" << map_file_name << "'" << endl; + else { + map >> names; + cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl; + } + } + if (!names.valid()) + cout << "No symbol map. I'll only show you disassembled code." << endl; + cout << endl; + + char buffer[1024]; + while (!cin.eof()) + { + long eip_addr; + cin >> buffer; + if (strequ(buffer, "EIP:") && names.valid()) { + cin >> ::hex >> eip_addr; + cin >> c >> c >> c; + cin >> ::hex >> eip_addr; + cin >> c >> c >> buffer; + if (!strequ(buffer, "EFLAGS:")) { + clog << "Please strip the line-prefixes and rerun " << program_name << endl; + exit(1); + } + KSym* ksym = names.find(eip_addr); + if (ksym) + cout << ">>EIP: " << *ksym << endl; + } else if (strequ(buffer, "Trace:") && names.valid()) { + unsigned long address; + while ((cin >> buffer) && + (sscanf(buffer, " [<%x>]", &address) == 1) && + address > 0xc) { + cout << "Trace: "; + KSym* ksym = names.find(address); + if (ksym) + cout << *ksym; + else + cout << ::hex << address; + cout << endl; + } + cout << endl; + } + if (strequ(buffer, "ode:") || strequ(buffer, "Code:")) { + // The 'C' might have been consumed as a hex number + unsigned char code[code_size]; + unsigned char* cp = code; + unsigned char* end = &code[code_size]; + while (cp < end) { + int c; + cin >> ::hex >> c; + *cp++ = c; + } + names.decode(code, eip_addr); + } + } + cout << flush; + + return 0; +} diff --git a/scripts/lxdialog/BIG.FAT.WARNING b/scripts/lxdialog/BIG.FAT.WARNING new file mode 100644 index 000000000..a8999d82b --- /dev/null +++ b/scripts/lxdialog/BIG.FAT.WARNING @@ -0,0 +1,4 @@ +This is NOT the official version of dialog. This version has been +significantly modified from the original. It is for use by the Linux +kernel configuration script. Please do not bother Savio Lam with +questions about this program. diff --git a/scripts/lxdialog/Makefile b/scripts/lxdialog/Makefile new file mode 100644 index 000000000..fc5ee7ffd --- /dev/null +++ b/scripts/lxdialog/Makefile @@ -0,0 +1,46 @@ +CC = gcc +CPP = gcc -E +OPTIM = -O2 -Wall -fomit-frame-pointer + +CFLAGS = $(OPTIM) -DLOCALE +LDFLAGS = -s -L . +LDLIBS = -lncurses + +ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h)) + CFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>" +else +ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h)) + CFLAGS += -I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>" +else +ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h)) + CFLAGS += -DCURSES_LOC="<ncurses.h>" +else + CFLAGS += -DCURSES_LOC="<curses.h>" +endif +endif +endif + + +OBJS = checklist.o menubox.o textbox.o yesno.o inputbox.o \ + util.o lxdialog.o msgbox.o +SRCS = $(OBJS:.o=.c) + + +all: ncurses lxdialog + +lxdialog: $(OBJS) + +ncurses: + @x=`find /lib/ /usr/lib/ /usr/local/lib/ -maxdepth 1 -name 'libncurses.*'` ;\ + if [ ! "$$x" ]; then \ + echo -e "\007" ;\ + echo ">> Unable to find the Ncurses libraries." ;\ + echo ">>" ;\ + echo ">> You must have Ncurses installed in order" ;\ + echo ">> to use 'make menuconfig'" ;\ + echo ;\ + exit 1 ;\ + fi + +clean: + rm -f core *.o *~ lxdialog diff --git a/scripts/lxdialog/checklist.c b/scripts/lxdialog/checklist.c new file mode 100644 index 000000000..b986b3a2e --- /dev/null +++ b/scripts/lxdialog/checklist.c @@ -0,0 +1,344 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +static int list_width, check_x, item_x, checkflag; + +/* + * Print list item + */ +static void +print_item (WINDOW * win, const char *item, int status, + int choice, int selected) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); + for (i = 0; i < list_width; i++) + waddch (win, ' '); + + wmove (win, choice, check_x); + wattrset (win, selected ? check_selected_attr : check_attr); + if (checkflag == FLAG_CHECK) + wprintw (win, "[%c]", status ? 'X' : ' '); + else + wprintw (win, "(%c)", status ? 'X' : ' '); + + wattrset (win, selected ? tag_selected_attr : tag_attr); + mvwaddch(win, choice, item_x, item[0]); + wattrset (win, selected ? item_selected_attr : item_attr); + waddstr (win, (char *)item+1); +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void +print_buttons( WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, "Select", y, x, selected == 0); + print_button (dialog, " Help ", y, x + 14, selected == 1); + + wmove(dialog, y, x+1 + 14*selected); + wrefresh (dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * The `flag' parameter is used to select between radiolist and checklist. + */ +int +dialog_checklist (const char *title, const char *prompt, int height, int width, + int list_height, int item_no, const char * const * items, int flag) + +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status; + WINDOW *dialog, *list; + + checkflag = flag; + + /* Allocate space for storing item on/off status */ + if ((status = malloc (sizeof (int) * item_no)) == NULL) { + endwin (); + fprintf (stderr, + "\nCan't allocate memory in dialog_checklist().\n"); + exit (-1); + } + + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = !strcasecmp (items[i * 3 + 2], "on"); + if (!choice && status[i]) + choice = i; + } + + max_choice = MIN (list_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1); + + keypad (list, TRUE); + + /* draw a box around the list items */ + draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2, + menubox_border_attr, menubox_attr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + for (i = 0; i < item_no; i++) + check_x = MAX (check_x, + strlen (items[i * 3 + 1]) + 4); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + /* Print the list */ + for (i = 0; i < max_choice; i++) + print_item (list, items[i * 3 + 1], status[i], i, i == choice); + + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + while (key != ESC) { + key = wgetch (dialog); + + for (i = 0; i < max_choice; i++) + if (toupper(key) == toupper(items[(scroll+i)*3+1][0])) + break; + + + if ( i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-' ) { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + print_item (list, items[scroll * 3 + 1], + status[scroll], 0, FALSE); + scrollok (list, TRUE); + wscrl (list, -1); + scrollok (list, FALSE); + } + scroll--; + print_item (list, items[scroll * 3 + 1], + status[scroll], 0, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_no - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item (list, items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], + max_choice - 1, FALSE); + scrollok (list, TRUE); + scroll (list); + scrollok (list, FALSE); + } + scroll++; + print_item (list, items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], + max_choice - 1, TRUE); + wnoutrefresh (list); + + print_arrows(dialog, choice, item_no, scroll, + box_y, box_x + check_x + 5, list_height); + + wrefresh (dialog); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + print_item (list, items[(scroll + choice) * 3 + 1], + status[scroll + choice], choice, FALSE); + /* Highlight new item */ + choice = i; + print_item (list, items[(scroll + choice) * 3 + 1], + status[scroll + choice], choice, TRUE); + wnoutrefresh (list); + wrefresh (dialog); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + delwin (dialog); + free (status); + return 1; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case 'S': + case 's': + case ' ': + case '\n': + if (!button) { + if (flag == FLAG_CHECK) { + status[scroll + choice] = !status[scroll + choice]; + wmove (list, choice, check_x); + wattrset (list, check_selected_attr); + wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' '); + } else { + if (!status[scroll + choice]) { + for (i = 0; i < item_no; i++) + status[i] = 0; + status[scroll + choice] = 1; + for (i = 0; i < max_choice; i++) + print_item (list, items[(scroll + i) * 3 + 1], + status[scroll + i], i, i == choice); + } + } + wnoutrefresh (list); + wrefresh (dialog); + + for (i = 0; i < item_no; i++) { + if (status[i]) { + if (flag == FLAG_CHECK) { + fprintf (stderr, "\"%s\" ", items[i * 3]); + } else { + fprintf (stderr, "%s", items[i * 3]); + } + + } + } + } + delwin (dialog); + free (status); + return button; + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + free (status); + return -1; /* ESC pressed */ +} diff --git a/scripts/lxdialog/colors.h b/scripts/lxdialog/colors.h new file mode 100644 index 000000000..d34dd37c6 --- /dev/null +++ b/scripts/lxdialog/colors.h @@ -0,0 +1,161 @@ +/* + * colors.h -- color attribute definitions + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * 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. + */ + + +/* + * Default color definitions + * + * *_FG = foreground + * *_BG = background + * *_HL = highlight? + */ +#define SCREEN_FG COLOR_CYAN +#define SCREEN_BG COLOR_BLUE +#define SCREEN_HL TRUE + +#define SHADOW_FG COLOR_BLACK +#define SHADOW_BG COLOR_BLACK +#define SHADOW_HL TRUE + +#define DIALOG_FG COLOR_BLACK +#define DIALOG_BG COLOR_WHITE +#define DIALOG_HL FALSE + +#define TITLE_FG COLOR_YELLOW +#define TITLE_BG COLOR_WHITE +#define TITLE_HL TRUE + +#define BORDER_FG COLOR_WHITE +#define BORDER_BG COLOR_WHITE +#define BORDER_HL TRUE + +#define BUTTON_ACTIVE_FG COLOR_WHITE +#define BUTTON_ACTIVE_BG COLOR_BLUE +#define BUTTON_ACTIVE_HL TRUE + +#define BUTTON_INACTIVE_FG COLOR_BLACK +#define BUTTON_INACTIVE_BG COLOR_WHITE +#define BUTTON_INACTIVE_HL FALSE + +#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE +#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE +#define BUTTON_KEY_ACTIVE_HL TRUE + +#define BUTTON_KEY_INACTIVE_FG COLOR_RED +#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE +#define BUTTON_KEY_INACTIVE_HL FALSE + +#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW +#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE +#define BUTTON_LABEL_ACTIVE_HL TRUE + +#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK +#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE +#define BUTTON_LABEL_INACTIVE_HL TRUE + +#define INPUTBOX_FG COLOR_BLACK +#define INPUTBOX_BG COLOR_WHITE +#define INPUTBOX_HL FALSE + +#define INPUTBOX_BORDER_FG COLOR_BLACK +#define INPUTBOX_BORDER_BG COLOR_WHITE +#define INPUTBOX_BORDER_HL FALSE + +#define SEARCHBOX_FG COLOR_BLACK +#define SEARCHBOX_BG COLOR_WHITE +#define SEARCHBOX_HL FALSE + +#define SEARCHBOX_TITLE_FG COLOR_YELLOW +#define SEARCHBOX_TITLE_BG COLOR_WHITE +#define SEARCHBOX_TITLE_HL TRUE + +#define SEARCHBOX_BORDER_FG COLOR_WHITE +#define SEARCHBOX_BORDER_BG COLOR_WHITE +#define SEARCHBOX_BORDER_HL TRUE + +#define POSITION_INDICATOR_FG COLOR_YELLOW +#define POSITION_INDICATOR_BG COLOR_WHITE +#define POSITION_INDICATOR_HL TRUE + +#define MENUBOX_FG COLOR_BLACK +#define MENUBOX_BG COLOR_WHITE +#define MENUBOX_HL FALSE + +#define MENUBOX_BORDER_FG COLOR_WHITE +#define MENUBOX_BORDER_BG COLOR_WHITE +#define MENUBOX_BORDER_HL TRUE + +#define ITEM_FG COLOR_BLACK +#define ITEM_BG COLOR_WHITE +#define ITEM_HL FALSE + +#define ITEM_SELECTED_FG COLOR_WHITE +#define ITEM_SELECTED_BG COLOR_BLUE +#define ITEM_SELECTED_HL TRUE + +#define TAG_FG COLOR_YELLOW +#define TAG_BG COLOR_WHITE +#define TAG_HL TRUE + +#define TAG_SELECTED_FG COLOR_YELLOW +#define TAG_SELECTED_BG COLOR_BLUE +#define TAG_SELECTED_HL TRUE + +#define TAG_KEY_FG COLOR_YELLOW +#define TAG_KEY_BG COLOR_WHITE +#define TAG_KEY_HL TRUE + +#define TAG_KEY_SELECTED_FG COLOR_YELLOW +#define TAG_KEY_SELECTED_BG COLOR_BLUE +#define TAG_KEY_SELECTED_HL TRUE + +#define CHECK_FG COLOR_BLACK +#define CHECK_BG COLOR_WHITE +#define CHECK_HL FALSE + +#define CHECK_SELECTED_FG COLOR_WHITE +#define CHECK_SELECTED_BG COLOR_BLUE +#define CHECK_SELECTED_HL TRUE + +#define UARROW_FG COLOR_GREEN +#define UARROW_BG COLOR_WHITE +#define UARROW_HL TRUE + +#define DARROW_FG COLOR_GREEN +#define DARROW_BG COLOR_WHITE +#define DARROW_HL TRUE + +/* End of default color definitions */ + +#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y))) +#define COLOR_NAME_LEN 10 +#define COLOR_COUNT 8 + +/* + * Global variables + */ + +typedef struct { + char name[COLOR_NAME_LEN]; + int value; +} color_names_st; + +extern color_names_st color_names[]; +extern int color_table[][3]; diff --git a/scripts/lxdialog/dialog.h b/scripts/lxdialog/dialog.h new file mode 100644 index 000000000..3be5feb21 --- /dev/null +++ b/scripts/lxdialog/dialog.h @@ -0,0 +1,166 @@ + +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * 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. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include CURSES_LOC + +#define ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* + * Attribute names + */ +#define screen_attr attributes[0] +#define shadow_attr attributes[1] +#define dialog_attr attributes[2] +#define title_attr attributes[3] +#define border_attr attributes[4] +#define button_active_attr attributes[5] +#define button_inactive_attr attributes[6] +#define button_key_active_attr attributes[7] +#define button_key_inactive_attr attributes[8] +#define button_label_active_attr attributes[9] +#define button_label_inactive_attr attributes[10] +#define inputbox_attr attributes[11] +#define inputbox_border_attr attributes[12] +#define searchbox_attr attributes[13] +#define searchbox_title_attr attributes[14] +#define searchbox_border_attr attributes[15] +#define position_indicator_attr attributes[16] +#define menubox_attr attributes[17] +#define menubox_border_attr attributes[18] +#define item_attr attributes[19] +#define item_selected_attr attributes[20] +#define tag_attr attributes[21] +#define tag_selected_attr attributes[22] +#define tag_key_attr attributes[23] +#define tag_key_selected_attr attributes[24] +#define check_attr attributes[25] +#define check_selected_attr attributes[26] +#define uarrow_attr attributes[27] +#define darrow_attr attributes[28] + +/* number of attributes */ +#define ATTRIBUTE_COUNT 29 + +/* + * Global variables + */ +extern bool use_colors; +extern bool use_shadow; + +extern chtype attributes[]; + +extern const char *backtitle; + +/* + * Function prototypes + */ +extern void create_rc (const char *filename); +extern int parse_rc (void); + + +void init_dialog (void); +void end_dialog (void); +void attr_clear (WINDOW * win, int height, int width, chtype attr); +void dialog_clear (void); +void color_setup (void); +void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x); +void print_button (WINDOW * win, const char *label, int y, int x, int selected); +void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow (WINDOW * win, int y, int x, int height, int width); + +int first_alpha (const char *string, const char *exempt); +int dialog_yesno (const char *title, const char *prompt, int height, int width); +int dialog_msgbox (const char *title, const char *prompt, int height, + int width, int pause); +int dialog_textbox (const char *title, const char *file, int height, int width); +int dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *choice, int item_no, + const char * const * items); +int dialog_checklist (const char *title, const char *prompt, int height, + int width, int list_height, int item_no, + const char * const * items, int flag); +extern unsigned char dialog_input_result[]; +int dialog_inputbox (const char *title, const char *prompt, int height, + int width, const char *init); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#define M_EVENT (KEY_MAX+1) + + +/* + * The `flag' parameter in checklist is used to select between + * radiolist and checklist + */ +#define FLAG_CHECK 1 +#define FLAG_RADIO 0 diff --git a/scripts/lxdialog/inputbox.c b/scripts/lxdialog/inputbox.c new file mode 100644 index 000000000..1d0200fa4 --- /dev/null +++ b/scripts/lxdialog/inputbox.c @@ -0,0 +1,232 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +unsigned char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button (dialog, " Ok ", y, x, selected==0); + print_button (dialog, " Help ", y, x + 14, selected==1); + + wmove(dialog, y, x+1+14*selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int +dialog_inputbox (const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, scroll = 0, key = 0, button = -1; + unsigned char *instr = dialog_input_result; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx (dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2, + border_attr, dialog_attr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove (dialog, box_y, box_x); + wattrset (dialog, inputbox_attr); + + if (!init) + instr[0] = '\0'; + else + strcpy (instr, init); + + input_x = strlen (instr); + + if (input_x >= box_width) { + scroll = input_x - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else + waddstr (dialog, instr); + + wmove (dialog, box_y, box_x + input_x); + + wrefresh (dialog); + + while (key != ESC) { + key = wgetch (dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_LEFT: + continue; + case KEY_RIGHT: + continue; + case KEY_BACKSPACE: + case 127: + if (input_x || scroll) { + wattrset (dialog, inputbox_attr); + if (!input_x) { + scroll = scroll < box_width - 1 ? + 0 : scroll - (box_width - 1); + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width; i++) + waddch (dialog, instr[scroll + input_x + i] ? + instr[scroll + input_x + i] : ' '); + input_x = strlen (instr) - scroll; + } else + input_x--; + instr[scroll + input_x] = '\0'; + mvwaddch (dialog, box_y, input_x + box_x, ' '); + wmove (dialog, box_y, input_x + box_x); + wrefresh (dialog); + } + continue; + default: + if (key < 0x100 && isprint (key)) { + if (scroll + input_x < MAX_LEN) { + wattrset (dialog, inputbox_attr); + instr[scroll + input_x] = key; + instr[scroll + input_x + 1] = '\0'; + if (input_x == box_width - 1) { + scroll++; + wmove (dialog, box_y, box_x); + for (i = 0; i < box_width - 1; i++) + waddch (dialog, instr[scroll + i]); + } else { + wmove (dialog, box_y, input_x++ + box_x); + waddch (dialog, key); + } + wrefresh (dialog); + } else + flash (); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin (dialog); + return 0; + case 'H': + case 'h': + delwin (dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Cancel" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove (dialog, box_y, box_x + input_x); + wrefresh (dialog); + break; + } + break; + case ' ': + case '\n': + delwin (dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/scripts/lxdialog/lxdialog.c b/scripts/lxdialog/lxdialog.c new file mode 100644 index 000000000..94d75ded6 --- /dev/null +++ b/scripts/lxdialog/lxdialog.c @@ -0,0 +1,223 @@ +/* + * dialog - Display simple dialog boxes from shell scripts + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +static void Usage (const char *name); + +typedef int (jumperFn) (const char *title, int argc, const char * const * argv); + +struct Mode { + char *name; + int argmin, argmax, argmod; + jumperFn *jumper; +}; + +jumperFn j_menu, j_checklist, j_radiolist, j_yesno, j_textbox, j_inputbox; +jumperFn j_msgbox, j_infobox; + +static struct Mode modes[] = +{ + {"--menu", 9, 0, 3, j_menu}, + {"--checklist", 9, 0, 3, j_checklist}, + {"--radiolist", 9, 0, 3, j_radiolist}, + {"--yesno", 5,5,1, j_yesno}, + {"--textbox", 5,5,1, j_textbox}, + {"--inputbox", 5, 6, 1, j_inputbox}, + {"--msgbox", 5, 5, 1, j_msgbox}, + {"--infobox", 5, 5, 1, j_infobox}, + {NULL, 0, 0, 0, NULL} +}; + +static struct Mode *modePtr; + +#ifdef LOCALE +#include <locale.h> +#endif + +int +main (int argc, const char * const * argv) +{ + int offset = 0, clear_screen = 0, end_common_opts = 0, retval; + const char *title = NULL; + +#ifdef LOCALE + (void) setlocale (LC_ALL, ""); +#endif + + if (argc < 2) { + Usage (argv[0]); + exit (-1); + } + + while (offset < argc - 1 && !end_common_opts) { /* Common options */ + if (!strcmp (argv[offset + 1], "--title")) { + if (argc - offset < 3 || title != NULL) { + Usage (argv[0]); + exit (-1); + } else { + title = argv[offset + 2]; + offset += 2; + } + } else if (!strcmp (argv[offset + 1], "--backtitle")) { + if (backtitle != NULL) { + Usage (argv[0]); + exit (-1); + } else { + backtitle = argv[offset + 2]; + offset += 2; + } + } else if (!strcmp (argv[offset + 1], "--clear")) { + if (clear_screen) { /* Hey, "--clear" can't appear twice! */ + Usage (argv[0]); + exit (-1); + } else if (argc == 2) { /* we only want to clear the screen */ + init_dialog (); + refresh (); /* init_dialog() will clear the screen for us */ + end_dialog (); + return 0; + } else { + clear_screen = 1; + offset++; + } + } else /* no more common options */ + end_common_opts = 1; + } + + if (argc - 1 == offset) { /* no more options */ + Usage (argv[0]); + exit (-1); + } + /* use a table to look for the requested mode, to avoid code duplication */ + + for (modePtr = modes; modePtr->name; modePtr++) /* look for the mode */ + if (!strcmp (argv[offset + 1], modePtr->name)) + break; + + if (!modePtr->name) + Usage (argv[0]); + if (argc - offset < modePtr->argmin) + Usage (argv[0]); + if (modePtr->argmax && argc - offset > modePtr->argmax) + Usage (argv[0]); + + + + init_dialog (); + retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset); + + if (clear_screen) { /* clear screen before exit */ + attr_clear (stdscr, LINES, COLS, screen_attr); + refresh (); + } + end_dialog(); + + exit (retval); +} + +/* + * Print program usage + */ +static void +Usage (const char *name) +{ + fprintf (stderr, "\ +\ndialog, by Savio Lam (lam836@cs.cuhk.hk).\ +\n patched by Stuart Herbert (S.Herbert@shef.ac.uk)\ +\n modified/gutted for use as a Linux kernel config tool by \ +\n William Roadcap (roadcapw@cfw.com)\ +\n\ +\n* Display dialog boxes from shell scripts *\ +\n\ +\nUsage: %s --clear\ +\n %s [--title <title>] [--backtitle <backtitle>] --clear <Box options>\ +\n\ +\nBox options:\ +\n\ +\n --menu <text> <height> <width> <menu height> <tag1> <item1>...\ +\n --checklist <text> <height> <width> <list height> <tag1> <item1> <status1>...\ +\n --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...\ +\n --textbox <file> <height> <width>\ +\n --inputbox <text> <height> <width> [<init>]\ +\n --yesno <text> <height> <width>\ +", name, name); + exit (-1); +} + +/* + * These are the program jumpers + */ + +int +j_menu (const char *t, int ac, const char * const * av) +{ + return dialog_menu (t, av[2], atoi (av[3]), atoi (av[4]), + atoi (av[5]), av[6], (ac - 6) / 2, av + 7); +} + +int +j_checklist (const char *t, int ac, const char * const * av) +{ + return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]), + atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_CHECK); +} + +int +j_radiolist (const char *t, int ac, const char * const * av) +{ + return dialog_checklist (t, av[2], atoi (av[3]), atoi (av[4]), + atoi (av[5]), (ac - 6) / 3, av + 6, FLAG_RADIO); +} + +int +j_textbox (const char *t, int ac, const char * const * av) +{ + return dialog_textbox (t, av[2], atoi (av[3]), atoi (av[4])); +} + +int +j_yesno (const char *t, int ac, const char * const * av) +{ + return dialog_yesno (t, av[2], atoi (av[3]), atoi (av[4])); +} + +int +j_inputbox (const char *t, int ac, const char * const * av) +{ + int ret = dialog_inputbox (t, av[2], atoi (av[3]), atoi (av[4]), + ac == 6 ? av[5] : (char *) NULL); + if (ret == 0) + fprintf(stderr, dialog_input_result); + return ret; +} + +int +j_msgbox (const char *t, int ac, const char * const * av) +{ + return dialog_msgbox (t, av[2], atoi (av[3]), atoi (av[4]), 1); +} + +int +j_infobox (const char *t, int ac, const char * const * av) +{ + return dialog_msgbox (t, av[2], atoi (av[3]), atoi (av[4]), 0); +} + diff --git a/scripts/lxdialog/menubox.c b/scripts/lxdialog/menubox.c new file mode 100644 index 000000000..ad4380870 --- /dev/null +++ b/scripts/lxdialog/menubox.c @@ -0,0 +1,350 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void +print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey) +{ + int i, j; + char menu_item[menu_width+1]; + + strncpy(menu_item, item, menu_width); + menu_item[menu_width] = 0; + j = first_alpha(menu_item, "YyNnMm"); + + /* Clear 'residue' of last item */ + wattrset (win, menubox_attr); + wmove (win, choice, 0); + for (i = 0; i < menu_width; i++) + waddch (win, ' '); + wattrset (win, selected ? item_selected_attr : item_attr); + mvwaddstr (win, choice, item_x, menu_item); + if (hotkey) { + wattrset (win, selected ? tag_key_selected_attr : tag_key_attr); + mvwaddch(win, choice, item_x+j, menu_item[j]); + } +} + +/* + * Print the scroll indicators. + */ +static void +print_arrows (WINDOW * win, int item_no, int scroll, + int y, int x, int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset (win, uarrow_attr); + waddch (win, ACS_UARROW); + waddstr (win, "(-)"); + } + else { + wattrset (win, menubox_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset (win, darrow_attr); + waddch (win, ACS_DARROW); + waddstr (win, "(+)"); + } + else { + wattrset (win, menubox_border_attr); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + waddch (win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); +} + +/* + * Display the termination buttons. + */ +static void +print_buttons (WINDOW *win, int height, int width, int selected) +{ + int x = width / 2 - 16; + int y = height - 2; + + print_button (win, "Select", y, x, selected == 0); + print_button (win, " Exit ", y, x + 12, selected == 1); + print_button (win, " Help ", y, x + 24, selected == 2); + + wmove(win, y, x+1+12*selected); + wrefresh (win); +} + +/* + * Display a menu for choosing among a number of options + */ +int +dialog_menu (const char *title, const char *prompt, int height, int width, + int menu_height, const char *current, int item_no, + const char * const * items) + +{ + int i, j, x, y, box_x, box_y; + int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice; + WINDOW *dialog, *menu; + + max_choice = MIN (menu_height, item_no); + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin (dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad (menu, TRUE); + + /* draw a box around the menu items */ + draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2, + menubox_border_attr, menubox_attr); + + /* + * Find length of longest item in order to center menu. + * Set 'choice' to default item. + */ + item_x = 0; + for (i = 0; i < item_no; i++) { + item_x = MAX (item_x, MIN(menu_width, strlen (items[i * 2 + 1]) + 2)); + if (strcmp(current, items[i*2]) == 0) choice = i; + } + + item_x = (menu_width - item_x) / 2; + + if (choice >= max_choice){ + scroll = first_item = choice - max_choice + 1; + choice = max_choice-1; + } + + /* Print the menu */ + for (i=0; i < max_choice; i++) { + print_item (menu, items[(first_item + i) * 2 + 1], i, i == choice, + (items[(first_item + i)*2][0] != ':')); + } + + wnoutrefresh (menu); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + print_buttons (dialog, height, width, 0); + + while (key != ESC) { + key = wgetch(dialog); + + if (key < 256 && isalpha(key)) key = tolower(key); + + if (strchr("ynm", key)) + i = max_choice; + else { + for (i = choice+1; i < max_choice; i++) { + j = first_alpha(items[(scroll+i)*2+1], "YyNnMm"); + if (key == tolower(items[(scroll+i)*2+1][j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + j = first_alpha(items[(scroll+i)*2+1], "YyNnMm"); + if (key == tolower(items[(scroll+i)*2+1][j])) + break; + } + } + + if (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE) { + + print_item (menu, items[(scroll+choice)*2+1], choice, FALSE, + (items[(scroll+choice)*2][0] != ':')); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + scrollok (menu, TRUE); + wscrl (menu, -1); + scrollok (menu, FALSE); + + scroll--; + + print_item (menu, items[scroll * 2 + 1], 0, FALSE, + (items[scroll*2][0] != ':')); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + + print_item (menu, items[(scroll+choice)*2+1], choice, FALSE, + (items[(scroll+choice)*2][0] != ':')); + + if ((choice > max_choice-3) && + (scroll + max_choice < item_no) + ) { + /* Scroll menu up */ + scrollok (menu, TRUE); + scroll (menu); + scrollok (menu, FALSE); + + scroll++; + + print_item (menu, items[(scroll+max_choice-1)*2+1], + max_choice-1, FALSE, + (items[(scroll+max_choice-1)*2][0] != ':')); + } else + choice = MIN(choice+1, max_choice-1); + + } else if (key == KEY_PPAGE) { + scrollok (menu, TRUE); + for (i=0; (i < max_choice) && (scroll > 0); i++) { + wscrl (menu, -1); + scroll--; + print_item (menu, items[scroll * 2 + 1], 0, FALSE, + (items[scroll*2][0] != ':')); + } + scrollok (menu, FALSE); + choice = 0; + + } else if (key == KEY_NPAGE) { + scrollok (menu, TRUE); + for (i=0; (i < max_choice) && (scroll+max_choice < item_no); i++) { + scroll(menu); + scroll++; + print_item (menu, items[(scroll+max_choice-1)*2+1], + max_choice-1, FALSE, + (items[(scroll+max_choice-1)*2][0] != ':')); + } + scrollok (menu, FALSE); + choice = 0; + + } else + choice = i; + + print_item (menu, items[(scroll+choice)*2+1], choice, TRUE, + (items[(scroll+choice)*2][0] != ':')); + + print_arrows(dialog, item_no, scroll, + box_y, box_x+item_x+1, menu_height); + + wnoutrefresh (menu); + wrefresh (dialog); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 2 : (button > 2 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + delwin (dialog); + fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); + switch (key) { + case 's': return 3; + case 'y': return 3; + case 'n': return 4; + case 'm': return 5; + case ' ': return 6; + } + return 0; + case 'h': + case '?': + button = 2; + case '\n': + delwin (dialog); + if (button == 2) + fprintf(stderr, "%s \"%s\"\n", + items[(scroll + choice) * 2], + items[(scroll + choice) * 2 + 1] + + first_alpha(items[(scroll + choice) * 2 + 1],"")); + else + fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); + + return button; + case 'e': + case 'x': + key = ESC; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/scripts/lxdialog/msgbox.c b/scripts/lxdialog/msgbox.c new file mode 100644 index 000000000..19ae28c52 --- /dev/null +++ b/scripts/lxdialog/msgbox.c @@ -0,0 +1,77 @@ +/* + * msgbox.c -- implements the message box and info box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +/* + * Display a message box. Program will pause and display an "OK" button + * if the parameter 'pause' is non-zero. + */ +int +dialog_msgbox (const char *title, const char *prompt, int height, int width, + int pause) +{ + int i, x, y, key = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 2); + + if (pause) { + wattrset (dialog, border_attr); + mvwaddch (dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + print_button (dialog, " Ok ", + height - 2, width / 2 - 4, TRUE); + + wrefresh (dialog); + while (key != ESC && key != '\n' && key != ' ' && + key != 'O' && key != 'o' && key != 'X' && key != 'x') + key = wgetch (dialog); + } else { + key = '\n'; + wrefresh (dialog); + } + + delwin (dialog); + return key == ESC ? -1 : 0; +} diff --git a/scripts/lxdialog/textbox.c b/scripts/lxdialog/textbox.c new file mode 100644 index 000000000..df50f2c54 --- /dev/null +++ b/scripts/lxdialog/textbox.c @@ -0,0 +1,537 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +static void back_lines (int n); +static void print_page (WINDOW * win, int height, int width); +static void print_line (WINDOW * win, int row, int width); +static char *get_line (void); +static void print_position (WINDOW * win, int height, int width); + +static int hscroll = 0, fd, file_size, bytes_read; +static int begin_reached = 1, end_reached = 0, page_length; +static char *buf, *page; + +/* + * Display text from a file in a dialog box. + */ +int +dialog_textbox (const char *title, const char *file, int height, int width) +{ + int i, x, y, cur_x, cur_y, fpos, key = 0; + int passed_end; + char search_term[MAX_LEN + 1]; + WINDOW *dialog, *text; + + search_term[0] = '\0'; /* no search term entered yet */ + + /* Open input file for reading */ + if ((fd = open (file, O_RDONLY)) == -1) { + endwin (); + fprintf (stderr, + "\nCan't open input file in dialog_textbox().\n"); + exit (-1); + } + /* Get file size. Actually, 'file_size' is the real file size - 1, + since it's only the last byte offset from the beginning */ + if ((file_size = lseek (fd, 0, SEEK_END)) == -1) { + endwin (); + fprintf (stderr, "\nError getting file size in dialog_textbox().\n"); + exit (-1); + } + /* Restore file pointer to beginning of file after getting file size */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + /* Allocate space for read buffer */ + if ((buf = malloc (BUF_SIZE + 1)) == NULL) { + endwin (); + fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; /* mark end of valid data */ + page = buf; /* page is pointer to start of page to be displayed */ + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + /* Create window for text region, used for scrolling text */ + text = subwin (dialog, height - 4, width - 2, y + 1, x + 1); + + keypad (text, TRUE); + + /* register the new window, along with its borders */ + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE); + wnoutrefresh (dialog); + getyx (dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear (text, height - 4, width - 2, dialog_attr); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + + while ((key != ESC) && (key != '\n')) { + key = wgetch (dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + delwin (dialog); + free (buf); + close (fd); + return 0; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + /* First page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Yes, we have to read it in */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* Last page not in buffer? */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if (fpos < file_size) { /* Yes, we have to read it in */ + if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in dialog_textbox().\n"); + exit (-1); + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, + "\nError reading file in dialog_textbox().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } + page = buf + bytes_read; + back_lines (height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (!begin_reached) { + back_lines (page_length + 1); + + /* We don't call print_page() here but use scrolling to ensure + faster screen update. However, 'end_reached' and + 'page_length' should still be updated, and 'page' should + point to start of next page. This is done by calling + get_line() in the following 'for' loop. */ + scrollok (text, TRUE); + wscrl (text, -1); /* Scroll text region down one line */ + scrollok (text, FALSE); + page_length = 0; + passed_end = 0; + for (i = 0; i < height - 4; i++) { + if (!i) { + /* print first line of page */ + print_line (text, 0, width - 2); + wnoutrefresh (text); + } else + /* Called to update 'end_reached' and 'page' */ + get_line (); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case 'B': /* Previous page */ + case 'b': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines (page_length + height - 4); + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (!end_reached) { + begin_reached = 0; + scrollok (text, TRUE); + scroll (text); /* Scroll text region up one line */ + scrollok (text, FALSE); + print_line (text, height - 5, width - 2); + wnoutrefresh (text); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh (dialog); + } + break; + case KEY_NPAGE: /* Next page */ + case ' ': + if (end_reached) + break; + + begin_reached = 0; + print_page (text, height - 4, width - 2); + print_position (dialog, height, width); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines (page_length); + print_page (text, height - 4, width - 2); + wmove (dialog, cur_y, cur_x); + wrefresh (dialog); + break; + case ESC: + break; + } + } + + delwin (dialog); + free (buf); + close (fd); + return -1; /* ESC pressed */ +} + +/* + * Go back 'n' lines in text file. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void +back_lines (int n) +{ + int i, fpos; + + begin_reached = 0; + /* We have to distinguish between end_reached and !end_reached + since at end of file, the line is not ended by a '\n'. + The code inside 'if' basically does a '--page' to move one + character backward so as to skip '\n' of the previous line */ + if (!end_reached) { + /* Either beginning of buffer or beginning of file reached? */ + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { /* Not beginning of file yet */ + /* We've reached beginning of buffer, but not beginning of + file yet, so read previous part of file into buffer. + Note that we only move backward for BUF_SIZE/2 bytes, + but not BUF_SIZE bytes to avoid re-reading again in + print_page() later */ + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) + == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + if (*(--page) != '\n') { /* '--page' here */ + /* Something's wrong... */ + endwin (); + fprintf (stderr, "\nInternal error in back_lines().\n"); + exit (-1); + } + } + /* Go back 'n' lines */ + for (i = 0; i < n; i++) + do { + if (page == buf) { + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, + "\nError moving file pointer in back_lines().\n"); + exit (-1); + } + if (fpos > bytes_read) { + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE / 2 + bytes_read) { + /* No, move less then */ + if (lseek (fd, 0, SEEK_SET) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer " + "in back_lines().\n"); + exit (-1); + } + page = buf + fpos - bytes_read; + } else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), + SEEK_CUR) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer" + " in back_lines().\n"); + exit (-1); + } + page = buf + BUF_SIZE / 2; + } + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in " + "back_lines().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + } else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + } while (*(--page) != '\n'); + page++; +} + +/* + * Print a new page of text. Called by dialog_textbox(). + */ +static void +print_page (WINDOW * win, int height, int width) +{ + int i, passed_end = 0; + + page_length = 0; + for (i = 0; i < height; i++) { + print_line (win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh (win); +} + +/* + * Print a new line of text. Called by dialog_textbox() and print_page(). + */ +static void +print_line (WINDOW * win, int row, int width) +{ + int i, y, x; + char *line; + + line = get_line (); + line += MIN (strlen (line), hscroll); /* Scroll horizontally */ + wmove (win, row, 0); /* move cursor to correct line */ + waddch (win, ' '); + waddnstr (win, line, MIN (strlen (line), width - 2)); + + getyx (win, y, x); + /* Clear 'residue' of previous line */ + for (i = 0; i < width - x; i++) + waddch (win, ' '); +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char * +get_line (void) +{ + int i = 0, fpos; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + /* Either end of file or end of buffer reached */ + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in " + "get_line().\n"); + exit (-1); + } + if (fpos < file_size) { /* Not end of file yet */ + /* We've reached end of buffer, but not end of file yet, + so read next part of file into buffer */ + if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) { + endwin (); + fprintf (stderr, "\nError reading file in get_line().\n"); + exit (-1); + } + buf[bytes_read] = '\0'; + page = buf; + } else { + if (!end_reached) + end_reached = 1; + break; + } + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move pass '\n' */ + + return line; +} + +/* + * Print current position + */ +static void +print_position (WINDOW * win, int height, int width) +{ + int fpos, percent; + + if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) { + endwin (); + fprintf (stderr, "\nError moving file pointer in print_position().\n"); + exit (-1); + } + wattrset (win, position_indicator_attr); + percent = !file_size ? + 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; + wmove (win, height - 3, width - 9); + wprintw (win, "(%3d%%)", percent); +} diff --git a/scripts/lxdialog/util.c b/scripts/lxdialog/util.c new file mode 100644 index 000000000..b3a7af9d2 --- /dev/null +++ b/scripts/lxdialog/util.c @@ -0,0 +1,359 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + + +/* use colors by default? */ +bool use_colors = 1; + +const char *backtitle = NULL; + +const char *dialog_result; + +/* + * Attribute values, default is for mono display + */ +chtype attributes[] = +{ + A_NORMAL, /* screen_attr */ + A_NORMAL, /* shadow_attr */ + A_NORMAL, /* dialog_attr */ + A_BOLD, /* title_attr */ + A_NORMAL, /* border_attr */ + A_REVERSE, /* button_active_attr */ + A_DIM, /* button_inactive_attr */ + A_REVERSE, /* button_key_active_attr */ + A_BOLD, /* button_key_inactive_attr */ + A_REVERSE, /* button_label_active_attr */ + A_NORMAL, /* button_label_inactive_attr */ + A_NORMAL, /* inputbox_attr */ + A_NORMAL, /* inputbox_border_attr */ + A_NORMAL, /* searchbox_attr */ + A_BOLD, /* searchbox_title_attr */ + A_NORMAL, /* searchbox_border_attr */ + A_BOLD, /* position_indicator_attr */ + A_NORMAL, /* menubox_attr */ + A_NORMAL, /* menubox_border_attr */ + A_NORMAL, /* item_attr */ + A_REVERSE, /* item_selected_attr */ + A_BOLD, /* tag_attr */ + A_REVERSE, /* tag_selected_attr */ + A_BOLD, /* tag_key_attr */ + A_REVERSE, /* tag_key_selected_attr */ + A_BOLD, /* check_attr */ + A_REVERSE, /* check_selected_attr */ + A_BOLD, /* uarrow_attr */ + A_BOLD /* darrow_attr */ +}; + + +#include "colors.h" + +/* + * Table of color values + */ +int color_table[][3] = +{ + {SCREEN_FG, SCREEN_BG, SCREEN_HL}, + {SHADOW_FG, SHADOW_BG, SHADOW_HL}, + {DIALOG_FG, DIALOG_BG, DIALOG_HL}, + {TITLE_FG, TITLE_BG, TITLE_HL}, + {BORDER_FG, BORDER_BG, BORDER_HL}, + {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL}, + {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL}, + {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL}, + {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL}, + {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL}, + {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG, + BUTTON_LABEL_INACTIVE_HL}, + {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL}, + {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL}, + {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL}, + {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL}, + {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL}, + {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL}, + {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL}, + {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL}, + {ITEM_FG, ITEM_BG, ITEM_HL}, + {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL}, + {TAG_FG, TAG_BG, TAG_HL}, + {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL}, + {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL}, + {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL}, + {CHECK_FG, CHECK_BG, CHECK_HL}, + {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL}, + {UARROW_FG, UARROW_BG, UARROW_HL}, + {DARROW_FG, DARROW_BG, DARROW_HL}, +}; /* color_table */ + +/* + * Set window to attribute 'attr' + */ +void +attr_clear (WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset (win, attr); + for (i = 0; i < height; i++) { + wmove (win, i, 0); + for (j = 0; j < width; j++) + waddch (win, ' '); + } + touchwin (win); +} + +void dialog_clear (void) +{ + attr_clear (stdscr, LINES, COLS, screen_attr); + /* Display background title if it exists ... - SLH */ + if (backtitle != NULL) { + int i; + + wattrset (stdscr, screen_attr); + mvwaddstr (stdscr, 0, 1, (char *)backtitle); + wmove (stdscr, 1, 1); + for (i = 1; i < COLS - 1; i++) + waddch (stdscr, ACS_HLINE); + } + wnoutrefresh (stdscr); +} + +/* + * Do some initialization for dialog + */ +void +init_dialog (void) +{ + initscr (); /* Init curses */ + keypad (stdscr, TRUE); + cbreak (); + noecho (); + + + if (use_colors) /* Set up colors */ + color_setup (); + + + dialog_clear (); +} + +/* + * Setup for color display + */ +void +color_setup (void) +{ + int i; + + if (has_colors ()) { /* Terminal supports color? */ + start_color (); + + /* Initialize color pairs */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + init_pair (i + 1, color_table[i][0], color_table[i][1]); + + /* Setup color attributes */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + attributes[i] = C_ATTR (color_table[i][2], i + 1); + } +} + +/* + * End using dialog functions. + */ +void +end_dialog (void) +{ + endwin (); +} + + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are replaced by spaces. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void +print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int i, prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2; + + strcpy (tempstr, prompt); + + prompt_len = strlen(tempstr); + + /* + * Remove newlines + */ + for(i=0; i<prompt_len; i++) { + if(tempstr[i] == '\n') tempstr[i] = ' '; + } + + if (prompt_len <= width - x * 2) { /* If prompt is short */ + wmove (win, y, (width - prompt_len) / 2); + waddstr (win, tempstr); + } else { + cur_x = x; + cur_y = y; + newl = 1; + word = tempstr; + while (word && *word) { + sp = index(word, ' '); + if (sp) + *sp++ = 0; + + /* Wrap to next line if either the word does not fit, + or it is the first word of a new sentence, and it is + short, and the next word does not fit. */ + room = width - cur_x; + wlen = strlen(word); + if (wlen > room || + (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room + && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) { + cur_y++; + cur_x = x; + } + wmove (win, cur_y, cur_x); + waddstr (win, word); + getyx (win, cur_y, cur_x); + cur_x++; + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' '); + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void +print_button (WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove (win, y, x); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, "<"); + temp = strspn (label, " "); + label += temp; + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + for (i = 0; i < temp; i++) + waddch (win, ' '); + wattrset (win, selected ? button_key_active_attr + : button_key_inactive_attr); + waddch (win, label[0]); + wattrset (win, selected ? button_label_active_attr + : button_label_inactive_attr); + waddstr (win, (char *)label + 1); + wattrset (win, selected ? button_active_attr : button_inactive_attr); + waddstr (win, ">"); + wmove (win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box (WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset (win, 0); + for (i = 0; i < height; i++) { + wmove (win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch (win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch (win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch (win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch (win, box | ACS_LRCORNER); + else if (!i) + waddch (win, border | ACS_HLINE); + else if (i == height - 1) + waddch (win, box | ACS_HLINE); + else if (!j) + waddch (win, border | ACS_VLINE); + else if (j == width - 1) + waddch (win, box | ACS_VLINE); + else + waddch (win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void +draw_shadow (WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors ()) { /* Whether terminal supports color? */ + wattrset (win, shadow_attr); + wmove (win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch (win, winch (win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove (win, i, x + width); + waddch (win, winch (win) & A_CHARTEXT); + waddch (win, winch (win) & A_CHARTEXT); + } + wnoutrefresh (win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int +first_alpha(const char *string, const char *exempt) +{ + int i, in_paren=0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) ++in_paren; + if (strchr(">])", c)) --in_paren; + + if ((! in_paren) && isalpha(c) && + strchr(exempt, c) == 0) + return i; + } + + return 0; +} diff --git a/scripts/lxdialog/yesno.c b/scripts/lxdialog/yesno.c new file mode 100644 index 000000000..6f40af645 --- /dev/null +++ b/scripts/lxdialog/yesno.c @@ -0,0 +1,110 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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. + */ + +#include "dialog.h" + +/* + * Display termination buttons + */ +static void +print_buttons(WINDOW *dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button (dialog, " Yes ", y, x, selected == 0); + print_button (dialog, " No ", y, x + 13, selected == 1); + + wmove(dialog, y, x+1 + 13*selected ); + wrefresh (dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int +dialog_yesno (const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + + /* center dialog box on screen */ + x = (COLS - width) / 2; + y = (LINES - height) / 2; + + draw_shadow (stdscr, y, x, height, width); + + dialog = newwin (height, width, y, x); + keypad (dialog, TRUE); + + draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset (dialog, border_attr); + mvwaddch (dialog, height-3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch (dialog, ACS_HLINE); + wattrset (dialog, dialog_attr); + waddch (dialog, ACS_RTEE); + + if (title != NULL) { + wattrset (dialog, title_attr); + mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' '); + waddstr (dialog, (char *)title); + waddch (dialog, ' '); + } + + wattrset (dialog, dialog_attr); + print_autowrap (dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != ESC) { + key = wgetch (dialog); + switch (key) { + case 'Y': + case 'y': + delwin (dialog); + return 0; + case 'N': + case 'n': + delwin (dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh (dialog); + break; + case ' ': + case '\n': + delwin (dialog); + return button; + case ESC: + break; + } + } + + delwin (dialog); + return -1; /* ESC pressed */ +} diff --git a/scripts/mkdep.c b/scripts/mkdep.c new file mode 100644 index 000000000..43140b0a7 --- /dev/null +++ b/scripts/mkdep.c @@ -0,0 +1,297 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/fcntl.h> +#include <sys/mman.h> + +char *filename, *command, __depname[256] = "\n\t@touch "; +int needsconfig, hasconfig, hasdep; + +#define depname (__depname+9) + +struct path_struct { + int len; + char buffer[256-sizeof(int)]; +} path_array[2] = { + { 23, "/usr/src/linux/include/" }, + { 0, "" } +}; + +static void handle_include(int type, char *name, int len) +{ + int plen; + struct path_struct *path = path_array+type; + + if (!type) { + if (memcmp(name, "linux/", 6) && + memcmp(name, "asm/", 4) && + memcmp(name, "net/", 4) && + memcmp(name, "scsi/", 5)) + return; + if (len == 14 && !memcmp(name, "linux/config.h", len)) + hasconfig = 1; + } + + plen = path->len; + memcpy(path->buffer+plen, name, len); + len += plen; + path->buffer[len] = '\0'; + + if (!hasdep) { + hasdep = 1; + printf("%s:", depname); + } + printf(" \\\n %s", path->buffer); +} + +static void handle_config(void) +{ + needsconfig = 1; + if (!hasconfig) + fprintf(stderr, + "%s needs config but has not included config file\n", + filename); +} + +#if defined(__alpha__) || defined(__i386__) +#define LE_MACHINE +#endif + +#ifdef LE_MACHINE +#define next_byte(x) (x >>= 8) +#define current ((unsigned char) __buf) +#else +#define next_byte(x) (x <<= 8) +#define current (__buf >> 8*(sizeof(unsigned long)-1)) +#endif + +#define GETNEXT { \ +next_byte(__buf); \ +if (!__nrbuf) { \ + __buf = *(unsigned long *) next; \ + __nrbuf = sizeof(unsigned long); \ + if (!__buf) \ + break; \ +} next++; __nrbuf--; } +#define CASE(c,label) if (current == c) goto label +#define NOTCASE(c,label) if (current != c) goto label + +static void state_machine(register char *next) +{ + for(;;) { + register unsigned long __buf = 0; + register unsigned long __nrbuf = 0; + +normal: + GETNEXT +__normal: + CASE('/',slash); + CASE('"',string); + CASE('\'',char_const); + CASE('#',preproc); + goto normal; + +slash: + GETNEXT + CASE('*',comment); + goto __normal; + +string: + GETNEXT + CASE('"',normal); + NOTCASE('\\',string); + GETNEXT + goto string; + +char_const: + GETNEXT + CASE('\'',normal); + NOTCASE('\\',char_const); + GETNEXT + goto char_const; + +comment: + GETNEXT +__comment: + NOTCASE('*',comment); + GETNEXT + CASE('/',normal); + goto __comment; + +preproc: + GETNEXT + CASE('\n',normal); + CASE(' ',preproc); + CASE('\t',preproc); + CASE('i',i_preproc); + GETNEXT + +skippreproc: + CASE('\n',normal); + CASE('\\',skippreprocslash); + GETNEXT + goto skippreproc; + +skippreprocslash: + GETNEXT; + GETNEXT; + goto skippreproc; + +i_preproc: + GETNEXT + CASE('f',if_line); + NOTCASE('n',skippreproc); + GETNEXT + NOTCASE('c',skippreproc); + GETNEXT + NOTCASE('l',skippreproc); + GETNEXT + NOTCASE('u',skippreproc); + GETNEXT + NOTCASE('d',skippreproc); + GETNEXT + NOTCASE('e',skippreproc); + +/* "# include" found */ +include_line: + GETNEXT + CASE('\n',normal); + CASE('<', std_include_file); + NOTCASE('"', include_line); + +/* "local" include file */ +{ + char *incname = next; +local_include_name: + GETNEXT + CASE('\n',normal); + NOTCASE('"', local_include_name); + handle_include(1, incname, next-incname-1); + goto skippreproc; +} + +/* <std> include file */ +std_include_file: +{ + char *incname = next; +std_include_name: + GETNEXT + CASE('\n',normal); + NOTCASE('>', std_include_name); + handle_include(0, incname, next-incname-1); + goto skippreproc; +} + +if_line: + if (needsconfig) + goto skippreproc; +if_start: + GETNEXT + CASE('C', config); + CASE('\n', normal); + CASE('_', if_middle); + if (current >= 'a' && current <= 'z') + goto if_middle; + if (current < 'A' || current > 'Z') + goto if_start; +config: + GETNEXT + NOTCASE('O', __if_middle); + GETNEXT + NOTCASE('N', __if_middle); + GETNEXT + NOTCASE('F', __if_middle); + GETNEXT + NOTCASE('I', __if_middle); + GETNEXT + NOTCASE('G', __if_middle); + GETNEXT + NOTCASE('_', __if_middle); + handle_config(); + goto skippreproc; + +if_middle: + GETNEXT +__if_middle: + CASE('\n', normal); + CASE('_', if_middle); + if (current >= 'a' && current <= 'z') + goto if_middle; + if (current < 'A' || current > 'Z') + goto if_start; + goto if_middle; + } +} + +static void do_depend(void) +{ + char *map; + int mapsize; + int pagesizem1 = getpagesize()-1; + int fd = open(filename, O_RDONLY); + struct stat st; + + if (fd < 0) { + perror("mkdep: open"); + return; + } + fstat(fd, &st); + mapsize = st.st_size + 2*sizeof(unsigned long); + mapsize = (mapsize+pagesizem1) & ~pagesizem1; + map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0); + if (-1 == (long)map) { + perror("mkdep: mmap"); + close(fd); + return; + } + close(fd); + state_machine(map); + munmap(map, mapsize); + if (hasdep) + puts(command); +} + +int main(int argc, char **argv) +{ + int len; + char * hpath; + + hpath = getenv("HPATH"); + if (!hpath) + hpath = "/usr/src/linux/include"; + len = strlen(hpath); + memcpy(path_array[0].buffer, hpath, len); + if (len && hpath[len-1] != '/') { + path_array[0].buffer[len] = '/'; + len++; + } + path_array[0].buffer[len] = '\0'; + path_array[0].len = len; + + while (--argc > 0) { + int len; + char *name = *++argv; + + filename = name; + len = strlen(name); + memcpy(depname, name, len+1); + command = __depname; + if (len > 2 && name[len-2] == '.') { + switch (name[len-1]) { + case 'c': + case 'S': + depname[len-1] = 'o'; + command = ""; + } + } + needsconfig = hasconfig = hasdep = 0; + do_depend(); + if (hasconfig && !needsconfig) + fprintf(stderr, "%s doesn't need config\n", filename); + } + return 0; +} diff --git a/scripts/patch-kernel b/scripts/patch-kernel new file mode 100644 index 000000000..2028efcbc --- /dev/null +++ b/scripts/patch-kernel @@ -0,0 +1,53 @@ +#! /bin/sh +# Script to apply kernel patches. +# usage: patch-kernel [ sourcedir [ patchdir ] ] +# The source directory defaults to /usr/src/linux, and the patch +# directory defaults to the current directory. +# +# It determines the current kernel version from the top-level Makefile. +# It then looks for patches for the next sublevel in the patch directory. +# This is applied using "patch -p1 -s" from within the kernel directory. +# A check is then made for "*.rej" files to see if the patch was +# successful. If it is, then all of the "*.orig" files are removed. +# +# Nick Holloway <Nick.Holloway@alfie.demon.co.uk>, 2nd January 1995. + +# Set directories from arguments, or use defaults. +sourcedir=${1-/usr/src/linux} +patchdir=${2-.} + +# set current VERSION, PATCHLEVEL, SUBLEVEL +eval `sed -n 's/^\([A-Z]*\) = \([0-9]*\)$/\1=\2/p' $sourcedir/Makefile` +if [ -z "$VERSION" -o -z "$PATCHLEVEL" -o -z "$SUBLEVEL" ] +then + echo "unable to determine current kernel version" >&2 + exit 1 +fi + +echo "Current kernel version is $VERSION.$PATCHLEVEL.$SUBLEVEL" + +while : +do + SUBLEVEL=`expr $SUBLEVEL + 1` + patch=patch-$VERSION.$PATCHLEVEL.$SUBLEVEL.gz + if [ ! -r $patchdir/$patch ] + then + break + fi + + echo -n "Applying $patch... " + if gunzip -dc $patchdir/$patch | patch -p1 -s -N -E -d $sourcedir + then + echo "done." + else + echo "failed. Clean up yourself." + break + fi + if [ "`find $sourcedir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] + then + echo "Aborting. Reject files found." + break + fi + # Remove backup files + find $sourcedir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \; +done diff --git a/scripts/pathdown.sh b/scripts/pathdown.sh new file mode 100644 index 000000000..16c17b2a8 --- /dev/null +++ b/scripts/pathdown.sh @@ -0,0 +1,13 @@ +#!/bin/sh +UP= +DN=${PWD:?} +TP=${TOPDIR:?} + +while [ ! $TP/$UP/. -ef $DN ] ;do + UP=`basename $PWD`/$UP + cd .. + if [ "$PWD" = "/" ]; then echo "Lost"; exit 1; fi +done + +echo $UP +exit 0 diff --git a/scripts/tail.tk b/scripts/tail.tk new file mode 100644 index 000000000..ee2edae7e --- /dev/null +++ b/scripts/tail.tk @@ -0,0 +1,78 @@ + +pack .header -side top -padx 10 -pady 10 -expand on +pack .f0 -side top -padx 15 -pady 10 -fill y -expand on + +# +# Misc buttons to save/restore state and so forth. +# +frame .f0_bot +frame .f0_bot.r +frame .f0_bot.l + +# +# Read the user's settings from .config. These will override whatever is +# in config.in. Don't do this if the user specified a -D to force +# the defaults. +# +if { [file readable .config] == 1} then { + if { $argc > 0 } then { + if { [lindex $argv 0] != "-D" } then { + read_config .config + } + else + { + read_config $defaults + } + } else { + read_config .config + } +} else { + read_config $defaults +} + +update_mainmenu .f0 + +button .f0_bot.r.save -text "Save and Exit" -width 25 -command { + writeconfig .config include/linux/autoconf.h; wrapup .wrap } + +button .f0_bot.r.quit -text "Quit Without Saving" -width 25 \ + -command { maybe_exit .maybe } + +button .f0_bot.l.store -text "Store Configuration to File" -width 25 -command { + load_configfile .load "Save Configuration in file" write_config_file +} + +button .f0_bot.l.load -text "Load Configuration from File" -width 25 -command { + load_configfile .load "Load Configuration from file" read_config_file +} + +pack .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -ipady 2 -expand on +pack .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -ipady 2 -expand on + +pack .f0_bot.r -side left -padx 15 -pady 10 -expand on -fill y +pack .f0_bot.l -side right -padx 15 -pady 10 -expand on -fill y + +pack .f0_bot -fill both -expand on + +# +# If we cannot write our config files, disable the write button. +# +if { [file exists .config] == 1 } then { + if { [file writable .config] == 0 } then { + .f0_bot.r.save configure -state disabled + } + } else { + if { [file writable .] == 0 } then { + .f0_bot.r.save configure -state disabled + } + } + +if { [file exists include/linux/autoconf.h] == 1 } then { + if { [file writable include/linux/autoconf.h] == 0 } then { + .f0_bot.r.save configure -state disabled + } + } else { + if { [file writable include/linux/] == 0 } then { + .f0_bot.r.save configure -state disabled + } + } diff --git a/scripts/tkcond.c b/scripts/tkcond.c new file mode 100644 index 000000000..9cc10fa4f --- /dev/null +++ b/scripts/tkcond.c @@ -0,0 +1,542 @@ +/* parser config.in + * + * Version 1.0 + * Eric Youngdale + * 10/95 + * + * The general idea here is that we want to parse a config.in file and + * from this, we generate a wish script which gives us effectively the + * same functionality that the original config.in script provided. + * + * This task is split roughly into 3 parts. The first parse is the parse + * of the input file itself. The second part is where we analyze the + * #ifdef clauses, and attach a linked list of tokens to each of the + * menu items. In this way, each menu item has a complete list of + * dependencies that are used to enable/disable the options. + * The third part is to take the configuration database we have build, + * and build the actual wish script. + * + * This file contains the code to further process the conditions from + * the "ifdef" clauses. + * + * The conditions are assumed to be one of the following formats + * + * simple_condition:= "$VARIABLE" == y/n/m + * simple_condition:= "$VARIABLE != y/n/m + * + * simple_condition -a simple_condition + * + * If the input condition contains '(' or ')' it would screw us up, but for now + * this is not a problem. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "tkparse.h" + + +/* + * Walk a condition chain and invert it so that the logical result is + * inverted. + */ +static void invert_condition(struct condition * cnd) +{ + /* + * This is simple. Just walk through the list, and invert + * all of the operators. + */ + for(;cnd; cnd = cnd->next) + { + switch(cnd->op) + { + case op_and: + cnd->op = op_or; + break; + case op_or: + /* + * This is not turned into op_and - we need to keep track + * of what operators were used here since we have an optimization + * later on to remove duplicate conditions, and having + * inverted ors in there would make it harder if we did not + * distinguish an inverted or from an and we inserted because + * of nested ifs. + */ + cnd->op = op_and1; + break; + case op_neq: + cnd->op = op_eq; + break; + case op_eq: + cnd->op = op_neq; + break; + default: + break; + } + } +} + +/* + * Walk a condition chain, and free the memory associated with it. + */ +static void free_condition(struct condition * cnd) +{ + struct condition * next; + for(;cnd; cnd = next) + { + next = cnd->next; + + if( cnd->variable.str != NULL ) + free(cnd->variable.str); + + free(cnd); + } +} + +/* + * Walk all of the conditions, and look for choice values. Convert + * the tokens into something more digestible. + */ +void fix_choice_cond() +{ + struct condition * cond; + struct condition * cond2; + struct kconfig * cfg; + char tmpbuf[10]; + + for(cfg = config;cfg != NULL; cfg = cfg->next) + { + if( cfg->cond == NULL ) + { + continue; + } + + for(cond = cfg->cond; cond != NULL; cond = cond->next) + { + if( cond->op != op_kvariable ) + continue; + + if( cond->variable.cfg->tok != tok_choice ) + continue; + + /* + * Look ahead for what we are comparing this to. There should + * be one operator in between. + */ + cond2 = cond->next->next; + strcpy(tmpbuf, cond->variable.cfg->label); + + if( strcmp(cond2->variable.str, "y") == 0 ) + { + cond->variable.cfg = cond->variable.cfg->choice_label; + cond2->variable.str = strdup(tmpbuf); + } + else + { + fprintf(stderr,"Ooops\n"); + exit(0); + } + } + + } +} + +/* + * Walk the stack of conditions, and clone all of them with "&&" operators + * gluing them together. The conditions from each level of the stack + * are wrapped in parenthesis so as to guarantee that the results + * are logically correct. + */ +struct condition * get_token_cond(struct condition ** cond, int depth) +{ + int i; + struct condition * newcond; + struct condition * tail; + struct condition * new; + struct condition * ocond; + struct kconfig * cfg; + + newcond = tail = NULL; + for(i=0; i<depth; i++, cond++) + { + /* + * First insert the left parenthesis + */ + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = op_lparen; + if( tail == NULL ) + { + newcond = tail = new; + } + else + { + tail->next = new; + tail = new; + } + + /* + * Now duplicate the chain. + */ + ocond = *cond; + for(;ocond != NULL; ocond = ocond->next) + { + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = ocond->op; + if( ocond->variable.str != NULL ) + { + if( ocond->op == op_variable ) + { + /* + * Search for structure to insert here. + */ + for(cfg = config;cfg != NULL; cfg = cfg->next) + { + if( cfg->tok != tok_bool + && cfg->tok != tok_int + && cfg->tok != tok_hex + && cfg->tok != tok_tristate + && cfg->tok != tok_choice + && cfg->tok != tok_dep_tristate) + { + continue; + } + if( strcmp(cfg->optionname, ocond->variable.str) == 0) + { + new->variable.cfg = cfg; + new->op = op_kvariable; + break; + } + } + if( cfg == NULL ) + { + new->variable.str = strdup(ocond->variable.str); + } + } + else + { + new->variable.str = strdup(ocond->variable.str); + } + } + tail->next = new; + tail = new; + } + + /* + * Next insert the left parenthesis + */ + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = op_rparen; + tail->next = new; + tail = new; + + /* + * Insert an and operator, if we have another condition. + */ + if( i < depth - 1 ) + { + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = op_and; + tail->next = new; + tail = new; + } + + } + + return newcond; +} + +/* + * Walk a single chain of conditions and clone it. These are assumed + * to be created/processed by get_token_cond in a previous pass. + */ +struct condition * get_token_cond_frag(struct condition * cond, + struct condition ** last) +{ + struct condition * newcond; + struct condition * tail; + struct condition * new; + struct condition * ocond; + + newcond = tail = NULL; + + /* + * Now duplicate the chain. + */ + for(ocond = cond;ocond != NULL; ocond = ocond->next) + { + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = ocond->op; + new->variable.cfg = ocond->variable.cfg; + if( tail == NULL ) + { + newcond = tail = new; + } + else + { + tail->next = new; + tail = new; + } + } + + new = (struct condition *) malloc(sizeof(struct condition)); + memset(new, 0, sizeof(*new)); + new->op = op_and; + tail->next = new; + tail = new; + + *last = tail; + return newcond; +} + +/* + * Walk through the if conditionals and maintain a chain. + */ +void fix_conditionals(struct kconfig * scfg) +{ + int depth = 0; + int i; + struct kconfig * cfg; + struct kconfig * cfg1; + struct condition * conditions[25]; + struct condition * cnd; + struct condition * cnd1; + struct condition * cnd2; + struct condition * cnd3; + struct condition * newcond; + struct condition * last; + + /* + * Start by walking the chain. Every time we see an ifdef, push + * the condition chain on the stack. When we see an "else", we invert + * the condition at the top of the stack, and when we see an "endif" + * we free all of the memory for the condition at the top of the stack + * and remove the condition from the top of the stack. + * + * For any other type of token (i.e. a bool), we clone a new condition chain + * by anding together all of the conditions that are currently stored on + * the stack. In this way, we have a correct representation of whatever + * conditions govern the usage of each option. + */ + memset(conditions, 0, sizeof(conditions)); + for(cfg=scfg;cfg != NULL; cfg = cfg->next) + { + switch(cfg->tok) + { + case tok_if: + /* + * Push this condition on the stack, and nuke the token + * representing the ifdef, since we no longer need it. + */ + conditions[depth] = cfg->cond; + depth++; + cfg->tok = tok_nop; + cfg->cond = NULL; + break; + case tok_else: + /* + * For an else, we just invert the condition at the top of + * the stack. This is done in place with no reallocation + * of memory taking place. + */ + invert_condition(conditions[depth-1]); + cfg->tok = tok_nop; + break; + case tok_fi: + depth--; + free_condition(conditions[depth]); + conditions[depth] = NULL; + cfg->tok = tok_nop; + break; + case tok_comment: + case tok_define: + case tok_menuoption: + case tok_bool: + case tok_tristate: + case tok_int: + case tok_hex: + case tok_choice: + case tok_make: + /* + * We need to duplicate the chain of conditions and attach them to + * this token. + */ + cfg->cond = get_token_cond(&conditions[0], depth); + break; + case tok_dep_tristate: + /* + * Same as tok_tristate et al except we have a temporary + * conditional. (Sort of a hybrid tok_if, tok_tristate, tok_fi + * option) + */ + conditions[depth] = cfg->cond; + depth++; + cfg->cond = get_token_cond(&conditions[0], depth); + depth--; + free_condition(conditions[depth]); + conditions[depth] = NULL; + default: + break; + } + } + + /* + * Fix any conditions involving the "choice" operator. + */ + fix_choice_cond(); + + /* + * Walk through and see if there are multiple options that control the + * same kvariable. If there are we need to treat them a little bit + * special. + */ + for(cfg=scfg;cfg != NULL; cfg = cfg->next) + { + switch(cfg->tok) + { + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_int: + case tok_hex: + for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next) + { + switch(cfg1->tok) + { + case tok_define: + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_int: + case tok_hex: + if( strcmp(cfg->optionname, cfg1->optionname) == 0) + { + cfg->flags |= CFG_DUP; + cfg1->flags |= CFG_DUP; + } + break; + default: + break; + } + } + break; + default: + break; + } + } + + /* + * Now go through the list, and every time we see a kvariable, check + * to see whether it also has some dependencies. If so, then + * append it to our list. The reason we do this is that we might have + * option CONFIG_FOO which is only used if CONFIG_BAR is set. It may + * turn out that in config.in that the default value for CONFIG_BAR is + * set to "y", but that CONFIG_BAR is not enabled because CONFIG_XYZZY + * is not set. The current condition chain does not reflect this, but + * we can fix this by searching for the tokens that this option depends + * upon and cloning the conditions and merging them with the list. + */ + for(cfg=scfg;cfg != NULL; cfg = cfg->next) + { + /* + * Search for a token that has a condition list. + */ + if(cfg->cond == NULL) continue; + for(cnd = cfg->cond; cnd; cnd=cnd->next) + { + /* + * Now search the condition list for a known configuration variable + * that has conditions of its own. + */ + if(cnd->op != op_kvariable) continue; + if(cnd->variable.cfg->cond == NULL) continue; + + if(cnd->variable.cfg->flags & CFG_DUP) continue; + /* + * OK, we have some conditions to append to cfg. Make a clone + * of the conditions, + */ + newcond = get_token_cond_frag(cnd->variable.cfg->cond, &last); + + /* + * Finally, we splice it into our list. + */ + last->next = cfg->cond; + cfg->cond = newcond; + + } + } + + /* + * There is a strong possibility that we have duplicate conditions + * in here. It would make the script more efficient and readable to + * remove these. Here is where we assume here that there are no + * parenthesis in the input script. + */ + for(cfg=scfg;cfg != NULL; cfg = cfg->next) + { + /* + * Search for configuration options that have conditions. + */ + if(cfg->cond == NULL) continue; + for(cnd = cfg->cond; cnd; cnd=cnd->next) + { + /* + * Search for a left parenthesis. + */ + if(cnd->op != op_lparen) continue; + for(cnd1 = cnd->next; cnd1; cnd1=cnd1->next) + { + /* + * Search after the previous left parenthesis, and try + * and find a second left parenthesis. + */ + if(cnd1->op != op_lparen) continue; + + /* + * Now compare the next 5 tokens to see if they are + * identical. We are looking for two chains that + * are like: '(' $VARIABLE operator constant ')'. + */ + cnd2 = cnd; + cnd3 = cnd1; + for(i=0; i<5; i++, cnd2=cnd2->next, cnd3=cnd3->next) + { + if(!cnd2 || !cnd3) break; + if(cnd2->op != cnd3->op) break; + if(i == 1 && (cnd2->op != op_kvariable + || cnd2->variable.cfg != cnd3->variable.cfg) ) break; + if(i==2 && cnd2->op != op_eq && cnd2->op != op_neq) break; + if(i == 3 && cnd2->op != op_constant && + strcmp(cnd2->variable.str, cnd3->variable.str) != 0) + break; + if(i==4 && cnd2->op != op_rparen) break; + } + /* + * If these match, and there is an and gluing these together, + * then we can nuke the second one. + */ + if(i==5 && ((cnd3 && cnd3->op == op_and) + ||(cnd2 && cnd2->op == op_and))) + { + /* + * We have a duplicate. Nuke 5 ops. + */ + cnd3 = cnd1; + for(i=0; i<5; i++, cnd3=cnd3->next) + { + cnd3->op = op_nuked; + } + /* + * Nuke the and that glues the conditions together. + */ + if(cnd3 && cnd3->op == op_and) cnd3->op = op_nuked; + else if(cnd2 && cnd2->op == op_and) cnd2->op = op_nuked; + } + } + } + } +} diff --git a/scripts/tkgen.c b/scripts/tkgen.c new file mode 100644 index 000000000..fc9338db3 --- /dev/null +++ b/scripts/tkgen.c @@ -0,0 +1,1132 @@ +/* Generate tk script based upon config.in + * + * Version 1.0 + * Eric Youngdale + * 10/95 + * + * 1996 01 04 + * Avery Pennarun - Aesthetic improvements. + * + * 1996 01 24 + * Avery Pennarun - Bugfixes and more aesthetics. + * + * 1996 03 08 + * Avery Pennarun - The int and hex config.in commands work right. + * - Choice buttons are more user-friendly. + * - Disabling a text entry line greys it out properly. + * - dep_tristate now works like in Configure. (not pretty) + * - No warnings in gcc -Wall. (Fixed some "interesting" bugs.) + * - Faster/prettier "Help" lookups. + * + * 1996 03 15 + * Avery Pennarun - Added new sed script from Axel Boldt to make help even + * faster. (Actually awk is downright slow on some machines.) + * - Fixed a bug I introduced into Choice dependencies. Thanks + * to Robert Krawitz for pointing this out. + * + * 1996 03 16 + * Avery Pennarun - basic "do_make" support added to let sound config work. + * + * 1996 03 25 + * Axel Boldt - Help now works on "choice" buttons. + * + * 1996 04 06 + * Avery Pennarun - Improved sound config stuff. (I think it actually works + * now!) + * - Window-resize-limits don't use ugly /usr/lib/tk4.0 hack. + * - int/hex work with tk3 again. (The "cget" error.) + * - Next/Prev buttons switch between menus. I can't take + * much credit for this; the code was already there, but + * ifdef'd out for some reason. It flickers a lot, but + * I suspect there's no "easy" fix for that. + * - Labels no longer highlight as you move the mouse over + * them (although you can still press them... oh well.) + * - Got rid of the last of the literal color settings, to + * help out people with mono X-Windows systems. + * (Apparently there still are some out there!) + * - Tabstops seem sensible now. + * + * 1996 04 14 + * Avery Pennarun - Reduced flicker when creating windows, even with "update + * idletasks" hack. + * + * TO DO: + * - clean up - there are useless ifdef's everywhere. + * - better comments throughout - C code generating tcl is really cryptic. + * - eliminate silly "update idletasks" hack to improve display speed and + * reduce flicker. But how? + * - make canvas contents resize with the window (good luck). + * - some way to make submenus inside of submenus (ie. Main->Networking->IP) + * (perhaps a button where the description would be) + * - make the main menu use the same tcl code as the submenus. + * - make choice and int/hex input types line up vertically with + * bool/tristate. + * - general speedups - how? The canvas seems to slow it down a lot. + * - choice buttons should default to the first menu option, rather than a + * blank. Also look up the right variable when the help button + * is pressed. + * - clean up +/- 16 confusion for enabling/disabling variables; causes + * (theoretical, at the moment) problems with dependencies. + * + */ +#include <stdio.h> +#include <unistd.h> +#include "tkparse.h" + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +/* + * This is the total number of submenus that we have. + */ +static int tot_menu_num =0; + +/* + * Generate portion of wish script for the beginning of a submenu. + * The guts get filled in with the various options. + */ +static void start_proc(char * label, int menu_num, int flag) +{ + if( flag ) + printf("menu_option menu%d %d \"%s\"\n", menu_num, menu_num, label); + printf("proc menu%d {w title} {\n", menu_num); + printf("\tcatch {destroy $w}\n"); + printf("\ttoplevel $w -class Dialog\n"); + printf("\twm withdraw $w\n"); + printf("\tmessage $w.m -width 400 -aspect 300 -text \\\n"); + printf("\t\t\"%s\" -relief raised\n",label); + printf("\tpack $w.m -pady 10 -side top -padx 10\n"); + printf("\twm title $w \"%s\" \n\n", label); + + /* + * Attach the "Prev", "Next" and "OK" buttons at the end of the window. + */ + printf("\tset oldFocus [focus]\n"); + printf("\tframe $w.f\n"); + printf("\tbutton $w.f.back -text \"Main Menu\" \\\n" + "\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n"); + printf("\tbutton $w.f.next -text \"Next\" \\\n" + "\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n", + menu_num+1, menu_num+1); + if (menu_num == tot_menu_num) + printf("\t$w.f.next configure -state disabled\n"); + printf("\tbutton $w.f.prev -text \"Prev\" \\\n" + "\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n", + menu_num-1, menu_num-1); + if (1 == menu_num) + printf("\t$w.f.prev configure -state disabled\n"); + printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n"); + printf("\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n"); + + /* + * Lines between canvas and other areas of the window. + */ + printf("\tframe $w.topline -relief ridge -borderwidth 2 -height 2\n"); + printf("\tpack $w.topline -side top -fill x\n\n"); + printf("\tframe $w.botline -relief ridge -borderwidth 2 -height 2\n"); + printf("\tpack $w.botline -side bottom -fill x\n\n"); + + /* + * The "config" frame contains the canvas and a scrollbar. + */ + printf("\tframe $w.config\n"); + printf("\tpack $w.config -fill y -expand on\n\n"); + printf("\tscrollbar $w.config.vscroll -command \"$w.config.canvas yview\"\n"); + printf("\tpack $w.config.vscroll -side right -fill y\n\n"); + + /* + * The scrollable canvas itself, where the real work (and mess) gets done. + */ + printf("\tcanvas $w.config.canvas -height 1\\\n" + "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n" + "\t\t-width [expr [winfo screenwidth .] * 1 / 2] \n"); + printf("\tframe $w.config.f\n"); + printf("\tpack $w.config.canvas -side right -fill y\n"); + + printf("\n\n"); +} + +/* + * Each proc we create needs a global declaration for any global variables we + * use. To minimize the size of the file, we set a flag each time we output + * a global declaration so we know whether we need to insert one for a + * given function or not. + */ +void clear_globalflags(struct kconfig * cfg) +{ + for(; cfg != NULL; cfg = cfg->next) + { + cfg->flags &= ~GLOBAL_WRITTEN; + } +} + +/* + * Output a "global" line for a given variable. Also include the + * call to "vfix". (If vfix is not needed, then it's fine to just printf + * a "global" line). + */ +void inline global(char *var) +{ + printf("\tglobal %s; vfix %s\n", var, var); +} + +/* + * This function walks the chain of conditions that we got from cond.c, + * and creates a wish conditional to enable/disable a given widget. + */ +void generate_if(struct kconfig * item, + struct condition * cond, + int menu_num, + int line_num) +{ + struct condition * ocond; + + ocond = cond; + + /* + * First write any global declarations we need for this conditional. + */ + while(cond != NULL ) + { + switch(cond->op){ + case op_variable: + global(cond->variable.str); + break; + case op_kvariable: + if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break; + cond->variable.cfg->flags |= GLOBAL_WRITTEN; + global(cond->variable.cfg->optionname); + break; + default: + break; + } + cond = cond->next; + } + + /* + * Now write this option. + */ + if( (item->flags & GLOBAL_WRITTEN) == 0 + && (item->optionname != NULL) ) + { + global(item->optionname); + item->flags |= GLOBAL_WRITTEN; + } + /* + * Now generate the body of the conditional. + */ + printf("\tif {"); + cond = ocond; + while(cond != NULL ) + { + switch(cond->op){ + case op_bang: + printf(" ! "); + break; + case op_eq: + printf(" == "); + break; + case op_neq: + printf(" != "); + break; + case op_and: + case op_and1: + printf(" && "); + break; + case op_or: + printf(" || "); + break; + case op_lparen: + printf("("); + break; + case op_rparen: + printf(")"); + break; + case op_variable: + printf("$%s", cond->variable.str); + break; + case op_kvariable: + printf("$%s", cond->variable.cfg->optionname); + break; + case op_shellcmd: + printf("[exec %s]", cond->variable.str); + break; + case op_constant: + if( strcmp(cond->variable.str, "y") == 0 ) + printf("1"); + else if( strcmp(cond->variable.str, "n") == 0 ) + printf("0"); + else if( strcmp(cond->variable.str, "m") == 0 ) + printf("2"); + else + printf("\"%s\"", cond->variable.str); + break; + default: + break; + } + cond = cond->next; + } + + /* + * Now we generate what we do depending upon the value of the conditional. + * Depending upon what the token type is, there are different things + * we must do to enable/disable the given widget - this code needs to + * be closely coordinated with the widget creation procedures in header.tk. + */ + switch(item->tok) + { + case tok_define: + printf("} then { set %s %s } \n", item->optionname, item->value); + break; + case tok_menuoption: + printf("} then { .f0.x%d configure -state normal } else { .f0.x%d configure -state disabled }\n", + menu_num, menu_num); + break; + case tok_int: + case tok_hex: + printf("} then { "); + printf(".menu%d.config.f.x%d.x configure -state normal -fore [ cget .ref -foreground ]; ", menu_num, line_num); + printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num); + printf("} else { "); + printf(".menu%d.config.f.x%d.x configure -state disabled -fore [ cget .ref -disabledforeground ];", menu_num, line_num ); + printf(".menu%d.config.f.x%d.l configure -state disabled;", menu_num, line_num ); + printf("}\n"); + break; + case tok_bool: +#ifdef BOOL_IS_BUTTON + /* + * If a bool is just a button, then use this definition. + */ + printf("} then { .menu%d.config.f.x%d configure -state normal } else { .menu%d.config.f.x%d configure -state disabled }\n", + menu_num, line_num, + menu_num, line_num ); +#else + /* + * If a bool is a radiobutton, then use this instead. + */ + printf("} then { "); + printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num); + printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num); + printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num); + printf("set %s [expr $%s&15];", item->optionname, item->optionname); + printf("} else { "); + printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num); + printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num); + printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num); + printf("set %s [expr $%s|16];", item->optionname, item->optionname); + printf("}\n"); +#endif + break; + case tok_tristate: + case tok_dep_tristate: + printf("} then { "); + if( item->tok == tok_dep_tristate ) + { + global(item->depend.str); + printf("if { $%s != 1 && $%s != 0 } then {", + item->depend.str,item->depend.str); + printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num); + printf("} else {"); + printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num); + printf("}; "); + } + else + { + printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num); + } + + printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num); + printf(".menu%d.config.f.x%d.m configure -state normal;",menu_num, line_num); + printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num); + /* + * Or in a bit to the variable - this causes all of the radiobuttons + * to be deselected (i.e. not be red). + */ + printf("set %s [expr $%s&15];", item->optionname, item->optionname); + printf("} else { "); + printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num); + printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num); + printf(".menu%d.config.f.x%d.m configure -state disabled;",menu_num, line_num); + printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num); + /* + * Clear the disable bit - this causes the correct radiobutton + * to appear selected (i.e. turn red). + */ + printf("set %s [expr $%s|16];", item->optionname, item->optionname); + printf("}\n"); + break; + case tok_choose: + case tok_choice: + fprintf(stderr,"Fixme\n"); + exit(0); + default: + break; + } +} + +/* + * Similar to generate_if, except we come here when generating an + * output file. Thus instead of enabling/disabling a widget, we + * need to decide whether to write out a given configuration variable + * to the output file. + */ +void generate_if_for_outfile(struct kconfig * item, + struct condition * cond) +{ + struct condition * ocond; + + /* + * First write any global declarations we need for this conditional. + */ + ocond = cond; + for(; cond != NULL; cond = cond->next ) + { + switch(cond->op){ + case op_variable: + global(cond->variable.str); + break; + case op_kvariable: + if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break; + cond->variable.cfg->flags |= GLOBAL_WRITTEN; + global(cond->variable.cfg->optionname); + break; + default: + break; + } + } + + /* + * Now generate the body of the conditional. + */ + printf("\tif {"); + cond = ocond; + while(cond != NULL ) + { + switch(cond->op){ + case op_bang: + printf(" ! "); + break; + case op_eq: + printf(" == "); + break; + case op_neq: + printf(" != "); + break; + case op_and: + case op_and1: + printf(" && "); + break; + case op_or: + printf(" || "); + break; + case op_lparen: + printf("("); + break; + case op_rparen: + printf(")"); + break; + case op_variable: + printf("$%s", cond->variable.str); + break; + case op_shellcmd: + printf("[exec %s]", cond->variable.str); + break; + case op_kvariable: + printf("$%s", cond->variable.cfg->optionname); + break; + case op_constant: + if( strcmp(cond->variable.str, "y") == 0 ) + printf("1"); + else if( strcmp(cond->variable.str, "n") == 0 ) + printf("0"); + else if( strcmp(cond->variable.str, "m") == 0 ) + printf("2"); + else + printf("\"%s\"", cond->variable.str); + break; + default: + break; + } + cond = cond->next; + } + + /* + * Now we generate what we do depending upon the value of the + * conditional. Depending upon what the token type is, there are + * different things we must do write the value the given widget - + * this code needs to be closely coordinated with the widget + * creation procedures in header.tk. + */ + switch(item->tok) + { + case tok_define: + printf("} then {write_tristate $cfg $autocfg %s %s $notmod }\n", item->optionname, item->value); + break; + case tok_comment: + printf("} then {write_comment $cfg $autocfg \"%s\"}\n", item->label); + break; + case tok_dep_tristate: + printf("} then { write_tristate $cfg $autocfg %s $%s $%s } \n", + item->optionname, item->optionname, item->depend.str); + break; + case tok_tristate: + case tok_bool: + printf("} then { write_tristate $cfg $autocfg %s $%s $notmod }\n", + item->optionname, item->optionname); + break; + case tok_int: + printf("} then { write_int $cfg $autocfg %s $%s $notmod }\n", + item->optionname, item->optionname); + break; + case tok_hex: + printf("} then { write_hex $cfg $autocfg %s $%s $notmod }\n", + item->optionname, item->optionname); + break; + case tok_make: + printf("} then { do_make {%s} }\n",item->value); + break; + case tok_choose: + case tok_choice: + fprintf(stderr,"Fixme\n"); + exit(0); + default: + break; + } +} + +/* + * Generates a fragment of wish script that closes out a submenu procedure. + */ +static void end_proc(int menu_num) +{ + struct kconfig * cfg; + + printf("\n\n\n"); + printf("\tfocus $w\n"); + printf("\tupdate_menu%d $w.config.f\n", menu_num); + printf("\tglobal winx; global winy\n"); + printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n"); + printf("\twm geometry $w +$winx+$winy\n"); + + /* + * Now that the whole window is in place, we need to wait for an "update" + * so we can tell the canvas what its virtual size should be. + * + * Unfortunately, this causes some ugly screen-flashing because the whole + * window is drawn, and then it is immediately resized. It seems + * unavoidable, though, since "frame" objects won't tell us their size + * until after an update, and "canvas" objects can't automatically pack + * around frames. Sigh. + */ + printf("\tupdate idletasks\n"); + printf("\t$w.config.canvas create window 0 0 -anchor nw -window $w.config.f\n\n"); + printf("\t$w.config.canvas configure \\\n" + "\t\t-width [expr [winfo reqwidth $w.config.f] + 1]\\\n" + "\t\t-scrollregion \"-1 -1 [expr [winfo reqwidth $w.config.f] + 1] \\\n" + "\t\t\t [expr [winfo reqheight $w.config.f] + 1]\"\n\n"); + + /* + * If the whole canvas will fit in 3/4 of the screen height, do it; + * otherwise, resize to around 1/2 the screen and let us scroll. + */ + printf("\tset winy [expr [winfo reqh $w] - [winfo reqh $w.config.canvas]]\n"); + printf("\tset scry [expr [winfo screenh $w] / 2]\n"); + printf("\tset maxy [expr [winfo screenh $w] * 3 / 4]\n"); + printf("\tset canvtotal [expr [winfo reqh $w.config.f] + 2]\n"); + printf("\tif [expr $winy + $canvtotal < $maxy] {\n" + "\t\t$w.config.canvas configure -height $canvtotal\n" + "\t} else {\n" + "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n" + "\t}\n"); + + /* + * Limit the min/max window size. Height can vary, but not width, + * because of the limitations of canvas and our laziness. + */ + printf("\tupdate idletasks\n"); + printf("\twm maxsize $w [winfo width $w] [winfo screenheight $w]\n"); + printf("\twm minsize $w [winfo width $w] 100\n\n"); + printf("\twm deiconify $w\n"); + + printf("}\n\n\n"); + + /* + * Now we generate the companion procedure for the menu we just + * generated. This procedure contains all of the code to + * disable/enable widgets based upon the settings of the other + * widgets, and will be called first when the window is mapped, + * and each time one of the buttons in the window are clicked. + */ + printf("proc update_menu%d {w} {\n", menu_num); + + printf("\tupdate_define\n"); + clear_globalflags(config); + for(cfg = config;cfg != NULL; cfg = cfg->next) + { + /* + * Skip items not for this menu, or ones having no conditions. + */ + if (cfg->menu_number != menu_num ) continue; + if (cfg->tok != tok_define) continue; + /* + * Clear all of the booleans that are defined in this menu. + */ + if( (cfg->flags & GLOBAL_WRITTEN) == 0 + && (cfg->optionname != NULL) ) + { + printf("\tglobal %s\n", cfg->optionname); + cfg->flags |= GLOBAL_WRITTEN; + printf("\tset %s 0\n", cfg->optionname); + } + + } + for(cfg = config;cfg != NULL; cfg = cfg->next) + { + /* + * Skip items not for this menu, or ones having no conditions. + */ + if (cfg->menu_number != menu_num ) continue; + if (cfg->tok == tok_menuoption) continue; + if (cfg->cond != NULL ) + generate_if(cfg, cfg->cond, menu_num, cfg->menu_line); + else + { + /* + * If this token has no conditionals, check to see whether + * it is a tristate - if so, then generate the conditional + * to enable/disable the "y" button based upon the setting + * of the option it depends upon. + */ + if(cfg->tok == tok_dep_tristate) + { + global(cfg->depend.str); + printf("\tif {$%s != 1 && $%s != 0 } then { .menu%d.config.f.x%d.y configure -state disabled } else { .menu%d.config.f.x%d.y configure -state normal}\n", + cfg->depend.str,cfg->depend.str, + menu_num, cfg->menu_line, + menu_num, cfg->menu_line); + } + } + + } + + + printf("}\n\n\n"); +} + +/* + * This function goes through and counts up the number of items in + * each submenu. If there are too many options, we need to split it + * into submenus. This function just calculates how many submenus, + * and how many items go in each submenu. + */ +static void find_menu_size(struct kconfig *cfg, + int *menu_max, + int *menu_maxlines) + +{ + struct kconfig * pnt; + int tot; + + /* + * First count up the number of options in this menu. + */ + tot = 0; + for(pnt = cfg->next; pnt; pnt = pnt->next) + { + if( pnt->tok == tok_menuoption) break; + switch (pnt->tok) + { + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_int: + case tok_hex: + case tok_choose: + tot++; + break; + case tok_choice: + default: + break; + } + } + + *menu_max = cfg->menu_number; + *menu_maxlines = tot; +} + +/* + * This is the top level function for generating the tk script. + */ +void dump_tk_script(struct kconfig *scfg) +{ + int menu_num =0; + int menu_max =0; + int menu_min =0; + int menu_line = 0; + int menu_maxlines = 0; + struct kconfig * cfg; + struct kconfig * cfg1 = NULL; + char * menulabel; + + /* + * Start by assigning menu numbers, and submenu numbers. + */ + for(cfg = scfg;cfg != NULL; cfg = cfg->next) + { + switch (cfg->tok) + { + case tok_menuname: + break; + case tok_menuoption: + /* + * At the start of a new menu, calculate the number of items + * we will put into each submenu so we know when to bump the + * menu number. The submenus are really no different from a + * normal menu, but the top level buttons only access the first + * of the chain of menus, and the prev/next buttons are used + * access the submenus. + */ + cfg->menu_number = ++menu_num; + find_menu_size(cfg, &menu_max, &menu_maxlines); + cfg->submenu_start = menu_num; + cfg->submenu_end = menu_max; + menu_line = 0; + break; + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_int: + case tok_hex: + case tok_choose: + /* + * If we have overfilled the menu, then go to the next one. + */ + if( menu_line == menu_maxlines ) + { + menu_line = 0; + menu_num++; + } + cfg->menu_number = menu_num; + cfg->submenu_start = menu_min; + cfg->submenu_end = menu_max; + cfg->menu_line = menu_line++; + break; + case tok_define: + cfg->menu_number = -1; + case tok_choice: + default: + break; + }; + } + + /* + * Record this so we can set up the prev/next buttons correctly. + */ + tot_menu_num = menu_num; + + /* + * Now start generating the actual wish script that we will use. + * We need to keep track of the menu numbers of the min/max menu + * for a range of submenus so that we can correctly limit the + * prev and next buttons so that they don't go over into some other + * category. + */ + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + switch (cfg->tok) + { + case tok_menuname: + printf("mainmenu_name \"%s\"\n", cfg->label); + break; + case tok_menuoption: + /* + * We are at the start of a new menu. If we had one that + * we were working on before, close it out, and then generate + * the script to start the new one. + */ + if( cfg->menu_number > 1 ) + { + end_proc(menu_num); + } + menulabel = cfg->label; + start_proc(cfg->label, cfg->menu_number, TRUE); + menu_num = cfg->menu_number; + menu_max = cfg->submenu_end; + menu_min = cfg->submenu_start; + break; + case tok_bool: + /* + * If we reached the point where we need to switch over + * to the next submenu, then bump the menu number and generate + * the code to close out the old menu and start the new one. + */ + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\tbool $w.config.f %d %d \"%s\" %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname); + break; + + case tok_choice: + printf("\t$w.config.f.x%d.x.menu add radiobutton -label \"%s\" -variable %s -value \"%s\" -command \"update_menu%d .menu%d.config.f\"\n", + cfg1->menu_line, + cfg->label, + cfg1->optionname, + cfg->label, + cfg1->menu_number, cfg1->menu_number); + break; + case tok_choose: + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\tglobal %s\n",cfg->optionname); + printf("\tminimenu $w.config.f %d %d \"%s\" %s %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname, + /* + * We rely on the fact that the first tok_choice corresponding + * to the current tok_choose is cfg->next (compare parse() in + * tkparse.c). We need its name to pick out the right help + * text from Configure.help. + */ + cfg->next->optionname); + printf("\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line); + cfg1 = cfg; + break; + case tok_tristate: + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\ttristate $w.config.f %d %d \"%s\" %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname); + break; + case tok_dep_tristate: + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\tdep_tristate $w.config.f %d %d \"%s\" %s %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname, + cfg->depend.str); + break; + case tok_int: + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\tint $w.config.f %d %d \"%s\" %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname); + break; + case tok_hex: + if( cfg->menu_number != menu_num ) + { + end_proc(menu_num); + start_proc(menulabel, cfg->menu_number, FALSE); + menu_num = cfg->menu_number; + } + printf("\thex $w.config.f %d %d \"%s\" %s\n", + cfg->menu_number, + cfg->menu_line, + cfg->label, + cfg->optionname); + break; + default: + break; + } + + } + + /* + * Generate the code to close out the last menu. + */ + end_proc(menu_num); + +#ifdef ERIC_DONT_DEF + /* + * Generate the code for configuring the sound driver. Right now this + * cannot be done from the X script, but we insert the menu anyways. + */ + start_proc("Configure sound driver", ++menu_num, TRUE); +#if 0 + printf("\tdo_make -C drivers/sound config\n"); + printf("\techo check_sound_config %d\n",menu_num); +#endif + printf("\tlabel $w.config.f.m0 -bitmap error\n"); + printf("\tmessage $w.config.f.m1 -width 400 -aspect 300 -text \"The sound drivers cannot as of yet be configured via the X-based interface\" -relief raised\n"); + printf("\tpack $w.config.f.m0 $w.config.f.m1 -side top -pady 10 -expand on\n"); + /* + * Close out the last menu. + */ + end_proc(menu_num); +#endif + + /* + * The top level menu also needs an update function. When we exit a + * submenu, we may need to disable one or more of the submenus on + * the top level menu, and this procedure will ensure that things are + * correct. + */ + printf("proc update_mainmenu {w} {\n"); + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + switch (cfg->tok) + { + case tok_menuoption: + if (cfg->cond != NULL ) + generate_if(cfg, cfg->cond, cfg->menu_number, cfg->menu_line); + break; + default: + break; + } + } + + printf("}\n\n\n"); + +#if 0 + /* + * Generate some code to set the variables that are "defined". + */ + for(cfg = config;cfg != NULL; cfg = cfg->next) + { + /* + * Skip items not for this menu, or ones having no conditions. + */ + if( cfg->tok != tok_define) continue; + if (cfg->cond != NULL ) + generate_if(cfg, cfg->cond, menu_num, cfg->menu_line); + else + { + printf("\twrite_define %s %s\n", cfg->optionname, cfg->value); + } + + } +#endif + + /* + * Now generate code to load the default settings into the variables. + * Note that the script in tail.tk will attempt to load .config, + * which may override these settings, but that's OK. + */ + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + switch (cfg->tok) + { + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_choice: + printf("set %s 0\n", cfg->optionname); + break; + case tok_int: + case tok_hex: + printf("set %s %s\n", cfg->optionname, cfg->value); + break; + case tok_choose: + printf("set %s \"(not set)\"\n",cfg->optionname); + default: + break; + } + } + + /* + * Next generate a function that can be called from the main menu that will + * write all of the variables out. This also serves double duty - we can + * save configuration to a file using this. + */ + printf("proc writeconfig {file1 file2} {\n"); + printf("\tset cfg [open $file1 w]\n"); + printf("\tset autocfg [open $file2 w]\n"); + printf("\tset notmod 1\n"); + printf("\tset notset 0\n"); + clear_globalflags(config); + printf("\tputs $cfg \"#\"\n"); + printf("\tputs $cfg \"# Automatically generated make config: don't edit\"\n"); + printf("\tputs $cfg \"#\"\n"); + + printf("\tputs $autocfg \"/*\"\n"); + printf("\tputs $autocfg \" * Automatically generated C config: don't edit\"\n"); + printf("\tputs $autocfg \" */\"\n"); + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + switch (cfg->tok) + { + case tok_int: + case tok_hex: + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_define: + case tok_choose: + if(!(cfg->flags & GLOBAL_WRITTEN)) + { + cfg->flags |= GLOBAL_WRITTEN; + printf("\tglobal %s\n", cfg->optionname); + } + /* fall through */ + case tok_make: + case tok_comment: + if (cfg->cond != NULL ) + generate_if_for_outfile(cfg, cfg->cond); + else + { + if(cfg->tok == tok_dep_tristate) + { + printf("\tif {$%s == 0 } then {\n" + "\t\twrite_tristate $cfg $autocfg %s $notset $notmod\n" + "\t} else {\n" + "\t\twrite_tristate $cfg $autocfg %s $%s $%s\n" + "\t}\n", + cfg->depend.str, + cfg->optionname, + cfg->optionname, + cfg->optionname, + cfg->depend.str); + } + else if(cfg->tok == tok_comment) + { + printf("\twrite_comment $cfg $autocfg \"%s\"\n", cfg->label); + } +#if 0 + else if(cfg->tok == tok_define) + { + printf("\twrite_define %s %s\n", cfg->optionname, + cfg->value); + } +#endif + else if (cfg->tok == tok_choose ) + { + for(cfg1 = cfg->next; + cfg1 != NULL && cfg1->tok == tok_choice; + cfg1 = cfg1->next) + { + printf("\tif { $%s == \"%s\" } then { write_tristate $cfg $autocfg %s 1 $notmod }\n", + cfg->optionname, + cfg1->label, + cfg1->optionname); + } + } + else if (cfg->tok == tok_int ) + { + printf("\twrite_int $cfg $autocfg %s $%s $notmod\n", + cfg->optionname, + cfg->optionname); + } + else if (cfg->tok == tok_hex ) + { + printf("\twrite_hex $cfg $autocfg %s $%s $notmod\n", + cfg->optionname, + cfg->optionname); + } + else if (cfg->tok == tok_make ) + { + printf("\tdo_make {%s}\n",cfg->value); + } + else + { + printf("\twrite_tristate $cfg $autocfg %s $%s $notmod\n", + cfg->optionname, + cfg->optionname); + } + } + break; + default: + break; + } + } + printf("\tclose $cfg\n"); + printf("\tclose $autocfg\n"); + printf("}\n\n\n"); + + /* + * Finally write a simple function that updates the master choice + * variable depending upon what values were loaded from a .config + * file. + */ + printf("proc clear_choices { } {\n"); + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + if( cfg->tok != tok_choose ) continue; + for(cfg1 = cfg->next; + cfg1 != NULL && cfg1->tok == tok_choice; + cfg1 = cfg1->next) + { + printf("\tglobal %s; set %s 0\n",cfg1->optionname,cfg1->optionname); + } + } + printf("}\n\n\n"); + + printf("proc update_choices { } {\n"); + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + if( cfg->tok != tok_choose ) continue; + printf("\tglobal %s\n", cfg->optionname); + for(cfg1 = cfg->next; + cfg1 != NULL && cfg1->tok == tok_choice; + cfg1 = cfg1->next) + { + printf("\tglobal %s\n", cfg1->optionname); + printf("\tif { $%s == 1 } then { set %s \"%s\" }\n", + cfg1->optionname, + cfg->optionname, + cfg1->label); + } + } + printf("}\n\n\n"); + + printf("proc update_define { } {\n"); + clear_globalflags(config); + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + if( cfg->tok != tok_define ) continue; + printf("\tglobal %s; set %s 0\n", cfg->optionname, cfg->optionname); + cfg->flags |= GLOBAL_WRITTEN; + } + for(cfg = scfg; cfg != NULL; cfg = cfg->next) + { + if( cfg->tok != tok_define ) continue; + if (cfg->cond != NULL ) + generate_if(cfg, cfg->cond, -1, 0); + else + { + printf("\tset %s %s\n", + cfg->optionname, cfg->value); + } + } + printf("}\n\n\n"); + /* + * That's it. We are done. The output of this file will have header.tk + * prepended and tail.tk appended to create an executable wish script. + */ +} diff --git a/scripts/tkparse.c b/scripts/tkparse.c new file mode 100644 index 000000000..2a000217c --- /dev/null +++ b/scripts/tkparse.c @@ -0,0 +1,751 @@ +/* parser config.in + * + * Version 1.0 + * Eric Youngdale + * 10/95 + * + * The general idea here is that we want to parse a config.in file and + * from this, we generate a wish script which gives us effectively the + * same functionality that the original config.in script provided. + * + * This task is split roughly into 3 parts. The first parse is the parse + * of the input file itself. The second part is where we analyze the + * #ifdef clauses, and attach a linked list of tokens to each of the + * menu items. In this way, each menu item has a complete list of + * dependencies that are used to enable/disable the options. + * The third part is to take the configuration database we have build, + * and build the actual wish script. + * + * This file contains the code to do the first parse of config.in. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "tkparse.h" + +struct kconfig * config = NULL; +struct kconfig * clast = NULL; +struct kconfig * koption = NULL; +static int lineno = 0; +static int menus_seen = 0; +static char * current_file = NULL; +static int do_source(char * filename); +static char * get_string(char *pnt, char ** labl); +static int choose_number = 0; + + +/* + * Simple function just to skip over spaces and tabs in config.in. + */ +static char * skip_whitespace(char * pnt) +{ + while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++; + return pnt; +} + +/* + * This function parses a conditional from a config.in (i.e. from an ifdef) + * and generates a linked list of tokens that describes the conditional. + */ +static struct condition * parse_if(char * pnt) +{ + char * opnt; + struct condition *list; + struct condition *last; + struct condition *cpnt; + char varname[64]; + char * pnt1; + + opnt = pnt; + + /* + * We need to find the various tokens, and build the linked list. + */ + pnt = skip_whitespace(pnt); + if( *pnt != '[' ) return NULL; + pnt++; + pnt = skip_whitespace(pnt); + + list = last = NULL; + while(*pnt && *pnt != ']') { + + pnt = skip_whitespace(pnt); + if(*pnt== '\0' || *pnt == ']') break; + + /* + * Allocate memory for the token we are about to parse, and insert + * it in the linked list. + */ + cpnt = (struct condition *) malloc(sizeof(struct condition)); + memset(cpnt, 0, sizeof(struct condition)); + if( last == NULL ) + { + list = last = cpnt; + } + else + { + last->next = cpnt; + last = cpnt; + } + + /* + * Determine what type of operation this token represents. + */ + if( *pnt == '-' && pnt[1] == 'a' ) + { + cpnt->op = op_and; + pnt += 2; + continue; + } + + if( *pnt == '-' && pnt[1] == 'o' ) + { + cpnt->op = op_or; + pnt += 2; + continue; + } + + if( *pnt == '!' && pnt[1] == '=' ) + { + cpnt->op = op_neq; + pnt += 2; + continue; + } + + if( *pnt == '=') + { + cpnt->op = op_eq; + pnt += 1; + continue; + } + + if( *pnt == '!') + { + cpnt->op = op_bang; + pnt += 1; + continue; + } + + if( *pnt != '"' ) goto error; /* This cannot be right. */ + pnt++; + if( *pnt == '`' ) + { + cpnt->op = op_shellcmd; + pnt1 = varname; + pnt++; + while(*pnt && *pnt != '`') *pnt1++ = *pnt++; + *pnt1++ = '\0'; + cpnt->variable.str = strdup(varname); + if( *pnt == '`' ) pnt++; + if( *pnt == '"' ) pnt++; + continue; + } + if( *pnt == '$' ) + { + cpnt->op = op_variable; + pnt1 = varname; + pnt++; + while(*pnt && *pnt != '"') *pnt1++ = *pnt++; + *pnt1++ = '\0'; + cpnt->variable.str = strdup(varname); + if( *pnt == '"' ) pnt++; + continue; + } + + cpnt->op = op_constant; + pnt1 = varname; + while(*pnt && *pnt != '"') *pnt1++ = *pnt++; + *pnt1++ = '\0'; + cpnt->variable.str = strdup(varname); + if( *pnt == '"' ) pnt++; + continue; + } + + return list; + + error: + if(current_file != NULL) + fprintf(stderr, + "Bad if clause at line %d(%s):%s\n", lineno, current_file, opnt); + else + fprintf(stderr, + "Bad if clause at line %d:%s\n", lineno, opnt); + return NULL; +} + +/* + * This function looks for a quoted string, from the input buffer, and + * returns a pointer to a copy of this string. Any characters in + * the string that need to be "quoted" have a '\' character inserted + * in front - this way we can directly write these strings into + * wish scripts. + */ +static char * get_qstring(char *pnt, char ** labl) +{ + char quotechar; + char newlabel[1024]; + char * pnt1; + char * pnt2; + + while( *pnt && *pnt != '"' && *pnt != '\'') pnt++; + if (*pnt == '\0') return pnt; + + quotechar = *pnt++; + pnt1 = newlabel; + while(*pnt && *pnt != quotechar && pnt[-1] != '\\') + { + /* + * Quote the character if we need to. + */ + if( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']') + *pnt1++ = '\\'; + + *pnt1++ = *pnt++; + } + *pnt1++ = '\0'; + + pnt2 = (char *) malloc(strlen(newlabel) + 1); + strcpy(pnt2, newlabel); + *labl = pnt2; + + /* + * Skip over last quote, and whitespace. + */ + pnt++; + pnt = skip_whitespace(pnt); + return pnt; +} + +static char * parse_choices(struct kconfig * choice_kcfg, char * pnt) +{ + struct kconfig * kcfg; + int index = 1; + + /* + * Choices appear in pairs of strings. The parse is fairly trivial. + */ + while(1) + { + pnt = skip_whitespace(pnt); + if(*pnt == '\0') break; + + kcfg = (struct kconfig *) malloc(sizeof(struct kconfig)); + memset(kcfg, 0, sizeof(struct kconfig)); + kcfg->tok = tok_choice; + if( clast != NULL ) + { + clast->next = kcfg; + clast = kcfg; + } + else + { + clast = config = kcfg; + } + + pnt = get_string(pnt, &kcfg->label); + pnt = skip_whitespace(pnt); + pnt = get_string(pnt, &kcfg->optionname); + kcfg->choice_label = choice_kcfg; + kcfg->choice_value = index++; + if( strcmp(kcfg->label, choice_kcfg->value) == 0 ) + choice_kcfg->choice_value = kcfg->choice_value; + } + + return pnt; +} + + +/* + * This function grabs one text token from the input buffer + * and returns a pointer to a copy of just the identifier. + * This can be either a variable name (i.e. CONFIG_NET), + * or it could be the default value for the option. + */ +static char * get_string(char *pnt, char ** labl) +{ + char newlabel[1024]; + char * pnt1; + char * pnt2; + + if (*pnt == '\0') return pnt; + + pnt1 = newlabel; + while(*pnt && *pnt != ' ' && *pnt != '\t') + { + *pnt1++ = *pnt++; + } + *pnt1++ = '\0'; + + pnt2 = (char *) malloc(strlen(newlabel) + 1); + strcpy(pnt2, newlabel); + *labl = pnt2; + + if( *pnt ) pnt++; + return pnt; +} + + +/* + * Top level parse function. Input pointer is one complete line from config.in + * and the result is that we create a token that describes this line + * and insert it into our linked list. + */ +void parse(char * pnt) { + enum token tok; + struct kconfig * kcfg; + char tmpbuf[24],fake_if[1024]; + + /* + * Ignore comments and leading whitespace. + */ + + pnt = skip_whitespace(pnt); + while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++; + if(! *pnt ) return; + if( *pnt == '#' ) return; + + /* + * Now categorize the next token. + */ + tok = tok_unknown; + if (strncmp(pnt, "mainmenu_name", 13) == 0) + { + tok = tok_menuname; + pnt += 13; + } + else if (strncmp(pnt, "source", 6) == 0) + { + pnt += 7; + pnt = skip_whitespace(pnt); + do_source(pnt); + return; + } + else if (strncmp(pnt, "mainmenu_option", 15) == 0) + { + menus_seen++; + tok = tok_menuoption; + pnt += 15; + } + else if (strncmp(pnt, "$MAKE ", 6) == 0) + { + tok = tok_make; + } + else if (strncmp(pnt, "comment", 7) == 0) + { + tok = tok_comment; + pnt += 7; + } + else if (strncmp(pnt, "choice", 6) == 0) + { + tok = tok_choose; + pnt += 6; + } + else if (strncmp(pnt, "define_bool", 11) == 0) + { + tok = tok_define; + pnt += 11; + } + else if (strncmp(pnt, "bool", 4) == 0) + { + tok = tok_bool; + pnt += 4; + } + else if (strncmp(pnt, "tristate", 8) == 0) + { + tok = tok_tristate; + pnt += 8; + } + else if (strncmp(pnt, "dep_tristate", 12) == 0) + { + tok = tok_dep_tristate; + pnt += 12; + } + else if (strncmp(pnt, "int", 3) == 0) + { + tok = tok_int; + pnt += 3; + } + else if (strncmp(pnt, "hex", 3) == 0) + { + tok = tok_hex; + pnt += 3; + } + else if (strncmp(pnt, "if", 2) == 0) + { + tok = tok_if; + pnt += 2; + } + else if (strncmp(pnt, "else", 4) == 0) + { + tok = tok_else; + pnt += 4; + } + else if (strncmp(pnt, "fi", 2) == 0) + { + tok = tok_fi; + pnt += 2; + } + else if (strncmp(pnt, "endmenu", 7) == 0) + { + tok = tok_endmenu; + pnt += 7; + } + + if( tok == tok_unknown) + { + if( clast != NULL && clast->tok == tok_if + && strcmp(pnt,"then") == 0) return; + if( current_file != NULL ) + fprintf(stderr, "unknown command=%s(%s %d)\n", pnt, + current_file, lineno); + else + fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno); + return; + } + + /* + * Allocate memory for this item, and attach it to the end of the linked + * list. + */ + kcfg = (struct kconfig *) malloc(sizeof(struct kconfig)); + memset(kcfg, 0, sizeof(struct kconfig)); + kcfg->tok = tok; + if( clast != NULL ) + { + clast->next = kcfg; + clast = kcfg; + } + else + { + clast = config = kcfg; + } + + pnt = skip_whitespace(pnt); + + /* + * Now parse the remaining parts of the option, and attach the results + * to the structure. + */ + switch (tok) + { + case tok_choose: + pnt = get_qstring(pnt, &kcfg->label); + pnt = get_qstring(pnt, &kcfg->optionname); + pnt = get_string(pnt, &kcfg->value); + /* + * Now we need to break apart the individual options into their + * own configuration structures. + */ + parse_choices(kcfg, kcfg->optionname); + free(kcfg->optionname); + sprintf(tmpbuf, "tmpvar_%d", choose_number++); + kcfg->optionname = strdup(tmpbuf); + break; + case tok_define: + pnt = get_string(pnt, &kcfg->optionname); + if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1"; + if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0"; + if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2"; + break; + case tok_menuname: + pnt = get_qstring(pnt, &kcfg->label); + break; + case tok_bool: + case tok_tristate: + pnt = get_qstring(pnt, &kcfg->label); + pnt = get_string(pnt, &kcfg->optionname); + break; + case tok_int: + case tok_hex: + pnt = get_qstring(pnt, &kcfg->label); + pnt = get_string(pnt, &kcfg->optionname); + pnt = get_string(pnt, &kcfg->value); + break; + case tok_dep_tristate: + pnt = get_qstring(pnt, &kcfg->label); + pnt = get_string(pnt, &kcfg->optionname); + pnt = skip_whitespace(pnt); + if( *pnt == '$') pnt++; + pnt = get_string(pnt, &kcfg->depend.str); + + /* + * Create a conditional for this object's dependency. + * + * We can't use "!= n" because this is internally converted to "!= 0" + * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT + * is disabled MSDOS has 16 added to its value, making UMSDOS fully + * available. Whew. + * + * This is more of a hack than a fix. Nested "if" conditionals are + * probably affected too - that +/- 16 affects things in too many + * places. But this should do for now. + */ + sprintf(fake_if,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then", + kcfg->depend.str,kcfg->depend.str); + kcfg->cond = parse_if(fake_if); + if(kcfg->cond == NULL ) + { + exit(1); + } + break; + case tok_comment: + pnt = get_qstring(pnt, &kcfg->label); + if( koption != NULL ) + { + pnt = get_qstring(pnt, &kcfg->label); + koption->label = kcfg->label; + koption = NULL; + } + break; + case tok_menuoption: + if( strncmp(pnt, "next_comment", 12) == 0) + { + koption = kcfg; + } + else + { + pnt = get_qstring(pnt, &kcfg->label); + } + break; + case tok_make: + kcfg->value=strdup(pnt); + break; + case tok_else: + case tok_fi: + case tok_endmenu: + break; + case tok_if: + /* + * Conditionals are different. For the first level parse, only + * tok_if and tok_dep_tristate items have a ->cond chain attached. + */ + kcfg->cond = parse_if(pnt); + if(kcfg->cond == NULL ) + { + exit(1); + } + break; + default: + exit(0); + } + + return; +} + +/* + * Simple function to dump to the screen what the condition chain looks like. + */ +void dump_if(struct condition * cond) +{ + printf(" "); + while(cond != NULL ) + { + switch(cond->op){ + case op_eq: + printf(" = "); + break; + case op_bang: + printf(" ! "); + break; + case op_neq: + printf(" != "); + break; + case op_and: + printf(" -a "); + break; + case op_lparen: + printf("("); + break; + case op_rparen: + printf(")"); + break; + case op_variable: + printf("$%s", cond->variable.str); + break; + case op_constant: + printf("'%s'", cond->variable.str); + break; + default: + break; + } + cond = cond->next; + } + + printf("\n"); +} + +static int do_source(char * filename) +{ + char buffer[1024]; + int offset; + int old_lineno; + char * old_file; + char * pnt; + FILE * infile; + + if( strcmp(filename, "-") == 0 ) + infile = stdin; + else + infile = fopen(filename,"r"); + + /* + * If our cwd was in the scripts directory, we might have to go up one + * to find the sourced file. + */ + if(!infile) { + strcpy (buffer, "../"); + strcat (buffer, filename); + infile = fopen(buffer,"r"); + } + + if(!infile) { + fprintf(stderr,"Unable to open file %s\n", filename); + return 1; + } + old_lineno = lineno; + lineno = 0; + if( infile != stdin ) { + old_file = current_file; + current_file = filename; + } + offset = 0; + while(1) + { + fgets(&buffer[offset], sizeof(buffer) - offset, infile); + if(feof(infile)) break; + + /* + * Strip the trailing return character. + */ + pnt = buffer + strlen(buffer) - 1; + if( *pnt == '\n') *pnt-- = 0; + lineno++; + if( *pnt == '\\' ) + { + offset = pnt - buffer; + } + else + { + parse(buffer); + offset = 0; + } + } + fclose(infile); + if( infile != stdin ) { + current_file = old_file; + } + lineno = old_lineno; + return 0; +} + +int main(int argc, char * argv[]) +{ +#if 0 + char buffer[1024]; + char * pnt; + struct kconfig * cfg; + int i; +#endif + + /* + * Read stdin to get the top level script. + */ + do_source("-"); + + if( menus_seen == 0 ) + { + fprintf(stderr,"The config.in file for this platform does not support\n"); + fprintf(stderr,"menus.\n"); + exit(1); + } + /* + * Input file is now parsed. Next we need to go through and attach + * the correct conditions to each of the actual menu items and kill + * the if/else/endif tokens from the list. We also flag the menu items + * that have other things that depend upon its setting. + */ + fix_conditionals(config); + + /* + * Finally, we generate the wish script. + */ + dump_tk_script(config); + +#if 0 + /* + * Now dump what we have so far. This is only for debugging so that + * we can display what we think we have in the list. + */ + for(cfg = config; cfg; cfg = cfg->next) + { + + if(cfg->cond != NULL && cfg->tok != tok_if) + dump_if(cfg->cond); + + switch(cfg->tok) + { + case tok_menuname: + printf("main_menuname "); + break; + case tok_bool: + printf("bool "); + break; + case tok_tristate: + printf("tristate "); + break; + case tok_dep_tristate: + printf("dep_tristate "); + break; + case tok_int: + printf("int "); + break; + case tok_hex: + printf("hex "); + break; + case tok_comment: + printf("comment "); + break; + case tok_menuoption: + printf("menuoption "); + break; + case tok_else: + printf("else"); + break; + case tok_fi: + printf("fi"); + break; + case tok_if: + printf("if"); + break; + default: + } + + switch(cfg->tok) + { + case tok_menuoption: + case tok_comment: + case tok_menuname: + printf("%s\n", cfg->label); + break; + case tok_bool: + case tok_tristate: + case tok_dep_tristate: + case tok_int: + case tok_hex: + printf("%s %s\n", cfg->label, cfg->optionname); + break; + case tok_if: + dump_if(cfg->cond); + break; + case tok_nop: + case tok_endmenu: + break; + default: + printf("\n"); + } + } +#endif + + return 0; + +} diff --git a/scripts/tkparse.h b/scripts/tkparse.h new file mode 100644 index 000000000..911abdfbe --- /dev/null +++ b/scripts/tkparse.h @@ -0,0 +1,82 @@ + +enum token { + tok_menuname, + tok_menuoption, + tok_comment, + tok_bool, + tok_tristate, + tok_dep_tristate, + tok_nop, + tok_if, + tok_else, + tok_fi, + tok_int, + tok_hex, + tok_make, + tok_define, + tok_choose, + tok_choice, + tok_endmenu, + tok_unknown +}; + +enum operator { + op_eq, + op_neq, + op_and, + op_and1, + op_or, + op_bang, + op_lparen, + op_rparen, + op_variable, + op_kvariable, + op_shellcmd, + op_constant, + op_nuked +}; + +union var +{ + char * str; + struct kconfig * cfg; +}; + +struct condition +{ + struct condition * next; + enum operator op; + union var variable; +}; + +#define GLOBAL_WRITTEN 1 +#define CFG_DUP 2 +#define UNSAFE 4 + +struct kconfig +{ + struct kconfig * next; + int flags; + enum token tok; + char menu_number; + char menu_line; + char submenu_start; + char submenu_end; + char * optionname; + char * label; + char * value; + int choice_value; + struct kconfig * choice_label; + union var depend; + struct condition * cond; +}; + +extern struct kconfig * config; +extern struct kconfig * clast; +extern struct kconfig * koption; + +/* + * Prototypes + */ +void fix_conditionals(struct kconfig * scfg); /* tkcond.c */ +void dump_tk_script(struct kconfig *scfg); /* tkgen.c */ |