The point of origin was the zconf_dump() routine.
As it turned out this was nothing like a “lint” or “dump” tool. It reports errors just because it's not supporting the kconfig syntax completely . It's obviously not the thing to mess with.
Next try was to implement the printconf stuff.
/*
* V 5c
* Released under the terms of the GNU GPL v2.0.
*/
#include <stdio.h>
#include <stdlib.h>
#include "lkc.h"
void printconf(FILE *);
void printsym(FILE *, struct menu *);
int main(int ac, char **av)
{
const char *name;
if (ac != 2) exit(1);
name = av[1];
conf_parse(name);
printconf(stdout);
/*zconfdump(stdout);*/
return 0;
}
void decodeflags (FILE *out, int flags);
void decodeflags (FILE *out, int flags) {
/* taken from expr.h */
int flaglist[] =
{
SYMBOL_CONST , /* 0x0001 symbol is const */
SYMBOL_CHECK , /* 0x0008 used during dependency checking */
SYMBOL_CHOICE , /* 0x0010 start of a choice block (null name) */
SYMBOL_CHOICEVAL, /* 0x0020 used as a value in a choice block */
SYMBOL_VALID , /* 0x0080 set when symbol.curr is calculated */
SYMBOL_OPTIONAL , /* 0x0100 choice is optional - values can be 'n' */
SYMBOL_WRITE , /* 0x0200 ? */
SYMBOL_CHANGED , /* 0x0400 ? */
SYMBOL_AUTO , /* 0x1000 value from environment variable */
SYMBOL_CHECKED , /* 0x2000 used during dependency checking */
SYMBOL_WARNED , /* 0x8000 warning has been issued */
0x0
};
char *flagnames[] = {
"CONST" , /* 0x0001 symbol is const */
"CHECK" , /* 0x0008 used during dependency checking */
"CHOICE" , /* 0x0010 start of a choice block (null name) */
"CHOICEVAL", /* 0x0020 used as a value in a choice block */
"VALID" , /* 0x0080 set when symbol.curr is calculated */
"OPTIONAL" , /* 0x0100 choice is optional - values can be 'n' */
"WRITE" , /* 0x0200 ? */
"CHANGED" , /* 0x0400 ? */
"AUTO" , /* 0x1000 value from environment variable */
"CHECKED" , /* 0x2000 used during dependency checking */
"WARNED" /* 0x8000 warning has been issued */
};
fprintf(out,"\t");
int i;
for(i=0; flaglist[i]; i++) {
if (flags & flaglist[i]) {
fprintf(out,"<flag>%s</flag>",flagnames[i]);
}
}
fprintf(out,"\n");
}
void decodeexpr_type(FILE *out, char* tagname,int val);
void decodeexpr_type(FILE *out, char* tagname,int val) {
char *value;
switch (val) {
case E_NONE:
value = "NONE";
break;
case E_OR:
value = "OR";
break;
case E_AND:
value = "AND";
break;
case E_NOT:
value = "NOT";
break;
case E_EQUAL:
value = "EQUAL";
break;
case E_UNEQUAL:
value = "UNEQUAL";
break;
case E_LIST:
value = "LIST";
break;
case E_SYMBOL:
value = "SYMBOL";
break;
case E_RANGE:
value = "RANGE";
break;
default:
fprintf(stderr, "ERROR: decodeexpr_type() '%d': should never happen\n",val);
exit(1);
}
fprintf(out, "\t<%s>%s</%s>\n", tagname, value, tagname);
}
void decodesym(FILE *out, char* tagname,struct symbol_value symval);
void decodesym(FILE *out, char* tagname,struct symbol_value symval) {
fprintf(out, "\t<%s>%s</%s>\n", tagname, (char *)symval.val, tagname);
}
void printsym(FILE *out, struct menu *menu)
{
struct property *prop;
// int have_properties = 0;
int open_menu = 0;
fprintf(out, "<option>\n");
if (menu->sym->name)
fprintf(out, "\t<symbol>%s</symbol>\n", menu->sym->name);
if (menu->prompt)
fprintf(out, "\t<prompt><![CDATA[%s]]></prompt>\n", menu->prompt->text);
if (menu->help)
fprintf(out, "\t<help><![CDATA[%s]]></help>\n", menu->help);
switch (menu->sym->type) {
case S_UNKNOWN:
fprintf(out, "\t<type>%s</type>\n", "unknown");
break;
case S_BOOLEAN:
fprintf(out, "\t<type>%s</type>\n", "boolean");
break;
case S_TRISTATE:
fprintf(out, "\t<type>%s</type>\n", "tristate");
break;
case S_INT:
fprintf(out, "\t<type>%s</type>\n", "int");
break;
case S_HEX:
fprintf(out, "\t<type>%s</type>\n", "hex");
break;
case S_STRING:
fprintf(out, "\t<type>%s</type>\n", "string");
break;
case S_OTHER:
fprintf(out, "\t<type>%s</type>\n", "other");
break;
default:
fprintf(out, "\t<type>unknown=%d</type>\n", menu->sym->type);
}
for (prop = menu->sym->prop; prop; prop = prop->next) {
if (prop->menu != menu)
continue;
switch (prop->type) {
case P_PROMPT:
fprintf(out, "\t<property.prompt>\n\t\t<value>");
expr_fprint(prop->expr, out);
fprintf(out, "</value>\n");
if (!expr_is_yes(prop->visible.expr)) {
fprintf(out, "\t\t<cond><![CDATA[");
expr_fprint(prop->visible.expr, out);
fprintf(out,"]]></cond>\n");
}
fprintf(out, "\t</property.prompt>\n");
break;
case P_DEFAULT:
fprintf(out, "\t<property.default>\n");
fprintf(out, "\t\t<value><![CDATA[");
expr_fprint(prop->expr, out);
fprintf(out, "]]></value>\n");
if (!expr_is_yes(prop->visible.expr)) {
fprintf(out, "\t\t<cond><![CDATA[");
expr_fprint(prop->visible.expr, out);
fprintf(out,"]]></cond>\n");
}
fprintf(out, "\t</property.default>\n");
break;
case P_CHOICE:
fprintf(out, "\t<property.choice/>\n");
break;
case P_SELECT:
fprintf(out, "\t<property.select>");
expr_fprint(prop->expr, out);
fprintf(out, "</property.select>\n");
break;
case P_RANGE:
fprintf(out, "\t<property.range>");
expr_fprint(prop->expr, out);
fprintf(out, "</property.range>\n");
break;
case P_MENU:
fprintf(out, "\t<property.menu>");
expr_fprint(prop->expr, out);
fprintf(out, "</property.menu>\n");
open_menu = 1;
break;
case P_SYMBOL:
fprintf(out, "\t<property.symbol/>\n");
break;
default:
fprintf(stderr, "unknown property type: %d\n", prop->type);
}
}
decodeflags(out,menu->sym->flags);
fprintf(out, "\t<file>%s</file>\n", menu->sym->prop->file->name);
fprintf(out, "\t<lineno>%d</lineno>\n", menu->sym->prop->lineno);
fprintf(out, "\t<visible>%s</visible>\n", expr_is_yes(menu->sym->prop->visible.expr) ? "y" : "n");
fprintf(out, "\t<flags>0x%x</flags>\n",menu->sym->flags);
fprintf(out, "</option>\n");
if (open_menu)
// fprintf(out, "<menu><!--%s-->\n", menu->sym->name);
fprintf(out, "<menu name=\"%s\" file=\"%s\" lineno=\"%d\">\n", menu->sym->name,menu->sym->prop->file->name,menu->sym->prop->lineno);
}
void tag (FILE *out, char *tagname, char *value);
void tag (FILE *out, char *tagname, char *value) {
fprintf(out, "\t<%s>%s</%s>\n", tagname, value, tagname);
}
void printconf(FILE *out)
{
/* struct property *prop; */
struct menu *menu;
fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
fprintf(out, "<kconfig formatversion=\"20140523-083928\" kernelversion=\"3.10.39\">\n");
fprintf(out, "<menu><!--root menu-->\n");
menu = rootmenu.list;
while (menu) {
if ( menu->prompt ) {
switch (menu->prompt->type) {
case P_UNKNOWN:
fprintf(out, "\t<propertytype>%s</propertytype>\n", "unknown");
break;
case P_PROMPT: /* prompt "foo prompt" or "BAZ Value" */
// fprintf(out, "\t<propertytype>%s</propertytype>\n", "prompt");
break;
case P_COMMENT: /* text associated with a comment */
// fprintf(out, "\t<propertytype>%s</propertytype>\n", "comment");
break;
case P_MENU: /* prompt associated with a menuconfig option */
// fprintf(out, "\t<propertytype>%s</propertytype>\n", "menu");
break;
case P_DEFAULT: /* default y */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "default");
break;
case P_CHOICE: /* choice value */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "choice");
break;
case P_SELECT: /* select BAR */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "select");
break;
case P_RANGE: /* range 7..100 (for a symbol) */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "range");
break;
case P_ENV: /* value from environment variable */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "env");
break;
case P_SYMBOL: /* where a symbol is defined */
fprintf(out, "\t<propertytype>%s</propertytype>\n", "symbol");
break;
default:
fprintf(stderr, "ERROR: menu->prompt->type '%d': should never happen\n",menu->prompt->type);
exit(1);
}
}
if (menu->sym) {
printsym(out, menu);
} else if (menu->prompt) {
switch (menu->prompt->type) {
case P_COMMENT:
fprintf(out, "<comment>%s</comment>\n", menu->prompt->text);
break;
case P_MENU:
if ( menu->sym ) {
fprintf(out, "<menu name=\"%s\" file=\"%s\" lineno=\"%d\">\n\t<symbol>%s</symbol>\n", menu->sym->name, menu->sym->prop->file->name, menu->sym->prop->lineno, menu->prompt->text);
} else {
fprintf(out, "<menu name=\"%s\" file=\"%s\" lineno=\"%d\" f=\"3\">\n\t<symbol>%s</symbol>\n", menu->prompt->text, menu->prompt->file->name, menu->prompt->file->lineno, menu->prompt->text);
}
// fprintf(out, "<menu name=\"%s\" file=\"%s\" lineno=\"%d\">\n", menu->sym->name,menu->sym->prop->file->name,menu->sym->prop->lineno);
break;
case P_PROMPT:
fprintf(out, "<prompt><![CDATA[%s]]></prompt>\n", menu->prompt->text);
break;
default:
fprintf(stderr, "ERROR: menu->prompt->type '%d': should never happen\n",menu->prompt->type);
exit(1);
}
if (menu->visibility) {
decodeexpr_type(out,"visibility",menu->visibility->type);
}
}
if (menu->list)
menu = menu->list;
else if (menu->next)
menu = menu->next;
else while ((menu = menu->parent)) {
if (menu->prompt && menu->prompt->type == P_MENU)
fprintf(out, "</menu><!--%s-->\n", menu->sym?menu->sym->name:menu->prompt->text);
if (menu->next) {
menu = menu->next;
break;
}
}
}
fprintf(out, "</menu><!--root menu-->\n");
fprintf(out, "</kconfig>\n");
}
Then there is an assertion to be thrown on empty values, even if they are perfectly legal. The following patch helps:
--- lkc.h.orig 2014-05-21 11:35:33.210489800 +0200
+++ lkc.h 2014-05-21 11:35:33.210489800 +0200
@@ -90,7 +90,10 @@ struct conf_printer {
/* confdata.c and expr.c */
static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
{
- assert(len != 0);
+ /*assert(len != 0);*/
+ if (len==0) {
+ return;
+ }
if (fwrite(str, len, count, out) != count)
fprintf(stderr, "Error in writing or end of file.\n");
To do things more easily, there is a script. You may want to change the settings of BASEDIR and DIR.
#! /bin/sh
if [ -z "$1" ]; then
echo "usage: $0 <kernel-source-version>"
exit 1
fi
KVER=$1
BASEDIR=/home/chris/prog/C/kconfig
DIR=/home/chris/work/scratch/ssd01/kernels/linux-$KVER/scripts/kconfig
if [ ! -e "$DIR" ]; then
echo "$DIR does not exist"
exit 1
fi
if [ -e "$DIR" -a ! -d "$DIR" ]; then
echo "$DIR exists but is not a directory"
exit 1
fi
if [ ! -e "$BASEDIR" ]; then
echo "basedir '$BASEDIR' does not exist"
exit 1
fi
if [ -e "$BASEDIR" -a ! -d "$DIR" ]; then
echo "basedir '$BASEDIR' exists but is not a directory"
exit 1
fi
cd $BASEDIR
# ln -s jo/printconf.c-5a ./printconf.c
cp printconf.c $DIR
cp patches/lkc.h.diff $DIR
#cp data/Kconfig* $DIR
cd $DIR
#flex --outfile zconf.lex.c zconf.l
#gperf zconf.gperf --output-file zconf.hash.c
#bison zconf.y
cp zconf.hash.c_shipped zconf.hash.c
cp zconf.lex.c_shipped zconf.lex.c
cp zconf.tab.c_shipped zconf.tab.c
patch < lkc.h.diff
make () {
gcc -Wp,-MD,.conf.o.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>" -DLOCALE -c -o printconf.o printconf.c
gcc -Wp,-MD,.zconf.tab.o.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>" -DLOCALE -I. -c -o zconf.tab.o zconf.tab.c
gcc -o printconf printconf.o zconf.tab.o
}
make
cd ../..
SRCARCH=x86 ARCH=x86 KERNELVERSION=$KVER scripts/kconfig/printconf Kconfig > Kconfig.xml
cp -ax Kconfig.xml ../kconfig-$KVER.xml
</menu> tags are missing. 3.4's just fine.<option> but without a <symbol> (Name). They fail if being read as XML which show's something's not understood, wrong or whatever.