summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Menuconfig175
-rw-r--r--scripts/checkhelp.pl30
-rw-r--r--scripts/header.tk86
-rw-r--r--scripts/ksymoops.cc411
-rw-r--r--scripts/ksymoops/Makefile79
-rw-r--r--scripts/ksymoops/README395
-rw-r--r--scripts/ksymoops/io.c139
-rw-r--r--scripts/ksymoops/ksymoops.c678
-rw-r--r--scripts/ksymoops/ksymoops.h146
-rw-r--r--scripts/ksymoops/ksyms.c294
-rw-r--r--scripts/ksymoops/map.c251
-rw-r--r--scripts/ksymoops/misc.c108
-rw-r--r--scripts/ksymoops/object.c230
-rw-r--r--scripts/ksymoops/oops.c1200
-rw-r--r--scripts/ksymoops/re.c145
-rw-r--r--scripts/ksymoops/symbol.c444
-rw-r--r--scripts/lxdialog/menubox.c7
-rw-r--r--scripts/mkdep.c34
-rw-r--r--scripts/tail.tk53
-rw-r--r--scripts/tkcond.c753
-rw-r--r--scripts/tkgen.c1759
-rw-r--r--scripts/tkparse.c1158
-rw-r--r--scripts/tkparse.h154
-rw-r--r--scripts/ver_linux32
24 files changed, 6072 insertions, 2689 deletions
diff --git a/scripts/Menuconfig b/scripts/Menuconfig
index d8ca35755..a179e5161 100644
--- a/scripts/Menuconfig
+++ b/scripts/Menuconfig
@@ -47,6 +47,24 @@
#
# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help
# texts.
+#
+# 12 Dec 1998, Michael Elizabeth Chastain (mec@shout.net)
+# Remove a /tmp security hole in get_def (also makes it faster).
+# Give uninitialized variables canonical values rather than null value.
+# Change a lot of places to call set_x_info uniformly.
+# Take out message about preparing version (old sound driver cruft).
+#
+# 13 Dec 1998, Riley H Williams (rhw@bigfoot.com)
+# When an error occurs, actually display the error message as well as
+# our comments thereon.
+#
+# 31 Dec 1998, Michael Elizabeth Chastain (mec@shout.net)
+# Fix mod_bool to honor $CONFIG_MODULES.
+# Fix dep_tristate to call define_bool when dependency is "n".
+#
+# 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+# Blow away lxdialog.scrltmp on entry to activate_menu. This protects
+# against people who use commands like ' ' to select menus.
#
@@ -65,20 +83,24 @@ single_menu_mode=
#
set -h +o posix
-#
-# Converts "# xxx is not..." to xxx=n
-#
-parse_config () {
- sed -e 's/# \(.*\) is not.*/\1=n/'
-}
+
+# Given a configuration variable, set the global variable $x to its value,
+# and the global variable $info to the string " (NEW)" if this is a new
+# variable.
#
-# Parses the defconfig file to set the default for a new parameter.
-#
-function get_def () {
- parse_config < arch/$ARCH/defconfig | grep "^$1=" > /tmp/conf.$$
- . /tmp/conf.$$
- rm /tmp/conf.$$
+# This function looks for: (1) the current value, or (2) the default value
+# from the arch-dependent defconfig file, or (3) a default passed by the caller.
+
+function set_x_info () {
+ eval x=\$$1
+ if [ -z "$x" ]; then
+ eval `sed -n -e 's/# \(.*\) is not set.*/\1=n/' -e "/^$1=/p" arch/$ARCH/defconfig`
+ eval x=\${$1:-"$2"}
+ eval $1=$x
+ eval INFO_$1="' (NEW)'"
+ fi
+ eval info="\$INFO_$1"
}
#
@@ -93,19 +115,6 @@ function get_def () {
load_functions () {
#
-# Macro for setting the x and info varibles. get's default from defconfig
-# file if it's a new parameter.
-#
-function set_x () {
- eval x=\$$1
- if [ -z "$x" ]; then
- get_def "$1"
- eval x=\${$1:-'n'} INFO_$1="' (NEW)'"
- fi
- eval info="\$INFO_$1"
-}
-
-#
# Additional comments
#
function comment () {
@@ -125,7 +134,7 @@ function define_bool () {
# which calls our local bool function.
#
function bool () {
- set_x "$2"
+ set_x_info "$2" "n"
case $x in
y|m) flag="*" ;;
@@ -148,7 +157,7 @@ function tristate () {
then
bool "$1" "$2"
else
- set_x "$2"
+ set_x_info "$2" "n"
case $x in
y) flag="*" ;;
@@ -176,26 +185,22 @@ function tristate () {
# 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
+ if [ "$3" = "y" ]; then
+ tristate "$1" "$2"
+ else if [ "$3" = "m" ]; then
+ mod_bool "$1" "$2"
+ else
+ define_bool "$2" "$n"
+ fi; fi
}
#
# Add a menu item which will call our local int function.
#
function int () {
- eval $2=\${$2:-"$3"} x=\$$2
+ set_x_info "$2" "$3"
- echo -ne "'$2' '($x) $1' " >>MCmenu
+ echo -ne "'$2' '($x) $1$info' " >>MCmenu
echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' ;}" >>MCradiolists
}
@@ -204,9 +209,10 @@ function int () {
# Add a menu item which will call our local hex function.
#
function hex () {
- eval $2=\${$2:-"$3"} x=\${$2##*[x,X]}
+ set_x_info "$2" "$3"
+ x=${x##*[x,X]}
- echo -ne "'$2' '($x) $1' " >>MCmenu
+ echo -ne "'$2' '($x) $1$info' " >>MCmenu
echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' ;}" >>MCradiolists
}
@@ -215,9 +221,9 @@ function hex () {
# Add a menu item which will call our local string function.
#
function string () {
- eval $2=\${$2:-"$3"} x=\$$2
+ set_x_info "$2" "$3"
- echo -ne "'$2' ' $1: \"$x\"' " >>MCmenu
+ echo -ne "'$2' ' $1: \"$x\"$info' " >>MCmenu
echo -e "function $2 () { l_string '$1' '$2' '$3' '$x' ;}" >>MCradiolists
}
@@ -370,16 +376,20 @@ function l_bool () {
# Same as bool() except options are (Module/No)
#
function mod_bool () {
- set_x "$2"
-
- case $x in
- y|m) flag='M' ;;
- *) flag=' ' ;;
- esac
-
- echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
-
- echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
+ if [ "$CONFIG_MODULES" != "y" ]; then
+ define_bool "$2" "n"
+ else
+ set_x_info "$2" "n"
+
+ case $x in
+ y|m) flag='M' ;;
+ *) flag=' ' ;;
+ esac
+
+ echo -ne "'$2' '<$flag> $1$info' " >>MCmenu
+
+ echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
+ fi
}
#
@@ -718,18 +728,25 @@ function parse_config_files () {
# dialog commands or recursively call other menus.
#
function activate_menu () {
+ rm -f lxdialog.scrltmp
while true
do
comment_ctr=0 #So comment lines get unique tags
- $1 "$default" #Create the lxdialog menu & functions
+ $1 "$default" 2> MCerror #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.
+configuration files and is unable to continue. Here is the error
+report:
+
+EOM
+ sed 's/^/ Q> /' MCerror
+ cat <<EOM
Please report this to the maintainer <mec@shout.net>. You may also
send a problem report to <linux-kernel@vger.rutgers.edu>.
@@ -741,6 +758,7 @@ EOM
cleanup
exit 1
fi
+ rm -f MCerror
. ./MCradiolists #Source the menu's functions
@@ -778,6 +796,7 @@ EOM
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
@@ -954,61 +973,41 @@ save_configuration () {
echo -n "Saving your kernel configuration."
#
- # Macro for setting the newval varible. get's default from defconfig
- # file if it's a new parameter and it has not been shown yet.
- #
- function set_newval () {
- eval newval=\$$1
- if [ -z "$newval" ]; then
- get_def "$1"
- eval newval=\${$1:-'n'}
- fi
- }
-
- #
# Now, let's redefine the configuration functions for final
# output to the config files.
#
# Nested function definitions, YIPEE!
#
function bool () {
- set_newval "$2"
- eval define_bool "$2" "$newval"
+ set_x_info "$2" "n"
+ eval define_bool "$2" "$x"
}
function tristate () {
- set_newval "$2"
- eval define_bool "$2" "$newval"
+ set_x_info "$2" "n"
+ eval define_bool "$2" "$x"
}
function dep_tristate () {
- set_newval "$2"
-
- if eval [ "_$3" = "_m" ]
- then
- if [ "$newval" = "y" ]
- then
- newval="m"
- fi
- fi
-
- define_bool "$2" "$newval"
+ set_x_info "$2" "n"
+ if [ "$3" = "m" -a "$x" = "y" ]; then x="m"; fi
+ define_bool "$2" "$x"
}
function int () {
- eval x=\${$2:-"$3"}
+ set_x_info "$2" "$3"
echo "$2=$x" >>$CONFIG
echo "#define $2 ($x)" >>$CONFIG_H
}
function hex () {
- eval x=\${$2:-"$3"}
+ set_x_info "$2" "$3"
echo "$2=$x" >>$CONFIG
echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H
}
function string () {
- eval x=\${$2:-"$3"}
+ set_x_info "$2" "$3"
echo "$2=\"$x\"" >>$CONFIG
echo "#define $2 \"$x\"" >>$CONFIG_H
}
@@ -1250,10 +1249,8 @@ fi
# Fresh new log.
>.menuconfig.log
-echo -n "Preparing configuration scripts: version"
-
# Load the functions used by the config.in files.
-echo -n ", functions"
+echo -n "Preparing scripts: functions"
load_functions
if [ ! -e $CONFIG_IN ]
diff --git a/scripts/checkhelp.pl b/scripts/checkhelp.pl
new file mode 100644
index 000000000..e76523325
--- /dev/null
+++ b/scripts/checkhelp.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+# checkhelp.pl - finds configuration options that have no
+# corresponding section in the help file
+#
+# made by Meelis Roos (mroos@tartu.cyber.ee)
+
+# read the help file
+@options=split /\n/, `grep '^CONFIG' Documentation/Configure.help`;
+die "Can't read Documentation/Configure.help\n" if $#options == -1;
+
+#read all the files
+foreach $file (@ARGV)
+{
+ open (FILE, $file) || die "Can't open $file: $!\n";
+ while (<FILE>) {
+ # repeat until no CONFIG_* are left
+ while (/^\s*(bool|tristate|dep_tristate|string|int|hex).*' *(.*)'.*(CONFIG_\w*)/) {
+ $what=$3;
+ $name=$2;
+ s/$3//;
+ @found = grep (/$what$/, @options);
+ if ($#found == -1) {
+ next if $nohelp{$what};
+ print "$name\n$what\n No help for $what\n\n";
+ $nohelp{$what}=1;
+ }
+ }
+ }
+ close (FILE);
+}
diff --git a/scripts/header.tk b/scripts/header.tk
index 028aede3d..b4f4b5439 100644
--- a/scripts/header.tk
+++ b/scripts/header.tk
@@ -1,3 +1,16 @@
+# FILE: header.tk
+# This file is boilerplate TCL/TK function definitions for 'make xconfig'.
+#
+# CHANGES
+# =======
+#
+# 8 January 1998, Michael Elizabeth Chastain, <mec@shout.net>
+# Remove unused do_cmd function (part of the 2.0 sound support).
+# Arrange buttons in three columns for better screen fitting.
+# Add CONSTANT_Y, CONSTANT_M, CONSTANT_N for commands like:
+# dep_tristate 'foo' CONFIG_FOO m
+#
+
#
# This is a handy replacement for ".widget cget" that requires neither tk4
# nor additional source code uglification.
@@ -20,9 +33,17 @@ proc vfix { var } {
}
#
+# Constant values used by certain dep_tristate commands.
+#
+set CONSTANT_Y 1
+set CONSTANT_M 2
+set CONSTANT_N 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.
@@ -35,50 +56,23 @@ if { [cget .ref -disabledforeground] == "" } {
#
# Define some macros we will need to parse the config.in file.
#
+
proc mainmenu_name { text } {
- message .header.message -width 400 -text "$text"
- pack .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 0 -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.
-#
-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
+ global menus_per_column
+ if { $menu_num <= $menus_per_column } then {
+ set myframe left
+ } elseif { $menu_num <= [expr 2 * $menus_per_column] } then {
+ set myframe middle
+ } else {
+ set myframe right
+ }
+ button .f0.x$menu_num -anchor w -text "$text" \
+ -command "$w .$w \"$text\""
+ pack .f0.x$menu_num -pady 0 -side top -fill x -in .f0.$myframe
}
proc load_configfile { w title func } {
@@ -145,7 +139,7 @@ proc read_config_file { w } {
-relief raised
label $w.bm -bitmap error
pack $w.bm $w.m -pady 10 -side top -padx 10
- wm title $w "Oops"
+ wm title $w "Xconfig Internal Error"
set oldFocus [focus]
frame $w.f
@@ -173,7 +167,7 @@ proc write_config_file { w } {
-relief raised
label $w.bm -bitmap error
pack $w.bm $w.m -pady 10 -side top -padx 10
- wm title $w "Oops"
+ wm title $w "Xconfig Internal Error"
set oldFocus [focus]
frame $w.f
@@ -207,7 +201,7 @@ proc read_config { filename } {
eval $cmd
}
if [regexp {([0-9A-Za-z_]+)="([^"]*)"} $line foo var value] {
- set cmd "global $var; set $var $value"
+ set cmd "global $var; set $var \"$value\""
eval $cmd
}
}
@@ -306,6 +300,10 @@ proc toggle_switch3 {w mnum line text variable} {
option_name $w $mnum $line $text $variable
+ global CONFIG_MODULES
+ if {($CONFIG_MODULES == 0)} then {
+ $w.x$line.m configure -state disabled
+ }
pack $w.x$line.n $w.x$line.m $w.x$line.y -side right -fill y
}
@@ -453,7 +451,9 @@ proc wrapup {w } {
# 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
frame .f0
+frame .f0.left
+frame .f0.middle
+frame .f0.right
diff --git a/scripts/ksymoops.cc b/scripts/ksymoops.cc
deleted file mode 100644
index 13fb4518d..000000000
--- a/scripts/ksymoops.cc
+++ /dev/null
@@ -1,411 +0,0 @@
-// 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
-
-// Update to binutils 2.8 and handling of header text on oops lines by
-// Keith Owens <kaos@ocs.com.au>
-
-//////////////////////////////////////////////////////////////////////////////
-
-// 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.
-
-// Changed by Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
-// adapted to Linux/m68k
-
-//////////////////////////////////////////////////////////////////////////////
-
-// BUGS:
-// * Only resolves operands of jump and call instructions.
-
-#include <fstream.h>
-#include <strstream.h>
-#include <iomanip.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <a.out.h>
-
-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. */
- static struct exec objfile_head = {
- OMAGIC, code_size + 4, 0, 0, sizeof (struct nlist) * 3, 0, 0, 0
- };
- static struct {
- unsigned char tail[4];
- struct nlist syms[3];
- unsigned long strsize;
- char strings[42];
- } objfile_tail = {
-#ifdef i386
- { 0x00, 0x90, 0x90, 0x90 },
-#endif
-#ifdef mc68000
- { 0x00, 0x00, 0x00, 0x00 },
-#endif
- { { (char *) 4, N_TEXT, 0, 0, 0 },
- { (char *) 19, N_TEXT, 0, 0, 0 },
- { (char *) 37, N_TEXT | N_EXT, 0, 0, 0 } },
- 42,
- "gcc2_compiled.\0___gnu_compiled_c\0_EIP\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((char *) &objfile_head, sizeof(objfile_head));
- objfile_stream.write(code, code_size);
- objfile_stream.write((char *) &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;
- int eip_seen = 0;
- long offset;
- while (fgets(buf, sizeof(buf), objdump_FILE)) {
- if (strlen(buf) < 14)
- continue;
- if (eip_seen && buf[4] == ':') {
- // assume objdump from binutils 2.8..., reformat to old style
- offset = strtol(buf, 0, 16);
- char newbuf[sizeof(buf)];
- memset(newbuf, '\0', sizeof(newbuf));
- ostrstream ost(newbuf, sizeof(newbuf));
- ost.width(8);
- ost << hex << offset;
- ost << " <_EIP+" << hex << offset << ">: " << &buf[6] << ends;
- strcpy(buf, newbuf);
- }
- if (!strnequ(&buf[9], "<_EIP", 5))
- continue;
- eip_seen = 1;
- if (strstr(buf, " is out of bounds"))
- break;
- lines++;
- cout << "Code: ";
- if (!valid()) {
- cout << buf;
- continue;
- }
- offset = strtol(buf, 0, 16);
- char* bp_0 = strchr(buf, '>');
- KSym* ksym = find(eip_addr + offset);
- if (bp_0)
- bp_0 += 2;
- else
- bp_0 = strchr(buf, ':');
- if (ksym)
- cout << *ksym << ' ';
- char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8...
- if (bp_1)
- ++bp_1;
- else
- bp_1 = bp_0;
- char *bp = bp_1;
- while (!isspace(*bp))
- bp++;
- while (isspace(*bp))
- bp++;
- if (!isxdigit(*bp)) {
- cout << bp_0;
-#ifdef i386
- } else if (*bp_1 == 'j' || strnequ(bp_1, "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;
-#endif
-#ifdef mc68000
- } else if ((bp_1[0] == 'b' && bp_1[4] == ' ' && strchr("swl", bp_1[3]))
- || (bp_1[0] == 'd' && bp_1[1] == 'b')) {
- // a branch or decr-and-branch insn
- if (bp_1[0] == 'd') // skip register
- while (*bp && *bp++ != ',');
- long rel_addr = strtoul(bp, 0, 16);
- ksym = find(eip_addr + rel_addr);
- if (ksym) {
- *bp++ = '\0';
- cout << bp_0 << *ksym << endl;
- } else
- cout << bp_0;
-#endif
- } 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;
- char *oops_column = NULL;
- 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 (1) {
- long eip_addr;
- cin.get(buffer, sizeof(buffer));
- if (cin.eof())
- break;
- cin.get(c); /* swallow newline */
-#ifdef i386
- if (strstr(buffer, "EIP:") && names.valid()) {
- oops_column = strstr(buffer, "EIP:");
- if (sscanf(oops_column+13, "[<%x>]", &eip_addr) != 1) {
- cout << "Cannot read eip address from EIP: line. Is this a valid oops file?" << endl;
- exit(1);
- }
- cout << ">>EIP: ";
- KSym* ksym = names.find(eip_addr);
- if (ksym)
- cout << *ksym << endl;
- else
- cout << ::hex << eip_addr << " cannot be resolved" << endl;
- }
-#endif
-#ifdef mc68000
- if (strstr(buffer, "PC:") && names.valid()) {
- oops_column = strstr(buffer, "PC:");
- if (sscanf(oops_column+4, "[<%x>]", &eip_addr) != 1) {
- cout << "Cannot read pc address from PC: line. Is this a valid oops file?" << endl;
- exit(1);
- }
- cout << ">>PC: ";
- KSym* ksym = names.find(eip_addr);
- if (ksym)
- cout << *ksym << endl;
- else
- cout << ::hex << eip_addr << " cannot be resolved" << endl;
- }
-#endif
- else if (oops_column && strstr(oops_column, "[<") && names.valid()) {
- unsigned long address;
- while (strstr(oops_column, "[<")) {
- char *p = oops_column;
- while (1) {
- while (*p && *p++ != '[')
- ;
- if (sscanf(p, "<%x>]", &address) != 1)
- break;
- cout << "Trace: ";
- KSym* ksym = names.find(address);
- if (ksym)
- cout << *ksym;
- else
- cout << ::hex << address;
- cout << endl;
- }
- cin.get(buffer, sizeof(buffer));
- if (cin.eof())
- break;
- cin.get(c); /* swallow newline */
- }
- }
- if (oops_column && strnequ(oops_column, "Code:", 5)) {
- unsigned char code[code_size];
- unsigned char* cp = code;
- unsigned char* end = &code[code_size];
- char *p = oops_column + 5;
- int c;
- memset(code, '\0', sizeof(code));
- while (*p && cp < end) {
- while (*p == ' ')
- ++p;
- if (sscanf(p, "%x", &c) != 1)
- break;
-#ifdef mc68000
- *cp++ = c >> 8;
-#endif
- *cp++ = c;
- while (*p && *p++ != ' ')
- ;
- }
- names.decode(code, eip_addr);
- }
- }
- cout << flush;
-
- return 0;
-}
diff --git a/scripts/ksymoops/Makefile b/scripts/ksymoops/Makefile
new file mode 100644
index 000000000..81e80c1e0
--- /dev/null
+++ b/scripts/ksymoops/Makefile
@@ -0,0 +1,79 @@
+# Description file for ksymoops
+
+# Thu Nov 26 16:37:46 EST 1998
+# Version 0.6c
+# Add -c option.
+
+# Tue Nov 3 02:31:01 EST 1998
+# Version 0.6
+# Read lsmod (/proc/modules).
+# Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+# Upper case variables.
+# Convert from a.out to bfd, using same format as ksymoops.
+
+DEFS = Makefile ksymoops.h
+
+# Defaults for vmlinux, ksyms, objects, lsmod, System.map. Externalised so
+# distributions can tweak to suit their own file system layout.
+
+# To default to not reading a source, set to any empty string.
+# To default to reading a source, supply a quoted and escaped string.
+
+# If the string contains *r (*m, *n, *s) then it is replaced at run time by
+# the current value of `uname -r` (-m, -n, -s). '*' was chosen as something
+# that rarely appears in filenames and does not cause problems like '%' or '$'.
+
+DEF_VMLINUX = # default no vmlinux
+DEF_OBJECTS = \"/lib/modules/*r/\" # default current modules
+DEF_KSYMS = \"/proc/ksyms\" # default current ksyms
+DEF_LSMOD = \"/proc/modules\" # default current lsmod
+DEF_MAP = \"/usr/src/linux/System.map\" # default current map
+DEF_CODE_BYTES = 1 # default bytes per code unit
+
+# RedHat users might want defaults like these
+# DEF_MAP = \"/boot/System.map-*r\"
+# DEF_OBJECTS = \"/boot/module-info-*r\"
+
+PROGS = ksymoops
+
+CC=gcc
+CFLAGS = -Dlinux \
+ -Wall \
+ -Wno-conversion \
+ -Waggregate-return \
+ -Wstrict-prototypes \
+ -Wmissing-prototypes \
+ $(DEBUG)
+
+ifneq ($(strip $(DEF_VMLINUX)),)
+ CFLAGS += -DDEF_VMLINUX=$(strip $(DEF_VMLINUX))
+endif
+ifneq ($(strip $(DEF_OBJECTS)),)
+ CFLAGS += -DDEF_OBJECTS=$(strip $(DEF_OBJECTS))
+endif
+ifneq ($(strip $(DEF_KSYMS)),)
+ CFLAGS += -DDEF_KSYMS=$(strip $(DEF_KSYMS))
+endif
+ifneq ($(strip $(DEF_LSMOD)),)
+ CFLAGS += -DDEF_LSMOD=$(strip $(DEF_LSMOD))
+endif
+ifneq ($(strip $(DEF_MAP)),)
+ CFLAGS += -DDEF_MAP=$(strip $(DEF_MAP))
+endif
+
+CFLAGS += -DDEF_CODE_BYTES=$(strip $(DEF_CODE_BYTES))
+
+OBJECTS = io.o ksyms.o ksymoops.o map.o misc.o object.o oops.o re.o symbol.o
+
+all: $(PROGS)
+
+: $(OBJECTS)
+
+$(OBJECTS): $(DEFS)
+
+$(PROGS): %: %.o $(DEFS) $(OBJECTS)
+ $(CC) $(OBJECTS) $(CFLAGS) -lbfd -liberty -o $@
+ -@size $@
+
+clean:
+ rm -f core *.o $(PROGS)
diff --git a/scripts/ksymoops/README b/scripts/ksymoops/README
new file mode 100644
index 000000000..134ef02d9
--- /dev/null
+++ b/scripts/ksymoops/README
@@ -0,0 +1,395 @@
+ ksymoops.
+
+ Read a kernel Oops file and make the best stab at converting the code to
+ instructions and mapping stack values to kernel symbols.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ To compile, simply type "make" in the ksymoops directory.
+
+ TESTERS WANTED.
+
+ ksymoops handles ix86. It appears to handle Alpha, Sparc, M68K, PPC,
+ MIPS but I have no machine to test on. I would appreciate feedback
+ from users of non ix86 machines. In particular, it would be nice if
+ you could run
+
+ ksymoops -VMO -k /proc/ksyms -dd <oops.file >/tmp/ksymoops.log 2>&1
+
+ and mail /tmp/ksymoops.log to kaos@ocs.com.au
+
+ TODO:
+ Clean up these docs.
+ Tweak System.map to include arch information.
+ Tweak modutils to log at least one symbol for each module loaded,
+ otherwise they are invisible to ksymoops. Also arch and version data.
+ Include sparc/sparc64 patches from Jakub Jelinek <jj@sunsite.mff.cuni.cz>.
+ Add object format override for sparc/soparc64 or any cross platform
+ oops debugging.
+
+ Mon Jan 4 09:48:13 EST 1999
+ Version 0.6e
+ Added to kernel.
+ Add ARM support.
+ Typo in oops_code.
+ Add -c option.
+ Add -1 option.
+ Report if options were specified or defaulted.
+ Remove false warnings when comparing ksyms and lsmod.
+ Performance inprovements.
+
+ Wed Oct 28 23:14:55 EST 1998
+ Version 0.5
+ No longer read vmlinux by default, it only duplicates System.map.
+
+ Wed Oct 28 13:46:39 EST 1998
+ Version 0.4
+ Split into separate sources.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3c
+ Add alpha (arm) processing.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3b
+ Add sparc processing.
+ Handle kernel symbol versions.
+
+ Fri Oct 23 13:11:20 EST 1998
+ Version 0.3
+ Add -follow to find command for people who use symlinks to modules.
+ Add Version_ checking.
+
+ Thu Oct 22 22:28:30 EST 1998
+ Version 0.2.
+ Generalise text prefix handling.
+ Handle messages on Code: line.
+ Format addresses with leading zeroes.
+ Minor bug fixes.
+
+ Wed Oct 21 23:28:48 EST 1998
+ Version 0.1. Rewrite from scratch in C.
+
+ CREDITS.
+ Oops disassembly based on ksymoops.cc,
+ Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+ m68k code based on ksymoops.cc changes by
+ Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+
+ This code subsumes the Perl script make_System.map.pl which is no longer
+ supported.
+
+ Why another ksymoops I hear you ask? Various complaints about
+ ksymoops.cc -
+
+ * It requires C++.
+ * It has hard wired limitations on the number of symbols.
+ * It does not handle modules at all.
+ * Very rigid requirements on the format of input, especially the Oops
+ log.
+ * No cross checking between ksyms, modules, System.map etc.
+ * Very little error checking, diagnostics are not suitable for
+ beginners.
+ * It only prints the trace and decoded code, users have to manually
+ extract the other lines from the Oops.
+ * Gives up on the slightest problem.
+ * Only handles i386 and possibly m68k. The code is difficult to extend
+ to other architectures.
+ * Stops after the first Oops, you have to manually extract each one and
+ run through ksymoops one at a time.
+
+ This version is -
+ * C.
+ * No hard wired limitations (malloc as far as the eye can see).
+ * Handles modules by default.
+ * Uses regular pattern matching so it is a lot more forgiving about
+ input formats.
+ * By default, cross checks ksyms, modules, System.map and vmlinux.
+ * Lots of diagnostics and error checking.
+ * Prints all relevant lines for a complete Oops report.
+ * Tries to provide output no matter how bad the input is. The level of
+ progress and error reporting is aimed at beginners.
+ * Handles i386, alpha, sparc, m68k. It is a lot easier to extend to
+ other architectures (patches and/or sample data gratefully accepted).
+ * Handles all Oops in the input file(s).
+
+
+ Usage: ksymoops
+ [-v vmlinux] Where to read vmlinux
+ [-V] No vmlinux is available
+ [-o object_dir] Directory containing modules
+ [-O] No modules is available
+ [-k ksyms] Where to read ksyms
+ [-K] No ksyms is available
+ [-l lsmod] Where to read lsmod
+ [-L] No lsmod is available
+ [-m system.map] Where to read System.map
+ [-M] No System.map is available
+ [-s save.map] Save consolidated map
+ [-c code_bytes] How many bytes in each unit of code
+ [-1] One shot toggle (exit after first Oops)
+ [-d] Increase debug level by 1
+ [-h] Print help text
+ Oops.file Oops to decode
+
+ All flags can occur more than once. With the exception of -o
+ and -d which are cumulative, the last occurrence of each flag is
+ used. Note that "-v my.vmlinux -V" will be taken as "No vmlinux
+ available" but "-V -v my.vmlinux" will read my.vmlinux. You
+ will be warned about such combinations.
+
+ Each occurrence of -d increases the debug level.
+
+ Each -o flag can refer to a directory or to a single object
+ file. If a directory is specified then all *.o files in that
+ directory and its subdirectories are assumed to be modules.
+
+ If any of the vmlinux, object_dir, ksyms or system.map options
+ contain the string *r (*m, *n, *s) then it is replaced at run time
+ by the current value of `uname -r` (-m, -n, -s).
+
+ The defaults can be changed in the Makefile, typical options are
+
+ Defaults: -V
+ -o /lib/modules/%r
+ -k /proc/ksyms
+ -l /proc/modules
+ -m /usr/src/linux/System.map
+ -c 1
+ Oops report is read from stdin
+
+ Note: Unless you tell ksymoops *NOT* to read a particular file, it
+ will try to read and reconcile almost all possible sources of kernel
+ symbol information. This is intended for beginners, they just
+ type
+
+ ksymoops < /var/log/syslog
+
+ no thinking required. Experts can point at different files or
+ suppress the input from selected files. For example, if you
+ save /proc/ksyms before doing a test that creates an Oops, you
+ can point ksymoops at the saved ksyms instead of using
+ /proc/ksyms.
+
+ vmlinux is not read by default, it only duplicates the
+ information in System.map. If you want to read vmlinux as well
+ as or instead of System.map, use -v.
+
+ To get the equivalent of the old ksymoops.cc (no vmlinux, no
+ modules objects, no ksyms, no System.map) just do ksymoops
+ -VOKLM. Or to just read System.map, ksymoops -VOKL -m mapfile.
+
+
+ Return codes: 0 - normal.
+ 1 - error(s) or warning(s) issued, results may not be
+ reliable.
+ 2 - fatal error, no useful results.
+ 3 - One shot mode, end of input reached.
+
+ Supported architectures
+
+ i386 tested.
+ m68k code derived from ksymoops.cc and reading traps.c, untested.
+ MIPS tested.
+ Sparc tested.
+ Alpha tested.
+ ARM tested.
+
+ The term "eip" is generic, for example it includes the i386 EIP
+ and the m68k PC. Remember that objdump output always says EIP,
+ no matter what the architecture, see objfile_head.
+
+ To support another arch, check the Oops_ procedures between
+ 'Start architecture sensitive code' and 'End architecture
+ sensitive code'.
+
+ The pattern matching should take care of different lengths for
+ the address, i.e. addresses should not be arch sensitive. I
+ assume that all addresses are at least 4 characters.
+
+ If nm output has a different format on your arch, check for uses
+ of re_nm.
+
+
+
+ Because ksymoops reads kernel information from multiple sources, there
+ could be mismatches. ksymoops does the following cross checks, but only
+ if the specified files exist -
+
+ * Compare Version_nnn numbers from all sources against each other. Pity
+ that only vmlinux and System.map have these symbols (as at 2.1.125),
+ however I check ksyms, modules and Oops as well. If somebody adds
+ symbol Version_nnn to ksyms or modules or adds a Version_nnn line to
+ the Oops log, this code is ready.
+
+ * Compare kernel ksyms against vmlinux. vmlinux takes precedence.
+
+ * Compare System.map against vmlinux. vmlinux takes precedence.
+
+ * Compare vmlinux against System.map. vmlinux takes precedence.
+
+ * Compare kernel ksyms against System.map. System.map takes precedence.
+
+ * Compare modules against module ksyms. modules take precedence. Only
+ if at least one module appears in ksyms.
+
+ * Compare module names in ksyms against lsmod. Warn if a module
+ appears in lsmod but not in ksyms. Error if a modules appears in
+ ksyms but is not in lsmod. Only if both ksyms and lsmod have being
+ read.
+
+ The precedence order is somewhat arbitrary, however it only applies if
+ there is any difference between the various sources.
+
+ Handling modules is awkward. They can be loaded under different names
+ (insmod -o dummy1 dummy.o) and the text, data and read only data are
+ loaded at different offsets. Although you can give the -m option to
+ insmod which will output the module map when it is loaded, this has a
+ few problems -
+
+ * No equivalent for removing a module. If you load and remove a lot of
+ modules, you end up with multiple sets of symbols around the same
+ offsets, which set is correct?
+
+ * "insmod -o dummy1 dummy.o" still reports as dummy. That is, there is
+ no way of telling which particular version of a multiply loaded
+ module the insmod output refers to. Therefore there is no way of
+ telling which instantiation failed.
+
+ * Even if the above problems are fixed, how do you tell what the module
+ environment looked like when the Oops occurred? What if a module is
+ loaded or removed just after Oops, how is the user expected to edit
+ the insmod log? Rule 1 - make ksymoops easy for beginners.
+
+ Although those problems could be fixed, they require changes to
+ modutils. Working from ksyms and the module objects can be done without
+ changing modutils and without confusing beginners.
+
+ Alas the ksyms plus object approach has another problem - matching ksyms
+ to module objects. Nowhere does the kernel say that module dummy1 came
+ from module /lib/modules/2.1.215/net/dummy.o, ksyms just says dummy1. I
+ have to match ksyms to the relevant object by finding a globally unique
+ external symbol in each module that can be used to map to the external
+ symbols in ksyms. This assumes that each module exports at least one
+ text symbol that is unique amongst all modules.
+
+ It may not be possible to correctly map other sections such as data and
+ readonly data for modules because they may not have exported symbols.
+ Since the main aim of ksymoops is to map a code Oops, this should not be
+ a problem.
+
+ Unfortunately some modules export no symbols. They are marked as
+ EXPORT_NO_SYMBOLS are simply do not export anything. It is
+ impossible to detect these in ksyms because, by definition, ksyms
+ only contains exported symbols for modules. Since all modules appear
+ in lsmod (/proc/modules), a cross check of lsmod against the module
+ names will find loaded modules with no symbols, at least I can warn
+ about these.
+
+ After merging the various sources, ksymoops has a (hopefully) accurate
+ map including modules. The -s option lets you save the merged
+ System.map, but remember that module data and readonly data sections may
+ not be correctly relocated, see above.
+
+ Environment Variables.
+ KSYMOOPS_NM path for nm, defaults to /usr/bin/nm.
+ KSYMOOPS_FIND path for find, defaults to /usr/bin/find.
+ KSYMOOPS_OBJDUMP path for objdump, defaults to /usr/bin/objdump.
+
+
+ Input Oops data.
+
+ The ideal input is to feed the syslog straight into this program. If
+ you cannot do that, you need to know what the program looks for.
+ Especially if you are typing in the Oops by hand :(. All input is case
+ insensitive.
+
+ * White space in this context means space or tab. It does not include
+ newline.
+
+ * Oops in syslog has a syslog prefix. Leading text up to and including
+ ' kernel: ' is always ignored, there is no need to edit syslog first.
+ This leading text need not exist but if it does, it must end in
+ ' kernel: '.
+
+ * An alternative prefix is <n> where n is the kernel print level. Also
+ ignored if present.
+
+ * Leading white space is treated as a prefix and ignored, the input is
+ not indentation sensitive.
+
+ * In the following paragraphs, assume that any prefixes have been
+ skipped. If there is more than one prefix, all are skipped, no matter
+ which order they appear in.
+
+ * A bracketed address is optional '[', required '<', at least 4 hex
+ digits, required '>', optional ']'. For example [<01234567>] or
+ <1234>.
+
+ * The ix86 EIP line is identified by optional white space followed by
+ 'EIP:', followed by a least one white space, followed by a bracketed
+ address.
+
+ * The m68k PC line is identified by optional white space followed by
+ 'PC', optionally followed by white space, followed by '=', optionally
+ followed by white space, followed by a bracketed address.
+
+ * The sparc PC line starts with PSR and PC is the second hex value, not
+ bracketed.
+
+ * A call trace line is identified by 'Call Trace:' followed by at least
+ one white space. Or it is a line starting with a bracketed address,
+ but only if the previous line was a call trace line (I hate multi line
+ output that relies on identation for recognition, especially when
+ lines can have a variable prefix).
+
+ * The Code line is identified by 'Code:' followed by a least one white
+ space character followed by at least one hex value. The line can
+ contain multiple hex values, each separated by at least one white
+ space. Each hex value must be 2 to 8 digits and must be a multiple of
+ 2 digits.
+
+ On some architectures the Code: data is a stream of single bytes,
+ in machine order. On other architectures, it is a stream of shorts
+ or ints in human readable order which does not always match the
+ machine order, endianess raises its ugly head. We are consistently
+ inconsistent.
+
+ To cater for these architecture inconsistencies, use the -c option.
+ If the Code: line is already in machine order, use -c 1. If the
+ Code: data is a stream of shorts or ints which do not match the
+ machine order, use -c 2 or -c 4. Each set of 'c' bytes are swapped
+ to (hopefully) reflect the machine order.
+
+ Special cases where Code: can be followed by text.
+ 'Code: general protection'
+ 'Code: <n>'
+ Dump the data anyway, the code was unavailable.
+
+ * Formatted data is only output when the Code: line is seen. If any
+ data has been stored and more than 5 lines other than Oops text (see
+ Oops_print) or end of file are encountered then ksymoops assumes that
+ the Code: line is missing or garbled and dumps the formatted data
+ anyway. Fail safe, I hope.
+
+ * By default, ksymoops reads its entire input file. If the -1 toggle
+ is set, it will run in one shot mode and exit after the first Oops.
+ This is useful for automatically mailing reports as they happen,
+ like this :-
+
+ #!/bin/sh
+ # ksymoops1
+ while (true)
+ do
+ ksymoops -1 > $HOME/oops1
+ if [ $? -eq 3 ]
+ then
+ exit 0
+ fi
+ mail -s Oops admin < $HOME/oops1
+ done
+
+ tail -f /var/log/messages | ksymoops1
+
+ Restarting after log rotation is left as an exercise for the reader.
diff --git a/scripts/ksymoops/io.c b/scripts/ksymoops/io.c
new file mode 100644
index 000000000..b54e8ad90
--- /dev/null
+++ b/scripts/ksymoops/io.c
@@ -0,0 +1,139 @@
+/*
+ io.c.
+
+ Local I/O routines for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ fwrite_local is redundant, replaced by bfd.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+int regular_file(const char *file, const char *msg)
+{
+ struct stat statbuf;
+ if (stat(file, &statbuf)) {
+ fprintf(stderr, "%s: %s stat %s failed",
+ prefix, msg, file);
+ perror(" ");
+ ++errors;
+ return 0;
+ }
+
+ if (!S_ISREG(statbuf.st_mode)) {
+ fprintf(stderr,
+ "%s: %s %s is not a regular file, ignored\n",
+ prefix, msg, file);
+ ++errors;
+ return 0;
+ }
+ return 1;
+}
+
+FILE *fopen_local(const char *file, const char *mode, const char *msg)
+{
+ FILE *f;
+ if (!(f = fopen(file, mode))) {
+ fprintf(stderr, "%s: %s fopen '%s' failed",
+ prefix, msg, file);
+ perror(" ");
+ ++errors;
+ }
+ return f;
+}
+
+void fclose_local(FILE *f, const char *msg)
+{
+ int i;
+ if ((i = fclose(f))) {
+ fprintf(stderr, "%s: %s fclose failed %d", prefix, msg, i);
+ perror(" ");
+ ++errors;
+ }
+}
+
+/* Read a line, increasing the size of the line as necessary until \n is read */
+#define INCREMENT 10 /* arbitrary */
+char *fgets_local(char **line, int *size, FILE *f, const char *msg)
+{
+ char *l, *p, *r;
+ int longline = 1;
+
+ if (!*line) {
+ *size = INCREMENT;
+ *line = malloc(*size);
+ if (!*line)
+ malloc_error("fgets_local alloc line");
+ }
+
+ l = *line;
+ while (longline) {
+ r = fgets(l, *size-(l-*line), f);
+ if (!r) {
+ if (ferror(f)) {
+ fprintf(stderr,
+ "%s: %s fgets failed", prefix, msg);
+ perror(" ");
+ ++errors;
+ }
+ if (l != *line)
+ return(*line);
+ else
+ return(r);
+ }
+ if (!(p = strchr(*line, '\n'))) {
+ *size += INCREMENT;
+ *line = realloc(*line, *size);
+ if (!*line)
+ malloc_error("fgets_local realloc line");
+ l = *line+*size-INCREMENT-1;
+ }
+ else {
+ *p = '\0';
+ longline = 0;
+ }
+ }
+
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s line '%s'\n", msg, *line);
+ return(*line);
+}
+
+FILE *popen_local(const char *cmd, const char *msg)
+{
+ FILE *f;
+ if (!(f = popen(cmd, "r"))) {
+ fprintf(stderr, "%s: %s popen '%s' failed",
+ prefix, msg, cmd);
+ perror(" ");
+ ++errors;
+ }
+ return f;
+}
+
+void pclose_local(FILE *f, const char *msg)
+{
+ int i;
+ errno = 0;
+ if ((i = pclose(f))) {
+ fprintf(stderr, "%s: %s pclose failed 0x%x", prefix, msg, i);
+ if (errno)
+ perror(" ");
+ else
+ fprintf(stderr, "\n");
+ ++errors;
+ }
+}
diff --git a/scripts/ksymoops/ksymoops.c b/scripts/ksymoops/ksymoops.c
new file mode 100644
index 000000000..0cd65795b
--- /dev/null
+++ b/scripts/ksymoops/ksymoops.c
@@ -0,0 +1,678 @@
+/*
+ ksymoops.c.
+
+ Read a kernel Oops file and make the best stab at converting the code to
+ instructions and mapping stack values to kernel symbols.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+*/
+
+#define VERSION "0.6e"
+
+/*
+
+ Tue Jan 5 19:26:02 EST 1999
+ Version 0.6e
+ Added to kernel.
+
+ Mon Jan 4 09:48:13 EST 1999
+ Version 0.6d
+ Add ARM support.
+
+ Thu Nov 26 16:37:46 EST 1998
+ Version 0.6c
+ Typo in oops_code.
+ Add -c option.
+ Add -1 option.
+ Report if options were specified or defaulted.
+
+ Fri Nov 6 10:38:42 EST 1998
+ Version 0.6b
+ Remove false warnings when comparing ksyms and lsmod.
+
+ Tue Nov 3 23:33:04 EST 1998
+ Version 0.6a
+ Performance inprovements.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Read lsmod (/proc/modules).
+ Ignore addresses 0-4095 when mapping address to symbol.
+ Discard default objects if -o specified.
+ Oops file must be regular.
+ Add "invalid operand" to Oops_print.
+ Move "Using_Version" copy to map.c.
+ Add Makefile defaults for vmlinux, ksyms, objects, System.map, lsmod.
+ Minor adjustment to re for ppc.
+ Minor adjustment to re for objdump lines with <_EIP+xxx>.
+ Convert from a.out to bfd, using same format as ksymoops.
+ Added MIPS.
+ PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+ Wed Oct 28 23:14:55 EST 1998
+ Version 0.5
+ No longer read vmlinux by default, it only duplicates System.map.
+
+ Wed Oct 28 13:47:38 EST 1998
+ Version 0.4
+ Split into separate sources.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3c
+ Add alpha (arm) processing.
+
+ Mon Oct 26 00:01:47 EST 1998
+ Version 0.3b
+ Add sparc processing.
+ Handle kernel symbol versions.
+
+ Fri Oct 23 13:11:20 EST 1998
+ Version 0.3
+ Add -follow to find command for people who use symlinks to modules.
+ Add Version_ checking.
+
+ Thu Oct 22 22:28:30 EST 1998
+ Version 0.2.
+ Generalise text prefix handling.
+ Handle messages on Code: line.
+ Format addresses with leading zeroes.
+ Minor bug fixes.
+
+ Wed Oct 21 23:28:48 EST 1998
+ Version 0.1. Rewrite from scratch in C.
+
+ CREDITS.
+ Oops disassembly based on ksymoops.cc,
+ Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+ m68k code based on ksymoops.cc changes by
+ Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+ */
+
+#include "ksymoops.h"
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+char *prefix;
+char *path_nm = "/usr/bin/nm"; /* env KSYMOOPS_NM */
+char *path_find = "/usr/bin/find"; /* env KSYMOOPS_FIND */
+char *path_objdump = "/usr/bin/objdump"; /* env KSYMOOPS_OBJDUMP */
+int debug = 0;
+int errors = 0;
+int warnings = 0;
+
+SYMBOL_SET ss_vmlinux;
+SYMBOL_SET ss_ksyms_base;
+SYMBOL_SET *ss_ksyms_module;
+int ss_ksyms_modules;
+SYMBOL_SET ss_lsmod;
+SYMBOL_SET *ss_object;
+int ss_objects;
+SYMBOL_SET ss_system_map;
+
+SYMBOL_SET ss_merged; /* merged map with info from all sources */
+SYMBOL_SET ss_Version; /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+regex_t re_nm;
+regmatch_t *re_nm_pmatch;
+regex_t re_bracketed_address;
+regmatch_t *re_bracketed_address_pmatch;
+regex_t re_unbracketed_address;
+regmatch_t *re_unbracketed_address_pmatch;
+
+static void usage(void)
+{
+ fprintf(stderr, "Version " VERSION "\n");
+ fprintf(stderr, "usage: %s\n", prefix);
+ fprintf(stderr,
+ "\t\t[-v vmlinux]\tWhere to read vmlinux\n"
+ "\t\t[-V]\t\tNo vmlinux is available\n"
+ "\t\t[-o object_dir]\tDirectory containing modules\n"
+ "\t\t[-O]\t\tNo modules is available\n"
+ "\t\t[-k ksyms]\tWhere to read ksyms\n"
+ "\t\t[-K]\t\tNo ksyms is available\n"
+ "\t\t[-l lsmod]\tWhere to read lsmod\n"
+ "\t\t[-L]\t\tNo lsmod is available\n"
+ "\t\t[-m system.map]\tWhere to read System.map\n"
+ "\t\t[-M]\t\tNo System.map is available\n"
+ "\t\t[-s save.map]\tSave consolidated map\n"
+ "\t\t[-d]\t\tIncrease debug level by 1\n"
+ "\t\t[-h]\t\tPrint help text\n"
+ "\t\t[-c code_bytes]\tHow many bytes in each unit of code\n"
+ "\t\t[-1]\t\tOne shot toggle (exit after first Oops)\n"
+ "\t\t<Oops.file\tOops report to decode\n"
+ "\n"
+ "\t\tAll flags can occur more than once. With the exception "
+ "of -o\n"
+ "\t\tand -d which are cumulative, the last occurrence of each "
+ "flag is\n"
+ "\t\tused. Note that \"-v my.vmlinux -V\" will be taken as "
+ "\"No vmlinux\n"
+ "\t\tavailable\" but \"-V -v my.vmlinux\" will read "
+ "my.vmlinux. You\n"
+ "\t\twill be warned about such combinations.\n"
+ "\n"
+ "\t\tEach occurrence of -d increases the debug level.\n"
+ "\n"
+ "\t\tEach -o flag can refer to a directory or to a single "
+ "object\n"
+ "\t\tfile. If a directory is specified then all *.o files in "
+ "that\n"
+ "\t\tdirectory and its subdirectories are assumed to be "
+ "modules.\n"
+ "\n"
+ "\t\tIf any of the vmlinux, object_dir, ksyms or system.map "
+ "options\n"
+ "\t\tcontain the string *r (*m, *n, *s) then it is replaced "
+ "at run\n"
+ "\t\ttime by the current value of `uname -r` (-m, -n, -s).\n"
+ "\n"
+ "\t\tThe defaults can be changed in the Makefile, current "
+ "defaults\n"
+ "\t\tare\n\n"
+ "\t\t\t"
+#ifdef DEF_VMLINUX
+ "-v " DEF_LINUX
+#else
+ "-V"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_OBJECTS
+ "-o " DEF_OBJECTS
+#else
+ "-O"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_KSYMS
+ "-k " DEF_KSYMS
+#else
+ "-K"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_LSMOD
+ "-l " DEF_LSMOD
+#else
+ "-L"
+#endif
+ "\n"
+ "\t\t\t"
+#ifdef DEF_MAP
+ "-m " DEF_MAP
+#else
+ "-M"
+#endif
+ "\n"
+ "\t\t\t-c %d\n" /* DEF_CODE_BYTES */
+ "\t\t\tOops report is read from stdin\n"
+ "\n",
+ DEF_CODE_BYTES
+ );
+}
+
+/* Check if possibly conflicting options were specified */
+static void multi_opt(int specl, int specu, char type, const char *using)
+{
+ if (specl && specu) {
+ fprintf(stderr,
+ "Warning - you specified both -%c and -%c. Using '",
+ type, toupper(type));
+ ++warnings;
+ if (using) {
+ fprintf(stderr, "-%c %s", type, using);
+ if (type == 'o')
+ fprintf(stderr, " ...");
+ fprintf(stderr, "'\n");
+ }
+ else
+ fprintf(stderr, "-%c'\n", toupper(type));
+ }
+ else if (specl > 1 && type != 'o') {
+ fprintf(stderr,
+ "Warning - you specified -%c more than once. "
+ "Using '-%c %s'\n",
+ type, type, using);
+ ++warnings;
+ }
+ else if (specu > 1) {
+ fprintf(stderr,
+ "Warning - you specified -%c more than once. "
+ "Second and subsequent '-%c' ignored\n",
+ toupper(type), toupper(type));
+ ++warnings;
+ }
+}
+
+/* If a name contains *r (*m, *n, *s), replace with the current value of
+ * `uname -r` (-m, -n, -s). Actually uses uname system call rather than the
+ * uname command but the result is the same.
+ */
+static void convert_uname(char **name)
+{
+ char *p, *newname, *oldname, *replacement;
+ unsigned len;
+ int free_oldname = 0;
+ static char procname[] = "convert_uname";
+
+ if (!*name)
+ return;
+
+ while ((p = strchr(*name, '*'))) {
+ struct utsname buf;
+ int i = uname(&buf);
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s in\n", procname, *name);
+ if (i) {
+ fprintf(stderr,
+ "%s: uname failed, %s will not be processed\n",
+ prefix, *name);
+ perror(prefix);
+ ++errors;
+ return;
+ }
+ switch (*(p+1)) {
+ case 'r':
+ replacement = buf.release;
+ break;
+ case 'm':
+ replacement = buf.machine;
+ break;
+ case 'n':
+ replacement = buf.nodename;
+ break;
+ case 's':
+ replacement = buf.sysname;
+ break;
+ default:
+ fprintf(stderr,
+ "%s: invalid replacement character '*%c' "
+ "in %s\n",
+ prefix, *(p+1), *name);
+ ++errors;
+ return;
+ }
+ len = strlen(*name)-2+strlen(replacement)+1;
+ if (!(newname = malloc(len)))
+ malloc_error(procname);
+ strncpy(newname, *name, (p-*name));
+ strcpy(newname+(p-*name), replacement);
+ strcpy(newname+(p-*name)+strlen(replacement), p+2);
+ p = newname+(p-*name)+strlen(replacement); /* no rescan */
+ oldname = *name;
+ *name = newname;
+ if (free_oldname)
+ free(oldname);
+ free_oldname = 1;
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s out\n", procname, *name);
+ }
+ return;
+}
+
+/* Report if the option was specified or defaulted */
+static void spec_or_default(int spec, int *some_spec) {
+ if (spec) {
+ printf(" (specified)\n");
+ if (some_spec)
+ *some_spec = 1;
+ }
+ else
+ printf(" (default)\n");
+}
+
+/* Parse the options. Verbose but what's new with getopt? */
+static void parse(int argc,
+ char **argv,
+ char **vmlinux,
+ char ***object,
+ int *objects,
+ char **ksyms,
+ char **lsmod,
+ char **system_map,
+ char **save_system_map,
+ char ***filename,
+ int *filecount,
+ int *spec_h,
+ int *code_bytes,
+ int *one_shot
+ )
+{
+ int spec_v = 0, spec_V = 0;
+ int spec_o = 0, spec_O = 0;
+ int spec_k = 0, spec_K = 0;
+ int spec_l = 0, spec_L = 0;
+ int spec_m = 0, spec_M = 0;
+ int spec_s = 0;
+ int spec_c = 0;
+
+ int c, i, some_spec = 0;
+ char *p;
+
+ while ((c = getopt(argc, argv, "v:Vo:Ok:Kl:Lm:Ms:dhc:1")) != EOF) {
+ if (debug && c != 'd')
+ fprintf(stderr, "DEBUG: getopt '%c' '%s'\n", c, optarg);
+ switch(c) {
+ case 'v':
+ *vmlinux = optarg;
+ ++spec_v;
+ break;
+ case 'V':
+ *vmlinux = NULL;
+ ++spec_V;
+ break;
+ case 'o':
+ if (!spec_o) {
+ /* First -o, discard default value(s) */
+ for (i = 0; i < *objects; ++i)
+ free((*object)[i]);
+ free(*object);
+ *object = NULL;
+ *objects = 0;
+ }
+ *object = realloc(*object,
+ ((*objects)+1)*sizeof(**object));
+ if (!*object)
+ malloc_error("object");
+ if (!(p = strdup(optarg)))
+ malloc_error("strdup -o");
+ else {
+ (*object)[(*objects)++] = p;
+ ++spec_o;
+ }
+ break;
+ case 'O':
+ ++spec_O;
+ for (i = 0; i < *objects; ++i)
+ free((*object)[i]);
+ free(*object);
+ *object = NULL;
+ *objects = 0;
+ break;
+ case 'k':
+ *ksyms = optarg;
+ ++spec_k;
+ break;
+ case 'K':
+ *ksyms = NULL;
+ ++spec_K;
+ break;
+ case 'l':
+ *lsmod = optarg;
+ ++spec_l;
+ break;
+ case 'L':
+ *lsmod = NULL;
+ ++spec_L;
+ break;
+ case 'm':
+ *system_map = optarg;
+ ++spec_m;
+ break;
+ case 'M':
+ *system_map = NULL;
+ ++spec_M;
+ break;
+ case 's':
+ *save_system_map = optarg;
+ ++spec_s;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'h':
+ usage();
+ ++*spec_h;
+ break;
+ case 'c':
+ ++spec_c;
+ errno = 0;
+ *code_bytes = strtoul(optarg, &p, 10);
+ /* Oops_code_values assumes that code_bytes is a
+ * multiple of 2.
+ */
+ if (!*optarg || *p || errno ||
+ (*code_bytes != 1 &&
+ *code_bytes != 2 &&
+ *code_bytes != 4 &&
+ *code_bytes != 8)) {
+ fprintf(stderr,
+ "%s Invalid value for -c '%s'\n",
+ prefix, optarg);
+ ++errors;
+ if (errno)
+ perror(" ");
+ *code_bytes = DEF_CODE_BYTES;
+ }
+ break;
+ case '1':
+ *one_shot = !*one_shot;
+ break;
+ case '?':
+ usage();
+ exit(2);
+ }
+ }
+
+ *filecount = argc - optind;
+ *filename = argv + optind;
+
+ /* Expand any requests for the current uname values */
+ convert_uname(vmlinux);
+ if (*objects) {
+ for (i = 0; i < *objects; ++i)
+ convert_uname(*object+i);
+ }
+ convert_uname(ksyms);
+ convert_uname(lsmod);
+ convert_uname(system_map);
+
+ /* Check for multiple options specified */
+ multi_opt(spec_v, spec_V, 'v', *vmlinux);
+ multi_opt(spec_o, spec_O, 'o', *object ? **object : NULL);
+ multi_opt(spec_k, spec_K, 'k', *ksyms);
+ multi_opt(spec_l, spec_L, 'l', *lsmod);
+ multi_opt(spec_m, spec_M, 'm', *system_map);
+
+ printf("Options used:");
+ if (*vmlinux)
+ printf(" -v %s", *vmlinux);
+ else
+ printf(" -V");
+ spec_or_default(spec_v || spec_V, &some_spec);
+
+ printf(" ");
+ if (*objects) {
+ for (i = 0; i < *objects; ++i)
+ printf(" -o %s", (*object)[i]);
+ }
+ else
+ printf(" -O");
+ spec_or_default(spec_o || spec_O, &some_spec);
+
+ printf(" ");
+ if (*ksyms)
+ printf(" -k %s", *ksyms);
+ else
+ printf(" -K");
+ spec_or_default(spec_k || spec_K, &some_spec);
+
+ printf(" ");
+ if (*lsmod)
+ printf(" -l %s", *lsmod);
+ else
+ printf(" -L");
+ spec_or_default(spec_l || spec_L, &some_spec);
+
+ printf(" ");
+ if (*system_map)
+ printf(" -m %s", *system_map);
+ else
+ printf(" -M");
+ spec_or_default(spec_m || spec_M, &some_spec);
+
+ printf(" ");
+ printf(" -c %d", *code_bytes);
+ spec_or_default(spec_c, NULL);
+
+ if (*one_shot) {
+ printf(" ");
+ printf(" -1");
+ }
+
+ printf("\n");
+
+ if (!some_spec) {
+ printf(
+"You did not tell me where to find symbol information. I will assume\n"
+"that the log matches the kernel and modules that are running right now\n"
+"and I'll use the default options above for symbol resolution.\n"
+"If the current kernel and/or modules do not match the log, you can get\n"
+"more accurate output by telling me the kernel version and where to find\n"
+"map, modules, ksyms etc. ksymoops -h explains the options.\n"
+ "\n");
+ ++warnings;
+ }
+}
+
+/* Read environment variables */
+static void read_env(const char *external, char **internal)
+{
+ char *p;
+ if ((p = getenv(external))) {
+ *internal = p;
+ if (debug)
+ fprintf(stderr,
+ "DEBUG: env override %s=%s\n",
+ external, *internal);
+ }
+ else {
+ if (debug)
+ fprintf(stderr,
+ "DEBUG: env default %s=%s\n",
+ external, *internal);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ char *vmlinux = NULL;
+ char **object = NULL;
+ int objects = 0;
+ char *ksyms = NULL;
+ char *lsmod = NULL;
+ char *system_map = NULL;
+ char *save_system_map = NULL;
+ char **filename;
+ int filecount = 0;
+ int spec_h = 0; /* -h was specified */
+ int code_bytes = DEF_CODE_BYTES;
+ int one_shot = 0;
+ int i, ret;
+
+ prefix = *argv;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifdef DEF_VMLINUX
+ vmlinux = DEF_LINUX;
+#endif
+#ifdef DEF_OBJECTS
+ {
+ char *p;
+ object = realloc(object, (objects+1)*sizeof(*object));
+ if (!object)
+ malloc_error("DEF_OBJECTS");
+ if (!(p = strdup(DEF_OBJECTS)))
+ malloc_error("DEF_OBJECTS");
+ else
+ object[objects++] = p;
+ }
+#endif
+#ifdef DEF_KSYMS
+ ksyms = DEF_KSYMS;
+#endif
+#ifdef DEF_LSMOD
+ lsmod = DEF_LSMOD;
+#endif
+#ifdef DEF_MAP
+ system_map = DEF_MAP;
+#endif
+
+ parse(argc,
+ argv,
+ &vmlinux,
+ &object,
+ &objects,
+ &ksyms,
+ &lsmod,
+ &system_map,
+ &save_system_map,
+ &filename,
+ &filecount,
+ &spec_h,
+ &code_bytes,
+ &one_shot
+ );
+
+ if (spec_h && filecount == 0)
+ return(0); /* just the help text */
+
+ if (errors)
+ return(1);
+
+ if (debug)
+ fprintf(stderr, "DEBUG: level %d\n", debug);
+
+ read_env("KSYMOOPS_NM", &path_nm);
+ read_env("KSYMOOPS_FIND", &path_find);
+ read_env("KSYMOOPS_OBJDUMP", &path_objdump);
+
+ re_compile_common();
+ ss_init_common();
+
+ read_vmlinux(vmlinux);
+ read_ksyms(ksyms);
+ /* No point in reading modules unless ksyms shows modules loaded */
+ if (ss_ksyms_modules) {
+ expand_objects(object, objects);
+ for (i = 0; i < ss_objects; ++i)
+ read_object(ss_object[i].source, i);
+ }
+ else if (objects)
+ printf("No modules in ksyms, skipping objects\n");
+ /* No point in reading lsmod without ksyms */
+ if (ss_ksyms_modules || ss_ksyms_base.used)
+ read_lsmod(lsmod);
+ else if (lsmod)
+ printf("No ksyms, skipping lsmod\n");
+ read_system_map(system_map);
+ merge_maps(save_system_map);
+
+ /* After all that work, it is finally time to read the Oops report */
+ ret = Oops_read(filecount, filename, code_bytes, one_shot);
+
+ if (warnings || errors) {
+ printf("\n");
+ if (warnings)
+ printf("%d warning%s ",
+ warnings, warnings == 1 ? "" : "s");
+ if (warnings && errors)
+ printf("and ");
+ if (errors)
+ printf("%d error%s ", errors, errors == 1 ? "" : "s");
+ printf("issued. Results may not be reliable.\n");
+ if (!ret)
+ return(1);
+ }
+
+ return(ret);
+}
diff --git a/scripts/ksymoops/ksymoops.h b/scripts/ksymoops/ksymoops.h
new file mode 100644
index 000000000..fb3e99d8f
--- /dev/null
+++ b/scripts/ksymoops/ksymoops.h
@@ -0,0 +1,146 @@
+/*
+ ksymoops.h.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Read lsmod (/proc/modules).
+ Convert from a.out to bfd, using same format as ksymoops.
+ PPC trace addresses are not bracketed, add new re.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+*/
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+
+
+/* Pity this is not externalised, see binfmt_elf.c */
+#define elf_addr_t unsigned long
+
+extern char *prefix;
+extern char *path_nm; /* env KSYMOOPS_NM */
+extern char *path_find; /* env KSYMOOPS_FIND */
+extern char *path_objdump; /* env KSYMOOPS_OBJDUMP */
+extern int debug;
+extern int errors;
+extern int warnings;
+
+typedef struct symbol SYMBOL;
+
+struct symbol {
+ char *name; /* name of symbol */
+ char type; /* type of symbol from nm/System.map */
+ char keep; /* keep this symbol in merged map? */
+ elf_addr_t address; /* address in kernel */
+};
+
+/* Header for symbols from one particular source */
+
+typedef struct symbol_set SYMBOL_SET;
+
+struct symbol_set {
+ char *source; /* where the symbols came from */
+ int used; /* number of symbols used */
+ int alloc; /* number of symbols allocated */
+ SYMBOL *symbol; /* dynamic array of symbols */
+ SYMBOL_SET *related; /* any related symbol set */
+};
+
+extern SYMBOL_SET ss_vmlinux;
+extern SYMBOL_SET ss_ksyms_base;
+extern SYMBOL_SET *ss_ksyms_module;
+extern int ss_ksyms_modules;
+extern SYMBOL_SET ss_lsmod;
+extern SYMBOL_SET *ss_object;
+extern int ss_objects;
+extern SYMBOL_SET ss_system_map;
+
+extern SYMBOL_SET ss_merged; /* merged map with info from all sources */
+extern SYMBOL_SET ss_Version; /* Version_ numbers where available */
+
+/* Regular expression stuff */
+
+extern regex_t re_nm;
+extern regmatch_t *re_nm_pmatch;
+extern regex_t re_bracketed_address;
+extern regmatch_t *re_bracketed_address_pmatch;
+extern regex_t re_unbracketed_address;
+extern regmatch_t *re_unbracketed_address_pmatch;
+
+/* Bracketed address: optional '[', required '<', at least 4 hex characters,
+ * required '>', optional ']', optional white space.
+ */
+#define BRACKETED_ADDRESS "\\[*<([0-9a-fA-F]{4,})>\\]*[ \t]*"
+
+#define UNBRACKETED_ADDRESS "([0-9a-fA-F]{4,})[ \t]*"
+
+/* io.c */
+extern int regular_file(const char *file, const char *msg);
+extern FILE *fopen_local(const char *file, const char *mode, const char *msg);
+extern void fclose_local(FILE *f, const char *msg);
+extern char *fgets_local(char **line, int *size, FILE *f, const char *msg);
+extern int fwrite_local(void const *ptr, size_t size, size_t nmemb,
+ FILE *stream, const char *msg);
+extern FILE *popen_local(const char *cmd, const char *msg);
+extern void pclose_local(FILE *f, const char *msg);
+
+/* ksyms.c */
+extern void read_ksyms(const char *ksyms);
+extern void map_ksyms_to_modules(void);
+extern void read_lsmod(const char *lsmod);
+extern void compare_ksyms_lsmod(void);
+
+/* misc.c */
+extern void malloc_error(const char *msg);
+extern const char *format_address(elf_addr_t address);
+extern char *find_fullpath(const char *program);
+
+/* map.c */
+extern void read_system_map(const char *system_map);
+extern void merge_maps(const char *save_system_map);
+extern void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+ int precedence);
+
+
+/* object.c */
+extern SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss);
+extern void read_vmlinux(const char *vmlinux);
+extern void expand_objects(char * const *object, int objects);
+extern void read_object(const char *object, int i);
+
+/* oops.c */
+extern int Oops_read(int filecount, char * const *filename, int code_bytes,
+ int one_shot);
+
+/* re.c */
+extern void re_compile(regex_t *preg, const char *regex, int cflags,
+ regmatch_t **pmatch);
+extern void re_compile_common(void);
+extern void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+ char ***string);
+extern void re_strings_free(const regex_t *preg, char ***string);
+extern void re_string_check(int need, int available, const char *msg);
+
+/* symbol.c */
+extern void ss_init(SYMBOL_SET *ss, const char *msg);
+extern void ss_free(SYMBOL_SET *ss);
+extern void ss_init_common(void);
+extern SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol,
+ int *start);
+extern void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+ const char type, const char keep, const char *symbol);
+extern void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+ const char keep, const char *symbol);
+extern char *map_address(const SYMBOL_SET *ss, const elf_addr_t address);
+extern void ss_sort_atn(SYMBOL_SET *ss);
+extern void ss_sort_na(SYMBOL_SET *ss);
+extern SYMBOL_SET *ss_copy(const SYMBOL_SET *ss);
+extern void add_Version(const char *version, const char *source);
+extern void extract_Version(SYMBOL_SET *ss);
+extern void compare_Version(void);
diff --git a/scripts/ksymoops/ksyms.c b/scripts/ksymoops/ksyms.c
new file mode 100644
index 000000000..608254ad3
--- /dev/null
+++ b/scripts/ksymoops/ksyms.c
@@ -0,0 +1,294 @@
+/*
+ ksyms.c.
+
+ Process ksyms for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Fri Nov 6 10:38:42 EST 1998
+ Version 0.6b
+ Remove false warnings when comparing ksyms and lsmod.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Read lsmod (/proc/modules).
+ Move "Using_Version" copy to map.c.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Scan one line from ksyms. Split lines into the base symbols and the module
+ * symbols. Separate ss for base and each module.
+ */
+static void scan_ksyms_line(const char *line)
+{
+ int i;
+ char **string = NULL;
+ SYMBOL_SET *ssp;
+ static char *prev_module = NULL;
+ static regex_t re_ksyms;
+ static regmatch_t *re_ksyms_pmatch;
+ static char const procname[] = "scan_ksyms_line";
+
+ /* ksyms: address, symbol, optional module */
+ re_compile(&re_ksyms,
+ "^([0-9a-fA-F]{4,}) +([^ \t]+)([ \t]+\\[([^ ]+)\\])?$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_ksyms_pmatch);
+
+ i = regexec(&re_ksyms, line,
+ re_ksyms.re_nsub+1, re_ksyms_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i)
+ return;
+
+ /* string [1] - address, [2] - symbol, [3] - white space+module,
+ * [4] - module.
+ */
+ re_strings(&re_ksyms, line, re_ksyms_pmatch, &string);
+ if (string[4]) {
+ if (!prev_module || strcmp(prev_module, string[4])) {
+ /* start of a new module in ksyms */
+ ++ss_ksyms_modules;
+ ss_ksyms_module = realloc(ss_ksyms_module,
+ ss_ksyms_modules*sizeof(*ss_ksyms_module));
+ if (!ss_ksyms_module)
+ malloc_error("realloc ss_ksyms_module");
+ ssp = ss_ksyms_module+ss_ksyms_modules-1;
+ ss_init(ssp, string[4]);
+ prev_module = strdup(string[4]);
+ if (!prev_module)
+ malloc_error("strdup prev_module");
+ }
+ ssp = ss_ksyms_module+ss_ksyms_modules-1;
+ }
+ else
+ ssp = &ss_ksyms_base;
+ add_symbol(ssp, string[1], ' ', 1, string[2]);
+ re_strings_free(&re_ksyms, &string);
+}
+
+/* Read the symbols from ksyms. */
+void read_ksyms(const char *ksyms)
+{
+ FILE *f;
+ char *line = NULL;
+ int i, size;
+ static char const procname[] = "read_ksyms";
+
+ if (!ksyms)
+ return;
+ ss_init(&ss_ksyms_base, "ksyms_base");
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, ksyms);
+
+ if (!regular_file(ksyms, procname))
+ return;
+
+ if (!(f = fopen_local(ksyms, "r", procname)))
+ return;
+
+ while (fgets_local(&line, &size, f, procname))
+ scan_ksyms_line(line);
+
+ fclose_local(f, procname);
+ free(line);
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss_sort_na(ss_ksyms_module+i);
+ extract_Version(ss_ksyms_module+i);
+ }
+ if (ss_ksyms_base.used) {
+ ss_sort_na(&ss_ksyms_base);
+ extract_Version(&ss_ksyms_base);
+ }
+ else {
+ fprintf(stderr,
+ "Warning, no kernel symbols in ksyms, is %s a valid "
+ "ksyms file?\n",
+ ksyms);
+ ++warnings;
+ }
+
+ if (debug > 1) {
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname,
+ ss_ksyms_module[i].source,
+ ss_ksyms_module[i].used,
+ ss_ksyms_module[i].alloc);
+ }
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname, ss_ksyms_base.source, ss_ksyms_base.used,
+ ss_ksyms_base.alloc);
+ }
+}
+
+/* Map each ksyms module entry to the corresponding object entry. Tricky,
+ * see the comments in the docs about needing a unique symbol in each
+ * module.
+ */
+static void map_ksym_to_module(SYMBOL_SET *ss)
+{
+ int i, j, matches;
+ char *name = NULL;
+
+ for (i = 0; i < ss->used; ++i) {
+ matches = 0;
+ for (j = 0; j < ss_objects; ++j) {
+ name = (ss->symbol)[i].name;
+ if (find_symbol_name(ss_object+j, name, NULL)) {
+ ++matches;
+ ss->related = ss_object+j;
+ }
+ }
+ if (matches == 1)
+ break; /* unique symbol over all objects */
+ ss->related = NULL; /* keep looking */
+ }
+ if (!(ss->related)) {
+ fprintf(stderr,
+ "Warning: cannot match loaded module %s to any "
+ "module object. Trace may not be reliable.\n",
+ ss->source);
+ ++warnings;
+ }
+ else if (debug)
+ fprintf(stderr,
+ "DEBUG: ksyms %s matches to %s based on unique "
+ "symbol %s\n",
+ ss->source, ss->related->source, name);
+}
+
+/* Map all ksyms module entries to their corresponding objects */
+void map_ksyms_to_modules(void)
+{
+ int i;
+ SYMBOL_SET *ss, *ssc;
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss = ss_ksyms_module+i;
+ map_ksym_to_module(ss);
+ if (ss->related) {
+ ssc = adjust_object_offsets(ss);
+ compare_maps(ss, ssc, 1);
+ }
+ }
+}
+
+/* Read the modules from lsmod. */
+void read_lsmod(const char *lsmod)
+{
+ FILE *f;
+ char *line = NULL;
+ int i, size;
+ char **string = NULL;
+ static regex_t re_lsmod;
+ static regmatch_t *re_lsmod_pmatch;
+ static char const procname[] = "read_lsmod";
+
+ if (!lsmod)
+ return;
+ ss_init(&ss_lsmod, "lsmod");
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, lsmod);
+
+ if (!regular_file(lsmod, procname))
+ return;
+
+ if (!(f = fopen_local(lsmod, "r", procname)))
+ return;
+
+ /* lsmod: module, size, use count, optional used by */
+ re_compile(&re_lsmod,
+ "^"
+ "[ \t]*([^ \t]+)" /* 1 module */
+ "[ \t]*([^ \t]+)" /* 2 size */
+ "[ \t]*([^ \t]+)" /* 3 count */
+ "[ \t]*(.*)" /* 4 used by */
+ "$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_lsmod_pmatch);
+
+ while (fgets_local(&line, &size, f, procname)) {
+ i = regexec(&re_lsmod, line,
+ re_lsmod.re_nsub+1, re_lsmod_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i)
+ continue;
+ re_strings(&re_lsmod, line, re_lsmod_pmatch, &string);
+ add_symbol(&ss_lsmod, string[2], ' ', 1, string[1]);
+ }
+
+ fclose_local(f, procname);
+ free(line);
+ re_strings_free(&re_lsmod, &string);
+ if (ss_lsmod.used)
+ ss_sort_na(&ss_lsmod);
+ else {
+ fprintf(stderr,
+ "Warning, no symbols in lsmod, is %s a valid "
+ "lsmod file?\n",
+ lsmod);
+ ++warnings;
+ }
+
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname, ss_lsmod.source, ss_lsmod.used,
+ ss_lsmod.alloc);
+}
+
+/* Compare modules from ksyms against module list in lsmod and vice versa.
+ * There is one ss_ for each ksyms module and a single ss_lsmod to cross
+ * check.
+ */
+void compare_ksyms_lsmod(void)
+{
+ int i, j;
+ SYMBOL_SET *ss;
+ SYMBOL *s;
+ static char const procname[] = "compare_ksyms_lsmod";
+
+ if (!(ss_lsmod.used && ss_ksyms_modules))
+ return;
+
+ s = ss_lsmod.symbol;
+ for (i = 0; i < ss_lsmod.used; ++i, ++s) {
+ for (j = 0; j < ss_ksyms_modules; ++j) {
+ ss = ss_ksyms_module+j;
+ if (strcmp(s->name, ss->source) == 0)
+ break;
+ }
+ if (j >= ss_ksyms_modules) {
+ fprintf(stderr,
+ "Warning in %s, module %s is in lsmod but not "
+ "in ksyms, probably no symbols exported\n",
+ procname, s->name);
+ ++warnings;
+ }
+ }
+
+ for (i = 0; i < ss_ksyms_modules; ++i) {
+ ss = ss_ksyms_module+i;
+ if (!find_symbol_name(&ss_lsmod, ss->source, NULL)) {
+ fprintf(stderr,
+ "Error in %s, module %s is in ksyms but not "
+ "in lsmod\n",
+ procname, ss->source);
+ ++errors;
+ }
+ }
+}
diff --git a/scripts/ksymoops/map.c b/scripts/ksymoops/map.c
new file mode 100644
index 000000000..6f91e9daf
--- /dev/null
+++ b/scripts/ksymoops/map.c
@@ -0,0 +1,251 @@
+/*
+ map.c.
+
+ Read System.map for ksymoops, create merged System.map.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Remove addresses 0-4095 from merged map after writing new map.
+ Move "Using_Version" copy to map.c.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+
+/* Read the symbols from System.map */
+void read_system_map(const char *system_map)
+{
+ FILE *f;
+ char *line = NULL, **string = NULL;
+ int i, size = 0;
+ static char const procname[] = "read_system_map";
+
+ if (!system_map)
+ return;
+ ss_init(&ss_system_map, "System.map");
+ if (debug)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, system_map);
+
+ if (!regular_file(system_map, procname))
+ return;
+
+ if (!(f = fopen_local(system_map, "r", procname)))
+ return;
+
+ while (fgets_local(&line, &size, f, procname)) {
+ i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0) {
+ re_strings(&re_nm, line, re_nm_pmatch, &string);
+ add_symbol(&ss_system_map, string[1], *string[2],
+ 1, string[3]);
+ }
+ }
+
+ fclose_local(f, procname);
+ re_strings_free(&re_nm, &string);
+ free(line);
+ if (ss_system_map.used) {
+ ss_sort_na(&ss_system_map);
+ extract_Version(&ss_system_map);
+ }
+ else {
+ fprintf(stderr,
+ "Warning, no kernel symbols in System.map, is %s a "
+ "valid System.map file?\n",
+ system_map);
+ ++warnings;
+ }
+
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname,
+ ss_system_map.source,
+ ss_system_map.used,
+ ss_system_map.alloc);
+}
+
+/* Compare two maps, all symbols in the first should appear in the second. */
+void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2,
+ int precedence)
+{
+ int i, start = 0;
+ SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1;
+ const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1;
+
+ if (!(ss1->used && ss2->used))
+ return;
+
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: compare_maps %s vs %s, %s takes precedence\n",
+ ss1->source, ss2->source,
+ precedence == 1 ? ss1->source : ss2->source);
+
+ for (i = 0; i < ss1->used; ++i) {
+ s1 = ss1->symbol+i;
+ if (!(s1->keep))
+ continue;
+ s2 = find_symbol_name(ss2, s1->name, &start);
+ if (!s2) {
+ /* Some types only appear in nm output, not in things
+ * like System.map. Silently ignore them.
+ */
+ if (s1->type == 'a' || s1->type == 't')
+ continue;
+ fprintf(stderr,
+ "Warning: %s symbol %s not found in %s. "
+ "Ignoring %s entry\n",
+ ss1->source, s1->name,
+ ss2->source, (*ssdrop)->source);
+ ++warnings;
+ if (*sdrop)
+ (*sdrop)->keep = 0;
+ }
+ else if (s1->address != s2->address) {
+ /* Type C symbols cannot be resolved from nm to ksyms,
+ * silently ignore them.
+ */
+ if (s1->type == 'C' || s2->type == 'C')
+ continue;
+ fprintf(stderr,
+ "Warning: mismatch on symbol %s %c, "
+ "%s says %lx, %s says %lx. "
+ "Ignoring %s entry\n",
+ s1->name, s1->type, ss1->source, s1->address,
+ ss2->source, s2->address, (*ssdrop)->source);
+ ++warnings;
+ if (*sdrop)
+ (*sdrop)->keep = 0;
+ }
+ else
+ ++start; /* step to next entry in ss2 */
+ }
+}
+
+/* Append the second symbol set onto the first */
+static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2)
+{
+ int i;
+ SYMBOL *s;
+
+ if (!ss2 || !ss2->used)
+ return;
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: append_map %s to %s\n",
+ ss2->source, ss1->source);
+
+ for (i = 0; i < ss2->used; ++i) {
+ s = ss2->symbol+i;
+ if (s->keep)
+ add_symbol_n(ss1, s->address, s->type, 1,
+ s->name);
+ }
+}
+
+/* Compare the various sources and build a merged system map */
+void merge_maps(const char *save_system_map)
+{
+ int i;
+ SYMBOL *s;
+ FILE *f;
+ static char const procname[] = "merge_maps";
+
+ if (debug)
+ fprintf(stderr, "DEBUG: %s\n", procname);
+
+ /* Using_Versions only appears in ksyms, copy to other tables */
+ if ((s = find_symbol_name(&ss_ksyms_base,
+ "Using_Versions", 0))) {
+ if (ss_system_map.used) {
+ add_symbol_n(&ss_system_map, s->address,
+ s->type, s->keep, s->name);
+ ss_sort_na(&ss_system_map);
+ }
+ if (ss_vmlinux.used) {
+ add_symbol_n(&ss_vmlinux, s->address, s->type,
+ s->keep, s->name);
+ ss_sort_na(&ss_vmlinux);
+ }
+ }
+
+ compare_Version(); /* highlight any version problems first */
+ compare_ksyms_lsmod(); /* highlight any missing modules next */
+ compare_maps(&ss_ksyms_base, &ss_vmlinux, 2);
+ compare_maps(&ss_system_map, &ss_vmlinux, 2);
+ compare_maps(&ss_vmlinux, &ss_system_map, 1);
+ compare_maps(&ss_ksyms_base, &ss_system_map, 2);
+
+ if (ss_objects) {
+ map_ksyms_to_modules();
+ }
+
+ ss_init(&ss_merged, "merged");
+ append_map(&ss_merged, &ss_vmlinux);
+ append_map(&ss_merged, &ss_ksyms_base);
+ append_map(&ss_merged, &ss_system_map);
+ for (i = 0; i < ss_ksyms_modules; ++i)
+ append_map(&ss_merged, (ss_ksyms_module+i)->related);
+ if (!ss_merged.used) {
+ fprintf(stderr, "Warning, no symbols in merged map\n");
+ ++warnings;
+ }
+
+ /* drop duplicates, type a (registers) and gcc2_compiled. */
+ ss_sort_atn(&ss_merged);
+ s = ss_merged.symbol;
+ for (i = 0; i < ss_merged.used-1; ++i) {
+ if (s->type == 'a' ||
+ (s->type == 't' && !strcmp(s->name, "gcc2_compiled.")))
+ s->keep = 0;
+ else if (strcmp(s->name, (s+1)->name) == 0 &&
+ s->address == (s+1)->address) {
+ if (s->type != ' ')
+ (s+1)->keep = 0;
+ else
+ s->keep = 0;
+ }
+ ++s;
+ }
+ ss_sort_atn(&ss_merged); /* will remove dropped variables */
+
+ if (save_system_map) {
+ if (debug)
+ fprintf(stderr, "DEBUG: writing merged map to %s\n",
+ save_system_map);
+ if (!(f = fopen_local(save_system_map, "w", procname)))
+ return;
+ s = ss_merged.symbol;
+ for (i = 0; i < ss_merged.used; ++i) {
+ if (s->keep)
+ fprintf(f, "%s %c %s\n",
+ format_address(s->address),
+ s->type, s->name);
+ ++s;
+ }
+ }
+
+ /* The merged map may contain symbols with an address of 0, e.g.
+ * Using_Versions. These give incorrect results for low addresses in
+ * map_address, such addresses map to "Using_Versions+xxx". Remove
+ * any addresses below (arbitrary) 4096 from the merged map. AFAIK,
+ * Linux does not use the first page on any arch.
+ */
+ for (i = 0; i < ss_merged.used; ++i) {
+ if ((ss_merged.symbol+i)->address < 4096)
+ (ss_merged.symbol+i)->keep = 0;
+ else
+ break;
+ }
+ if (i)
+ ss_sort_atn(&ss_merged); /* remove dropped variables */
+}
diff --git a/scripts/ksymoops/misc.c b/scripts/ksymoops/misc.c
new file mode 100644
index 000000000..7876f7e41
--- /dev/null
+++ b/scripts/ksymoops/misc.c
@@ -0,0 +1,108 @@
+/*
+ misc.c.
+
+ Miscellaneous routines for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Convert from a.out to bfd, using same format as ksymoops.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void malloc_error(const char *msg)
+{
+ fprintf(stderr, "%s: fatal malloc error for %s\n", prefix, msg);
+ exit(2);
+}
+
+/* Format an address with the correct number of leading zeroes */
+const char *format_address(elf_addr_t address)
+{
+ /* Well oversized */
+ static char format[10], text[200];
+ if (!*format)
+ snprintf(format, sizeof(format), "%%0%dlx",
+ 2*sizeof(address));
+ snprintf(text, sizeof(text), format, address);
+ return(text);
+}
+
+/* Find the full pathname of a program. Code heavily based on
+ * glibc-2.0.5/posix/execvp.c.
+ */
+char *find_fullpath(const char *program)
+{
+ char *fullpath = NULL;
+ char *path, *p;
+ size_t len;
+ static const char procname[] = "find_fullpath";
+
+ /* Don't search when it contains a slash. */
+ if (strchr(program, '/')) {
+ if (!(fullpath = strdup(program)))
+ malloc_error(procname);
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s %s\n", procname, fullpath);
+ return(fullpath);
+ }
+
+ path = getenv ("PATH");
+ if (!path) {
+ /* There is no `PATH' in the environment. The default search
+ path is the current directory followed by the path `confstr'
+ returns for `_CS_PATH'.
+ */
+ len = confstr(_CS_PATH, (char *) NULL, 0);
+ if (!(path = malloc(1 + len)))
+ malloc_error(procname);
+ path[0] = ':';
+ confstr(_CS_PATH, path+1, len);
+ }
+
+ len = strlen(program) + 1;
+ if (!(fullpath = malloc(strlen(path) + len)))
+ malloc_error(procname);
+ p = path;
+ do {
+ path = p;
+ p = strchr(path, ':');
+ if (p == NULL)
+ p = strchr(path, '\0');
+
+ /* Two adjacent colons, or a colon at the beginning or the end
+ * of `PATH' means to search the current directory.
+ */
+ if (p == path)
+ memcpy(fullpath, program, len);
+ else {
+ /* Construct the pathname to try. */
+ memcpy(fullpath, path, p - path);
+ fullpath[p - path] = '/';
+ memcpy(&fullpath[(p - path) + 1], program, len);
+ }
+
+ /* If we have execute access, assume this is the program. */
+ if (access(fullpath, X_OK) == 0) {
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s %s\n",
+ procname, fullpath);
+ return(fullpath);
+ }
+ } while (*p++ != '\0');
+
+ fprintf(stderr, "Error: %s %s could not find executable %s\n",
+ prefix, procname, program);
+ ++errors;
+ return(NULL);
+}
diff --git a/scripts/ksymoops/object.c b/scripts/ksymoops/object.c
new file mode 100644
index 000000000..7a44e4c35
--- /dev/null
+++ b/scripts/ksymoops/object.c
@@ -0,0 +1,230 @@
+/*
+ object.c.
+
+ object handling routines for ksymoops. Read modules, vmlinux, etc.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/* Extract all symbols definitions from an object using nm */
+static void read_nm_symbols(SYMBOL_SET *ss, const char *file)
+{
+ FILE *f;
+ char *cmd, *line = NULL, **string = NULL;
+ int i, size = 0;
+ static char const procname[] = "read_nm_symbols";
+
+ if (!regular_file(file, procname))
+ return;
+
+ cmd = malloc(strlen(path_nm)+strlen(file)+2);
+ if (!cmd)
+ malloc_error("nm command");
+ strcpy(cmd, path_nm);
+ strcat(cmd, " ");
+ strcat(cmd, file);
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+ if (!(f = popen_local(cmd, procname)))
+ return;
+ free(cmd);
+
+ while (fgets_local(&line, &size, f, procname)) {
+ i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0) {
+ re_strings(&re_nm, line, re_nm_pmatch, &string);
+ add_symbol(ss, string[1], *string[2], 1, string[3]);
+ }
+ }
+
+ pclose_local(f, procname);
+ re_strings_free(&re_nm, &string);
+ free(line);
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: %s %s used %d out of %d entries\n",
+ procname, ss->source, ss->used, ss->alloc);
+}
+
+/* Read the symbols from vmlinux */
+void read_vmlinux(const char *vmlinux)
+{
+ if (!vmlinux)
+ return;
+ ss_init(&ss_vmlinux, "vmlinux");
+ read_nm_symbols(&ss_vmlinux, vmlinux);
+ if (ss_vmlinux.used) {
+ ss_sort_na(&ss_vmlinux);
+ extract_Version(&ss_vmlinux);
+ }
+ else {
+ fprintf(stderr,
+ "Warning, no kernel symbols in vmlinux, is %s a valid "
+ "vmlinux file?\n",
+ vmlinux);
+ ++warnings;
+ }
+}
+
+
+/* Read the symbols from one object (module) */
+void read_object(const char *object, int i)
+{
+ ss_init(ss_object+i, object);
+ read_nm_symbols(ss_object+i, object);
+ if ((ss_object+i)->used) {
+ ss_sort_na(ss_object+i);
+ extract_Version(ss_object+i);
+ }
+ else {
+ fprintf(stderr, "Warning, no symbols in %s\n", object);
+ ++warnings;
+ }
+}
+
+/* Add a new entry to the list of objects */
+static void add_ss_object(const char *file)
+{
+ ++ss_objects;
+ ss_object = realloc(ss_object, ss_objects*sizeof(*ss_object));
+ if (!ss_object)
+ malloc_error("realloc ss_object");
+ ss_init(ss_object+ss_objects-1, file);
+}
+
+/* Run a directory and its subdirectories, looking for *.o files */
+static void find_objects(const char *dir)
+{
+ FILE *f;
+ char *cmd, *line = NULL;
+ int size = 0, files = 0;
+ static char const procname[] = "find_objects";
+ static char const options[] = " -follow -name '*.o' -print";
+
+ cmd = malloc(strlen(path_find)+1+strlen(dir)+strlen(options)+1);
+ if (!cmd)
+ malloc_error("find command");
+ strcpy(cmd, path_find);
+ strcat(cmd, " ");
+ strcat(cmd, dir);
+ strcat(cmd, options);
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+ if (!(f = popen_local(cmd, procname)))
+ return;
+ free(cmd);
+
+ while (fgets_local(&line, &size, f, procname)) {
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+ add_ss_object(line);
+ ++files;
+ }
+
+ pclose_local(f, procname);
+ if (!files) {
+ fprintf(stderr,
+ "Warning: no *.o files in %s. "
+ "Is %s a valid module directory?\n",
+ dir, dir);
+ ++warnings;
+ }
+}
+
+/* Take the user supplied list of objects which can include directories.
+ * Expand directories into any *.o files. The results are stored in
+ * ss_object, leaving the user supplied options untouched.
+ */
+void expand_objects(char * const *object, int objects)
+{
+ struct stat statbuf;
+ int i;
+ const char *file;
+ static char const procname[] = "expand_objects";
+
+ for (i = 0; i < objects; ++i) {
+ file = object[i];
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s checking '%s' - ",
+ procname, file);
+ if (!stat(file, &statbuf) && S_ISDIR(statbuf.st_mode)) {
+ if (debug > 1)
+ fprintf(stderr, "directory, expanding\n");
+ find_objects(file);
+ }
+ else {
+ if (debug > 1)
+ fprintf(stderr, "not directory\n");
+ add_ss_object(file);
+ }
+ }
+}
+
+/* Map a symbol type to a section code. 0 - text, 1 - data, 2 - read only data,
+ * 3 - C (cannot relocate), 4 - the rest.
+ */
+static int section(char type)
+{
+ switch (type) {
+ case 'T':
+ case 't':
+ return 0;
+ case 'D':
+ case 'd':
+ return 1;
+ case 'R':
+ case 'r':
+ return 2;
+ case 'C':
+ return 3;
+ default:
+ return 4;
+ }
+}
+
+/* Given ksyms module data which has a related object, create a copy of the
+ * object data, adjusting the offsets to match where the module was loaded.
+ */
+SYMBOL_SET *adjust_object_offsets(SYMBOL_SET *ss)
+{
+ int i;
+ elf_addr_t adjust[] = {0, 0, 0, 0, 0};
+ SYMBOL *sk, *so;
+ SYMBOL_SET *ssc;
+
+ if (debug > 1)
+ fprintf(stderr,
+ "DEBUG: adjust_object_offsets %s\n", ss->source);
+
+ ssc = ss_copy(ss->related);
+
+ /* For common symbols, calculate the adjustment */
+ for (i = 0; i < ss->used; ++i) {
+ sk = ss->symbol+i;
+ if ((so = find_symbol_name(ssc, sk->name, NULL)))
+ adjust[section(so->type)] = sk->address - so->address;
+ }
+ for (i = 0; i < ssc->used; ++i) {
+ so = ssc->symbol+i;
+ /* Type C does not relocate well, silently ignore */
+ if (so->type != 'C' && adjust[section(so->type)])
+ so->address += adjust[section(so->type)];
+ else
+ so->keep = 0; /* do not merge into final map */
+ }
+
+ ss->related = ssc; /* map using adjusted copy */
+ return(ssc);
+}
diff --git a/scripts/ksymoops/oops.c b/scripts/ksymoops/oops.c
new file mode 100644
index 000000000..1df01f40c
--- /dev/null
+++ b/scripts/ksymoops/oops.c
@@ -0,0 +1,1200 @@
+/*
+ oops.c.
+
+ Oops processing for ksymoop.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Mon Jan 4 08:47:55 EST 1999
+ Version 0.6d
+ Add ARM support.
+
+ Thu Nov 26 16:37:46 EST 1998
+ Version 0.6c
+ Typo in oops_code.
+ Add -c option.
+
+ Tue Nov 3 23:33:04 EST 1998
+ Version 0.6a
+ Performance inprovements.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Oops file must be regular.
+ Add "invalid operand" to Oops_print.
+ Minor adjustment to re for ppc.
+ Minor adjustment to re for objdump lines with <_EIP+xxx>.
+ Convert from a.out to bfd, using same format as ksymoops.
+ Added MIPS.
+ PPC handling based on patches by "Ryan Nielsen" <ran@krazynet.com>
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into seperate sources.
+ */
+
+#include "ksymoops.h"
+#include <bfd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <malloc.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Error detected by bfd */
+static void Oops_bfd_perror(const char *msg)
+{
+ fprintf(stderr, "Error ");
+ bfd_perror(msg);
+ ++errors;
+}
+
+/* Safest way to get correct output bfd format is to copy ksymoops' format. */
+static int Oops_copy_bfd_format(bfd **ibfd, bfd **obfd, asection **isec,
+ const char *file)
+{
+ char *me, **matches, **match;
+
+ if (!(*obfd = bfd_openw(file, NULL))) {
+ Oops_bfd_perror(file);
+ return(0);
+ }
+
+ me = find_fullpath(prefix);
+ if (!me)
+ return(0);
+
+ if (!(*ibfd = bfd_openr(me, NULL))) {
+ Oops_bfd_perror(me);
+ return(0);
+ }
+ free(me); /* Who is Tommy? */
+
+ if (!bfd_check_format_matches(*ibfd, bfd_object, &matches)) {
+ Oops_bfd_perror(me);
+ if (bfd_get_error() == bfd_error_file_ambiguously_recognized) {
+ fprintf(stderr, "Matching formats:");
+ match = matches;
+ while (*match)
+ fprintf(stderr, " %s", *match++);
+ fprintf(stderr, "\n");
+ free(matches);
+ }
+ return(0);
+ }
+
+ if (!(*isec = bfd_get_section_by_name(*ibfd, ".text"))) {
+ Oops_bfd_perror("get_section");
+ return(0);
+ }
+
+ bfd_set_format(*obfd, bfd_object);
+ bfd_set_arch_mach(*obfd, bfd_get_arch(*ibfd), bfd_get_mach(*ibfd));
+
+ if (!bfd_set_file_flags(*obfd, bfd_get_file_flags(*ibfd))) {
+ Oops_bfd_perror("set_file_flags");
+ return(0);
+ }
+
+ return(1);
+}
+
+/* Write the code values to a file using bfd. */
+static int Oops_write_bfd_data(bfd *ibfd, bfd *obfd, asection *isec,
+ const char *code, int size)
+{
+ asection *osec;
+ asymbol *osym;
+
+ if (!bfd_set_start_address(obfd, 0)) {
+ Oops_bfd_perror("set_start_address");
+ return(0);
+ }
+ if (!(osec = bfd_make_section(obfd, ".text"))) {
+ Oops_bfd_perror("make_section");
+ return(0);
+ }
+ if (!bfd_set_section_flags(obfd, osec,
+ bfd_get_section_flags(ibfd, isec))) {
+ Oops_bfd_perror("set_section_flags");
+ return(0);
+ }
+ if (!bfd_set_section_alignment(obfd, osec,
+ bfd_get_section_alignment(ibfd, isec))) {
+ Oops_bfd_perror("set_section_alignment");
+ return(0);
+ }
+ osec->output_section = osec;
+ if (!(osym = bfd_make_empty_symbol(obfd))) {
+ Oops_bfd_perror("make_empty_symbol");
+ return(0);
+ }
+ osym->name = "_EIP";
+ osym->section = osec;
+ osym->flags = BSF_GLOBAL;
+ osym->value = 0;
+ if (!bfd_set_symtab(obfd, &osym, 1)) {
+ Oops_bfd_perror("set_symtab");
+ return(0);
+ }
+ if (!bfd_set_section_size(obfd, osec, size)) {
+ Oops_bfd_perror("set_section_size");
+ return(0);
+ }
+ if (!bfd_set_section_vma(obfd, osec, 0)) {
+ Oops_bfd_perror("set_section_vma");
+ return(0);
+ }
+ if (!bfd_set_section_contents(obfd, osec, (PTR) code, 0, size)) {
+ Oops_bfd_perror("set_section_contents");
+ return(0);
+ }
+ if (!bfd_close(obfd)) {
+ Oops_bfd_perror("close(obfd)");
+ return(0);
+ }
+ if (!bfd_close(ibfd)) {
+ Oops_bfd_perror("close(ibfd)");
+ return(0);
+ }
+ return 1;
+}
+
+/* Write the Oops code to a temporary file with suitable header and trailer. */
+static char *Oops_code_to_file(const char *code, int size)
+{
+ char *file;
+ bfd *ibfd, *obfd;
+ asection *isec;
+
+ bfd_init();
+ file = tmpnam(NULL);
+ if (!Oops_copy_bfd_format(&ibfd, &obfd, &isec, file))
+ return(NULL);
+ if (!Oops_write_bfd_data(ibfd, obfd, isec, code, size))
+ return(NULL);
+ return(file);
+}
+
+/* Run objdump against the binary Oops code */
+static FILE *Oops_objdump(const char *file)
+{
+ char *cmd;
+ FILE *f;
+ static char const options[] = "-dhf ";
+ static char const procname[] = "Oops_objdump";
+
+ cmd = malloc(strlen(path_objdump)+1+strlen(options)+strlen(file)+1);
+ if (!cmd)
+ malloc_error(procname);
+ strcpy(cmd, path_objdump);
+ strcat(cmd, " ");
+ strcat(cmd, options);
+ strcat(cmd, file);
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s command '%s'\n", procname, cmd);
+ f = popen_local(cmd, procname);
+ free(cmd);
+ return(f);
+}
+
+/* Process one code line from objdump, ignore everything else */
+static void Oops_decode_one(SYMBOL_SET *ss, const char *line, elf_addr_t eip,
+ int adjust)
+{
+ int i;
+ elf_addr_t address, eip_relative;
+ char *line2, *map, **string = NULL;
+ static regex_t re_Oops_objdump;
+ static regmatch_t *re_Oops_objdump_pmatch;
+ static char const procname[] = "Oops_decode_one";
+
+ /* objdump output. Optional whitespace, hex digits, optional
+ * ' <_EIP+offset>', ':'. The '+offset' after _EIP is also optional.
+ * Older binutils output 'xxxxxxxx <_EIP+offset>:', newer versions do
+ * '00000000 <_EIP>:' first followed by ' xx:' lines.
+ *
+ * Just to complicate things even more, objdump recognises jmp, call,
+ * etc., converts the code to something like this :-
+ * " f: e8 32 34 00 00 call 3446 <_EIP+0x3446>"
+ * Recognise this and append the eip adjusted address, followed by the
+ * map_address text for that address.
+ *
+ * With any luck, objdump will take care of all such references which
+ * makes this routine architecture insensitive. No need to test for
+ * i386 jmp, call or m68k swl etc.
+ */
+ re_compile(&re_Oops_objdump,
+ "^[ \t]*"
+ "([0-9a-fA-F]+)" /* 1 */
+ "( <_EIP[^>]*>)?" /* 2 */
+ ":"
+ "(" /* 3 */
+ ".* +<_EIP\\+0?x?([0-9a-fA-F]+)>[ \t]*$" /* 4 */
+ ")?"
+ ".*"
+ ,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_objdump_pmatch);
+
+ i = regexec(&re_Oops_objdump, line, re_Oops_objdump.re_nsub+1,
+ re_Oops_objdump_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i != 0)
+ return;
+
+ re_strings(&re_Oops_objdump, line, re_Oops_objdump_pmatch, &string);
+ errno = 0;
+ address = strtoul(string[1], NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s Invalid hex value in objdump line, "
+ "treated as zero - '%s'\n"
+ " objdump line '%s'\n",
+ procname, string[1], line);
+ perror(" ");
+ ++errors;
+ address = 0;
+ }
+ address += eip + adjust;
+ if (string[4]) {
+ /* EIP relative data to be adjusted */
+ errno = 0;
+ eip_relative = strtoul(string[4], NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s Invalid hex value in objdump line, "
+ "treated as zero - '%s'\n"
+ " objdump line '%s'\n",
+ procname, string[4], line);
+ perror(" ");
+ ++errors;
+ eip_relative = 0;
+ }
+ eip_relative += eip + adjust;
+ map = map_address(&ss_merged, eip_relative);
+ /* new text is original line, eip_relative in hex, map text */
+ i = strlen(line)+1+2*sizeof(eip_relative)+1+strlen(map)+1;
+ line2 = malloc(i);
+ if (!line2)
+ malloc_error(procname);
+ snprintf(line2, i, "%s %s %s",
+ line, format_address(eip_relative), map);
+ add_symbol_n(ss, address, 'C', 1, line2);
+ free(line2);
+ }
+ else
+ add_symbol_n(ss, address, 'C', 1, line); /* as is */
+ re_strings_free(&re_Oops_objdump, &string);
+}
+
+/* Maximum number of code bytes to process. It needs to be a multiple of 2 for
+ * code_byte (-c) swapping. Sparc and alpha dump 36 bytes so use 64.
+ */
+#define CODE_SIZE 64
+
+/******************************************************************************/
+/* Start architecture sensitive code */
+/******************************************************************************/
+
+/* Extract the hex values from the Code: line and convert to binary */
+static int Oops_code_values(const unsigned char* code_text, char *code,
+ int *adjust, char ***string, int string_max,
+ int code_bytes)
+{
+ int byte = 0, i, l;
+ unsigned long c;
+ char *value;
+ const char *p;
+ static regex_t re_Oops_code_value;
+ static regmatch_t *re_Oops_code_value_pmatch;
+ static const char procname[] = "Oops_code_values";
+
+ /* Given by re_Oops_code: code_text is a message (e.g. "general
+ * protection") or one or more hex fields separated by space or tab.
+ * Some architectures bracket the current instruction with '<'
+ * and '>', others use '(' and ')'. The first character is
+ * nonblank.
+ */
+ if (!isxdigit(*code_text)) {
+ fprintf(stderr,
+ "Warning, Code looks like message, not hex digits. "
+ "No disassembly attempted.\n");
+ ++warnings;
+ return(0);
+ }
+ memset(code, '\0', CODE_SIZE);
+ p = code_text;
+ *adjust = 0; /* EIP points to code byte 0 */
+
+ /* Code values. Hex values separated by white space. On sparc, the
+ * current instruction is bracketed in '<' and '>'.
+ */
+ re_compile(&re_Oops_code_value,
+ "^"
+ "([<(]?)" /* 1 */
+ "([0-9a-fA-F]+)" /* 2 */
+ "[>)]?"
+ "[ \t]*"
+ ,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_code_value_pmatch);
+
+ re_string_check(re_Oops_code_value.re_nsub+1, string_max, procname);
+ while (regexec(&re_Oops_code_value, p, re_Oops_code_value.re_nsub+1,
+ re_Oops_code_value_pmatch, 0) == 0) {
+ re_strings(&re_Oops_code_value, p,
+ re_Oops_code_value_pmatch, string);
+ if (byte >= CODE_SIZE)
+ break;
+ errno = 0;
+ value = (*string)[2];
+ c = strtoul(value, NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s Invalid hex value in code_value line, "
+ "treated as zero - '%s'\n"
+ " code_value line '%s'\n",
+ procname, value, code_text);
+ perror(" ");
+ ++errors;
+ c = 0;
+ }
+ if ((*string)[1] && *((*string)[1]))
+ *adjust = -byte; /* this byte is EIP */
+ /* i386 - 2 byte code, m68k - 4 byte, sparc - 8 byte.
+ * On some architectures Code: is a stream of bytes, on some it
+ * is a stream of shorts, on some it is a stream of ints.
+ * Consistent we're not!
+ */
+ l = strlen(value);
+ if (l%2) {
+ fprintf(stderr,
+ "%s invalid value 0x%s in Code line, not a "
+ "multiple of 2 digits, value ignored\n",
+ procname, value);
+ ++errors;
+ }
+ else while (l) {
+ if (byte >= CODE_SIZE) {
+ fprintf(stderr,
+ "%s Warning: extra values in Code "
+ "line, ignored - '%s'\n",
+ procname, value);
+ ++warnings;
+ break;
+ }
+ l -= 2;
+ code[byte++] = (c >> l*4) & 0xff;
+ value += 2;
+ }
+ p += re_Oops_code_value_pmatch[0].rm_eo;
+ }
+
+ if (*p) {
+ fprintf(stderr,
+ "Warning garbage '%s' at end of code line ignored "
+ "by %s\n",
+ p, procname);
+ ++warnings;
+ }
+
+ /* The code_bytes parameter says how many readable bytes form a single
+ * code unit in machine terms. -c 1 says that the text is already in
+ * machine order, -c 2 (4, 8) says each chunk of 2 (4, 8) bytes must be
+ * swapped to get back to machine order. Which end is up?
+ */
+ if (code_bytes != 1) {
+ if (byte % code_bytes) {
+ fprintf(stderr,
+ "Warning: the number of code bytes (%d) is not "
+ "a multiple of -c (%d)\n"
+ "Byte swapping may not give sensible results\n",
+ byte, code_bytes);
+ ++warnings;
+ }
+ for (l = 0; l < byte; l+= code_bytes) {
+ for (i = 0; i < code_bytes/2; ++i) {
+ c = code[l+i];
+ code[l+i] = code[l+code_bytes-i-1];
+ code[l+code_bytes-i-1] = c;
+ }
+ }
+ }
+
+ return(1);
+}
+
+/* Look for the EIP: line, returns start of the relevant hex value */
+static char *Oops_eip(const char *line, char ***string, int string_max)
+{
+ int i;
+ static regex_t re_Oops_eip_sparc;
+ static regmatch_t *re_Oops_eip_sparc_pmatch;
+ static regex_t re_Oops_eip_ppc;
+ static regmatch_t *re_Oops_eip_ppc_pmatch;
+ static regex_t re_Oops_eip_mips;
+ static regmatch_t *re_Oops_eip_mips_pmatch;
+ static regex_t re_Oops_eip_other;
+ static regmatch_t *re_Oops_eip_other_pmatch;
+ static const char procname[] = "Oops_eip";
+
+ /* Oops 'EIP:' line for sparc, actually PSR followed by PC */
+ re_compile(&re_Oops_eip_sparc,
+ "^PSR: [0-9a-fA-F]+ PC: " UNBRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_eip_sparc_pmatch);
+
+ i = regexec(&re_Oops_eip_sparc, line, re_Oops_eip_sparc.re_nsub+1,
+ re_Oops_eip_sparc_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec sparc %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_eip_sparc.re_nsub+1, string_max,
+ procname);
+ re_strings(&re_Oops_eip_sparc, line, re_Oops_eip_sparc_pmatch,
+ string);
+ return((*string)[re_Oops_eip_sparc.re_nsub]);
+ }
+
+ /* Oops 'EIP:' line for PPC, all over the place */
+ re_compile(&re_Oops_eip_ppc,
+ "("
+ "(kernel pc )"
+ "|(trap at PC: )"
+ "|(bad area pc )"
+ "|(NIP: )"
+ ")"
+ UNBRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_eip_ppc_pmatch);
+
+ i = regexec(&re_Oops_eip_ppc, line, re_Oops_eip_ppc.re_nsub+1,
+ re_Oops_eip_ppc_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec ppc %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_eip_ppc.re_nsub+1, string_max,
+ procname);
+ re_strings(&re_Oops_eip_ppc, line, re_Oops_eip_ppc_pmatch,
+ string);
+ return((*string)[re_Oops_eip_ppc.re_nsub]);
+ }
+
+ /* Oops 'EIP:' line for MIPS, epc, optional white space, ':',
+ * optional white space, unbracketed address.
+ */
+ re_compile(&re_Oops_eip_mips,
+ "^(epc[ \t]*:+[ \t]*)"
+ UNBRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_eip_mips_pmatch);
+
+ i = regexec(&re_Oops_eip_mips, line, re_Oops_eip_mips.re_nsub+1,
+ re_Oops_eip_mips_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec mips %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_eip_mips.re_nsub+1, string_max,
+ procname);
+ re_strings(&re_Oops_eip_mips, line, re_Oops_eip_mips_pmatch,
+ string);
+ return((*string)[re_Oops_eip_mips.re_nsub]);
+ }
+
+ /* Oops 'EIP:' line for other architectures */
+ re_compile(&re_Oops_eip_other,
+ "^("
+ /* i386 */ "(EIP:[ \t]+.*)"
+ /* m68k */ "|(PC[ \t]*=[ \t]*)"
+ /* ARM */ "|(pc *: *)"
+ ")"
+ BRACKETED_ADDRESS
+ ,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_eip_other_pmatch);
+
+ i = regexec(&re_Oops_eip_other, line, re_Oops_eip_other.re_nsub+1,
+ re_Oops_eip_other_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec other %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_eip_other.re_nsub+1, string_max,
+ procname);
+ re_strings(&re_Oops_eip_other, line, re_Oops_eip_other_pmatch,
+ string);
+ return((*string)[re_Oops_eip_other.re_nsub]);
+ }
+ return(NULL);
+}
+
+/* Set the eip from the EIP line */
+static void Oops_set_eip(const char *value, elf_addr_t *eip, SYMBOL_SET *ss)
+{
+ static const char procname[] = "Oops_set_eip";
+ errno = 0;
+ *eip = strtoul(value, NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s Invalid hex value in EIP line, ignored - '%s'\n",
+ procname, value);
+ perror(" ");
+ ++errors;
+ *eip = 0;
+ }
+ add_symbol_n(ss, *eip, 'E', 1, ">>EIP:");
+}
+
+/* Look for the MIPS ra line, returns start of the relevant hex value */
+static char *Oops_ra(const char *line, char ***string, int string_max)
+{
+ int i;
+ static regex_t re_Oops_ra;
+ static regmatch_t *re_Oops_ra_pmatch;
+ static const char procname[] = "Oops_ra";
+
+ /* Oops 'ra:' line for MIPS, ra, optional white space, one or
+ * more '=', optional white space, unbracketed address.
+ */
+ re_compile(&re_Oops_ra,
+ "(ra[ \t]*=+[ \t]*)"
+ UNBRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_ra_pmatch);
+
+ i = regexec(&re_Oops_ra, line, re_Oops_ra.re_nsub+1,
+ re_Oops_ra_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_ra.re_nsub+1, string_max, procname);
+ re_strings(&re_Oops_ra, line, re_Oops_ra_pmatch,
+ string);
+ return((*string)[re_Oops_ra.re_nsub]);
+ }
+ return(NULL);
+}
+
+/* Set the MIPS ra from the ra line */
+static void Oops_set_ra(const char *value, SYMBOL_SET *ss)
+{
+ static const char procname[] = "Oops_set_ra";
+ elf_addr_t ra;
+ errno = 0;
+ ra = strtoul(value, NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s Invalid hex value in ra line, ignored - '%s'\n",
+ procname, value);
+ perror(" ");
+ ++errors;
+ ra = 0;
+ }
+ add_symbol_n(ss, ra, 'R', 1, ">>RA :");
+}
+
+/* Look for the Trace multilines :(. Returns start of addresses. */
+static const char *Oops_trace(const char *line, char ***string, int string_max)
+{
+ int i;
+ const char *start = NULL;
+ static int trace_line = 0;
+ static regex_t re_Oops_trace;
+ static regmatch_t *re_Oops_trace_pmatch;
+ static const char procname[] = "Oops_trace";
+
+ /* ppc is different, not a bracketed address, just an address */
+ /* ARM is different, two bracketed addresses on each line */
+
+ /* Oops 'Trace' lines */
+ re_compile(&re_Oops_trace,
+ "^(" /* 1 */
+ "(Call Trace: )" /* 2 */
+ /* alpha */ "|(Trace: )" /* 3 */
+ /* various */ "|(" BRACKETED_ADDRESS ")" /* 4,5*/
+ /* ppc */ "|(Call backtrace:)" /* 6 */
+ /* ppc */ "|(" UNBRACKETED_ADDRESS ")" /* 7,8*/
+ /* ARM */ "|(Function entered at (" BRACKETED_ADDRESS "))" /* 9,10,11 */
+ ")",
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_trace_pmatch);
+
+ i = regexec(&re_Oops_trace, line, re_Oops_trace.re_nsub+1,
+ re_Oops_trace_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0) {
+#undef MATCHED
+#define MATCHED(n) (re_Oops_trace_pmatch[n].rm_so != -1)
+ if (MATCHED(2) || MATCHED(3)) {
+ trace_line = 1;
+ start = line + re_Oops_trace_pmatch[0].rm_eo;
+ }
+ else if (MATCHED(6)) {
+ trace_line = 2; /* ppc */
+ start = line + re_Oops_trace_pmatch[0].rm_eo;
+ }
+ else if (trace_line == 1 && MATCHED(5))
+ start = line + re_Oops_trace_pmatch[5].rm_so;
+ else if (trace_line == 2 && MATCHED(8)) /* ppc */
+ start = line + re_Oops_trace_pmatch[8].rm_so;
+ else if (MATCHED(10)){
+ trace_line = 1; /* ARM */
+ start = line + re_Oops_trace_pmatch[10].rm_so;
+ }
+ else
+ trace_line = 0;
+ }
+ else
+ trace_line = 0;
+ if (trace_line)
+ return(start);
+ return(NULL);
+}
+
+/* Process a trace call line, extract addresses */
+static void Oops_trace_line(const char *line, const char *p, SYMBOL_SET *ss)
+{
+ char **string = NULL;
+ regex_t *pregex;
+ regmatch_t *pregmatch;
+ static const char procname[] = "Oops_trace_line";
+
+ /* ppc does not bracket its addresses */
+ if (isxdigit(*p)) {
+ pregex = &re_unbracketed_address;
+ pregmatch = re_unbracketed_address_pmatch;
+ }
+ else {
+ pregex = &re_bracketed_address;
+ pregmatch = re_bracketed_address_pmatch;
+ }
+
+ /* Loop over [un]?bracketed addresses */
+ while (1) {
+ if (regexec(pregex, p, pregex->re_nsub+1, pregmatch, 0) == 0) {
+ re_strings(pregex, p, pregmatch, &string);
+ add_symbol(ss, string[1], 'T', 1, "Trace:");
+ p += pregmatch[0].rm_eo;
+ }
+ else if (strncmp(p, "from ", 5) == 0)
+ p += 5; /* ARM does "address from address" */
+ else
+ break;
+ }
+
+ if (*p && !strcmp(p, "...")) {
+ fprintf(stderr,
+ "Warning garbage '%s' at end of trace line ignored "
+ "by %s\n",
+ p, procname);
+ ++warnings;
+ }
+ re_strings_free(pregex, &string);
+}
+
+/* Do pattern matching to decide if the line should be printed. When reading a
+ * syslog containing multiple Oops, you need the intermediate data (registers,
+ * tss etc.) to go with the decoded text. Sets text to the start of the useful
+ * text, after any prefix. Note that any leading white space is treated as part
+ * of the prefix, later routines do not see any indentation.
+ *
+ * Note: If a line is not printed, it will not be scanned for any other text.
+ */
+static int Oops_print(const char *line, const char **text, char ***string,
+ int string_max)
+{
+ int i, print = 0;
+ static int stack_line = 0, trace_line = 0;
+ static regex_t re_Oops_prefix;
+ static regmatch_t *re_Oops_prefix_pmatch;
+ static regex_t re_Oops_print_s;
+ static regmatch_t *re_Oops_print_s_pmatch;
+ static regex_t re_Oops_print_a;
+ static regmatch_t *re_Oops_print_a_pmatch;
+ static const char procname[] = "Oops_print";
+
+ *text = line;
+
+ /* Lines to be ignored. For some reason the "amuse the user" print in
+ * some die_if_kernel routines causes regexec to run very slowly.
+ */
+
+ if (strstr(*text, "\\|/ ____ \\|/") ||
+ strstr(*text, "\"@'/ ,. \\`@\"") ||
+ strstr(*text, "/_| \\__/ |_\\") ||
+ strstr(*text, " \\__U_/"))
+ return(1); /* print but avoid regexec */
+
+ /* Prefixes to be ignored */
+ re_compile(&re_Oops_prefix,
+ "^(" /* start of line */
+ "([^ ]{3} [ 0-9][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} "
+ "[^ ]+ kernel: +)" /* syslogd */
+ "|(<[0-9]+>)" /* kmsg */
+ "|([ \t]+)" /* leading white space */
+ ")+" /* any prefixes, in any order */
+ ,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_prefix_pmatch);
+
+ i = regexec(&re_Oops_prefix, *text, re_Oops_prefix.re_nsub+1,
+ re_Oops_prefix_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec prefix %d\n", procname, i);
+ if (i == 0)
+ *text += re_Oops_prefix_pmatch[0].rm_eo; /* step over prefix */
+
+
+ /* Lots of possibilities. Expand as required for all architectures.
+ *
+ * Trial and error shows that regex does not like a lot of sub patterns
+ * that start with "^". So split the patterns into two groups, one set
+ * must appear at the start of the line, the other set can appear
+ * anywhere.
+ */
+
+ /* These patterns must appear at the start of the line, after stripping
+ * the prefix above.
+ *
+ * The order below is required to handle multiline outupt.
+ * string 2 is defined if the text is 'Stack from '.
+ * string 3 is defined if the text is 'Stack: '.
+ * string 4 is defined if the text might be a stack continuation.
+ * string 5 is defined if the text is 'Call Trace: '.
+ * string 6 is defined if the text might be a trace continuation.
+ * string 7 is the address part of the BRACKETED_ADDRESS.
+ *
+ * string 8 is defined if the text contains a version number. No Oops
+ * report contains this as of 2.1.125 but IMHO it should be added. If
+ * anybody wants to print a VERSION_nnnn line in their Oops, this code
+ * is ready.
+ *
+ * string 9 is defined if the text is 'Trace: ' (alpha).
+ * string 10 is defined if the text is 'Call backtrace:' (ppc).
+ */
+ re_compile(&re_Oops_print_s,
+ /* arch type */ /* Required order */
+ "^(" /* 1 */
+ /* i386 */ "(Stack: )" /* 2 */
+ /* m68k */ "|(Stack from )" /* 3 */
+ /* various */ "|([0-9a-fA-F]{4,})" /* 4 */
+ /* various */ "|(Call Trace: )" /* 5 */
+ /* various */ "|(" BRACKETED_ADDRESS ")" /* 6,7*/
+ /* various */ "|(Version_[0-9]+)" /* 8 */
+ /* alpha */ "|(Trace: )" /* 9 */
+ /* ppc */ "|(Call backtrace:)" /* 10 */
+
+ /* order does not matter from here on */
+
+ /* various */ "|(Process .*stackpage=)"
+ /* various */ "|(Call Trace:[ \t])"
+ /* various */ "|(Code *:[ \t])"
+ /* various */ "|(Kernel panic)"
+ /* various */ "|(In swapper task)"
+
+ /* i386 2.0 */ "|(Corrupted stack page)"
+ /* i386 */ "|(invalid operand: )"
+ /* i386 */ "|(Oops: )"
+ /* i386 */ "|(Cpu: +[0-9])"
+ /* i386 */ "|(current->tss)"
+ /* i386 */ "|(\\*pde +=)"
+ /* i386 */ "|(EIP: )"
+ /* i386 */ "|(EFLAGS: )"
+ /* i386 */ "|(eax: )"
+ /* i386 */ "|(esi: )"
+ /* i386 */ "|(ds: )"
+
+ /* m68k */ "|(pc[:=])"
+ /* m68k */ "|(68060 access)"
+ /* m68k */ "|(Exception at )"
+ /* m68k */ "|(d[04]: )"
+ /* m68k */ "|(Frame format=)"
+ /* m68k */ "|(wb [0-9] stat)"
+ /* m68k */ "|(push data: )"
+ /* m68k */ "|(baddr=)"
+ /* any other m68K lines to print? */
+
+ /* sparc */ "|(Bad unaligned kernel)"
+ /* sparc */ "|(Forwarding unaligned exception)"
+ /* sparc */ "|(: unhandled unaligned exception)"
+ /* sparc */ "|(<sc)"
+ /* sparc */ "|(pc *=)"
+ /* sparc */ "|(r[0-9]+ *=)"
+ /* sparc */ "|(gp *=)"
+ /* any other sparc lines to print? */
+
+ /* alpha */ "|(tsk->)"
+ /* alpha */ "|(PSR: )"
+ /* alpha */ "|([goli]0: )"
+ /* alpha */ "|(Instruction DUMP: )"
+ /* any other alpha lines to print? */
+
+ /* ppc */ "|(MSR: )"
+ /* ppc */ "|(TASK = )"
+ /* ppc */ "|(last math )"
+ /* ppc */ "|(GPR[0-9]+: )"
+ /* any other ppc lines to print? */
+
+ /* MIPS */ "|(\\$[0-9 ]+:)"
+ /* MIPS */ "|(epc )"
+ /* MIPS */ "|(Status:)"
+ /* MIPS */ "|(Cause :)"
+ /* any other MIPS lines to print? */
+
+ /* ARM */ "|(Backtrace:)"
+ /* ARM */ "|(Function entered at)"
+ /* ARM */ "|(\\*pgd =)"
+ /* ARM */ "|(Internal error)"
+ /* ARM */ "|(pc :)"
+ /* ARM */ "|(sp :)"
+ /* ARM */ "|(r[0-9][0-9 ]:)"
+ /* ARM */ "|(Flags:)"
+ /* ARM */ "|(Control:)"
+ /* any other ARM lines to print? */
+
+ ")",
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_print_s_pmatch);
+
+ i = regexec(&re_Oops_print_s, *text, re_Oops_print_s.re_nsub+1,
+ re_Oops_print_s_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec start %d\n", procname, i);
+ print = 0;
+ if (i == 0) {
+#undef MATCHED
+#define MATCHED(n) (re_Oops_print_s_pmatch[n].rm_so != -1)
+ print = 1;
+ /* Handle multiline messages, messy */
+ if (!MATCHED(2) && !MATCHED(3) && !MATCHED(4))
+ stack_line = 0;
+ else if (MATCHED(2) || MATCHED(3))
+ stack_line = 1;
+ else if (stack_line && !MATCHED(4)) {
+ print = 0;
+ stack_line = 0;
+ }
+ if (!MATCHED(5) && !MATCHED(6) && !MATCHED(9) && !MATCHED(10))
+ trace_line = 0;
+ else if (MATCHED(5) || MATCHED(9) || MATCHED(10))
+ trace_line = 1;
+ else if (stack_line && !MATCHED(6)) {
+ print = 0;
+ trace_line = 0;
+ }
+ /* delay splitting into strings until we really them */
+ if (MATCHED(8)) {
+ re_string_check(re_Oops_print_s.re_nsub+1, string_max,
+ procname);
+ re_strings(&re_Oops_print_s, *text,
+ re_Oops_print_s_pmatch,
+ string);
+ add_Version((*string)[8]+8, "Oops");
+ }
+ }
+
+ /* These patterns can appear anywhere in the line, after stripping
+ * the prefix above.
+ */
+ re_compile(&re_Oops_print_a,
+ /* arch type */
+
+ /* various */ "(Unable to handle kernel)"
+ /* various */ "|(Aiee)" /* anywhere in text is a bad sign (TM) */
+ /* various */ "|(die_if_kernel)" /* ditto */
+
+ /* sparc */ "|(\\([0-9]\\): Oops )"
+ /* sparc */ "|(: memory violation)"
+ /* sparc */ "|(: Exception at)"
+ /* sparc */ "|(: Arithmetic fault)"
+ /* sparc */ "|(: Instruction fault)"
+ /* sparc */ "|(: arithmetic trap)"
+ /* sparc */ "|(: unaligned trap)"
+
+ /* sparc die_if_kernel has no fixed text, identify by (pid): text.
+ * Somebody has been playful with the texts.
+ *
+ * Alas adding this next pattern increases run time by 15% on
+ * its own! It would be considerably faster if sparc had
+ * consistent error texts.
+ */
+ /* sparc */ "|("
+ "\\([0-9]+\\): "
+ "("
+ "(Whee)"
+ "|(Oops)"
+ "|(Kernel)"
+ "|(Penguin)"
+ "|(Too many Penguin)"
+ "|(BOGUS)"
+ ")"
+ ")"
+
+ /* ppc */ "|(kernel pc )"
+ /* ppc */ "|(trap at PC: )"
+ /* ppc */ "|(bad area pc )"
+ /* ppc */ "|(NIP: )"
+
+ /* MIPS */ "|( ra *=)"
+
+ ")",
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_print_a_pmatch);
+
+ i = regexec(&re_Oops_print_a, *text, re_Oops_print_a.re_nsub+1,
+ re_Oops_print_a_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec anywhere %d\n", procname, i);
+ if (i == 0)
+ print = 1;
+
+ return(print);
+}
+
+/* Look for the Code: line. Returns start of the code bytes. */
+static const char *Oops_code(const char *line, char ***string, int string_max)
+{
+ int i;
+ static regex_t re_Oops_code;
+ static regmatch_t *re_Oops_code_pmatch;
+ static const char procname[] = "Oops_code";
+
+ /* Oops 'Code: ' hopefully followed by at least one hex code. sparc
+ * brackets the PC in '<' and '>'. ARM brackets the PC in '(' and ')'.
+ */
+ re_compile(&re_Oops_code,
+ "^(" /* 1 */
+ /* sparc */ "(Instruction DUMP)" /* 2 */
+ /* various */ "|(Code *)" /* 3 */
+ ")"
+ ":[ \t]+"
+ "(" /* 4 */
+ "(general protection.*)"
+ "|(<[0-9]+>)"
+ "|(([<(]?[0-9a-fA-F]+[>)]?[ \t]*)+)"
+ ")"
+ "(.*)$" /* trailing garbage */
+ ,
+ REG_NEWLINE|REG_EXTENDED|REG_ICASE,
+ &re_Oops_code_pmatch);
+
+ i = regexec(&re_Oops_code, line, re_Oops_code.re_nsub+1,
+ re_Oops_code_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0) {
+ re_string_check(re_Oops_code.re_nsub+1, string_max, procname);
+ re_strings(&re_Oops_code, line, re_Oops_code_pmatch,
+ string);
+ if ((*string)[re_Oops_code.re_nsub] &&
+ *((*string)[re_Oops_code.re_nsub])) {
+ fprintf(stderr,
+ "Warning: trailing garbage ignored on Code: "
+ "line\n"
+ " Text: '%s'\n"
+ " Garbage: '%s'\n",
+ line, (*string)[re_Oops_code.re_nsub]);
+ ++warnings;
+ }
+ return((*string)[4]);
+ }
+ return(NULL);
+}
+
+/******************************************************************************/
+/* End architecture sensitive code */
+/******************************************************************************/
+
+/* Decode the Oops Code: via objdump*/
+static void Oops_decode(const unsigned char* code_text, elf_addr_t eip,
+ SYMBOL_SET *ss, char ***string, int string_max,
+ int code_bytes)
+{
+ FILE *f;
+ char *file, *line = NULL, code[CODE_SIZE];
+ int size = 0, adjust;
+ static char const procname[] = "Oops_decode";
+
+ if (debug)
+ fprintf(stderr, "DEBUG: %s\n", procname);
+ /* text to binary */
+ if (!Oops_code_values(code_text, code, &adjust, string, string_max,
+ code_bytes))
+ return;
+ /* binary to same format as ksymoops */
+ if (!(file = Oops_code_to_file(code, CODE_SIZE)))
+ return;
+ /* objdump the pseudo object */
+ if (!(f = Oops_objdump(file)))
+ return;
+ while (fgets_local(&line, &size, f, procname)) {
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s - %s\n", procname, line);
+ Oops_decode_one(ss, line, eip, adjust);
+ }
+ pclose_local(f, procname); /* opened in Oops_objdump */
+ free(line);
+ if (unlink(file)) {
+ fprintf(stderr, "%s could not unlink %s", prefix, file);
+ perror(" ");
+ }
+}
+
+/* Reached the end of an Oops report, format the extracted data. */
+static void Oops_format(const SYMBOL_SET *ss_format)
+{
+ int i;
+ SYMBOL *s;
+ static const char procname[] = "Oops_format";
+
+ if (debug)
+ fprintf(stderr, "DEBUG: %s\n", procname);
+
+ compare_Version(); /* Oops might have a version one day */
+ printf("\n");
+ for (s = ss_format->symbol, i = 0; i < ss_format->used; ++i, ++s) {
+ /* For type C data, print Code:, address, map, "name" (actually
+ * the text of an objdump line). For other types print name,
+ * address, map.
+ */
+ if (s->type == 'C')
+ printf("Code: %s %-30s %s\n",
+ format_address(s->address),
+ map_address(&ss_merged, s->address),
+ s->name);
+ else
+ printf("%s %s %s\n",
+ s->name,
+ format_address(s->address),
+ map_address(&ss_merged, s->address));
+ }
+ printf("\n");
+}
+
+/* Select next Oops input file */
+static FILE *Oops_next_file(int *filecount, char * const **filename)
+{
+ static FILE *f = NULL;
+ static const char procname[] = "Oops_next_file";
+ static int first_file = 1;
+
+ if (first_file) {
+ f = stdin;
+ first_file = 0;
+ }
+ while (*filecount) {
+ if (f)
+ fclose_local(f, procname);
+ f = NULL;
+ if (regular_file(**filename, procname))
+ f = fopen_local(**filename, "r", procname);
+ if (f) {
+ if (debug)
+ fprintf(stderr,
+ "DEBUG: reading Oops report "
+ "from %s\n", **filename);
+ }
+ ++*filename;
+ --*filecount;
+ if (f)
+ return(f);
+ }
+ return(f);
+}
+
+/* Read the Oops report */
+#define MAX_STRINGS 300 /* Maximum strings in any Oops re */
+int Oops_read(int filecount, char * const *filename, int code_bytes,
+ int one_shot)
+{
+ char *line = NULL, **string = NULL;
+ const char *start, *text;
+ int i, size = 0, lineno = 0, lastprint = 0;
+ elf_addr_t eip = 0;
+ FILE *f;
+ SYMBOL_SET ss_format;
+ static const char procname[] = "Oops_read";
+
+ ss_init(&ss_format, "Oops log data");
+
+ if (!filecount && isatty(0))
+ printf("Reading Oops report from the terminal\n");
+
+ string = malloc(MAX_STRINGS*sizeof(*string));
+ if (!string)
+ malloc_error(procname);
+ memset(string, '\0', MAX_STRINGS*sizeof(*string));
+
+ do {
+ if (!(f = Oops_next_file(&filecount, &filename)))
+ continue;
+ while (fgets_local(&line, &size, f, procname)) {
+ if (debug > 2)
+ fprintf(stderr,
+ "DEBUG: %s - %s\n", procname, line);
+ ++lineno;
+ if (Oops_print(line, &text, &string, MAX_STRINGS)) {
+ puts(line);
+ lastprint = lineno;
+ if ((start = Oops_eip(text,
+ &string, MAX_STRINGS)))
+ Oops_set_eip(start, &eip, &ss_format);
+ if ((start = Oops_ra(text,
+ &string, MAX_STRINGS)))
+ Oops_set_ra(start, &ss_format);
+ if ((start = Oops_trace(text,
+ &string, MAX_STRINGS)))
+ Oops_trace_line(text, start,
+ &ss_format);
+ if ((start = Oops_code(text,
+ &string, MAX_STRINGS))) {
+ Oops_decode(start, eip, &ss_format,
+ &string, MAX_STRINGS,
+ code_bytes);
+ Oops_format(&ss_format);
+ ss_free(&ss_format);
+ if (one_shot)
+ return(0);
+ }
+ }
+ /* More than 5 (arbitrary) lines which were not printed
+ * and there is some saved data, assume we missed the
+ * Code: line.
+ */
+ if (ss_format.used && lineno > lastprint+5) {
+ fprintf(stderr,
+ "Warning, Code line not seen, dumping "
+ "what data is available\n");
+ ++warnings;
+ Oops_format(&ss_format);
+ ss_free(&ss_format);
+ if (one_shot)
+ return(0);
+ }
+ }
+ if (ss_format.used) {
+ fprintf(stderr,
+ "Warning, Code line not seen, dumping "
+ "what data is available\n");
+ ++warnings;
+ Oops_format(&ss_format);
+ ss_free(&ss_format);
+ if (one_shot)
+ return(0);
+ }
+ } while (filecount != 0);
+
+ for (i = 0; i < sizeof(string); ++i) {
+ free(string[i]);
+ string[i] = NULL;
+ }
+ free(line);
+ if (one_shot)
+ return(3); /* one shot mode, end of input, no data */
+ return(0);
+}
diff --git a/scripts/ksymoops/re.c b/scripts/ksymoops/re.c
new file mode 100644
index 000000000..9c65832f7
--- /dev/null
+++ b/scripts/ksymoops/re.c
@@ -0,0 +1,145 @@
+/*
+ re.c.
+
+ Regular expression processing for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ PPC trace addresses are not bracketed, add new re.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <malloc.h>
+#include <string.h>
+
+/* Compile a regular expression */
+void re_compile(regex_t *preg, const char *regex, int cflags,
+ regmatch_t **pmatch)
+{
+ int i, l;
+ char *p;
+ static char const procname[] = "re_compile";
+
+ if (preg->re_nsub)
+ return; /* already compiled */
+
+ if (debug)
+ fprintf(stderr, "DEBUG: %s '%s'", procname, regex);
+ if ((i = regcomp(preg, regex, cflags))) {
+ l = regerror(i, preg, NULL, 0);
+ ++l; /* doc is ambiguous, be safe */
+ p = malloc(l);
+ if (!p)
+ malloc_error("regerror text");
+ regerror(i, preg, p, l);
+ fprintf(stderr,
+ "%s: fatal %s error on '%s' - %s\n",
+ prefix, procname, regex, p);
+ exit(2);
+ }
+ if (debug)
+ fprintf(stderr, " %d sub expression(s)\n", preg->re_nsub);
+ /* [0] is entire match, [1] is first substring */
+ *pmatch = malloc((preg->re_nsub+1)*sizeof(**pmatch));
+ if (!*pmatch)
+ malloc_error("pmatch");
+
+}
+
+/* Compile common regular expressions */
+void re_compile_common(void)
+{
+
+ /* nm: address, type, symbol */
+ re_compile(&re_nm,
+ "^([0-9a-fA-F]{4,}) +([^ ]) +([^ ]+)$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_nm_pmatch);
+
+ /* bracketed address preceded by optional white space */
+ re_compile(&re_bracketed_address,
+ "^[ \t]*" BRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED,
+ &re_bracketed_address_pmatch);
+
+ /* unbracketed address preceded by optional white space */
+ re_compile(&re_unbracketed_address,
+ "^[ \t*]*" UNBRACKETED_ADDRESS,
+ REG_NEWLINE|REG_EXTENDED,
+ &re_unbracketed_address_pmatch);
+
+}
+
+/* Split text into the matching re substrings - Perl is so much easier :).
+ * Each element of *string is set to a malloced copy of the substring or
+ * NULL if the substring did not match (undef). A zero length substring match
+ * is represented by a zero length **string.
+ */
+void re_strings(regex_t *preg, const char *text, regmatch_t *pmatch,
+ char ***string)
+{
+ int i;
+ if (!*string) {
+ *string = malloc((preg->re_nsub+1)*sizeof(**string));
+ if (!*string)
+ malloc_error("re_strings base");
+ for (i = 0; i < preg->re_nsub+1; ++i)
+ (*string)[i] = NULL;
+ }
+ for (i = 0; i < preg->re_nsub+1; ++i) {
+ if (debug > 4)
+ fprintf(stderr,
+ "DEBUG: re_string %d offsets %d %d",
+ i, pmatch[i].rm_so, pmatch[i].rm_eo);
+ if (pmatch[i].rm_so == -1) {
+ /* no match for this sub expression */
+ free((*string)[i]);
+ (*string)[i] = NULL;
+ if (debug > 4)
+ fprintf(stderr, " (undef)\n");
+ }
+ else {
+ int l = pmatch[i].rm_eo - pmatch[i].rm_so + 1;
+ char *p;
+ p = malloc(l);
+ if (!p)
+ malloc_error("re_strings");
+ strncpy(p, text+pmatch[i].rm_so, l-1);
+ *(p+l-1) = '\0';
+ (*string)[i] = p;
+ if (debug > 4)
+ fprintf(stderr, " '%s'\n", p);
+ }
+ }
+}
+
+/* Free the matching re substrings */
+void re_strings_free(const regex_t *preg, char ***string)
+{
+ if (*string) {
+ int i;
+ for (i = 0; i < preg->re_nsub+1; ++i)
+ free((*string)[i]);
+ free(*string);
+ *string = NULL;
+ }
+}
+
+/* Check that there are enough strings for an re */
+void re_string_check(int need, int available, const char *msg)
+{
+ if (need > available) {
+ fprintf(stderr,
+ "%s: fatal not enough re_strings in %s. "
+ "Need %d, available %d\n",
+ prefix, msg, need, available);
+ exit(2);
+ }
+}
diff --git a/scripts/ksymoops/symbol.c b/scripts/ksymoops/symbol.c
new file mode 100644
index 000000000..5c66cc994
--- /dev/null
+++ b/scripts/ksymoops/symbol.c
@@ -0,0 +1,444 @@
+/*
+ symbol.c.
+
+ Symbol handling routines for ksymoops.
+
+ Copyright Keith Owens <kaos@ocs.com.au>.
+ Released under the GNU Public Licence, Version 2.
+
+ Mon Jan 4 09:08:19 EST 1999
+ Version 0.6d
+ Cast Version to int, glibc 2.1 made elf_addr_t a long.
+
+ Tue Nov 3 02:31:01 EST 1998
+ Version 0.6
+ Fix end of code calculation.
+
+ Wed Oct 28 13:47:23 EST 1998
+ Version 0.4
+ Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Initialise a symbol source */
+void ss_init(SYMBOL_SET *ss, const char *msg)
+{
+ memset(ss, '\0', sizeof(*ss));
+ ss->source = strdup(msg);
+ if (!ss->source)
+ malloc_error(msg);
+}
+
+/* Free dynamic data from a symbol source */
+void ss_free(SYMBOL_SET *ss)
+{
+ int i;
+ SYMBOL *s;
+ for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
+ free(s->name);
+ free(ss->symbol);
+ free(ss->source);
+ memset(ss, '\0', sizeof(*ss));
+}
+
+/* Initialise common symbol sets */
+void ss_init_common(void)
+{
+ ss_init(&ss_Version, "Version_");
+}
+
+/* Find a symbol name in a symbol source. Brute force ascending order search,
+ * no hashing. If start is not NULL, it contains the starting point for the
+ * scan and is updated to point to the found entry. If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by name.
+ */
+SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
+{
+ int i, l;
+ SYMBOL *s;
+ for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+ if ((l = strcmp(symbol, s->name)) == 0) {
+ if (start)
+ *start = i;
+ return(s);
+ }
+ if (l < 0)
+ break;
+ }
+ if (start)
+ *start = i;
+ return NULL;
+}
+
+/* Find an address in a symbol source. Brute force ascending order search, no
+ * hashing. If start is not NULL, it contains the starting point for the scan
+ * and is updated to point to the found entry. If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by address.
+ */
+static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
+ const elf_addr_t address, int *start)
+{
+ int i;
+ SYMBOL *s;
+ for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+ if (address > s->address)
+ continue;
+ else if (address == s->address) {
+ if (start)
+ *start = i;
+ return(s);
+ }
+ else
+ break;
+ }
+ if (start)
+ *start = i;
+ return NULL;
+}
+
+/* Add a symbol to a symbol set, address in binary */
+void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+ const char type, const char keep, const char *symbol)
+{
+ int i;
+ char **string = NULL;
+ SYMBOL *s;
+ static regex_t re_symbol_ver;
+ static regmatch_t *re_symbol_ver_pmatch;
+ static const char procname[] = "add_symbol_n";
+
+ /* Strip out any trailing symbol version _Rxxxxxxxx. */
+ re_compile(&re_symbol_ver,
+ "^(.*)_R[0-9a-fA-F]{8,}$",
+ REG_NEWLINE|REG_EXTENDED,
+ &re_symbol_ver_pmatch);
+
+ i = regexec(&re_symbol_ver, symbol,
+ re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+ if (i == 0)
+ re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
+ &string);
+
+ if (debug > 3)
+ fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
+ procname, ss->source, format_address(address),
+ type, keep, i == 0 ? string[1] : symbol);
+ if (ss->used > ss->alloc) {
+ fprintf(stderr,
+ "%s: fatal %s ss %s used (%d) > alloc (%d)\n",
+ procname, prefix, ss->source, ss->used, ss->alloc);
+ exit(2);
+ }
+ if (ss->used == ss->alloc) {
+ /* increase by 20% or 10, whichever is larger, arbitrary */
+ int newsize = ss->alloc*120/100;
+ if (newsize < ss->alloc+10)
+ newsize = ss->alloc+10;
+ if (debug > 3)
+ fprintf(stderr,
+ "DEBUG: %s increasing %s from %d to %d "
+ "entries\n",
+ procname, ss->source, ss->alloc, newsize);
+ ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
+ if (!ss->symbol)
+ malloc_error("realloc ss");
+ ss->alloc = newsize;
+ }
+ s = ss->symbol+ss->used;
+ if (i == 0) {
+ s->name = string[1];
+ string[1] = NULL; /* don't free this one */
+ }
+ else {
+ s->name = strdup(symbol);
+ if (!s->name)
+ malloc_error("strdup symbol");
+ }
+ s->type = type;
+ s->keep = keep;
+ s->address = address;
+ ++ss->used;
+ re_strings_free(&re_symbol_ver, &string);
+}
+
+/* Add a symbol to a symbol set, address in character */
+void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+ const char keep, const char *symbol)
+{
+ elf_addr_t a;
+ static char const procname[] = "add_symbol";
+ errno = 0;
+ a = strtoul(address, NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "%s: %s address '%s' is in error",
+ prefix, procname, address);
+ perror(" ");
+ ++errors;
+ }
+ add_symbol_n(ss, a, type, 1, symbol);
+}
+
+/* Map an address to symbol, offset and length, address in binary */
+char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
+{
+ int i = 0, l;
+ SYMBOL *s;
+ static char *map = NULL;
+ static int size = 0;
+ static const char procname[] = "map_address_n";
+
+ if (debug > 2)
+ fprintf(stderr, "DEBUG: %s %s %s\n",
+ procname, ss->source, format_address(address));
+ s = find_symbol_address(ss, address, &i);
+ if (!s && --i >= 0)
+ s = ss->symbol+i; /* address is between s and s+1 */
+
+ /* Extra map text is always < 100 bytes */
+ if (s)
+ l = strlen(s->name) + 100;
+ else
+ l = 100;
+ if (l > size) {
+ map = realloc(map, l);
+ if (!map)
+ malloc_error(procname);
+ size = l;
+ }
+ if (!s) {
+ if (ss->used == 0)
+ snprintf(map, size, "No symbols available");
+ else
+ snprintf(map, size, "Before first symbol");
+ }
+ else if ((i+1) >= ss->used) {
+ /* Somewhere past last symbol. Length of last section of code
+ * is unknown, arbitrary cutoff at 32K.
+ */
+ elf_addr_t offset = address - s->address;
+ if (offset > 32768)
+ snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
+ else
+ snprintf(map, size, "<%s+%lx/????>", s->name, offset);
+ }
+ else
+ snprintf(map, size,
+ "<%s+%lx/%lx>",
+ s->name, address - s->address,
+ (s+1)->address - s->address);
+ return(map);
+}
+
+/* After sorting, obsolete symbols are at the top. Delete them. */
+static void ss_compress(SYMBOL_SET *ss)
+{
+ int i, j;
+ SYMBOL *s;
+ static const char procname[] = "ss_compress";
+
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s on table %s, before %d ",
+ procname, ss->source, ss->used);
+ for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+ if (!s->keep) {
+ for (j = i; j < ss->used; ++j, ++s) {
+ if (s->keep) {
+ fprintf(stderr,
+ "%s: fatal %s table %s is not "
+ "sorted\n",
+ prefix, procname, ss->source);
+ exit(2);
+ }
+ }
+ break;
+ }
+ }
+ for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
+ free(s->name);
+ ss->used = i;
+ if (debug > 1)
+ fprintf(stderr, "after %d\n", ss->used);
+}
+
+static int ss_compare_atn(const void *a, const void *b)
+{
+ SYMBOL *c = (SYMBOL *) a;
+ SYMBOL *d = (SYMBOL *) b;
+ int i;
+
+ /* obsolete symbols to the top */
+ if (c->keep != d->keep)
+ return(d->keep - c->keep);
+ if (c->address > d->address)
+ return(1);
+ if (c->address < d->address)
+ return(-1);
+ if (c->type > d->type)
+ return(1);
+ if (c->type < d->type)
+ return(-1);
+ if ((i = strcmp(c->name, d->name)))
+ return(i);
+ return(0);
+}
+
+/* Sort a symbol set by address, type and name */
+void ss_sort_atn(SYMBOL_SET *ss)
+{
+ if (debug)
+ fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
+ ss->source);
+ qsort((char *) ss->symbol, (unsigned) ss->used,
+ sizeof(*(ss->symbol)), ss_compare_atn);
+ ss_compress(ss);
+}
+
+static int ss_compare_na(const void *a, const void *b)
+{
+ SYMBOL *c = (SYMBOL *) a;
+ SYMBOL *d = (SYMBOL *) b;
+ int i;
+
+ /* obsolete symbols to the top */
+ if (c->keep != d->keep)
+ return(d->keep - c->keep);
+ if ((i = strcmp(c->name, d->name)))
+ return(i);
+ if (c->address > d->address)
+ return(1);
+ if (c->address < d->address)
+ return(-1);
+ return(0);
+}
+
+/* Sort a symbol set by name and address, drop duplicates. There should be
+ * no duplicates but I have seen duplicates in ksyms on 2.0.35.
+ */
+void ss_sort_na(SYMBOL_SET *ss)
+{
+ int i;
+ SYMBOL *s;
+ if (debug)
+ fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
+ ss->source);
+ qsort((char *) ss->symbol, (unsigned) ss->used,
+ sizeof(*(ss->symbol)), ss_compare_na);
+ ss_compress(ss);
+ s = ss->symbol;
+ for (i = 0; i < ss->used-1; ++i) {
+ if (strcmp(s->name, (s+1)->name) == 0 &&
+ s->address == (s+1)->address) {
+ if (s->type != ' ')
+ (s+1)->keep = 0;
+ else
+ s->keep = 0;
+ }
+ ++s;
+ }
+ qsort((char *) ss->symbol, (unsigned) ss->used,
+ sizeof(*(ss->symbol)), ss_compare_na);
+ ss_compress(ss);
+}
+
+/* Copy a symbol set, including all its strings */
+SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
+{
+ SYMBOL_SET *ssc;
+ if (debug > 3)
+ fprintf(stderr,
+ "DEBUG: ss_copy %s\n", ss->source);
+ ssc = malloc(sizeof(*ssc));
+ if (!ssc)
+ malloc_error("copy ssc");
+ ss_init(ssc, ss->source);
+ ssc->used = ss->used;
+ ssc->alloc = ss->used; /* shrink the copy */
+ ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
+ if (!(ssc->symbol))
+ malloc_error("copy ssc symbols");
+ memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
+ return(ssc);
+}
+
+/* Convert version number to major, minor string. */
+static const char *format_Version(elf_addr_t Version)
+{
+ static char string[12]; /* 255.255.255\0 worst case */
+ snprintf(string, sizeof(string), "%d.%d.%d",
+ (int) ((Version >> 16) & 0xff),
+ (int) ((Version >> 8) & 0xff),
+ (int) ((Version) & 0xff));
+ return(string);
+}
+
+/* Save version number. The "address" is the version number, the "symbol" is
+ * the source of the version.
+ */
+void add_Version(const char *version, const char *source)
+{
+ static char const procname[] = "add_Version";
+ int i = atoi(version);
+ if (debug > 1)
+ fprintf(stderr, "DEBUG: %s %s %s %s\n",
+ procname, source, version, format_Version(i));
+ add_symbol_n(&ss_Version, i, 'V', 1, source);
+}
+
+/* Extract Version_ number from a symbol set and save it. */
+void extract_Version(SYMBOL_SET *ss)
+{
+ int i = 0;
+ SYMBOL *s;
+
+ s = find_symbol_name(ss, "Version_", &i);
+ if (!s && i < ss->used)
+ s = ss->symbol+i; /* first symbol after "Version_" */
+ if (!s || strncmp(s->name, "Version_", 8))
+ return;
+ add_Version(s->name+8, ss->source);
+}
+
+/* Compare all extracted Version numbers. Silent unless there is a problem. */
+void compare_Version(void)
+{
+ int i = 0;
+ SYMBOL *s, *s0;
+ static int prev_used = 0;
+
+ if (!ss_Version.used)
+ return;
+ /* Only check if the Version table has changed in size */
+ if (prev_used == ss_Version.used)
+ return;
+
+ ss_sort_na(&ss_Version);
+ s0 = s = ss_Version.symbol;
+ if (debug)
+ fprintf(stderr, "DEBUG: Version %s\n",
+ format_Version(s0->address));
+ for (i = 0; i < ss_Version.used; ++i, ++s) {
+ if (s->address != s0->address) {
+ fprintf(stderr,
+ "Version mismatch error. %s says %s, ",
+ s0->name,
+ format_Version(s0->address));
+ fprintf(stderr,
+ "%s says %s. Expect lots of address "
+ "mismatches.\n",
+ s->name,
+ format_Version(s->address));
+ ++errors;
+ }
+ }
+ prev_used = ss_Version.used;
+}
diff --git a/scripts/lxdialog/menubox.c b/scripts/lxdialog/menubox.c
index d6170ec46..638feee6d 100644
--- a/scripts/lxdialog/menubox.c
+++ b/scripts/lxdialog/menubox.c
@@ -48,6 +48,12 @@
* *) If for some reason the last scrolling position is not saved by
* lxdialog, it sets the scrolling so that the selected item is in the
* middle of the menu box, not at the bottom.
+ *
+ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+ * This fixes a bug in Menuconfig where using ' ' to descend into menus
+ * would leave mis-synchronized lxdialog.scrltmp files lying around,
+ * fscanf would read in 'scroll', and eventually that value would get used.
*/
#include "dialog.h"
@@ -227,6 +233,7 @@ dialog_menu (const char *title, const char *prompt, int height, int width,
choice = choice - scroll;
fclose(f);
} else {
+ scroll=0;
remove("lxdialog.scrltmp");
fclose(f);
f=NULL;
diff --git a/scripts/mkdep.c b/scripts/mkdep.c
index a05d2faac..8b009041b 100644
--- a/scripts/mkdep.c
+++ b/scripts/mkdep.c
@@ -210,9 +210,9 @@ void use_config(const char * name, int len)
#define GETNEXT { \
next_byte(__buf); \
if ((unsigned long) next % sizeof(unsigned long) == 0) { \
- __buf = * (unsigned long *) next; \
- if (!__buf) \
+ if (next >= end) \
break; \
+ __buf = * (unsigned long *) next; \
} \
next++; \
}
@@ -228,8 +228,8 @@ void use_config(const char * name, int len)
*/
#define MAX2(a,b) ((a)>(b)?(a):(b))
#define MIN2(a,b) ((a)<(b)?(a):(b))
-#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
-#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
+#define MAX6(a,b,c,d,e,f) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,MAX2(e,f))))))
+#define MIN6(a,b,c,d,e,f) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,MIN2(e,f))))))
@@ -243,14 +243,15 @@ void use_config(const char * name, int len)
* m|#\s*include\s*<(.*?>"|
* m|#\s*(?define|undef)\s*CONFIG_(\w*)|
* m|(?!\w)CONFIG_|
+ * m|__SMP__|
*
* About 98% of the CPU time is spent here, and most of that is in
* the 'start' paragraph. Because the current characters are
* in a register, the start loop usually eats 4 or 8 characters
- * per memory read. The MAX5 and MIN5 tests dispose of most
+ * per memory read. The MAX6 and MIN6 tests dispose of most
* input characters with 1 or 2 comparisons.
*/
-void state_machine(const char * map)
+void state_machine(const char * map, const char * end)
{
const char * next = map;
const char * map_dot;
@@ -260,13 +261,14 @@ void state_machine(const char * map)
start:
GETNEXT
__start:
- if (current > MAX5('/','\'','"','#','C')) goto start;
- if (current < MIN5('/','\'','"','#','C')) goto start;
+ if (current > MAX6('/','\'','"','#','C','_')) goto start;
+ if (current < MIN6('/','\'','"','#','C','_')) goto start;
CASE('/', slash);
CASE('\'', squote);
CASE('"', dquote);
CASE('#', pound);
CASE('C', cee);
+ CASE('_', underscore);
goto start;
/* / */
@@ -400,6 +402,18 @@ cee_CONFIG_word:
goto cee_CONFIG_word;
use_config(map_dot, next - map_dot - 1);
goto __start;
+
+/* __SMP__ */
+underscore:
+ GETNEXT NOTCASE('_', __start);
+ GETNEXT NOTCASE('S', __start);
+ GETNEXT NOTCASE('M', __start);
+ GETNEXT NOTCASE('P', __start);
+ GETNEXT NOTCASE('_', __start);
+ GETNEXT NOTCASE('_', __start);
+ use_config("SMP", 3);
+ goto __start;
+
}
}
@@ -429,7 +443,7 @@ void do_depend(const char * filename, const char * command)
return;
}
- mapsize = st.st_size + 2*sizeof(unsigned long);
+ mapsize = st.st_size;
mapsize = (mapsize+pagesizem1) & ~pagesizem1;
map = mmap(NULL, mapsize, PROT_READ, MAP_AUTOGROW | MAP_PRIVATE, fd, 0);
if ((long) map == -1) {
@@ -445,7 +459,7 @@ void do_depend(const char * filename, const char * command)
hasdep = 0;
clear_config();
- state_machine(map);
+ state_machine(map, map+st.st_size);
if (hasdep)
puts(command);
diff --git a/scripts/tail.tk b/scripts/tail.tk
index 2a727fe89..f088fee42 100644
--- a/scripts/tail.tk
+++ b/scripts/tail.tk
@@ -1,9 +1,12 @@
+# FILE: tail.tk
+# This file is boilerplate TCL/TK function definitions for 'make xconfig'.
+#
+# CHANGES
+# =======
+#
+# 8 January 1998, Michael Elizabeth Chastain, <mec@shout.net>
+# Arrange buttons in three columns for better screen fitting.
#
-# 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
@@ -28,53 +31,47 @@ if { [file readable .config] == 1} then {
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.right.save -anchor w -text "Save and Exit" \
+ -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.right.quit -anchor w -text "Quit Without Saving" \
+ -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.right.load -anchor w -text "Load Configuration from File" \
+ -command { load_configfile .load "Load Configuration from file" read_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
-}
+button .f0.right.store -anchor w -text "Store Configuration to File" \
+ -command { load_configfile .load "Store Configuration to file" write_config_file }
#
-# Now pack everything, important things first because of small screens.
+# Now pack everything.
#
-pack .f0_bot.r.save .f0_bot.r.quit -padx 25 -ipadx 10 -expand on
-pack .f0_bot.l.load .f0_bot.l.store -padx 25 -ipadx 10 -expand on
-
-pack .f0_bot.r -side left -padx 15 -expand on -fill y
-pack .f0_bot.l -side right -padx 15 -expand on -fill y
-
-pack .f0_bot -side bottom -fill both -expand on -pady 4
-pack .f0 -side bottom -padx 15 -pady 0 -fill y -expand on
-pack .header -padx 10 -pady 7 -expand on
+pack .f0.right.store .f0.right.load .f0.right.quit .f0.right.save \
+ -padx 0 -pady 0 -side bottom -fill x
+pack .f0.left .f0.middle .f0.right -side left -padx 5 -pady 0 -fill y
+pack .f0 -padx 5 -pady 5
#
# 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
+ .f0.right.save configure -state disabled
}
} else {
if { [file writable .] == 0 } then {
- .f0_bot.r.save configure -state disabled
+ .f0.right.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
+ .f0.right.save configure -state disabled
}
} else {
if { [file writable include/linux/] == 0 } then {
- .f0_bot.r.save configure -state disabled
+ .f0.right.save configure -state disabled
}
}
diff --git a/scripts/tkcond.c b/scripts/tkcond.c
index fc133295d..89069daad 100644
--- a/scripts/tkcond.c
+++ b/scripts/tkcond.c
@@ -1,544 +1,359 @@
-/* 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.
+/*
+ * tkcond.c
*
- * The conditions are assumed to be one of the following formats
+ * Eric Youngdale was the original author of xconfig.
+ * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
*
- * simple_condition:= "$VARIABLE" == y/n/m
- * simple_condition:= "$VARIABLE != y/n/m
+ * This file takes the tokenized statement list and transforms 'if ...'
+ * statements. For each simple statement, I find all of the 'if' statements
+ * that enclose it, and attach the aggregate conditionals of those 'if'
+ * statements to the cond list of the simple statement.
*
- * simple_condition -a simple_condition
+ * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
+ * - Steam-clean this file. I tested this by generating kconfig.tk for
+ * every architecture and comparing it character-for-character against
+ * the output of the old tkparse.
*
- * If the input condition contains '(' or ')' it would screw us up, but for now
- * this is not a problem.
+ * TO DO:
+ * - xconfig is at the end of its life cycle. Contact <mec@shout.net> if
+ * you are interested in working on the replacement.
*/
-#include <stdlib.h>
+
#include <stdio.h>
+#include <stdlib.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.
+ * Transform op_variable to op_kvariable.
+ *
+ * This works, but it's gross, speed-wise. It would benefit greatly
+ * from a simple hash table that maps names to cfg.
+ *
+ * Note well: this is actually better than the loop structure xconfig
+ * has been staggering along with for three years, which performs
+ * this whole procedure inside *another* loop on active conditionals.
*/
-void fix_choice_cond(void)
+void transform_to_kvariable( struct kconfig * scfg )
{
- struct condition * cond;
- struct condition * cond2;
- struct kconfig * cfg;
- char tmpbuf[255];
+ struct kconfig * cfg;
- for(cfg = config;cfg != NULL; cfg = cfg->next)
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
{
- if( cfg->cond == NULL )
- {
- continue;
- }
+ struct condition * cond;
- for(cond = cfg->cond; cond != NULL; cond = cond->next)
+ 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 )
+ if ( cond->op == op_variable )
{
- cond->variable.cfg = cond->variable.cfg->choice_label;
- cond2->variable.str = strdup(tmpbuf);
+ /* Here's where it gets DISGUSTING. */
+ struct kconfig * cfg1;
+
+ for ( cfg1 = scfg; cfg1 != NULL; cfg1 = cfg1->next )
+ {
+ if ( cfg1->token == token_bool
+ || cfg1->token == token_choice_item
+ || cfg1->token == token_dep_tristate
+ || cfg1->token == token_hex
+ || cfg1->token == token_int
+ || cfg1->token == token_string
+ || cfg1->token == token_tristate )
+ {
+ if ( strcmp( cond->str, cfg1->optionname ) == 0 )
+ {
+ cond->op = op_kvariable;
+ cond->str = NULL;
+ cond->cfg = cfg1;
+ break;
+ }
+ }
+ }
}
- else
+
+#if 0
+ /*
+ * Maybe someday this will be useful, but right now it
+ * gives a lot of false positives on files like
+ * drivers/video/Config.in that are meant for more
+ * than one architecture. Turn it on if you want to play
+ * with it though; it does work. -- mec
+ */
+ if ( cond->op == op_variable )
{
- fprintf(stderr,"Ooops\n");
- exit(0);
+ if ( strcmp( cond->str, "ARCH" ) != 0
+ && strcmp( cond->str, "CONSTANT_Y" ) != 0
+ && strcmp( cond->str, "CONSTANT_M" ) != 0
+ && strcmp( cond->str, "CONSTANT_N" ) != 0 )
+ {
+ fprintf( stderr, "warning: $%s used but not defined\n",
+ cond->str );
+ }
}
+#endif
}
-
}
}
+
+
/*
- * 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.
+ * Make a new condition chain by joining the current condition stack with
+ * the "&&" operator for glue.
*/
-struct condition * get_token_cond(struct condition ** cond, int depth)
+struct condition * join_condition_stack( struct condition * conditions [],
+ 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++)
+ struct condition * cond_list;
+ struct condition * cond_last;
+ int i;
+
+ cond_list = cond_last = NULL;
+ for ( i = 0; i < depth; i++ )
{
- /*
- * First insert the left parenthesis
- */
- new = (struct condition *) malloc(sizeof(struct condition));
- memset(new, 0, sizeof(*new));
- new->op = op_lparen;
- if( tail == NULL )
+ struct condition * cond;
+ struct condition * cnew;
+
+ /* add a '(' */
+ cnew = malloc( sizeof(*cnew) );
+ memset( cnew, 0, sizeof(*cnew) );
+ cnew->op = op_lparen;
+ if ( cond_last == NULL )
+ { cond_list = cond_last = cnew; }
+ else
+ { cond_last->next = cnew; cond_last = cnew; }
+
+ /* duplicate the chain */
+ for ( cond = conditions [i]; cond != NULL; cond = cond->next )
{
- newcond = tail = new;
+ cnew = malloc( sizeof(*cnew) );
+ cnew->next = NULL;
+ cnew->op = cond->op;
+ cnew->str = cond->str ? strdup( cond->str ) : NULL;
+ cnew->cfg = cond->cfg;
+ cond_last->next = cnew;
+ cond_last = cnew;
}
- else
+
+ /* add a ')' */
+ cnew = malloc( sizeof(*cnew) );
+ memset( cnew, 0, sizeof(*cnew) );
+ cnew->op = op_rparen;
+ cond_last->next = cnew;
+ cond_last = cnew;
+
+ /* if i have another condition, add an '&&' operator */
+ if ( i < depth - 1 )
{
- tail->next = new;
- tail = new;
+ cnew = malloc( sizeof(*cnew) );
+ memset( cnew, 0, sizeof(*cnew) );
+ cnew->op = op_and;
+ cond_last->next = cnew;
+ cond_last = cnew;
}
+ }
- /*
- * Now duplicate the chain.
- */
- ocond = *cond;
- for(;ocond != NULL; ocond = ocond->next)
+ /*
+ * Remove duplicate conditions.
+ */
+ {
+ struct condition *cond1, *cond1b, *cond1c, *cond1d, *cond1e, *cond1f;
+
+ for ( cond1 = cond_list; cond1 != NULL; cond1 = cond1->next )
{
- new = (struct condition *) malloc(sizeof(struct condition));
- memset(new, 0, sizeof(*new));
- new->op = ocond->op;
- if( ocond->variable.str != NULL )
+ if ( cond1->op == op_lparen )
{
- if( ocond->op == op_variable )
+ cond1b = cond1 ->next; if ( cond1b == NULL ) break;
+ cond1c = cond1b->next; if ( cond1c == NULL ) break;
+ cond1d = cond1c->next; if ( cond1d == NULL ) break;
+ cond1e = cond1d->next; if ( cond1e == NULL ) break;
+ cond1f = cond1e->next; if ( cond1f == NULL ) break;
+
+ if ( cond1b->op == op_kvariable
+ && ( cond1c->op == op_eq || cond1c->op == op_neq )
+ && cond1d->op == op_constant
+ && cond1e->op == op_rparen )
{
- /*
- * Search for structure to insert here.
- */
- for(cfg = config;cfg != NULL; cfg = cfg->next)
+ struct condition *cond2, *cond2b, *cond2c, *cond2d, *cond2e, *cond2f;
+
+ for ( cond2 = cond1f->next; cond2 != NULL; cond2 = cond2->next )
{
- if( cfg->tok != tok_bool
- && cfg->tok != tok_int
- && cfg->tok != tok_hex
- && cfg->tok != tok_string
- && cfg->tok != tok_tristate
- && cfg->tok != tok_choice
- && cfg->tok != tok_dep_tristate)
+ if ( cond2->op == op_lparen )
{
- continue;
+ cond2b = cond2 ->next; if ( cond2b == NULL ) break;
+ cond2c = cond2b->next; if ( cond2c == NULL ) break;
+ cond2d = cond2c->next; if ( cond2d == NULL ) break;
+ cond2e = cond2d->next; if ( cond2e == NULL ) break;
+ cond2f = cond2e->next;
+
+ /* look for match */
+ if ( cond2b->op == op_kvariable
+ && cond2b->cfg == cond1b->cfg
+ && cond2c->op == cond1c->op
+ && cond2d->op == op_constant
+ && strcmp( cond2d->str, cond1d->str ) == 0
+ && cond2e->op == op_rparen )
+ {
+ /* one of these must be followed by && */
+ if ( cond1f->op == op_and
+ || ( cond2f != NULL && cond2f->op == op_and ) )
+ {
+ /* nuke the first duplicate */
+ cond1 ->op = op_nuked;
+ cond1b->op = op_nuked;
+ cond1c->op = op_nuked;
+ cond1d->op = op_nuked;
+ cond1e->op = op_nuked;
+ if ( cond1f->op == op_and )
+ cond1f->op = op_nuked;
+ else
+ cond2f->op = op_nuked;
+ }
+ }
}
- 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;
+ return cond_list;
}
-/*
- * 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.
+ * This is the main transformation function.
*/
-void fix_conditionals(struct kconfig * scfg)
+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)
+ struct kconfig * cfg;
+
+ /*
+ * Transform op_variable to op_kvariable.
+ */
+ transform_to_kvariable( scfg );
+
+ /*
+ * Transform conditions that use variables from "choice" statements.
+ * Choice values appear to the user as a collection of booleans, and the
+ * script can test the individual booleans. But internally, all I have is
+ * the N-way value of an unnamed temporary for the whole statement. So I
+ * have to tranform '"$CONFIG_M386" != "y"'
+ * into '"$tmpvar_N" != "CONFIG_M386"'.
+ */
+ 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_string:
- case tok_choice:
- /*
- * 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;
- }
- }
+ struct condition * cond;
- /*
- * 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)
+ for ( cond = cfg->cond; cond != NULL; cond = cond->next )
{
- case tok_bool:
- case tok_tristate:
- case tok_dep_tristate:
- case tok_int:
- case tok_hex:
- case tok_string:
- for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next)
+ if ( cond->op == op_kvariable && cond->cfg->token == token_choice_item )
{
- switch(cfg1->tok)
+ /*
+ * Look two more tokens down for the comparison token.
+ * It has to be "y" for this trick to work.
+ *
+ * If you get this error, don't even think about relaxing the
+ * strcmp test. You will produce incorrect TK code. Instead,
+ * look for the place in your Config.in script where you are
+ * comparing a 'choice' variable to a value other than 'y',
+ * and rewrite the comparison to be '= "y"' or '!= "y"'.
+ */
+ struct condition * cond2 = cond->next->next;
+ const char * label;
+
+ if ( strcmp( cond2->str, "y" ) != 0 )
{
- case tok_define:
- case tok_bool:
- case tok_tristate:
- case tok_dep_tristate:
- case tok_int:
- case tok_hex:
- case tok_string:
- if( strcmp(cfg->optionname, cfg1->optionname) == 0)
- {
- cfg->flags |= CFG_DUP;
- cfg1->flags |= CFG_DUP;
- }
- break;
- default:
- break;
+ fprintf( stderr, "tkparse choked in fix_choice_cond\n" );
+ exit( 1 );
}
+
+ label = cond->cfg->label;
+ cond->cfg = cond->cfg->cfg_parent;
+ cond2->str = strdup( label );
}
- 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)
+ /*
+ * Walk the statement list, maintaining a stack of current conditions.
+ * token_if push its condition onto the stack.
+ * token_else invert the condition on the top of the stack.
+ * token_endif pop the stack.
+ *
+ * For a simple statement, create a condition chain by joining together
+ * all of the conditions on the stack.
+ */
{
- /*
- * 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;
-
- }
- }
+ struct condition * cond_stack [32];
+ int depth = 0;
- /*
- * 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)
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
{
- /*
- * Search for a left parenthesis.
- */
- if(cnd->op != op_lparen) continue;
- for(cnd1 = cnd->next; cnd1; cnd1=cnd1->next)
+ switch ( cfg->token )
{
- /*
- * 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)))
+ default:
+ break;
+
+ case token_if:
+ cond_stack [depth++] = cfg->cond;
+ cfg->cond = NULL;
+ break;
+
+ case token_else:
{
- /*
- * We have a duplicate. Nuke 5 ops.
- */
- cnd3 = cnd1;
- for(i=0; i<5; i++, cnd3=cnd3->next)
+ /*
+ * Invert the condition chain.
+ *
+ * Be careful to transfrom op_or to op_and1, not op_and.
+ * I will need this later in the code that removes
+ * duplicate conditions.
+ */
+ struct condition * cond;
+
+ for ( cond = cond_stack [depth-1];
+ cond != NULL;
+ cond = cond->next )
{
- cnd3->op = op_nuked;
+ switch( cond->op )
+ {
+ default: break;
+ case op_and: cond->op = op_or; break;
+ case op_or: cond->op = op_and1; break;
+ case op_neq: cond->op = op_eq; break;
+ case op_eq: cond->op = op_neq; break;
+ }
}
- /*
- * 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;
}
+ break;
+
+ case token_fi:
+ --depth;
+ break;
+
+ case token_bool:
+ case token_choice_item:
+ case token_comment:
+ case token_define_bool:
+ case token_hex:
+ case token_int:
+ case token_mainmenu_option:
+ case token_string:
+ case token_tristate:
+ cfg->cond = join_condition_stack( cond_stack, depth );
+ break;
+
+ case token_dep_tristate:
+ /*
+ * Same as the other simple statements, plus an additional
+ * condition for the dependency.
+ */
+ cond_stack [depth] = cfg->cond;
+ cfg->cond = join_condition_stack( cond_stack, depth+1 );
+ break;
}
}
}
diff --git a/scripts/tkgen.c b/scripts/tkgen.c
index 040c1fe80..d695b2e48 100644
--- a/scripts/tkgen.c
+++ b/scripts/tkgen.c
@@ -69,1083 +69,978 @@
* 0: they may have been set to 1 elsewhere. CONFIG_NETLINK is
* an example.
*
- * 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.
- * - clean up +/- 16 confusion for enabling/disabling variables; causes
- * (theoretical, at the moment) problems with dependencies.
- *
+ * 1999 01 04
+ * Michael Elizabeth Chastain <mec@shout.net>
+ * - Call clear_globalflags when writing out update_mainmenu.
+ * This fixes the missing global/vfix lines for ARCH=alpha on 2.2.0-pre4.
+ *
+ * 8 January 1999, Michael Elizabeth Chastain <mec@shout.net>
+ * - Emit menus_per_column
+ *
+ * 14 January 1999, Michael Elizabeth Chastain <mec@shout.net>
+ * - Steam-clean this file. I tested this by generating kconfig.tk for every
+ * architecture and comparing it character-for-character against the output
+ * of the old tkparse.
+ * - Fix flattening of nested menus. The old code simply assigned items to
+ * the most recent token_mainmenu_option, without paying attention to scope.
+ * For example: "menu-1 bool-a menu-2 bool-b endmenu bool-c bool-d endmenu".
+ * The old code would put bool-a in menu-1, bool-b in menu-2, and bool-c
+ * and bool-d in *menu-2*. This hosed the nested submenus in
+ * drives/net/Config.in and other places.
+ * - Fix menu line wraparound at 128 menus (some fool used a 'char' for
+ * a counter).
*/
+
#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.
+ * Total number of menus.
*/
-static int tot_menu_num =0;
+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)
+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");
+ 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" );
+ printf( "\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n" );
+ printf( "\tbutton $w.f.next -text \"Next\" \\\n" );
+ printf( "\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" );
+ printf( "\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n", menu_num-1, menu_num-1 );
+ if ( menu_num == 1 )
+ 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" );
+ printf( "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n" );
+ printf( "\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)
+void clear_globalflags( struct kconfig * scfg )
{
- for(; cfg != NULL; cfg = cfg->next)
- {
- cfg->flags &= ~GLOBAL_WRITTEN;
- }
+ struct kconfig * cfg;
+
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
+ cfg->global_written = 0;
}
+
+
/*
* 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)
+void global( const char *var )
{
- printf("\tglobal %s; vfix %s\n", var, 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.
+ * This function walks the chain of conditions that we got from cond.c
+ * and creates a TCL conditional to enable/disable a given widget.
*/
-void generate_if(struct kconfig * item,
- struct condition * cond,
- int menu_num,
- int line_num)
+void generate_if( struct kconfig * cfg, struct condition * ocond,
+ int menu_num, int line_num )
{
- struct condition * ocond;
+ struct condition * cond;
- ocond = cond;
+ /*
+ * First write any global declarations we need for this conditional.
+ */
+ for ( cond = ocond; cond != NULL; cond = cond->next )
+ {
+ switch ( cond->op )
+ {
+ default:
+ break;
+
+ case op_variable:
+ global( cond->str );
+ break;
+
+ case op_kvariable:
+ if ( ! cond->cfg->global_written )
+ {
+ cond->cfg->global_written = 1;
+ global( cond->cfg->optionname );
+ }
+ break;
+ }
+ }
- /*
- * First write any global declarations we need for this conditional.
- */
- while(cond != NULL )
+ /*
+ * Now write this option.
+ */
+ if ( ! cfg->global_written && cfg->optionname != 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;
+ cfg->global_written = 1;
+ global( cfg->optionname );
}
-
- /*
- * Now write this option.
- */
- if( (item->flags & GLOBAL_WRITTEN) == 0
- && (item->optionname != NULL) )
+
+ /*
+ * Generate the body of the conditional.
+ */
+ printf( "\tif {" );
+ for ( cond = ocond; cond != NULL; cond = cond->next )
{
- global(item->optionname);
- item->flags |= GLOBAL_WRITTEN;
+ switch ( cond->op )
+ {
+ default:
+ break;
+
+ case op_bang: printf( " ! " ); break;
+ case op_eq: printf( " == " ); break;
+ case op_neq: printf( " != " ); break;
+ case op_and: printf( " && " ); break;
+ 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->str );
+ break;
+
+ case op_kvariable:
+ printf( "$%s", cond->cfg->optionname );
+ break;
+
+ case op_constant:
+ if ( strcmp( cond->str, "y" ) == 0 ) printf( "1" );
+ else if ( strcmp( cond->str, "n" ) == 0 ) printf( "0" );
+ else if ( strcmp( cond->str, "m" ) == 0 ) printf( "2" );
+ else
+ printf( "\"%s\"", cond->str );
+ break;
+ }
}
- /*
- * Now generate the body of the conditional.
- */
- printf("\tif {");
- cond = ocond;
- while(cond != NULL )
+ printf( "} then { " );
+
+ /*
+ * Generate a procedure call to write the value.
+ * This code depends on procedures in header.tk.
+ */
+ switch ( cfg->token )
{
- 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("(");
+ default:
+ printf( " }\n" );
break;
- case op_rparen:
- printf(")");
+
+ case token_bool:
+ 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];",
+ cfg->optionname, cfg->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];}\n",
+ cfg->optionname, cfg->optionname );
break;
- case op_variable:
- printf("$%s", cond->variable.str);
+
+ case token_choice_header:
+ fprintf( stderr, "Internal error on token_choice_header\n" );
+ exit( 1 );
+
+ case token_choice_item:
+ fprintf( stderr, "Internal error on token_choice_item\n" );
+ exit( 1 );
+
+ case token_define_bool:
+ printf( "set %s %s } \n",
+ cfg->optionname, cfg->value );
break;
- case op_kvariable:
- printf("$%s", cond->variable.cfg->optionname);
+
+ case token_dep_tristate:
+ case token_tristate:
+ if ( cfg->token == token_dep_tristate )
+ {
+ global( cfg->depend );
+ printf( "if { $%s != 1 && $%s != 0 } then {",
+ cfg->depend, cfg->depend );
+ 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( "global CONFIG_MODULES; if {($CONFIG_MODULES == 1)} then { .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];",
+ cfg->optionname, cfg->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 to enable the correct radiobutton.
+ */
+ printf( "set %s [expr $%s|16];}\n",
+ cfg->optionname, cfg->optionname );
break;
- case op_shellcmd:
- printf("[exec %s]", cond->variable.str);
+
+ case token_hex:
+ case token_int:
+ case token_string:
+ printf( ".menu%d.config.f.x%d.x configure -state normal -foreground [ 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 -foreground [ cget .ref -disabledforeground ];",
+ menu_num, line_num );
+ printf( ".menu%d.config.f.x%d.l configure -state disabled;}\n",
+ menu_num, line_num );
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);
+
+ case token_mainmenu_option:
+ printf( ".f0.x%d configure -state normal } else { .f0.x%d configure -state disabled }\n",
+ menu_num, menu_num );
break;
- default:
- break;
- }
- cond = cond->next;
}
+}
+
+
+
+/*
+ * Generate a line that writes a variable to the output file.
+ */
+void generate_writeconfig( struct kconfig * cfg )
+{
+ struct condition * cond;
- /*
- * 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)
+ /*
+ * Generate global declaration for this symbol.
+ */
+ if ( cfg->token != token_comment )
{
- 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:
- case tok_string:
- 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 )
+ if ( ! cfg->global_written )
{
- 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("}; ");
+ cfg->global_written = 1;
+ printf( "\tglobal %s\n", cfg->optionname );
}
- else
+ }
+
+ /*
+ * Generate global declarations for the condition chain.
+ */
+ for ( cond = cfg->cond; cond != NULL; cond = cond->next )
+ {
+ switch( cond->op )
{
- printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
+ default:
+ break;
+
+ case op_variable:
+ global( cond->str );
+ break;
+
+ case op_kvariable:
+ if ( ! cond->cfg->global_written )
+ {
+ cond->cfg->global_written = 1;
+ global( cond->cfg->optionname );
+ }
+ break;
}
-
- 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;
+ /*
+ * Generate indentation.
+ */
+ if ( cfg->token != token_choice_header )
+ printf( "\t" );
- /*
- * First write any global declarations we need for this conditional.
- */
- ocond = cond;
- for(; cond != NULL; cond = cond->next )
+ /*
+ * Generate the conditional.
+ */
+ if ( cfg->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;
- }
+ printf( "if {" );
+ for ( cond = cfg->cond; cond != NULL; cond = cond->next )
+ {
+ switch ( cond->op )
+ {
+ default: break;
+ case op_bang: printf( " ! " ); break;
+ case op_eq: printf( " == " ); break;
+ case op_neq: printf( " != " ); break;
+ case op_and: printf( " && " ); break;
+ 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->str );
+ break;
+
+ case op_kvariable:
+ printf( "$%s", cond->cfg->optionname );
+ break;
+
+ case op_constant:
+ if ( strcmp( cond->str, "n" ) == 0 ) printf( "0" );
+ else if ( strcmp( cond->str, "y" ) == 0 ) printf( "1" );
+ else if ( strcmp( cond->str, "m" ) == 0 ) printf( "2" );
+ else
+ printf( "\"%s\"", cond->str );
+ break;
+ }
+ }
+ printf( "} then {" );
}
- /*
- * Now generate the body of the conditional.
- */
- printf("\tif {");
- cond = ocond;
- while(cond != NULL )
+ /*
+ * Generate a procedure call to write the value.
+ * This code depends on the write_* procedures in header.tk.
+ */
+ switch ( cfg->token )
{
- 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(" && ");
+ default:
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( "\n" );
break;
- case op_or:
- printf(" || ");
+
+ case token_bool:
+ case token_tristate:
+ if ( cfg->cond )
+ printf( " " );
+ printf( "write_tristate $cfg $autocfg %s $%s $notmod",
+ cfg->optionname, cfg->optionname );
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( "\n" );
break;
- case op_lparen:
- printf("(");
+
+ case token_choice_header:
+ /*
+ * This is funky code -- it fails if there were any conditionals.
+ * Fortunately all the conditionals got stripped off somewhere
+ * else.
+ */
+ {
+ struct kconfig * cfg1;
+ for ( cfg1 = cfg->next;
+ cfg1 != NULL && cfg1->token == token_choice_item;
+ cfg1 = cfg1->next )
+ {
+ printf("\tif { $%s == \"%s\" } then { write_tristate $cfg $autocfg %s 1 $notmod } else { write_tristate $cfg $autocfg %s 0 $notmod }\n",
+ cfg->optionname, cfg1->label,
+ cfg1->optionname,
+ cfg1->optionname );
+ }
+ }
break;
- case op_rparen:
- printf(")");
+
+ case token_choice_item:
+ fprintf( stderr, "Internal error on token_choice_item\n" );
+ exit( 1 );
+
+ case token_comment:
+ printf( "write_comment $cfg $autocfg \"%s\"",
+ cfg->label );
+ if ( cfg->cond != NULL )
+ printf( "}" );
+ printf( "\n" );
break;
- case op_variable:
- printf("$%s", cond->variable.str);
+
+ case token_define_bool:
+ if ( cfg->cond == NULL )
+ {
+ printf( "write_tristate $cfg $autocfg %s $%s $notmod\n",
+ cfg->optionname, cfg->optionname );
+ }
+ else
+ {
+ printf( "write_tristate $cfg $autocfg %s %s $notmod }\n",
+ cfg->optionname, cfg->value );
+ }
break;
- case op_shellcmd:
- printf("[exec %s]", cond->variable.str);
+
+ case token_dep_tristate:
+ if ( cfg->cond )
+ printf( " " );
+ printf( "write_tristate $cfg $autocfg %s $%s $%s",
+ cfg->optionname, cfg->optionname, cfg->depend );
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( " \n" );
break;
- case op_kvariable:
- printf("$%s", cond->variable.cfg->optionname);
+
+ case token_hex:
+ if ( cfg->cond != NULL )
+ printf( " " );
+ printf( "write_hex $cfg $autocfg %s $%s $notmod",
+ cfg->optionname, cfg->optionname );
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( "\n" );
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);
+
+ case token_int:
+ if ( cfg->cond != NULL )
+ printf( " " );
+ printf( "write_int $cfg $autocfg %s $%s $notmod",
+ cfg->optionname, cfg->optionname );
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( "\n" );
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_string:
- printf("} then { write_string $cfg $autocfg %s $%s $notmod }\n",
- item->optionname, item->optionname);
- break;
- case tok_choose:
- case tok_choice:
- fprintf(stderr,"Fixme\n");
- exit(0);
- default:
- break;
+ case token_string:
+ if ( cfg->cond != NULL )
+ printf( " " );
+ printf( "write_string $cfg $autocfg %s $%s $notmod",
+ cfg->optionname, cfg->optionname );
+ if ( cfg->cond != NULL )
+ printf( " }" );
+ printf( "\n" );
+ break;
}
}
+
+
/*
- * Generates a fragment of wish script that closes out a submenu procedure.
+ * Generates the end of a menu procedure.
*/
-static void end_proc(int menu_num)
+static void end_proc( struct kconfig * scfg, 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)
+ 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" );
+ printf( "\t\t-width [expr [winfo reqwidth $w.config.f] + 1]\\\n" );
+ printf( "\t\t-scrollregion \"-1 -1 [expr [winfo reqwidth $w.config.f] + 1] \\\n" );
+ printf( "\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" );
+ printf( "\t\t$w.config.canvas configure -height $canvtotal\n" );
+ printf( "\t} else {\n" );
+ printf( "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n" );
+ printf( "\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 all of the booleans that are defined in this menu.
+ */
+ clear_globalflags( scfg );
+ for ( cfg = scfg; 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) )
+ if ( cfg->menu_number == menu_num && cfg->token == token_define_bool
+ && cfg->optionname != NULL )
{
- printf("\tglobal %s\n", cfg->optionname);
- cfg->flags |= GLOBAL_WRITTEN;
- printf("\tset %s 0\n", cfg->optionname);
+ if ( ! cfg->global_written )
+ {
+ cfg->global_written = 1;
+ printf( "\tglobal %s\n", cfg->optionname );
+ printf( "\tset %s 0\n", cfg->optionname );
+ }
}
-
}
- for(cfg = config;cfg != NULL; cfg = cfg->next)
+
+ for ( cfg = scfg; 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 ( cfg->menu_number == menu_num
+ && cfg->token != token_mainmenu_option
+ && cfg->token != token_choice_item )
{
- /*
- * 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)
+ if ( cfg->cond != NULL )
+ generate_if( cfg, cfg->cond, cfg->menu_number, cfg->menu_line );
+ else
{
- 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);
+ /*
+ * Treat tristate like conditional here.
+ */
+ if ( cfg->token == token_dep_tristate )
+ {
+ global( cfg->depend );
+ 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, cfg->depend,
+ menu_num, cfg->menu_line,
+ menu_num, cfg->menu_line );
+ }
}
}
-
}
-
- printf("}\n\n\n");
+ 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_string:
- 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)
+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 = "tkgen error";
-
- /*
- * Start by assigning menu numbers, and submenu numbers.
- */
- for(cfg = scfg;cfg != NULL; cfg = cfg->next)
+ int menu_depth;
+ int menu_num [64];
+ struct kconfig * menu_first [256];
+ struct kconfig * menu_last [256];
+ int imenu;
+ struct kconfig * cfg;
+ struct kconfig * cfg1 = NULL;
+ const char * name = "No Name";
+
+ /*
+ * Thread the menu pointers so I can walk each menu separately.
+ */
+ tot_menu_num = 0;
+ menu_depth = 0;
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
{
- switch (cfg->tok)
+ switch ( cfg->token )
{
- 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_string:
- 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;
- };
+ break;
+
+ case token_mainmenu_name:
+ name = cfg->label;
+ break;
+
+ case token_mainmenu_option:
+ if ( ++menu_depth >= 64 )
+ { fprintf( stderr, "menus too deep\n" ); exit( 1 ); }
+ if ( ++tot_menu_num >= 256 )
+ { fprintf( stderr, "too many menus\n" ); exit( 1 ); }
+ menu_num [menu_depth] = tot_menu_num;
+ menu_first [tot_menu_num] = cfg;
+ menu_last [tot_menu_num] = cfg;
+ break;
+
+ case token_endmenu:
+#if ! defined(BUG_COMPATIBLE)
+ /* flatten menus with proper scoping */
+ if ( --menu_depth < 0 )
+ { fprintf( stderr, "unmatched endmenu\n" ); exit( 1 ); }
+#endif
+ break;
+
+ case token_bool:
+ case token_choice_header:
+ case token_choice_item:
+ case token_dep_tristate:
+ case token_hex:
+ case token_int:
+ case token_string:
+ case token_tristate:
+ if ( menu_depth == 0 )
+ { fprintf( stderr, "statement not in menu\n" ); exit( 1 ); }
+ menu_last [menu_num [menu_depth]]->menu_next = cfg;
+ menu_last [menu_num [menu_depth]] = cfg;
+ cfg->menu_next = NULL;
+ break;
+
+ case token_define_bool:
+ 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)
+ /*
+ * Generate menus per column setting.
+ * There are:
+ * four extra buttons for save/quit/load/store;
+ * one blank button
+ * add two to round up for division
+ */
+ printf( "set menus_per_column %d\n\n", (tot_menu_num + 4 + 1 + 2) / 3 );
+
+ /*
+ * Generate the menus.
+ */
+ printf( "mainmenu_name \"%s\"\n", name );
+ for ( imenu = 1; imenu <= tot_menu_num; ++imenu )
{
- switch (cfg->tok)
+ int menu_line = 0;
+
+ clear_globalflags( scfg );
+ start_proc( menu_first[imenu]->label, imenu, 1 );
+
+ for ( cfg = menu_first[imenu]; cfg != NULL; cfg = cfg->menu_next )
{
- 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;
- case tok_string:
- if( cfg->menu_number != menu_num )
+ cfg->menu_number = imenu;
+
+ switch ( cfg->token )
{
- end_proc(menu_num);
- start_proc(menulabel, cfg->menu_number, FALSE);
- menu_num = cfg->menu_number;
+ default:
+ break;
+
+ case token_bool:
+ cfg->menu_line = menu_line++;
+ printf( "\tbool $w.config.f %d %d \"%s\" %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname );
+ break;
+
+ case token_choice_header:
+ /*
+ * I need the first token_choice_item to pick out the right
+ * help text from Documentation/Configure.help.
+ */
+ cfg->menu_line = menu_line++;
+ 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, cfg->next->optionname );
+ printf( "\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line );
+ cfg1 = cfg;
+ break;
+
+ case token_choice_item:
+ /* note: no menu line; uses choice header menu line */
+ 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 token_dep_tristate:
+ cfg->menu_line = menu_line++;
+ printf( "\tdep_tristate $w.config.f %d %d \"%s\" %s %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname, cfg->depend );
+ break;
+
+ case token_hex:
+ cfg->menu_line = menu_line++;
+ printf( "\thex $w.config.f %d %d \"%s\" %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname );
+ break;
+
+ case token_int:
+ cfg->menu_line = menu_line++;
+ printf( "\tint $w.config.f %d %d \"%s\" %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname );
+ break;
+
+ case token_string:
+ cfg->menu_line = menu_line++;
+ printf( "\tistring $w.config.f %d %d \"%s\" %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname );
+ break;
+
+ case token_tristate:
+ cfg->menu_line = menu_line++;
+ printf( "\ttristate $w.config.f %d %d \"%s\" %s\n",
+ cfg->menu_number, cfg->menu_line, cfg->label,
+ cfg->optionname );
+ break;
}
- printf("\tistring $w.config.f %d %d \"%s\" %s\n",
- cfg->menu_number,
- cfg->menu_line,
- cfg->label,
- cfg->optionname);
- break;
- default:
- break;
}
+ end_proc( scfg, imenu );
}
- /*
- * Generate the code to close out the last menu.
- */
- end_proc(menu_num);
-
- /*
- * 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)
+ /*
+ * 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.
+ */
+ clear_globalflags( scfg );
+ 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;
- }
+ if ( cfg->token == token_mainmenu_option && cfg->cond != NULL )
+ generate_if( cfg, cfg->cond, cfg->menu_number, cfg->menu_line );
}
-
- printf("}\n\n\n");
+ printf( "}\n\n\n" );
#if 0
- /*
- * Generate some code to set the variables that are "defined".
- */
- for(cfg = config;cfg != NULL; cfg = cfg->next)
+ /*
+ * Generate 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
+ if ( cfg->token == token_define_bool )
{
- printf("\twrite_define %s %s\n", cfg->optionname, cfg->value);
+ 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
+ #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)
+ /*
+ * Generate code to load the default settings into the variables.
+ * 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)
+ switch ( cfg->token )
{
- 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:
- case tok_string:
- printf("set %s %s\n", cfg->optionname, cfg->value);
- break;
- case tok_choose:
- printf("set %s \"(not set)\"\n",cfg->optionname);
default:
- break;
+ break;
+
+ case token_bool:
+ case token_choice_item:
+ case token_dep_tristate:
+ case token_tristate:
+ printf( "set %s 0\n", cfg->optionname );
+ break;
+
+ case token_choice_header:
+ printf( "set %s \"(not set)\"\n", cfg->optionname );
+ break;
+
+ case token_hex:
+ case token_int:
+ case token_string:
+ printf( "set %s %s\n", cfg->optionname, cfg->value );
+ 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");
- printf("\tputs $autocfg \"#define AUTOCONF_INCLUDED\"\n");
- for(cfg = scfg; cfg != NULL; cfg = cfg->next)
+ /*
+ * Generate a function to write all of the variables to a file.
+ */
+ 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" );
+ 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" );
+ printf( "\tputs $autocfg \"#define AUTOCONF_INCLUDED\"\n" );
+
+ clear_globalflags( scfg );
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
{
- switch (cfg->tok)
+ switch ( cfg->token )
{
- case tok_int:
- case tok_hex:
- case tok_string:
- 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_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 } else { write_tristate $cfg $autocfg %s 0 $notmod }\n",
- cfg->optionname,
- cfg1->label,
- cfg1->optionname,
- 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_string )
- {
- printf("\twrite_string $cfg $autocfg %s $%s $notmod\n",
- cfg->optionname,
- cfg->optionname);
- }
- else
- {
- printf("\twrite_tristate $cfg $autocfg %s $%s $notmod\n",
- cfg->optionname,
- cfg->optionname);
- }
- }
- break;
default:
- break;
+ break;
+
+ case token_bool:
+ case token_choice_header:
+ case token_comment:
+ case token_define_bool:
+ case token_dep_tristate:
+ case token_hex:
+ case token_int:
+ case token_string:
+ case token_tristate:
+ generate_writeconfig( cfg );
+ 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)
+ printf( "\tclose $cfg\n" );
+ printf( "\tclose $autocfg\n" );
+ printf( "}\n\n\n" );
+
+ /*
+ * Generate 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)
+ if ( cfg->token == token_choice_header )
{
- printf("\tglobal %s; set %s 0\n",cfg1->optionname,cfg1->optionname);
+ for ( cfg1 = cfg->next;
+ cfg1 != NULL && cfg1->token == token_choice_item;
+ cfg1 = cfg1->next )
+ {
+ printf( "\tglobal %s; set %s 0\n",
+ cfg1->optionname, cfg1->optionname );
+ }
}
}
- printf("}\n\n\n");
+ printf( "}\n\n\n" );
- printf("proc update_choices { } {\n");
- for(cfg = scfg; cfg != NULL; cfg = cfg->next)
+ 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)
+ if ( cfg->token == token_choice_header )
{
- printf("\tglobal %s\n", cfg1->optionname);
- printf("\tif { $%s == 1 } then { set %s \"%s\" }\n",
- cfg1->optionname,
- cfg->optionname,
- cfg1->label);
+ printf( "\tglobal %s\n", cfg->optionname );
+ for ( cfg1 = cfg->next;
+ cfg1 != NULL && cfg1->token == token_choice_item;
+ 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( "}\n\n\n" );
- printf("proc update_define { } {\n");
- clear_globalflags(config);
- for(cfg = scfg; cfg != NULL; cfg = cfg->next)
+ printf( "proc update_define { } {\n" );
+ clear_globalflags( scfg );
+ for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
{
- if( cfg->tok != tok_define ) continue;
- printf("\tglobal %s\n", cfg->optionname);
- cfg->flags |= GLOBAL_WRITTEN;
+ if ( cfg->token == token_define_bool )
+ {
+ cfg->global_written = 1;
+ printf( "\tglobal %s\n", cfg->optionname );
+ }
}
- for(cfg = scfg; cfg != NULL; cfg = cfg->next)
+
+ 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
+ if( cfg->token == token_define_bool )
{
- printf("\tset %s %s\n",
- cfg->optionname, cfg->value);
+ if ( cfg->cond == NULL )
+ printf( "\tset %s %s\n", cfg->optionname, cfg->value );
+ else
+ generate_if( cfg, cfg->cond, -1, 0 );
}
}
- 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.
- */
+ 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
index f91d0f355..5bf317cdb 100644
--- a/scripts/tkparse.c
+++ b/scripts/tkparse.c
@@ -1,754 +1,622 @@
-/* parser config.in
+/*
+ * tkparse.c
+ *
+ * Eric Youngdale was the original author of xconfig.
+ * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
+ *
+ * Parse a config.in file and translate it to a wish script.
+ * This task has three parts:
+ *
+ * tkparse.c tokenize the input
+ * tkcond.c transform 'if ...' statements
+ * tkgen.c generate output
*
- * Version 1.0
- * Eric Youngdale
- * 10/95
+ * Change History
*
- * 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.
+ * 7 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
+ * - Teach dep_tristate about a few literals, such as:
+ * dep_tristate 'foo' CONFIG_FOO m
+ * Also have it print an error message and exit on some parse failures.
*
- * 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.
+ * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
+ * - Don't fclose stdin. Thanks to Tony Hoyle for nailing this one.
*
- * This file contains the code to do the first parse of config.in.
+ * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
+ * - Steam-clean this file. I tested this by generating kconfig.tk for
+ * every architecture and comparing it character-for-character against
+ * the output of the old tkparse.
+ *
+ * TO DO:
+ * - xconfig is at the end of its life cycle. Contact <mec@shout.net> if
+ * you are interested in working on the replacement.
*/
-#include <stdlib.h>
+
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+
#include "tkparse.h"
-struct kconfig * config = NULL;
-struct kconfig * clast = NULL;
-struct kconfig * koption = NULL;
+static struct kconfig * config_list = NULL;
+static struct kconfig * config_last = NULL;
+static const char * current_file = "<unknown file>";
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;
+
+static void do_source( const char * );
+
+#undef strcmp
+int my_strcmp( const char * s1, const char * s2 ) { return strcmp( s1, s2 ); }
+#define strcmp my_strcmp
+
/*
- * Simple function just to skip over spaces and tabs in config.in.
+ * Report a syntax error.
*/
-static char * skip_whitespace(char * pnt)
+static void syntax_error( const char * msg )
{
- while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++;
- return pnt;
+ fprintf( stderr, "%s: %d: %s\n", current_file, lineno, msg );
+ exit( 1 );
}
+
+
/*
- * 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.
+ * Get a string.
*/
-static struct condition * parse_if(char * pnt)
+static const char * get_string( const char * pnt, char ** label )
{
- char * opnt;
- struct condition *list;
- struct condition *last;
- struct condition *cpnt;
- char varname[64];
- char * pnt1;
+ const char * word;
- opnt = pnt;
+ word = pnt;
+ for ( ; ; )
+ {
+ if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' )
+ break;
+ 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);
+ *label = malloc( pnt - word + 1 );
+ memcpy( *label, word, pnt - word );
+ (*label)[pnt - word] = '\0';
- list = last = NULL;
- while(*pnt && *pnt != ']') {
+ if ( *pnt != '\0' )
+ pnt++;
+ return 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. */
+/*
+ * Get a quoted string.
+ * Insert a '\' before any characters that need quoting.
+ */
+static const char * get_qstring( const char * pnt, char ** label )
+{
+ char quote_char;
+ char newlabel [1024];
+ char * pnt1;
+
+ /* advance to the open quote */
+ for ( ; ; )
+ {
+ if ( *pnt == '\0' )
+ return pnt;
+ quote_char = *pnt++;
+ if ( quote_char == '"' || quote_char == '\'' )
+ break;
+ }
+
+ /* copy into an intermediate buffer */
+ pnt1 = newlabel;
+ for ( ; ; )
+ {
+ if ( *pnt == '\0' )
+ syntax_error( "unterminated quoted string" );
+ if ( *pnt == quote_char && pnt[-1] != '\\' )
+ break;
+
+ /* copy the character, quoting if needed */
+ if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' )
+ *pnt1++ = '\\';
+ *pnt1++ = *pnt++;
+ }
+
+ /* copy the label into a permanent location */
+ *pnt1++ = '\0';
+ *label = (char *) malloc( pnt1 - newlabel );
+ memcpy( *label, newlabel, pnt1 - newlabel );
+
+ /* skip over last quote and next whitespace */
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;
+ while ( *pnt == ' ' || *pnt == '\t' )
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;
+ return pnt;
}
+
/*
- * 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.
+ * Tokenize an 'if' statement condition.
*/
-static char * get_qstring(char *pnt, char ** labl)
+static struct condition * tokenize_if( const char * pnt )
{
- char quotechar;
- char newlabel[1024];
- char * pnt1;
- char * pnt2;
+ struct condition * list;
+ struct condition * last;
- while( *pnt && *pnt != '"' && *pnt != '\'') pnt++;
- if (*pnt == '\0') return pnt;
+ /* eat the open bracket */
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+ if ( *pnt != '[' )
+ syntax_error( "bad 'if' condition" );
+ pnt++;
- quotechar = *pnt++;
- pnt1 = newlabel;
- while(*pnt && *pnt != quotechar && pnt[-1] != '\\')
+ list = last = NULL;
+ for ( ; ; )
{
- /*
- * Quote the character if we need to.
- */
- if( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']')
- *pnt1++ = '\\';
+ struct condition * cond;
- *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;
-}
+ /* advance to the next token */
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+ if ( *pnt == '\0' )
+ syntax_error( "unterminated 'if' condition" );
+ if ( *pnt == ']' )
+ return list;
-static char * parse_choices(struct kconfig * choice_kcfg, char * pnt)
-{
- struct kconfig * kcfg;
- int index = 1;
+ /* allocate a new token */
+ cond = malloc( sizeof(*cond) );
+ memset( cond, 0, sizeof(*cond) );
+ if ( last == NULL )
+ { list = last = cond; }
+ else
+ { last->next = cond; last = cond; }
- /*
- * Choices appear in pairs of strings. The parse is fairly trivial.
- */
- while(1)
- {
- pnt = skip_whitespace(pnt);
- if(*pnt == '\0') break;
+ /* determine the token value */
+ if ( *pnt == '-' && pnt[1] == 'a' )
+ { cond->op = op_and; pnt += 2; continue; }
- 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
+ if ( *pnt == '-' && pnt[1] == 'o' )
+ { cond->op = op_or; pnt += 2; continue; }
+
+ if ( *pnt == '!' && pnt[1] == '=' )
+ { cond->op = op_neq; pnt += 2; continue; }
+
+ if ( *pnt == '=' )
+ { cond->op = op_eq; pnt += 1; continue; }
+
+ if ( *pnt == '!' )
+ { cond->op = op_bang; pnt += 1; continue; }
+
+ if ( *pnt == '"' )
{
- clast = config = kcfg;
+ const char * word;
+
+ /* advance to the word */
+ pnt++;
+ if ( *pnt == '$' )
+ { cond->op = op_variable; pnt++; }
+ else
+ { cond->op = op_constant; }
+
+ /* find the end of the word */
+ word = pnt;
+ for ( ; ; )
+ {
+ if ( *pnt == '\0' )
+ syntax_error( "unterminated double quote" );
+ if ( *pnt == '"' )
+ break;
+ pnt++;
+ }
+
+ /* store a copy of this word */
+ {
+ char * str = malloc( pnt - word + 1 );
+ memcpy( str, word, pnt - word );
+ str [pnt - word] = '\0';
+ cond->str = str;
+ }
+
+ pnt++;
+ continue;
}
- 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;
+ /* unknown token */
+ syntax_error( "bad if condition" );
}
-
- 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.
+ * Tokenize a choice list. Choices appear as pairs of strings;
+ * note that I am parsing *inside* the double quotes. Ugh.
*/
-static char * get_string(char *pnt, char ** labl)
+static const char * tokenize_choices( struct kconfig * cfg_choose,
+ const char * pnt )
{
- char newlabel[1024];
- char * pnt1;
- char * pnt2;
-
- if (*pnt == '\0') return pnt;
-
- pnt1 = newlabel;
- while(*pnt && *pnt != ' ' && *pnt != '\t')
+ for ( ; ; )
{
- *pnt1++ = *pnt++;
+ struct kconfig * cfg;
+
+ /* skip whitespace */
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+ if ( *pnt == '\0' )
+ return pnt;
+
+ /* allocate a new kconfig line */
+ cfg = malloc( sizeof(*cfg) );
+ memset( cfg, 0, sizeof(*cfg) );
+ if ( config_last == NULL )
+ { config_last = config_list = cfg; }
+ else
+ { config_last->next = cfg; config_last = cfg; }
+
+ /* fill out the line */
+ cfg->token = token_choice_item;
+ cfg->cfg_parent = cfg_choose;
+ pnt = get_string( pnt, &cfg->label );
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+ pnt = get_string( pnt, &cfg->optionname );
}
- *pnt1++ = '\0';
-
- pnt2 = (char *) malloc(strlen(newlabel) + 1);
- strcpy(pnt2, newlabel);
- *labl = pnt2;
- if( *pnt ) pnt++;
- return 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.
+ * Tokenize one line.
*/
-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, "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, "string", 6) == 0)
- {
- tok = tok_string;
- pnt += 6;
- }
- 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)
+static void tokenize_line( const char * pnt )
+{
+ static struct kconfig * last_menuoption = NULL;
+ enum e_token token;
+ struct kconfig * cfg;
+
+ /* skip white space */
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+
+ /*
+ * categorize the next token
+ */
+
+#define match_token(t, s) \
+ if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
+
+ token = token_UNKNOWN;
+ switch ( *pnt )
{
- tok = tok_endmenu;
- pnt += 7;
+ default:
+ break;
+
+ case '#':
+ case '\0':
+ return;
+
+ case 'b':
+ match_token( token_bool, "bool" );
+ break;
+
+ case 'c':
+ match_token( token_choice_header, "choice" );
+ match_token( token_comment, "comment" );
+ break;
+
+ case 'd':
+ match_token( token_define_bool, "define_bool" );
+ match_token( token_dep_tristate, "dep_tristate" );
+ break;
+
+ case 'e':
+ match_token( token_else, "else" );
+ match_token( token_endmenu, "endmenu" );
+ break;
+
+ case 'f':
+ match_token( token_fi, "fi" );
+ break;
+
+ case 'h':
+ match_token( token_hex, "hex" );
+ break;
+
+ case 'i':
+ match_token( token_if, "if" );
+ match_token( token_int, "int" );
+ break;
+
+ case 'm':
+ match_token( token_mainmenu_name, "mainmenu_name" );
+ match_token( token_mainmenu_option, "mainmenu_option" );
+ break;
+
+ case 's':
+ match_token( token_source, "source" );
+ match_token( token_string, "string" );
+ break;
+
+ case 't':
+ match_token( token_then, "then" );
+ match_token( token_tristate, "tristate" );
+ break;
+
+ case 'u':
+ match_token( token_unset, "unset" );
+ break;
}
- if( tok == tok_unknown)
+#undef match_token
+
+ if ( token == token_source )
{
- 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;
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+ do_source( pnt );
+ 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 )
+ if ( token == token_then )
{
- clast->next = kcfg;
- clast = kcfg;
+ if ( config_last != NULL && config_last->token == token_if )
+ return;
+ syntax_error( "bogus 'then'" );
}
- else
+
+ if ( token == token_unset )
{
- clast = config = kcfg;
+ fprintf( stderr, "Ignoring 'unset' command\n" );
+ return;
}
- pnt = skip_whitespace(pnt);
+ if ( token == token_UNKNOWN )
+ syntax_error( "unknown command" );
- /*
- * Now parse the remaining parts of the option, and attach the results
- * to the structure.
- */
- switch (tok)
+ /*
+ * Allocate an item.
+ */
+ cfg = malloc( sizeof(*cfg) );
+ memset( cfg, 0, sizeof(*cfg) );
+ if ( config_last == NULL )
+ { config_last = config_list = cfg; }
+ else
+ { config_last->next = cfg; config_last = cfg; }
+
+ /*
+ * Tokenize the arguments.
+ */
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+
+ cfg->token = token;
+ switch ( token )
{
- 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:
- case tok_string:
- 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 )
+ default:
+ syntax_error( "unknown token" );
+
+ case token_bool:
+ case token_tristate:
+ pnt = get_qstring ( pnt, &cfg->label );
+ pnt = get_string ( pnt, &cfg->optionname );
+ break;
+
+ case token_choice_header:
{
- exit(1);
+ static int choose_number = 0;
+ char * choice_list;
+
+ pnt = get_qstring ( pnt, &cfg->label );
+ pnt = get_qstring ( pnt, &choice_list );
+ pnt = get_string ( pnt, &cfg->value );
+
+ cfg->optionname = malloc( 32 );
+ sprintf( cfg->optionname, "tmpvar_%d", choose_number++ );
+
+ tokenize_choices( cfg, choice_list );
+ free( choice_list );
}
- break;
- case tok_comment:
- pnt = get_qstring(pnt, &kcfg->label);
- if( koption != NULL )
+ break;
+
+ case token_comment:
+ pnt = get_qstring(pnt, &cfg->label);
+ if ( last_menuoption != NULL )
{
- pnt = get_qstring(pnt, &kcfg->label);
- koption->label = kcfg->label;
- koption = NULL;
+ pnt = get_qstring(pnt, &cfg->label);
+ last_menuoption->label = cfg->label;
+ last_menuoption = NULL;
}
- break;
- case tok_menuoption:
- if( strncmp(pnt, "next_comment", 12) == 0)
+ break;
+
+ case token_define_bool:
+ pnt = get_string( pnt, &cfg->optionname );
+#if ! defined(BUG_COMPATIBLE)
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+#endif
+ if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = "0";
+ else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = "1";
+ else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = "2";
+ else
{
- koption = kcfg;
+#if ! defined(BUG_COMPATIBLE)
+ syntax_error( "unknown define_bool value" );
+#else
+ /*
+ * This ought to give the same output as printf'ing
+ * through the null pointer ... I don't want to be
+ * SIGSEGV compatible!
+ */
+ cfg->value = "(null)";
+#endif
}
- else
+ break;
+
+ case token_dep_tristate:
+ pnt = get_qstring ( pnt, &cfg->label );
+ pnt = get_string ( pnt, &cfg->optionname );
+
+ while ( *pnt == ' ' || *pnt == '\t' )
+ pnt++;
+
+ if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
+ || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
+ && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
{
- pnt = get_qstring(pnt, &kcfg->label);
+ /* dep_tristate 'foo' CONFIG_FOO m */
+ if ( pnt[0] == 'Y' || pnt[0] == 'y' )
+ cfg->depend = strdup( "CONSTANT_Y" );
+ else if ( pnt[0] == 'M' || pnt[0] == 'm' )
+ cfg->depend = strdup( "CONSTANT_M" );
+ else
+ cfg->depend = strdup( "CONSTANT_N" );
+ 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 )
+ else if ( *pnt == '$' )
{
- exit(1);
+ pnt++;
+ pnt = get_string( pnt, &cfg->depend );
+ }
+ else
+ {
+ syntax_error( "can't handle dep_tristate condition" );
}
- 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(" != ");
+ /*
+ * Create a conditional for this object's dependency.
+ */
+ {
+ char fake_if [1024];
+ sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
+ cfg->depend, cfg->depend );
+ cfg->cond = tokenize_if( fake_if );
+ }
break;
- case op_and:
- printf(" -a ");
+
+ case token_else:
+ case token_endmenu:
+ case token_fi:
break;
- case op_lparen:
- printf("(");
+
+ case token_hex:
+ case token_int:
+ case token_string:
+ pnt = get_qstring ( pnt, &cfg->label );
+ pnt = get_string ( pnt, &cfg->optionname );
+ pnt = get_string ( pnt, &cfg->value );
break;
- case op_rparen:
- printf(")");
+
+ case token_if:
+ cfg->cond = tokenize_if( pnt );
break;
- case op_variable:
- printf("$%s", cond->variable.str);
+
+ case token_mainmenu_name:
+ pnt = get_qstring( pnt, &cfg->label );
break;
- case op_constant:
- printf("'%s'", cond->variable.str);
+
+ case token_mainmenu_option:
+ if ( strncmp( pnt, "next_comment", 12 ) == 0 )
+ last_menuoption = cfg;
+ else
+ pnt = get_qstring( pnt, &cfg->label );
break;
- default:
- break;
- }
- cond = cond->next;
}
- printf("\n");
+ return;
}
-static int do_source(char * filename)
+
+
+/*
+ * Implement the "source" command.
+ */
+static void do_source( const char * filename )
{
- char buffer[1024];
- int offset;
- int old_lineno;
- char * old_file = 0; /* superfluous, just for gcc */
- 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)
+ char buffer [1024];
+ FILE * infile;
+ const char * old_file;
+ int old_lineno;
+ int offset;
+
+ /* open the file */
+ if ( strcmp( filename, "-" ) == 0 )
+ infile = stdin;
+ else
+ infile = fopen( filename, "r" );
+
+ /* if that failed, try ../filename */
+ if ( infile == NULL )
{
- 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;
- }
+ sprintf( buffer, "../%s", filename );
+ infile = fopen( buffer, "r" );
}
- 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;
+ if ( infile == NULL )
+ {
+ sprintf( buffer, "unable to open %s", filename );
+#if defined(BUG_COMPATIBLE)
+ fprintf( stderr, "%s\n", buffer );
+ return;
+#else
+ syntax_error( buffer );
#endif
+ }
- /*
- * Read stdin to get the top level script.
- */
- do_source("-");
+ /* push the new file name and line number */
+ old_file = current_file;
+ old_lineno = lineno;
+ current_file = filename;
+ lineno = 0;
- 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)
+ /* read and process lines */
+ for ( offset = 0; ; )
{
+ char * pnt;
- if(cfg->cond != NULL && cfg->tok != tok_if)
- dump_if(cfg->cond);
+ /* read a line */
+ fgets( buffer + offset, sizeof(buffer) - offset, infile );
+ if ( feof( infile ) )
+ break;
+ lineno++;
- 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_string:
- printf("istring ");
- 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:
- }
+ /* strip the trailing return character */
+ pnt = buffer + strlen(buffer) - 1;
+ if ( *pnt == '\n' )
+ *pnt-- = '\0';
- switch(cfg->tok)
+ /* eat \ NL pairs */
+ if ( *pnt == '\\' )
{
- 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:
- case tok_string:
- 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");
+ offset = pnt - buffer;
+ continue;
}
+
+ /* tokenize this line */
+ tokenize_line( buffer );
+ offset = 0;
}
-#endif
- return 0;
+ /* that's all, folks */
+ if ( infile != stdin )
+ fclose( infile );
+ current_file = old_file;
+ lineno = old_lineno;
+ return;
+}
+
+
+/*
+ * Main program.
+ */
+int main( int argc, const char * argv [] )
+{
+ do_source ( "-" );
+ fix_conditionals ( config_list );
+ dump_tk_script ( config_list );
+ return 0;
}
diff --git a/scripts/tkparse.h b/scripts/tkparse.h
index 523ba7131..e2bca1950 100644
--- a/scripts/tkparse.h
+++ b/scripts/tkparse.h
@@ -1,82 +1,112 @@
+/*
+ * tkparse.h
+ */
-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_string,
- tok_define,
- tok_choose,
- tok_choice,
- tok_endmenu,
- tok_unknown
-};
+/*
+ * Define this symbol to generate exactly the same output, byte for byte,
+ * as the previous version of xconfig. I need to do this to make sure I
+ * I don't break anything in my moby edit. -- mec
+ */
+
+#define BUG_COMPATIBLE
-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
+/*
+ * Token types (mostly statement types).
+ */
+
+enum e_token
+{
+ token_UNKNOWN,
+ token_bool,
+ token_choice_header,
+ token_choice_item,
+ token_comment,
+ token_define_bool,
+ token_dep_tristate,
+ token_else,
+ token_endmenu,
+ token_fi,
+ token_hex,
+ token_if,
+ token_int,
+ token_mainmenu_name,
+ token_mainmenu_option,
+ token_source,
+ token_string,
+ token_then,
+ token_tristate,
+ token_unset,
};
-union var
+/*
+ * Operator types for conditionals.
+ */
+
+enum operator
{
- char * str;
- struct kconfig * cfg;
+ op_eq,
+ op_neq,
+ op_and,
+ op_and1,
+ op_or,
+ op_bang,
+ op_lparen,
+ op_rparen,
+ op_constant,
+ op_variable,
+ op_kvariable,
+ op_nuked
};
+/*
+ * Conditions come in linked lists.
+ * Some operators take strings:
+ *
+ * op_constant "foo"
+ * op_variable "$ARCH", "$CONFIG_PMAC"
+ * op_kvariable "$CONFIG_EXPERIMENTAL"
+ *
+ * Most "$..." constructs refer to a variable which is defined somewhere
+ * in the script, so they become op_kvariable's instead. Note that it
+ * is legal to test variables which are never defined, such as variables
+ * that are meaningful only on other architectures.
+ */
+
struct condition
{
- struct condition * next;
- enum operator op;
- union var variable;
+ struct condition * next;
+ enum operator op;
+ const char * str; /* op_constant, op_variable */
+ struct kconfig * cfg; /* op_kvariable */
};
-#define GLOBAL_WRITTEN 1
-#define CFG_DUP 2
-#define UNSAFE 4
+/*
+ * A statement from a config.in file
+ */
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;
+ struct kconfig * next;
+ enum e_token token;
+ char * optionname;
+ char * label;
+ char * value;
+ struct condition * cond;
+ char * depend; /* token_dep_tristate */
+ struct kconfig * cfg_parent; /* token_choice_item */
+
+ /* used only in tkgen.c */
+ char global_written;
+ int menu_number;
+ int menu_line;
+ struct kconfig * menu_next;
};
-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 */
+
+extern void fix_conditionals ( struct kconfig * scfg ); /* tkcond.c */
+extern void dump_tk_script ( struct kconfig * scfg ); /* tkgen.c */
diff --git a/scripts/ver_linux b/scripts/ver_linux
new file mode 100644
index 000000000..42c582eab
--- /dev/null
+++ b/scripts/ver_linux
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Before running this script please ensure that your PATH is
+# typical as you use for compilation/istallation. I use
+# /bin /sbin /usr/bin /usr/sbin /usr/local/bin, but it may
+# differ on your system.
+#
+echo '-- Versions installed: (if some fields are empty or looks'
+echo '-- unusual then possibly you have very old versions)'
+uname -a
+insmod -V 1>/tmp/ver_linux.tmp 2>>/tmp/ver_linux.tmp
+awk 'NR==1{print "Kernel modules ",$NF}' /tmp/ver_linux.tmp
+rm -f /tmp/ver_linux.tmp
+echo "Gnu C " `gcc --version`
+ld -v 2>&1 | awk -F\) '{print $1}' | awk \
+ '/BFD/{print "Binutils ",$NF}'
+ls -l `ldd /bin/sh | awk '/libc/{print $3}'` | sed -e 's/\.so$//' \
+ | awk -F'[.-]' '{print "Linux C Library " $(NF-2)"."$(NF-1)"."$NF}'
+echo -n "Dynamic linker "
+ldd -v > /dev/null 2>&1 && ldd -v || ldd --version |head -1
+ls -l /usr/lib/lib{g,stdc}++.so 2>/dev/null | awk -F. \
+ '{print "Linux C++ Library " $4"."$5"."$6}'
+ps --version 2>&1 | awk 'NR==1{print "Procps ", $NF}'
+mount --version | awk -F\- '{print "Mount ", $NF}'
+netstat --version | awk \
+'NR==1{if ($5 != "") { n=split($5,buf,"-"); ver=buf[n]; done=1 }}
+ NR==2{if (done != 1) ver=$3 }
+ END{print "Net-tools ",ver}'
+loadkeys -h 2>&1 | awk \
+'(NR==1 && $3) {ver=$3}
+ (NR==2 && $1 ~ /console-tools/) {print "Console-tools ",$3; done=1}
+ END {if (!done) print "Kbd ",ver}'
+expr --v | awk '{print "Sh-utils ", $NF}'