2013-04-21 01:42:59 +08:00
/****************************************************************************
* tools / kconfig2html . c
*
* Copyright ( C ) 2013 Gregory Nutt . All rights reserved .
* Author : Gregory Nutt < gnutt @ nuttx . org >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/****************************************************************************
* Included Files
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <unistd.h>
# include <time.h>
# include <string.h>
# include <ctype.h>
# include <libgen.h>
# include <errno.h>
/****************************************************************************
* Pre - processor Definitions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define LINE_SIZE 1024
# define SCRATCH_SIZE 1024
# define MAX_DEPENDENCIES 100
# define MAX_LEVELS 100
2013-04-21 07:05:45 +08:00
# define MAX_SELECT 16
2013-04-21 04:18:08 +08:00
# define TAB_SIZE 4
2013-04-22 01:01:46 +08:00
# define VAR_SIZE 80
# define HTML_VAR_SIZE (2*VAR_SIZE + 64)
2013-04-21 01:42:59 +08:00
# define TMPFILE_NAME "kconfig2html-tmp.dat"
/****************************************************************************
* Private Types
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
enum token_type_e
{
TOKEN_NONE = 0 ,
TOKEN_NOTRESERVED ,
2013-04-21 07:05:45 +08:00
TOKEN_COMMENT ,
2013-04-21 01:42:59 +08:00
TOKEN_CONFIG ,
2013-04-21 07:05:45 +08:00
TOKEN_MENUCONFIG ,
2013-04-21 01:42:59 +08:00
TOKEN_BOOL ,
TOKEN_INT ,
TOKEN_HEX ,
TOKEN_STRING ,
TOKEN_DEFAULT ,
2013-04-21 07:29:10 +08:00
TOKEN_RANGE ,
2013-04-21 07:05:45 +08:00
TOKEN_SELECT ,
TOKEN_DEPENDS ,
TOKEN_ON ,
TOKEN_OPTION ,
2013-04-21 01:42:59 +08:00
TOKEN_HELP ,
2013-04-21 07:05:45 +08:00
TOKEN_MAINMENU ,
2013-04-21 01:42:59 +08:00
TOKEN_MENU ,
TOKEN_ENDMENU ,
TOKEN_CHOICE ,
TOKEN_ENDCHOICE ,
2013-04-21 04:18:08 +08:00
TOKEN_PROMPT ,
2013-04-21 01:42:59 +08:00
TOKEN_IF ,
TOKEN_ENDIF ,
TOKEN_SOURCE
} ;
enum config_type_e
{
VALUE_NONE = 0 ,
VALUE_INT ,
VALUE_HEX ,
VALUE_BOOL ,
VALUE_STRING
} ;
enum error_e
{
ERROR_UNRECOGNIZED_OPTION = 1 ,
ERROR_MISSING_OPTION_ARGUMENT ,
ERROR_UNEXPECTED_OPTION ,
ERROR_TOO_MANY_ARGUMENTS ,
ERROR_OUTFILE_OPEN_FAILURE ,
ERROR_TMPFILE_OPEN_FAILURE ,
ERROR_KCONFIG_OPEN_FAILURE ,
2013-04-21 07:05:45 +08:00
ERROR_TOO_MANY_SELECT ,
2013-04-21 01:42:59 +08:00
ERROR_TOO_MANY_DEPENDENCIES ,
ERROR_DEPENDENCIES_UNDERFLOW ,
2013-04-21 07:05:45 +08:00
ERRROR_ON_AFTER_DEPENDS ,
2013-04-21 01:42:59 +08:00
ERROR_NESTING_TOO_DEEP ,
ERROR_NESTING_UNDERFLOW
} ;
struct reserved_s
{
enum token_type_e ttype ;
const char * tname ;
} ;
union value_u
{
char * s ;
int i ;
bool b ;
} ;
struct config_s
{
enum config_type_e ctype ;
char * cname ;
char * cdesc ;
char * cdefault ;
2013-04-21 07:29:10 +08:00
char * clower ;
char * cupper ;
2013-04-21 07:05:45 +08:00
char * cselect [ MAX_SELECT ] ;
int cnselect ;
int cndependencies ;
2013-04-21 01:42:59 +08:00
} ;
2013-04-21 04:18:08 +08:00
struct choice_s
{
char * cprompt ;
char * cdefault ;
2013-04-21 07:05:45 +08:00
int cndependencies ;
} ;
struct menu_s
{
char * mname ;
int mndependencies ;
2013-04-21 04:18:08 +08:00
} ;
2013-04-21 01:42:59 +08:00
/****************************************************************************
* Private Data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char g_line [ LINE_SIZE + 1 ] ;
static char g_scratch [ SCRATCH_SIZE + 1 ] ;
static FILE * g_outfile ;
static FILE * g_tmpfile ;
static char * g_lasts ;
static bool g_debug ;
static bool g_internal ;
2013-04-21 04:18:08 +08:00
static bool g_preread ;
2013-04-21 01:42:59 +08:00
static const char * g_kconfigroot ;
static const char * g_appsdir ;
static int g_paranum [ MAX_LEVELS ] ;
static int g_level ;
static char * g_dependencies [ MAX_DEPENDENCIES ] ;
static int g_ndependencies ;
static int g_inchoice ;
static int g_menu_number ;
static int g_choice_number ;
static struct reserved_s g_reserved [ ] =
{
2013-04-21 07:05:45 +08:00
{ TOKEN_COMMENT , " comment " } ,
2013-04-21 01:42:59 +08:00
{ TOKEN_CONFIG , " config " } ,
2013-04-21 07:05:45 +08:00
{ TOKEN_MENUCONFIG , " menuconfig " } ,
2013-04-21 01:42:59 +08:00
{ TOKEN_BOOL , " bool " } ,
{ TOKEN_INT , " int " } ,
{ TOKEN_HEX , " hex " } ,
{ TOKEN_STRING , " string " } ,
{ TOKEN_DEFAULT , " default " } ,
2013-04-21 07:29:10 +08:00
{ TOKEN_RANGE , " range " } ,
2013-04-21 07:05:45 +08:00
{ TOKEN_SELECT , " select " } ,
{ TOKEN_DEPENDS , " depends " } ,
{ TOKEN_ON , " on " } ,
{ TOKEN_OPTION , " option " } ,
2013-04-21 01:42:59 +08:00
{ TOKEN_HELP , " help " } ,
{ TOKEN_HELP , " ---help--- " } ,
2013-04-21 07:05:45 +08:00
{ TOKEN_MAINMENU , " mainmenu " } ,
2013-04-21 01:42:59 +08:00
{ TOKEN_MENU , " menu " } ,
{ TOKEN_ENDMENU , " endmenu " } ,
{ TOKEN_CHOICE , " choice " } ,
{ TOKEN_ENDCHOICE , " endchoice " } ,
2013-04-21 04:18:08 +08:00
{ TOKEN_PROMPT , " prompt " } ,
2013-04-21 01:42:59 +08:00
{ TOKEN_SOURCE , " source " } ,
{ TOKEN_IF , " if " } ,
{ TOKEN_ENDIF , " endif " } ,
{ TOKEN_NOTRESERVED , NULL } /* Terminates list */
} ;
/****************************************************************************
* Public Data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/****************************************************************************
* Private Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 04:18:08 +08:00
/****************************************************************************
2013-04-21 23:37:45 +08:00
* Name : debug
2013-04-21 04:18:08 +08:00
*
* Description :
2013-04-21 23:37:45 +08:00
* Debug output ( conditional )
2013-04-21 04:18:08 +08:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 23:37:45 +08:00
static void debug ( const char * fmt , . . . )
2013-04-21 01:42:59 +08:00
{
2013-04-21 23:37:45 +08:00
va_list ap ;
2013-04-21 01:42:59 +08:00
2013-04-21 23:37:45 +08:00
if ( g_debug )
{
va_start ( ap , fmt ) ;
( void ) vfprintf ( stderr , fmt , ap ) ;
va_end ( ap ) ;
}
2013-04-21 01:42:59 +08:00
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
2013-04-21 23:37:45 +08:00
* Name : error
2013-04-21 04:18:08 +08:00
*
* Description :
2013-04-21 23:37:45 +08:00
* Error output ( unconditional )
2013-04-21 04:18:08 +08:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
2013-04-21 23:37:45 +08:00
static void error ( const char * fmt , . . . )
2013-04-21 01:42:59 +08:00
{
va_list ap ;
2013-04-21 23:37:45 +08:00
va_start ( ap , fmt ) ;
( void ) vfprintf ( stderr , fmt , ap ) ;
va_end ( ap ) ;
2013-04-21 01:42:59 +08:00
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : output
*
* Description :
* Output to the final HTML file
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void output ( const char * fmt , . . . )
{
va_list ap ;
va_start ( ap , fmt ) ;
( void ) vfprintf ( g_outfile , fmt , ap ) ;
va_end ( ap ) ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : body
*
* Description :
* HTML body output to a temporary file .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void body ( const char * fmt , . . . )
{
va_list ap ;
va_start ( ap , fmt ) ;
( void ) vfprintf ( g_tmpfile , fmt , ap ) ;
va_end ( ap ) ;
}
2013-04-21 23:37:45 +08:00
/****************************************************************************
* Name : show_usage
*
* Description :
* Show usage of this program and exit with the specified error code
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void show_usage ( const char * progname , int exitcode )
{
error ( " USAGE: %s [-d] [-i] [-a <apps directory>] {-o <out file>] [<Kconfig root>] \n " , progname ) ;
error ( " %s [-h] \n \n " , progname ) ;
error ( " Where: \n \n " ) ;
error ( " \t -a : Select relative path to the apps/ directory. Theis path is relative \n " ) ;
error ( " \t to the <Kconfig directory>. Default: ../apps \n " ) ;
error ( " \t -o : Send output to <out file>. Default: Output goes to stdout \n " ) ;
error ( " \t -i : Show hidden, internal configuration variables \n " ) ;
error ( " \t -d : Enable debug output \n " ) ;
error ( " \t -h : Prints this message and exits \n " ) ;
error ( " \t <Kconfig root> is the directory containing the root Kconfig file. \n " ) ;
error ( " \t Default <Kconfig directory>: . \n " ) ;
exit ( exitcode ) ;
}
/****************************************************************************
* Name : skip_space
*
* Description :
* Skip over any spaces
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * skip_space ( char * ptr )
{
while ( * ptr & & isspace ( ( int ) * ptr ) ) ptr + + ;
return ptr ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : dequote
*
* Description :
* Remove quotation marks from a string .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static char * dequote ( char * ptr )
{
int len ;
/* Check if there is a traiing quote */
len = strlen ( ptr ) ;
if ( ptr [ len - 1 ] = = ' " ' )
{
/* Yes... replace it with a terminator */
ptr [ len - 1 ] = ' \0 ' ;
len - - ;
}
/* Is there a leading quote? */
if ( ptr [ 0 ] = = ' " ' )
{
/* Yes.. skip over the leading quote */
ptr + + ;
len - - ;
}
/* Handle the case where nothing is left after dequoting */
if ( len < = 0 )
{
ptr = NULL ;
}
return ptr ;
}
2013-04-21 23:37:45 +08:00
/****************************************************************************
2013-04-22 01:01:46 +08:00
* Name : htmlize_character
2013-04-21 23:37:45 +08:00
*
* Description :
2013-04-22 01:01:46 +08:00
* Transfer and HTML - ize a character . Convert characters :
2013-04-21 23:37:45 +08:00
*
* " " quotation mark
2013-04-22 01:01:46 +08:00
* ' & apos ; apostrophe
2013-04-21 23:37:45 +08:00
* & & amp ; ampersand
* < & lt ; less - than
* > & gt ; greater - than
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-22 01:01:46 +08:00
static int htmlize_character ( char * dest , char ch )
2013-04-21 23:37:45 +08:00
{
const char * str ;
2013-04-22 01:01:46 +08:00
/* Transfer the character from into the destination buffer, perform the
* conversion only the the character is one of the special characters .
*/
str = NULL ;
switch ( ch )
{
case ' " ' :
str = " " " ;
break ;
case ' \' ' :
str = " ' " ;
break ;
case ' & ' :
str = " & " ;
break ;
case ' < ' :
str = " < " ;
break ;
case ' > ' :
str = " > " ;
break ;
default :
* dest + + = ch ;
* dest = ' \0 ' ;
return 1 ;
}
/* Transfer a string */
* dest = ' \0 ' ;
strcat ( dest , str ) ;
return strlen ( str ) ;
}
/****************************************************************************
* Name : htmlize_text
*
* Description :
* HTML - ize a free - text string . This function preforms the conversions of
* in htmlize_character ( ) for a text string .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * htmlize_text ( const char * src )
{
char * dest = g_scratch ;
/* We may get here with the source pointer equal to NULL. Return the
* disfavor .
*/
if ( ! src )
{
return NULL ;
}
/* Transfer each character from the source string into the scratch buffer */
for ( ; * src ; src + + )
{
/* Expand characters as necessary */
dest + = htmlize_character ( dest , * src ) ;
}
return g_scratch ;
}
/****************************************************************************
* Name : htmlize_expression
*
* Description :
* HTML - ize an expression of configuration variables . This function
* preforms the same conversions as in htmlize_character ( ) , but also
* expands and adds hyper links for configuration variables .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * htmlize_expression ( const char * src )
{
char varname [ VAR_SIZE + 1 ] ;
char htmlvar [ HTML_VAR_SIZE + 1 ] ;
char * dest = g_scratch ;
char ch = ' \0 ' ;
char lastc ;
2013-04-21 23:37:45 +08:00
/* We may get here with the source pointer equal to NULL. Return the
* disfavor .
*/
if ( ! src )
{
return NULL ;
}
/* Transfer each character from the source string into the scratch buffer */
dest = g_scratch ;
* dest = ' \0 ' ;
2013-04-22 01:01:46 +08:00
while ( * src )
2013-04-21 23:37:45 +08:00
{
2013-04-22 01:01:46 +08:00
/* Remember the last character and advance to the next character */
lastc = ch ;
ch = * src ;
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
/* Skip control characters and out-of-range 7-bit ASCII characters */
if ( * src < 0x20 | | * src > 0x7e )
2013-04-21 23:37:45 +08:00
{
2013-04-22 01:01:46 +08:00
src + + ;
continue ;
}
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
/* Output no more than one consecutive space character. This depends
* on the fact that kconfig_line has replaces all of the forms of
* whitespace with a space character .
*/
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
if ( * src = = ' ' )
{
if ( lastc ! = ' ' )
{
* dest + + = * src ;
* dest = ' \0 ' ;
}
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
src + + ;
continue ;
}
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
/* Concatenate variable name strings. There strings probably begin
* with a uppercase letter , but here all alphanumeric values ( plus ' _ ' _
* are concatenated .
*/
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
if ( isalnum ( ( ( int ) * src ) ) | | * src = = ' _ ' )
{
int namlen = 0 ;
do
{
/* Don't overflow the tiny variable name buffer */
if ( namlen > = VAR_SIZE )
{
error ( " Configuration variable name too long \n " ) ;
break ;
}
/* Add the next character to the name */
varname [ namlen ] = * src + + ;
namlen + + ;
varname [ namlen ] = ' \0 ' ;
}
while ( isalnum ( ( ( int ) * src ) ) | | * src = = ' _ ' ) ;
/* HTML-ize the name into our bigger, local scratch buffer */
snprintf ( htmlvar , HTML_VAR_SIZE , " <a href= \" #CONFIG_%s \" ><code>CONFIG_%s</code></a> " ,
varname , varname ) ;
/* Then transfer the string into the scratch buffer */
strcat ( dest , htmlvar ) ;
dest + = strlen ( htmlvar ) ;
2013-04-21 23:37:45 +08:00
}
2013-04-22 01:01:46 +08:00
/* All that remains are space and the punctuation characters */
else
{
/* Expand characters as necessary */
2013-04-21 23:37:45 +08:00
2013-04-22 01:01:46 +08:00
dest + = htmlize_character ( dest , * src ) ;
src + + ;
}
2013-04-21 23:37:45 +08:00
}
return g_scratch ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : read_line
*
* Description :
2013-04-21 23:37:45 +08:00
* Read a new line from the Kconfig file into the g_line [ ] buffer , using
* the g_scratch buffer if necessary to concatenate lines that end with a
* line continuation character ( backslash ) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * read_line ( FILE * stream )
{
char * ptr ;
int len ;
/* Read the next line */
g_line [ LINE_SIZE ] = ' \0 ' ;
if ( ! fgets ( g_line , LINE_SIZE , stream ) )
{
return NULL ;
}
/* Loop to handle continuation lines */
for ( ; ; )
{
/* How long is the line so far? */
len = strlen ( g_line ) ;
/* Remove any newline character at the end of the buffer */
if ( g_line [ len - 1 ] = = ' \n ' )
{
len - - ;
g_line [ len ] = ' \0 ' ;
}
/* Does this continue on the next line? Note taht this check
* could erroneoulsy combine two lines if a comment line ends with
* a line continuation . . . Don ' t do that !
*/
if ( g_line [ len - 1 ] ! = ' \\ ' )
{
/* No.. return now */
return g_line ;
}
/* Yes.. Replace the backslash with a space delimiter */
g_line [ len - 1 ] = ' ' ;
/* Read the next line into the scratch buffer */
g_scratch [ SCRATCH_SIZE ] = ' \0 ' ;
if ( ! fgets ( g_scratch , SCRATCH_SIZE , stream ) )
{
return NULL ;
}
/* Skip any leading whitespace and copy the rest of the next line
* into the line buffer . Note that the leading white space is
* replaced with a single character to serve as a delimiter .
*/
ptr = skip_space ( g_scratch ) ;
strncpy ( & g_line [ len ] , ptr , LINE_SIZE - len ) ;
}
}
/****************************************************************************
* Name : kconfig_line
*
* Description :
2013-04-21 04:18:08 +08:00
* Read a new line , skipping over leading white space and ignore lines
* that contain only comments .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 23:37:45 +08:00
static char * kconfig_line ( FILE * stream )
2013-04-21 01:42:59 +08:00
{
char * ptr ;
for ( ; ; )
{
2013-04-21 23:37:45 +08:00
/* Read the next line from the Kconfig file */
2013-04-21 04:18:08 +08:00
/* Is there already valid data in the line buffer? This can happen while parsing
* help text and we read one line too far .
*/
2013-04-21 01:42:59 +08:00
2013-04-21 04:18:08 +08:00
if ( ! g_preread )
{
2013-04-21 23:37:45 +08:00
/* Read the next line */
if ( ! read_line ( stream ) )
2013-04-21 01:42:59 +08:00
{
2013-04-21 04:18:08 +08:00
return NULL ;
2013-04-21 01:42:59 +08:00
}
2013-04-21 04:18:08 +08:00
}
g_preread = false ;
2013-04-21 01:42:59 +08:00
2013-04-21 04:18:08 +08:00
/* Replace all whitespace characters with spaces to simplify parsing */
2013-04-21 01:42:59 +08:00
2013-04-21 04:18:08 +08:00
for ( ptr = g_line ; * ptr ; ptr + + )
{
if ( isspace ( ( ( int ) * ptr ) ) )
2013-04-21 01:42:59 +08:00
{
2013-04-21 04:18:08 +08:00
* ptr = ' ' ;
2013-04-21 01:42:59 +08:00
}
}
2013-04-21 04:18:08 +08:00
/* Skip any leading whitespace. Ignore empty lines and lines that
* contain only comments .
*/
ptr = skip_space ( g_line ) ;
if ( * ptr & & * ptr ! = ' # ' & & * ptr ! = ' \n ' )
{
return ptr ;
}
2013-04-21 01:42:59 +08:00
}
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : tokenize
*
* Description :
* Check if this string corresponds to a string in the reserved word table .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
2013-04-21 04:18:08 +08:00
static enum token_type_e tokenize ( const char * token )
2013-04-21 01:42:59 +08:00
{
struct reserved_s * ptr ;
for ( ptr = g_reserved ; ptr - > tname ; ptr + + )
{
if ( strcmp ( token , ptr - > tname ) = = 0 )
{
break ;
}
}
return ptr - > ttype ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : MY_strtok_r
*
* Description :
* A replacement that can be used if your platform does not support strtok_r .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
# ifndef HAVE_STRTOK_R
static char * MY_strtok_r ( char * str , const char * delim , char * * saveptr )
{
char * pbegin ;
char * pend = NULL ;
/* Decide if we are starting a new string or continuing from
* the point we left off .
*/
if ( str )
{
pbegin = str ;
}
else if ( saveptr & & * saveptr )
{
pbegin = * saveptr ;
}
else
{
return NULL ;
}
/* Find the beginning of the next token */
for ( ;
* pbegin & & strchr ( delim , * pbegin ) ! = NULL ;
pbegin + + ) ;
/* If we are at the end of the string with nothing
* but delimiters found , then return NULL .
*/
if ( ! * pbegin )
{
return NULL ;
}
/* Find the end of the token */
for ( pend = pbegin + 1 ;
* pend & & strchr ( delim , * pend ) = = NULL ;
pend + + ) ;
/* pend either points to the end of the string or to
* the first delimiter after the string .
*/
if ( * pend )
{
/* Turn the delimiter into a null terminator */
* pend + + = ' \0 ' ;
}
/* Save the pointer where we left off and return the
* beginning of the token .
*/
if ( saveptr )
{
* saveptr = pend ;
}
return pbegin ;
}
# define strtok_r MY_strtok_r
# endif
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : findchar
*
* Description :
* Find a character in a string . This differs from strchr ( ) because it
* skips over quoted characters . For example , if you are searching for
* ' " ' , encountering ' " ' will terminate the search , but " \" " will not .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static char * findchar ( char * ptr , char ch )
{
bool escaped = false ;
/* Search for the leading quotation marked */
for ( ; * ptr ; ptr + + )
{
if ( escaped )
{
/* Skip over this character and reset the escaped flag */
escaped = false ;
}
else if ( * ptr = = ' \\ ' )
{
/* Set the escaped flag to skip over the next character */
escaped = true ;
}
else if ( * ptr = = ch )
{
/* We have found the (unescaped) character we are looking for */
return ptr ;
}
}
return NULL ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : getstring
*
* Description :
* Extract a quoted string
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static char * getstring ( char * ptr )
{
char * endptr ;
/* Search for the leading quotation mark */
ptr = findchar ( ptr , ' " ' ) ;
if ( ptr )
{
/* Skip over the quote */
ptr + + ;
/* Search for the trailing quotation mark */
endptr = findchar ( ptr , ' " ' ) ;
if ( endptr )
{
/* Replace the final quote with a NUL */
* endptr = ' \0 ' ;
}
}
2013-04-22 01:01:46 +08:00
return htmlize_text ( ptr ) ;
2013-04-21 01:42:59 +08:00
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : push_dependency
*
* Description :
* Add the new dependency to the current list of dependencies .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void push_dependency ( const char * dependency )
{
int ndx = g_ndependencies ;
if ( ndx > = MAX_DEPENDENCIES )
{
2013-04-21 23:37:45 +08:00
error ( " Too many dependencies, aborting \n " ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_TOO_MANY_DEPENDENCIES ) ;
}
g_dependencies [ ndx ] = strdup ( dependency ) ;
g_ndependencies = ndx + 1 ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : pop_dependency
*
* Description :
* Remove the last , pushed dependency
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void pop_dependency ( void )
{
int ndx = g_ndependencies - 1 ;
if ( ndx < 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Dependency underflow, aborting \n " ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_DEPENDENCIES_UNDERFLOW ) ;
}
if ( g_dependencies [ ndx ] )
{
free ( g_dependencies [ ndx ] ) ;
g_dependencies [ ndx ] = NULL ;
}
g_ndependencies = ndx ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : incr_level
*
* Description :
* Increment the paragraph numbering level
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void incr_level ( void )
{
int ndx = g_level ;
if ( ndx > = MAX_LEVELS )
{
2013-04-21 23:37:45 +08:00
error ( " Nesting level is too deep, aborting \n " ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_NESTING_TOO_DEEP ) ;
}
g_paranum [ ndx ] = 1 ;
g_level = ndx + 1 ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : decr_level
*
* Description :
* Decrease the paragraph numbering level .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void decr_level ( void )
{
int ndx = g_level ;
g_paranum [ ndx ] = ' \0 ' ;
ndx - - ;
if ( ndx < 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Nesting level underflow, aborting \n " ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_NESTING_UNDERFLOW ) ;
}
g_level = ndx ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : incr_paranum
*
* Description :
* Increment the paragraph number at this level
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void incr_paranum ( void )
{
int ndx = g_level - 1 ;
if ( ndx < 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Nesting level underflow, aborting \n " ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_NESTING_UNDERFLOW ) ;
}
g_paranum [ ndx ] + + ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : get_paranum
*
* Description :
* Return a string for this paragraph ( uses g_scratch [ ] ) .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static const char * get_paranum ( void )
{
char buffer [ 16 ] ;
int i ;
g_scratch [ 0 ] = ' \0 ' ;
for ( i = 0 ; i < g_level ; i + + )
{
if ( i > 0 )
{
strcat ( g_scratch , " . " ) ;
}
snprintf ( buffer , 16 , " %d " , g_paranum [ i ] ) ;
strcat ( g_scratch , buffer ) ;
}
return g_scratch ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : type2str
*
* Description :
* Return a string given a member of the configuration variable type
* enumeration .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static const char * type2str ( enum config_type_e valtype )
{
switch ( valtype )
{
case VALUE_BOOL :
return " Boolean " ;
case VALUE_INT :
return " Integer " ;
case VALUE_HEX :
return " Hexadecimal " ;
case VALUE_STRING :
return " String " ;
default :
break ;
}
return " Unknown " ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : process_help
*
* Description :
* Read and generate HTML for the help text that is expected after the
* configuration configuration variable description .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline void process_help ( FILE * stream )
{
char * ptr ;
int help_indent = 0 ;
int indent ;
bool blank ;
bool done ;
bool newpara ;
/* Read each comment line */
newpara = true ;
for ( ; ; )
{
/* Read the next line of comment text */
2013-04-21 23:37:45 +08:00
if ( ! read_line ( stream ) )
2013-04-21 04:18:08 +08:00
{
break ;
}
/* What is the indentation level? The first help line sets the
* indentation level . The first line encounter with lower
* indentation terminates the help .
*/
ptr = g_line ;
indent = 0 ;
blank = false ;
done = false ;
while ( ! done )
{
int ch = ( int ) * ptr ;
switch ( ch )
{
case ' ' :
indent + + ;
ptr + + ;
break ;
case ' \t ' :
indent + = TAB_SIZE ;
ptr + + ;
break ;
case ' # ' :
#if 0
blank = true ;
# endif
done = true ;
break ;
case ' \n ' :
case ' \0 ' :
blank = true ;
done = true ;
break ;
default :
done = true ;
break ;
}
}
/* Blank lines are a special case */
if ( blank )
{
/* Avoid putting an empty paragraph at the end of the help */
if ( ! newpara )
{
body ( " </p> \n " ) ;
newpara = true ;
}
continue ;
}
/* Check the indentation level */
if ( indent = = 0 )
{
g_preread = true ;
break ;
}
else if ( ! help_indent )
{
help_indent = indent ;
}
else if ( indent < help_indent )
{
g_preread = true ;
break ;
}
/* Add the next line of help text */
if ( newpara )
{
body ( " </p> \n " ) ;
newpara = false ;
}
2013-04-22 01:01:46 +08:00
body ( " %s " , htmlize_text ( ptr ) ) ;
2013-04-21 04:18:08 +08:00
}
if ( ! newpara )
{
body ( " </p> \n " ) ;
}
}
/****************************************************************************
* Name : process_config
*
* Description :
* Process one configuration variable paragraph
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline char * process_config ( FILE * stream , const char * configname ,
const char * kconfigdir )
2013-04-21 01:42:59 +08:00
{
enum token_type_e tokid ;
struct config_s config ;
bool help ;
const char * paranum ;
char * token ;
char * ptr ;
2013-04-21 07:05:45 +08:00
int i ;
2013-04-21 01:42:59 +08:00
/* Get the configuration information */
memset ( & config , 0 , sizeof ( struct config_s ) ) ;
config . cname = strdup ( configname ) ;
/* Process each line in the configuration */
help = false ;
2013-04-21 23:37:45 +08:00
while ( ( ptr = kconfig_line ( stream ) ) ! = NULL )
2013-04-21 01:42:59 +08:00
{
/* Process the first token on the Kconfig file line */
g_lasts = NULL ;
token = strtok_r ( ptr , " " , & g_lasts ) ;
if ( token ! = NULL )
{
tokid = tokenize ( token ) ;
switch ( tokid )
{
case TOKEN_BOOL :
{
/* Save the type of the configuration variable */
config . ctype = VALUE_BOOL ;
/* Get the description following the type */
ptr = getstring ( g_lasts ) ;
if ( ptr )
{
config . cdesc = strdup ( ptr ) ;
}
/* Indicate that the line has been consumed */
token = NULL ;
}
break ;
case TOKEN_INT :
{
/* Save the type of the configuration variable */
config . ctype = VALUE_INT ;
/* Get the description following the type */
ptr = getstring ( g_lasts ) ;
if ( ptr )
{
config . cdesc = strdup ( ptr ) ;
}
/* Indicate that the line has been consumed */
token = NULL ;
}
break ;
case TOKEN_HEX :
{
/* Save the type of the configuration variable */
config . ctype = VALUE_HEX ;
/* Get the description following the type */
ptr = getstring ( g_lasts ) ;
if ( ptr )
{
config . cdesc = strdup ( ptr ) ;
}
/* Indicate that the line has been consumed */
token = NULL ;
}
break ;
case TOKEN_STRING :
{
/* Save the type of the configuration variable */
config . ctype = VALUE_STRING ;
/* Get the description following the type */
ptr = getstring ( g_lasts ) ;
if ( ptr )
{
config . cdesc = strdup ( ptr ) ;
}
/* Indicate that the line has been consumed */
token = NULL ;
}
break ;
case TOKEN_DEFAULT :
{
char * value = strtok_r ( NULL , " " , & g_lasts ) ;
config . cdefault = strdup ( value ) ;
token = NULL ;
}
break ;
2013-04-21 07:29:10 +08:00
case TOKEN_RANGE :
{
char * value = strtok_r ( NULL , " , " , & g_lasts ) ;
if ( value )
{
config . clower = strdup ( value ) ;
value = strtok_r ( NULL , " " , & g_lasts ) ;
if ( value )
{
config . cupper = strdup ( value ) ;
}
}
token = NULL ;
}
break ;
2013-04-21 07:05:45 +08:00
case TOKEN_SELECT :
{
char * value ;
int ndx ;
2013-04-21 07:56:16 +08:00
ndx = config . cnselect ;
if ( ndx > = MAX_SELECT )
2013-04-21 07:05:45 +08:00
{
2013-04-21 23:37:45 +08:00
error ( " Too many 'select' lines \n " ) ;
2013-04-21 07:05:45 +08:00
exit ( ERROR_TOO_MANY_SELECT ) ;
}
2013-04-21 07:56:16 +08:00
value = strtok_r ( NULL , " " , & g_lasts ) ;
2013-04-21 07:05:45 +08:00
config . cselect [ ndx ] = strdup ( value ) ;
2013-04-21 07:56:16 +08:00
config . cnselect = ndx + 1 ;
2013-04-21 07:05:45 +08:00
token = NULL ;
}
break ;
case TOKEN_DEPENDS :
{
char * value = strtok_r ( NULL , " " , & g_lasts ) ;
if ( strcmp ( value , " on " ) ! = 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Expected \" on \" after \" depends \" \n " ) ;
2013-04-21 07:05:45 +08:00
exit ( ERRROR_ON_AFTER_DEPENDS ) ;
}
2013-04-22 01:01:46 +08:00
push_dependency ( htmlize_expression ( g_lasts ) ) ;
2013-04-21 07:05:45 +08:00
config . cndependencies + + ;
token = NULL ;
}
break ;
case TOKEN_OPTION :
{
token = NULL ; /* Ignored */
}
break ;
2013-04-21 01:42:59 +08:00
case TOKEN_HELP :
{
help = true ;
token = NULL ;
}
break ;
default :
{
2013-04-21 23:37:45 +08:00
debug ( " CONFIG_%s: Terminating token: %s \n " ,
config . cname , token ) ;
2013-04-21 01:42:59 +08:00
}
break ;
}
2013-04-21 04:18:08 +08:00
/* Break out on the help token (or the first unhandled token) */
2013-04-21 01:42:59 +08:00
2013-04-21 04:18:08 +08:00
if ( help | | token ! = NULL )
2013-04-21 01:42:59 +08:00
{
break ;
}
}
}
/* Is this an internal configuration varaible with no description?
* Were we asked to show these internal variables ? If not then
* don ' t output anything .
*/
if ( config . cdesc | | g_internal )
{
/* Print the configuration variable name and the short description */
2013-04-21 07:56:16 +08:00
body ( " <h3><a name= \" CONFIG_%s \" > " , config . cname ) ;
2013-04-21 01:42:59 +08:00
/* If we are not in a choice block, than give the variable a paragraph
* number and put it in the table of contents .
*/
if ( ! g_inchoice )
{
paranum = get_paranum ( ) ;
2013-04-21 07:56:16 +08:00
output ( " <li><a href= \" #CONFIG_%s \" >%s <code>CONFIG_%s</code> " ,
2013-04-21 01:42:59 +08:00
config . cname , paranum , config . cname ) ;
body ( " %s " , paranum ) ;
incr_paranum ( ) ;
}
body ( " <code>CONFIG_%s</code> " , config . cname ) ;
if ( config . cdesc )
{
if ( ! g_inchoice )
{
output ( " : %s " , config . cdesc ) ;
}
body ( " : %s " , config . cdesc ) ;
}
body ( " </a></h3> \n " ) ;
if ( ! g_inchoice )
{
output ( " </a></li> \n " ) ;
}
/* Configuration description is indented */
body ( " <ul> \n " ) ;
/* Print the type of the configuration variable */
if ( config . ctype ! = VALUE_NONE )
{
2013-04-21 07:05:45 +08:00
body ( " <li><i>Type</i>: %s</li> \n " , type2str ( config . ctype ) ) ;
2013-04-21 01:42:59 +08:00
}
/* Print the default value of the configuration variable */
if ( config . cdefault )
{
2013-04-21 07:05:45 +08:00
body ( " <li><i>Default</i>: %s</li> \n " , config . cdefault ) ;
}
2013-04-21 07:29:10 +08:00
/* Print the range of values of the configuration variable */
if ( config . clower | | config . cupper )
{
body ( " <li><i>Range</i>: \n " ) ;
if ( config . clower )
{
body ( " %s " , config . clower ) ;
}
body ( " - " , config . clower ) ;
if ( config . cupper )
{
body ( " %s " , config . cupper ) ;
}
body ( " </li> \n " ) ;
}
2013-04-21 07:05:45 +08:00
/* Print the default value of the configuration variable auto-selected by this setting */
if ( config . cnselect > 0 )
{
2013-04-21 07:56:16 +08:00
body ( " <li><i>Selects</i>: <a href= \" #CONFIG_%s \" >CONFIG_%s</a> " ,
2013-04-21 07:05:45 +08:00
config . cselect [ 0 ] , config . cselect [ 0 ] ) ;
for ( i = 1 ; i < config . cnselect ; i + + )
{
2013-04-21 07:56:16 +08:00
body ( " , <a href= \" #CONFIG_%s \" >CONFIG_%s</a> " ,
2013-04-21 07:05:45 +08:00
config . cselect [ i ] , config . cselect [ i ] ) ;
}
body ( " </li> \n " ) ;
2013-04-21 01:42:59 +08:00
}
/* Print the list of dependencies (if any) */
if ( g_ndependencies > 0 )
{
body ( " <li><i>Dependencies</i>: %s " , g_dependencies [ 0 ] ) ;
2013-04-21 07:05:45 +08:00
2013-04-21 01:42:59 +08:00
for ( i = 1 ; i < g_ndependencies ; i + + )
{
body ( " , %s \n " , g_dependencies [ i ] ) ;
}
2013-04-21 07:05:45 +08:00
2013-04-21 01:42:59 +08:00
body ( " </li> \n " ) ;
}
/* Show the configuration file */
body ( " <li><i>Kconfig file</i>: <code>%s/Kconfig</code> \n " , kconfigdir ) ;
/* Print any help text */
if ( help )
{
2013-04-21 04:18:08 +08:00
process_help ( stream ) ;
token = NULL ;
2013-04-21 01:42:59 +08:00
}
else if ( ! config . cdesc )
{
body ( " <p>This is a hidden, internal configuration variable that cannot be explicitly set by the user.</p> \n " ) ;
}
/* End of configuration description */
body ( " </ul> \n " ) ;
}
2013-04-21 07:05:45 +08:00
/* Remove any dependencies that apply only to this configuration */
for ( i = 0 ; i < config . cndependencies ; i + + )
{
pop_dependency ( ) ;
}
2013-04-21 01:42:59 +08:00
/* Free allocated memory */
if ( config . cname )
{
free ( config . cname ) ;
}
if ( config . cdesc )
{
free ( config . cdesc ) ;
}
if ( config . cdefault )
{
free ( config . cdefault ) ;
}
2013-04-21 07:29:10 +08:00
if ( config . clower )
{
free ( config . clower ) ;
}
if ( config . cupper )
{
free ( config . cupper ) ;
}
2013-04-21 07:05:45 +08:00
if ( config . cnselect > 0 )
{
for ( i = 0 ; i < config . cnselect ; i + + )
{
free ( config . cselect [ i ] ) ;
}
}
2013-04-21 01:42:59 +08:00
return token ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : process_choice
*
* Description :
* Process a choice paragraph
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * parse_kconfigfile ( FILE * stream , const char * kconfigdir ) ; /* Forward reference */
static inline char * process_choice ( FILE * stream , const char * kconfigdir )
{
enum token_type_e tokid ;
struct choice_s choice ;
const char * paranum ;
2013-04-21 07:05:45 +08:00
char * token = NULL ;
2013-04-21 04:18:08 +08:00
char * ptr ;
2013-04-21 07:05:45 +08:00
bool help ;
int i ;
2013-04-21 04:18:08 +08:00
/* Get the choice information */
memset ( & choice , 0 , sizeof ( struct choice_s ) ) ;
/* Process each line in the choice */
2013-04-21 23:37:45 +08:00
while ( ( ptr = kconfig_line ( stream ) ) ! = NULL )
2013-04-21 04:18:08 +08:00
{
/* Process the first token on the Kconfig file line */
g_lasts = NULL ;
token = strtok_r ( ptr , " " , & g_lasts ) ;
if ( token ! = NULL )
{
tokid = tokenize ( token ) ;
switch ( tokid )
{
case TOKEN_PROMPT :
{
/* Get the prompt string */
ptr = getstring ( g_lasts ) ;
if ( ptr )
{
choice . cprompt = strdup ( ptr ) ;
}
/* Indicate that the line has been consumed */
token = NULL ;
}
break ;
case TOKEN_DEFAULT :
{
char * value = strtok_r ( NULL , " " , & g_lasts ) ;
choice . cdefault = strdup ( value ) ;
token = NULL ;
}
break ;
2013-04-21 07:05:45 +08:00
case TOKEN_DEPENDS :
{
char * value = strtok_r ( NULL , " " , & g_lasts ) ;
if ( strcmp ( value , " on " ) ! = 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Expected \" on \" after \" depends \" \n " ) ;
2013-04-21 07:05:45 +08:00
exit ( ERRROR_ON_AFTER_DEPENDS ) ;
}
2013-04-22 01:01:46 +08:00
push_dependency ( htmlize_expression ( g_lasts ) ) ;
2013-04-21 07:05:45 +08:00
choice . cndependencies + + ;
token = NULL ;
}
break ;
case TOKEN_HELP :
{
help = true ;
token = NULL ;
}
break ;
2013-04-21 04:18:08 +08:00
default :
{
2013-04-21 23:37:45 +08:00
debug ( " Choice: Terminating token: %s \n " , token ) ;
2013-04-21 04:18:08 +08:00
}
break ;
}
2013-04-21 07:05:45 +08:00
/* Break out on the help token (or the first unhandled token) */
2013-04-21 04:18:08 +08:00
2013-04-21 07:05:45 +08:00
if ( help | | token ! = NULL )
2013-04-21 04:18:08 +08:00
{
break ;
}
}
}
2013-04-21 07:05:45 +08:00
paranum = get_paranum ( ) ;
output ( " <li><a href= \" #choice_%d \" >%s Choice " , g_choice_number , paranum ) ;
body ( " \n <h3><a name= \" choice_%d \" >%s Choice " , g_choice_number , paranum ) ;
if ( choice . cprompt )
{
output ( " : %s " , choice . cprompt ) ;
body ( " : %s " , choice . cprompt ) ;
}
output ( " </a></li> \n " ) ;
body ( " </a></h3> \n " ) ;
g_choice_number + + ;
2013-04-21 04:18:08 +08:00
2013-04-21 07:05:45 +08:00
/* Show the default */
body ( " <ul> \n " ) ;
if ( choice . cdefault )
{
body ( " <li><i>Default</i>: <code>CONFIG_%s</code> \n </li> " , choice . cdefault ) ;
}
/* Print the list of dependencies (if any) */
if ( g_ndependencies > 0 )
{
body ( " <li><i>Dependencies</i>: %s " , g_dependencies [ 0 ] ) ;
for ( i = 1 ; i < g_ndependencies ; i + + )
{
body ( " , %s \n " , g_dependencies [ i ] ) ;
}
body ( " </li> \n " ) ;
}
2013-04-21 04:18:08 +08:00
/* Show the configuration file */
2013-04-21 07:05:45 +08:00
body ( " <li><i>Kconfig file</i>: <code>%s/Kconfig</code> \n </li> " , kconfigdir ) ;
/* Print any help text */
if ( help )
{
process_help ( stream ) ;
token = NULL ;
}
body ( " </ul> \n " ) ;
/* Then show the choice options */
body ( " <p><b>Choice Options:</b></p> " , kconfigdir ) ;
2013-04-21 04:18:08 +08:00
body ( " <ul> \n " ) ;
2013-04-21 07:05:45 +08:00
/* Remove any dependencies that apply only to this configuration */
for ( i = 0 ; i < choice . cndependencies ; i + + )
{
pop_dependency ( ) ;
}
2013-04-21 04:18:08 +08:00
/* Free allocated memory */
if ( choice . cprompt )
{
free ( choice . cprompt ) ;
}
if ( choice . cdefault )
{
free ( choice . cdefault ) ;
}
/* Increment the nesting level */
incr_level ( ) ;
2013-04-21 07:05:45 +08:00
debug ( " process_choice: TOKEN_CHOICE \n " ) ;
2013-04-21 04:18:08 +08:00
debug ( " kconfigdir: %s \n " , kconfigdir ) ;
debug ( " level: %d \n " , g_level ) ;
2013-04-21 07:05:45 +08:00
/* Then return in choice mode */
2013-04-21 04:18:08 +08:00
g_inchoice + + ;
2013-04-21 07:05:45 +08:00
return token ;
}
/****************************************************************************
* Name : process_menu
*
* Description :
* Process a menu paragraph
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline char * process_menu ( FILE * stream , const char * kconfigdir )
{
enum token_type_e tokid ;
struct menu_s menu ;
const char * paranum ;
char * menuname ;
char * token = NULL ;
char * ptr ;
int i ;
/* Get the menu information */
memset ( & menu , 0 , sizeof ( struct menu_s ) ) ;
/* Get the menu name */
menuname = getstring ( g_lasts ) ;
menu . mname = strdup ( menuname ) ;
/* Process each line in the choice */
2013-04-21 23:37:45 +08:00
while ( ( ptr = kconfig_line ( stream ) ) ! = NULL )
2013-04-21 07:05:45 +08:00
{
/* Process the first token on the Kconfig file line */
g_lasts = NULL ;
token = strtok_r ( ptr , " " , & g_lasts ) ;
if ( token ! = NULL )
{
tokid = tokenize ( token ) ;
switch ( tokid )
{
case TOKEN_DEPENDS :
{
char * value = strtok_r ( NULL , " " , & g_lasts ) ;
if ( strcmp ( value , " on " ) ! = 0 )
{
2013-04-21 23:37:45 +08:00
error ( " Expected \" on \" after \" depends \" \n " ) ;
2013-04-21 07:05:45 +08:00
exit ( ERRROR_ON_AFTER_DEPENDS ) ;
}
2013-04-22 01:01:46 +08:00
push_dependency ( htmlize_expression ( g_lasts ) ) ;
2013-04-21 07:05:45 +08:00
menu . mndependencies + + ;
token = NULL ;
}
break ;
default :
{
2013-04-21 23:37:45 +08:00
debug ( " Menu: Terminating token: %s \n " , token ) ;
2013-04-21 07:05:45 +08:00
}
break ;
}
/* Break out on the first unhandled token */
if ( token ! = NULL )
{
break ;
}
}
}
/* Output menu information */
paranum = get_paranum ( ) ;
2013-04-21 07:29:10 +08:00
if ( menu . mname )
2013-04-21 07:05:45 +08:00
{
output ( " <li><a href= \" #menu_%d \" >%s Menu: %s</a></li> \n " ,
2013-04-21 07:29:10 +08:00
g_menu_number , paranum , menu . mname ) ;
2013-04-21 07:05:45 +08:00
output ( " <ul> \n " ) ;
body ( " \n <h1><a name= \" menu_%d \" >%s Menu: %s</a></h1> \n " ,
2013-04-21 07:29:10 +08:00
g_menu_number , paranum , menu . mname ) ;
2013-04-21 07:05:45 +08:00
}
else
{
output ( " <li><a href= \" #menu_%d \" >%s Menu</a></li> \n " ,
g_menu_number , paranum ) ;
body ( " \n <h1><a name= \" menu_%d \" >%s Menu</a></h1> \n " ,
g_menu_number , paranum ) ;
}
2013-04-21 07:56:16 +08:00
g_menu_number + + ;
2013-04-21 07:05:45 +08:00
/* Print the list of dependencies (if any) */
2013-04-21 07:56:16 +08:00
body ( " <ul> \n " ) ;
2013-04-21 07:05:45 +08:00
if ( g_ndependencies > 0 )
{
body ( " <li><i>Dependencies</i>: %s " , g_dependencies [ 0 ] ) ;
for ( i = 1 ; i < g_ndependencies ; i + + )
{
body ( " , %s \n " , g_dependencies [ i ] ) ;
}
body ( " </li> \n " ) ;
}
2013-04-21 07:56:16 +08:00
/* Show the configuration file */
body ( " <li><i>Kconfig file</i>: <code>%s/Kconfig</code> \n " , kconfigdir ) ;
body ( " </ul> \n " ) ;
2013-04-21 07:05:45 +08:00
/* Remove any dependencies that apply only to this configuration */
for ( i = 0 ; i < menu . mndependencies ; i + + )
{
pop_dependency ( ) ;
}
2013-04-21 07:29:10 +08:00
/* Free any allocated memory */
if ( menu . mname )
{
free ( menu . mname ) ;
}
2013-04-21 07:05:45 +08:00
/* Increment the nesting level */
incr_level ( ) ;
debug ( " process_menu: TOKEN_MENU \n " ) ;
debug ( " kconfigdir: %s \n " , kconfigdir ) ;
debug ( " level: %d \n " , g_level ) ;
/* Return the terminating token */
return token ;
2013-04-21 04:18:08 +08:00
}
/****************************************************************************
* Name : parse_kconfigfile
*
* Description :
* Parse a Kconfig file .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void process_kconfigfile ( const char * kconfigdir ) ; /* Forward reference */
static char * parse_kconfigfile ( FILE * stream , const char * kconfigdir )
{
enum token_type_e tokid ;
2013-04-21 07:05:45 +08:00
char * token = NULL ;
2013-04-21 01:42:59 +08:00
char * ptr ;
/* Process each line in the Kconfig file */
2013-04-21 23:37:45 +08:00
while ( ( ptr = kconfig_line ( stream ) ) ! = NULL )
2013-04-21 01:42:59 +08:00
{
/* Process the first token on the Kconfig file line */
g_lasts = NULL ;
token = strtok_r ( ptr , " " , & g_lasts ) ;
while ( token ! = NULL )
{
tokid = tokenize ( token ) ;
switch ( tokid )
{
case TOKEN_SOURCE :
{
/* Get the relative path from the Kconfig file line */
char * relpath = strtok_r ( NULL , " " , & g_lasts ) ;
/* Remove optional quoting */
relpath = dequote ( relpath ) ;
if ( relpath )
{
char * subdir = dirname ( relpath ) ;
char * dirpath ;
/* Check if the directory path contains $APPSDIR */
char * appsdir = strstr ( subdir , " $APPSDIR " ) ;
if ( appsdir )
{
char * tmp = appsdir + strlen ( " $APPSDIR " ) ;
* appsdir = ' \0 ' ;
asprintf ( & dirpath , " %s/%s%s%s " , g_kconfigroot , subdir , g_appsdir , tmp ) ;
}
else
{
asprintf ( & dirpath , " %s/%s " , g_kconfigroot , subdir ) ;
}
debug ( " parse_kconfigfile: Recursing for TOKEN_SOURCE \n " ) ;
debug ( " relpath: %s \n " , relpath ) ;
debug ( " subdir: %s \n " , subdir ) ;
debug ( " dirpath: %s \n " , dirpath ) ;
/* Then recurse */
process_kconfigfile ( dirpath ) ;
token = NULL ;
free ( dirpath ) ;
}
/* Set the token string to NULL to indicate that we need to read the next line */
token = NULL ;
}
break ;
case TOKEN_CONFIG :
2013-04-21 07:05:45 +08:00
case TOKEN_MENUCONFIG :
2013-04-21 01:42:59 +08:00
{
char * configname = strtok_r ( NULL , " " , & g_lasts ) ;
token = process_config ( stream , configname , kconfigdir ) ;
}
break ;
2013-04-21 07:05:45 +08:00
case TOKEN_COMMENT :
case TOKEN_MAINMENU :
2013-04-21 01:42:59 +08:00
{
2013-04-21 07:05:45 +08:00
token = NULL ; /* ignored */
}
break ;
2013-04-21 01:42:59 +08:00
2013-04-21 07:05:45 +08:00
case TOKEN_MENU :
{
token = process_menu ( stream , kconfigdir ) ;
2013-04-21 01:42:59 +08:00
}
break ;
case TOKEN_CHOICE :
{
2013-04-21 04:18:08 +08:00
token = process_choice ( stream , kconfigdir ) ;
2013-04-21 01:42:59 +08:00
}
break ;
case TOKEN_ENDCHOICE :
{
/* Reduce body indentation level */
body ( " </ul> \n " ) ;
g_inchoice - - ;
/* Decrement the nesting level */
decr_level ( ) ;
incr_paranum ( ) ;
2013-04-21 07:05:45 +08:00
token = NULL ;
2013-04-21 01:42:59 +08:00
}
break ;
case TOKEN_ENDMENU :
{
/* Reduce table of contents indentation level */
output ( " </ul> \n " ) ;
/* Decrement the nesting level */
decr_level ( ) ;
incr_paranum ( ) ;
2013-04-21 07:05:45 +08:00
token = NULL ;
2013-04-21 01:42:59 +08:00
}
break ;
case TOKEN_IF :
{
char * dependency = strtok_r ( NULL , " " , & g_lasts ) ;
2013-04-22 01:01:46 +08:00
push_dependency ( htmlize_expression ( dependency ) ) ;
2013-04-21 01:42:59 +08:00
token = NULL ;
}
break ;
case TOKEN_ENDIF :
{
pop_dependency ( ) ;
token = NULL ;
}
break ;
default :
{
/* Set token to NULL to skip to the next line */
2013-04-21 23:37:45 +08:00
error ( " Unhandled token: %s \n " , token ) ;
2013-04-21 01:42:59 +08:00
token = NULL ;
}
break ;
}
}
}
return token ;
}
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : process_kconfigfile
*
* Description :
* Open and parse a Kconfig file
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
static void process_kconfigfile ( const char * kconfigdir )
{
FILE * stream ;
char * kconfigpath ;
/* Create the full path to the Kconfig file */
asprintf ( & kconfigpath , " %s/Kconfig " , kconfigdir ) ;
debug ( " process_kconfigfile: Entry \n " ) ;
debug ( " kconfigdir: %s \n " , kconfigdir ) ;
debug ( " kconfigpath: %s \n " , kconfigpath ) ;
debug ( " level: %d \n " , g_level ) ;
/* Open the Kconfig file */
stream = fopen ( kconfigpath , " r " ) ;
if ( ! stream )
{
2013-04-21 23:37:45 +08:00
error ( " open %s failed: %s \n " , kconfigpath , strerror ( errno ) ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_KCONFIG_OPEN_FAILURE ) ;
}
/* Process each line in the Kconfig file */
parse_kconfigfile ( stream , kconfigdir ) ;
/* Close the Kconfig file and release the memory holding the full path */
fclose ( stream ) ;
free ( kconfigpath ) ;
}
/****************************************************************************
* Public Functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 04:18:08 +08:00
/****************************************************************************
* Name : main
*
* Description :
* Program entry point .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-04-21 01:42:59 +08:00
int main ( int argc , char * * argv , char * * envp )
{
char * outfile ;
const char * paranum ;
time_t now ;
struct tm * ptm ;
int ch ;
/* Parse command line options */
g_debug = false ;
g_internal = false ;
g_kconfigroot = " . " ;
g_appsdir = " ../apps " ;
g_outfile = stdout ;
outfile = NULL ;
while ( ( ch = getopt ( argc , argv , " :dha:o: " ) ) > 0 )
{
switch ( ch )
{
case ' a ' :
g_appsdir = optarg ;
break ;
case ' o ' :
outfile = optarg ;
break ;
case ' i ' :
g_internal = true ;
break ;
case ' h ' :
show_usage ( argv [ 0 ] , 0 ) ;
case ' d ' :
g_debug = true ;
break ;
case ' ? ' :
2013-04-21 23:37:45 +08:00
error ( " Unrecognized option: %c \n " , optopt ) ;
2013-04-21 01:42:59 +08:00
show_usage ( argv [ 0 ] , ERROR_UNRECOGNIZED_OPTION ) ;
case ' : ' :
2013-04-21 23:37:45 +08:00
error ( " Missing option argument, option: %c \n " , optopt ) ;
2013-04-21 01:42:59 +08:00
show_usage ( argv [ 0 ] , ERROR_MISSING_OPTION_ARGUMENT ) ;
break ;
2013-04-21 23:37:45 +08:00
error ( " Unexpected option: %c \n " , ch ) ;
2013-04-21 01:42:59 +08:00
show_usage ( argv [ 0 ] , ERROR_UNEXPECTED_OPTION ) ;
}
}
if ( optind < argc )
{
g_kconfigroot = argv [ optind ] ;
optind + + ;
}
debug ( " Using <Kconfig directory>: %s \n " , g_kconfigroot ) ;
debug ( " Using <apps directory>: %s \n " , g_appsdir ) ;
debug ( " Using <out file>: %s \n " , outfile ? outfile : " stdout " ) ;
if ( optind < argc )
{
2013-04-21 23:37:45 +08:00
error ( " Unexpected garbage at the end of the line \n " ) ;
2013-04-21 01:42:59 +08:00
show_usage ( argv [ 0 ] , ERROR_TOO_MANY_ARGUMENTS ) ;
}
/* Open the output file (if any) */
if ( outfile )
{
g_outfile = fopen ( outfile , " w " ) ;
if ( ! g_outfile )
{
2013-04-21 23:37:45 +08:00
error ( " open %s failed: %s \n " , outfile , strerror ( errno ) ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_OUTFILE_OPEN_FAILURE ) ;
}
}
/* Open the temporary file */
g_tmpfile = fopen ( TMPFILE_NAME , " w " ) ;
if ( ! g_tmpfile )
{
2013-04-21 23:37:45 +08:00
error ( " open %s failed: %s \n " , TMPFILE_NAME , strerror ( errno ) ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_TMPFILE_OPEN_FAILURE ) ;
}
/* Get the current date string in the scratch buffer */
now = time ( NULL ) ;
ptm = localtime ( & now ) ;
( void ) strftime ( g_scratch , SCRATCH_SIZE , " %B %d, %Y " , ptm ) ;
/* Output header boilerplater */
output ( " <html> \n " ) ;
output ( " <head> \n " ) ;
output ( " <title>NuttX Configuration Options</title> \n " ) ;
output ( " </head> \n " ) ;
output ( " <body background= \" backgd.gif \" > \n " ) ;
output ( " <hr><hr> \n " ) ;
output ( " <table width = \" 100%% \" > \n " ) ;
output ( " <tr align= \" center \" bgcolor= \" #e4e4e4 \" > \n " ) ;
output ( " <td> \n " ) ;
output ( " <h1><big><font color= \" #3c34ec \" ><i>NuttX Configuration Variables</i></font></big></h1> \n " ) ;
output ( " <p>Last Updated: %s</p> \n " , g_scratch ) ;
output ( " </td> \n " ) ;
output ( " </tr> \n " ) ;
output ( " </table> \n " ) ;
output ( " <center><h1>Table of contents</h1></center> \n " ) ;
output ( " <ul> \n " ) ;
incr_level ( ) ;
paranum = get_paranum ( ) ;
2013-04-21 07:56:16 +08:00
output ( " <li><a href= \" #menu_%d \" >%s Menu: Main</a></li> \n " ,
2013-04-21 01:42:59 +08:00
g_menu_number , paranum ) ;
2013-04-21 07:56:16 +08:00
body ( " <h1><a name= \" menu_%d \" >%s Menu: Main</a></h1> \n " ,
2013-04-21 01:42:59 +08:00
g_menu_number , paranum ) ;
g_menu_number + + ;
2013-04-22 01:01:46 +08:00
/* Tell the reader that this is an auto-generated file */
body ( " <p> \n " ) ;
body ( " <b>Maintaining this Document</b>. \n " ) ;
body ( " The NuttX RTOS is highly configurable. \n " ) ;
body ( " The NuttX configuration files are maintained using the <a href= \" http://ymorin.is-a-geek.org/projects/kconfig-frontends \" >kconfig-frontends</a> tool. \n " ) ;
body ( " That configuration tool uses <code>Kconfig</code> files that can be found through the NuttX source tree. \n " ) ;
body ( " Each <code>Kconfig</code> files contains declarations of configuration variables. \n " ) ;
body ( " Each configuration variable provides one configuration option for the NuttX RTOS. \n " ) ;
body ( " This configurable options are descrived in this document. \n " ) ;
body ( " </p> \n " ) ;
body ( " <p> \n " ) ;
body ( " <b>NOTE</b>: \n " ) ;
body ( " This documenation was auto-generated using the <a href= \" http://sourceforge.net/p/nuttx/git/ci/master/tree/nuttx/tools/kconfig2html.c \" >kconfig2html</a> tool \n " ) ;
body ( " That tools analyzes the NuttX <code>Kconfig</code> and generates this HTML document. \n " ) ;
body ( " This HTML document file should not be editted manually. \n " ) ;
body ( " In order to make changes to this document, you should instead modify the <code>Kconfig</code> file(s) that were used to generated this document and then execute the <code>kconfig2html</code> again to regenerate the HTML document file. \n " ) ;
body ( " </p> \n " ) ;
2013-04-21 01:42:59 +08:00
/* Process the Kconfig files through recursive descent */
process_kconfigfile ( g_kconfigroot ) ;
/* Terminate the table of contents */
output ( " </ul> \n " ) ;
/* Close the temporary file and copy it to the output file */
fclose ( g_tmpfile ) ;
g_tmpfile = fopen ( TMPFILE_NAME , " r " ) ;
if ( ! g_tmpfile )
{
2013-04-21 23:37:45 +08:00
error ( " open %s failed: %s \n " , TMPFILE_NAME , strerror ( errno ) ) ;
2013-04-21 01:42:59 +08:00
exit ( ERROR_TMPFILE_OPEN_FAILURE ) ;
}
while ( ( ch = getc ( g_tmpfile ) ) ! = EOF )
{
( void ) putc ( ch , g_outfile ) ;
}
/* Close and remove the temporary file again */
fclose ( g_tmpfile ) ;
unlink ( TMPFILE_NAME ) ;
/* Output trailer boilerplater */
output ( " </body> \n " ) ;
output ( " </html> \n " ) ;
/* Close the output file (if any) and the temporary file*/
if ( outfile )
{
fclose ( g_outfile ) ;
}
return 0 ;
}