diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/Menuconfig | 175 | ||||
-rw-r--r-- | scripts/checkhelp.pl | 30 | ||||
-rw-r--r-- | scripts/header.tk | 86 | ||||
-rw-r--r-- | scripts/ksymoops.cc | 411 | ||||
-rw-r--r-- | scripts/ksymoops/Makefile | 79 | ||||
-rw-r--r-- | scripts/ksymoops/README | 395 | ||||
-rw-r--r-- | scripts/ksymoops/io.c | 139 | ||||
-rw-r--r-- | scripts/ksymoops/ksymoops.c | 678 | ||||
-rw-r--r-- | scripts/ksymoops/ksymoops.h | 146 | ||||
-rw-r--r-- | scripts/ksymoops/ksyms.c | 294 | ||||
-rw-r--r-- | scripts/ksymoops/map.c | 251 | ||||
-rw-r--r-- | scripts/ksymoops/misc.c | 108 | ||||
-rw-r--r-- | scripts/ksymoops/object.c | 230 | ||||
-rw-r--r-- | scripts/ksymoops/oops.c | 1200 | ||||
-rw-r--r-- | scripts/ksymoops/re.c | 145 | ||||
-rw-r--r-- | scripts/ksymoops/symbol.c | 444 | ||||
-rw-r--r-- | scripts/lxdialog/menubox.c | 7 | ||||
-rw-r--r-- | scripts/mkdep.c | 34 | ||||
-rw-r--r-- | scripts/tail.tk | 53 | ||||
-rw-r--r-- | scripts/tkcond.c | 753 | ||||
-rw-r--r-- | scripts/tkgen.c | 1759 | ||||
-rw-r--r-- | scripts/tkparse.c | 1158 | ||||
-rw-r--r-- | scripts/tkparse.h | 154 | ||||
-rw-r--r-- | scripts/ver_linux | 32 |
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}' |