2005-04-17 02:20:36 +04:00
/*
* docproc is a simple preprocessor for the template files
* used as placeholders for the kernel internal documentation .
* docproc is used for documentation - frontend and
* dependency - generator .
* The two usages have in common that they require
* some knowledge of the . tmpl syntax , therefore they
* are kept together .
*
* documentation - frontend
* Scans the template file and call kernel - doc for
* all occurrences of ! [ EIF ] file
2007-09-05 08:23:22 +04:00
* Beforehand each referenced file is scanned for
* any symbols that are exported via these macros :
* EXPORT_SYMBOL ( ) , EXPORT_SYMBOL_GPL ( ) , &
* EXPORT_SYMBOL_GPL_FUTURE ( )
2005-04-17 02:20:36 +04:00
* This is used to create proper - function and
* - nofunction arguments in calls to kernel - doc .
* Usage : docproc doc file . tmpl
*
* dependency - generator :
* Scans the template file and list all files
* referenced in a format recognized by make .
* Usage : docproc depend file . tmpl
* Writes dependency information to stdout
* in the following format :
* file . tmpl src . c src2 . c
* The filenames are obtained from the following constructs :
* ! Efilename
* ! Ifilename
* ! Dfilename
* ! Ffilename
2007-10-25 02:08:48 +04:00
* ! Pfilename
2005-04-17 02:20:36 +04:00
*
*/
2010-09-12 02:55:22 +04:00
# define _GNU_SOURCE
2005-04-17 02:20:36 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <unistd.h>
# include <limits.h>
2010-09-12 02:55:22 +04:00
# include <errno.h>
2005-04-17 02:20:36 +04:00
# include <sys/types.h>
# include <sys/wait.h>
/* exitstatus is used to keep track of any failing calls to kernel-doc,
* but execution continues . */
int exitstatus = 0 ;
typedef void DFL ( char * ) ;
DFL * defaultline ;
typedef void FILEONLY ( char * file ) ;
FILEONLY * internalfunctions ;
FILEONLY * externalfunctions ;
FILEONLY * symbolsonly ;
2010-09-12 02:55:22 +04:00
FILEONLY * findall ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:59:22 +04:00
typedef void FILELINE ( char * file , char * line ) ;
2005-04-17 02:20:36 +04:00
FILELINE * singlefunctions ;
FILELINE * entity_system ;
2007-10-25 02:08:48 +04:00
FILELINE * docsection ;
2005-04-17 02:20:36 +04:00
# define MAXLINESZ 2048
# define MAXFILES 250
# define KERNELDOCPATH "scripts / "
# define KERNELDOC "kernel-doc"
# define DOCBOOK "-docbook"
2010-09-12 02:55:22 +04:00
# define LIST "-list"
2005-04-17 02:20:36 +04:00
# define FUNCTION "-function"
# define NOFUNCTION "-nofunction"
2007-10-25 02:08:48 +04:00
# define NODOCSECTIONS "-no-doc-sections"
2005-04-17 02:20:36 +04:00
2009-05-31 20:05:34 +04:00
static char * srctree , * kernsrctree ;
2007-10-09 10:25:18 +04:00
2010-09-12 02:55:22 +04:00
static char * * all_list = NULL ;
static int all_list_len = 0 ;
static void consume_symbol ( const char * sym )
{
int i ;
for ( i = 0 ; i < all_list_len ; i + + ) {
if ( ! all_list [ i ] )
continue ;
if ( strcmp ( sym , all_list [ i ] ) )
continue ;
all_list [ i ] = NULL ;
break ;
}
}
2009-09-18 23:49:23 +04:00
static void usage ( void )
2005-04-17 02:20:36 +04:00
{
fprintf ( stderr , " Usage: docproc {doc|depend} file \n " ) ;
fprintf ( stderr , " Input is read from file.tmpl. Output is sent to stdout \n " ) ;
fprintf ( stderr , " doc: frontend when generating kernel documentation \n " ) ;
fprintf ( stderr , " depend: generate list of files referenced within file \n " ) ;
2009-05-31 20:05:34 +04:00
fprintf ( stderr , " Environment variable SRCTREE: absolute path to sources. \n " ) ;
fprintf ( stderr , " KBUILD_SRC: absolute path to kernel source tree. \n " ) ;
2005-04-17 02:20:36 +04:00
}
/*
2007-09-05 08:23:22 +04:00
* Execute kernel - doc with parameters given in svec
2005-04-17 02:20:36 +04:00
*/
2009-09-18 23:49:23 +04:00
static void exec_kernel_doc ( char * * svec )
2005-04-17 02:20:36 +04:00
{
pid_t pid ;
int ret ;
char real_filename [ PATH_MAX + 1 ] ;
/* Make sure output generated so far are flushed */
fflush ( stdout ) ;
2007-09-05 08:23:22 +04:00
switch ( pid = fork ( ) ) {
2005-04-17 02:20:36 +04:00
case - 1 :
perror ( " fork " ) ;
exit ( 1 ) ;
case 0 :
memset ( real_filename , 0 , sizeof ( real_filename ) ) ;
2009-05-31 20:05:34 +04:00
strncat ( real_filename , kernsrctree , PATH_MAX ) ;
strncat ( real_filename , " / " KERNELDOCPATH KERNELDOC ,
2005-04-17 02:20:36 +04:00
PATH_MAX - strlen ( real_filename ) ) ;
execvp ( real_filename , svec ) ;
fprintf ( stderr , " exec " ) ;
perror ( real_filename ) ;
exit ( 1 ) ;
default :
waitpid ( pid , & ret , 0 ) ;
}
if ( WIFEXITED ( ret ) )
exitstatus | = WEXITSTATUS ( ret ) ;
else
exitstatus = 0xff ;
}
/* Types used to create list of all exported symbols in a number of files */
struct symbols
{
char * name ;
} ;
struct symfile
{
char * filename ;
struct symbols * symbollist ;
int symbolcnt ;
} ;
struct symfile symfilelist [ MAXFILES ] ;
int symfilecnt = 0 ;
2009-09-18 23:49:23 +04:00
static void add_new_symbol ( struct symfile * sym , char * symname )
2005-04-17 02:20:36 +04:00
{
sym - > symbollist =
realloc ( sym - > symbollist , ( sym - > symbolcnt + 1 ) * sizeof ( char * ) ) ;
sym - > symbollist [ sym - > symbolcnt + + ] . name = strdup ( symname ) ;
}
/* Add a filename to the list */
2009-09-18 23:49:23 +04:00
static struct symfile * add_new_file ( char * filename )
2005-04-17 02:20:36 +04:00
{
symfilelist [ symfilecnt + + ] . filename = strdup ( filename ) ;
return & symfilelist [ symfilecnt - 1 ] ;
}
2007-09-05 08:23:22 +04:00
2005-04-17 02:20:36 +04:00
/* Check if file already are present in the list */
2009-09-18 23:49:23 +04:00
static struct symfile * filename_exist ( char * filename )
2005-04-17 02:20:36 +04:00
{
int i ;
for ( i = 0 ; i < symfilecnt ; i + + )
if ( strcmp ( symfilelist [ i ] . filename , filename ) = = 0 )
return & symfilelist [ i ] ;
return NULL ;
}
/*
* List all files referenced within the template file .
* Files are separated by tabs .
*/
2009-09-18 23:49:23 +04:00
static void adddep ( char * file ) { printf ( " \t %s " , file ) ; }
static void adddep2 ( char * file , char * line ) { line = line ; adddep ( file ) ; }
static void noaction ( char * line ) { line = line ; }
static void noaction2 ( char * file , char * line ) { file = file ; line = line ; }
2005-04-17 02:20:36 +04:00
/* Echo the line without further action */
2009-09-18 23:49:23 +04:00
static void printline ( char * line ) { printf ( " %s " , line ) ; }
2005-04-17 02:20:36 +04:00
/*
2007-09-05 08:23:22 +04:00
* Find all symbols in filename that are exported with EXPORT_SYMBOL &
* EXPORT_SYMBOL_GPL ( & EXPORT_SYMBOL_GPL_FUTURE implicitly ) .
2005-04-17 02:20:36 +04:00
* All symbols located are stored in symfilelist .
*/
2009-09-18 23:49:23 +04:00
static void find_export_symbols ( char * filename )
2005-04-17 02:20:36 +04:00
{
FILE * fp ;
struct symfile * sym ;
char line [ MAXLINESZ ] ;
if ( filename_exist ( filename ) = = NULL ) {
char real_filename [ PATH_MAX + 1 ] ;
memset ( real_filename , 0 , sizeof ( real_filename ) ) ;
2007-10-09 10:25:18 +04:00
strncat ( real_filename , srctree , PATH_MAX ) ;
2009-05-31 20:05:34 +04:00
strncat ( real_filename , " / " , PATH_MAX - strlen ( real_filename ) ) ;
2005-04-17 02:20:36 +04:00
strncat ( real_filename , filename ,
PATH_MAX - strlen ( real_filename ) ) ;
sym = add_new_file ( filename ) ;
fp = fopen ( real_filename , " r " ) ;
if ( fp = = NULL )
{
fprintf ( stderr , " docproc: " ) ;
perror ( real_filename ) ;
2006-09-29 13:00:56 +04:00
exit ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2007-09-05 08:23:22 +04:00
while ( fgets ( line , MAXLINESZ , fp ) ) {
2005-06-26 01:59:22 +04:00
char * p ;
char * e ;
2007-09-05 08:23:22 +04:00
if ( ( ( p = strstr ( line , " EXPORT_SYMBOL_GPL " ) ) ! = NULL ) | |
( ( p = strstr ( line , " EXPORT_SYMBOL " ) ) ! = NULL ) ) {
2005-04-17 02:20:36 +04:00
/* Skip EXPORT_SYMBOL{_GPL} */
while ( isalnum ( * p ) | | * p = = ' _ ' )
p + + ;
2007-09-05 08:23:22 +04:00
/* Remove parentheses & additional whitespace */
2005-04-17 02:20:36 +04:00
while ( isspace ( * p ) )
p + + ;
if ( * p ! = ' ( ' )
continue ; /* Syntax error? */
else
p + + ;
while ( isspace ( * p ) )
p + + ;
e = p ;
while ( isalnum ( * e ) | | * e = = ' _ ' )
e + + ;
* e = ' \0 ' ;
add_new_symbol ( sym , p ) ;
}
}
fclose ( fp ) ;
}
}
/*
* Document all external or internal functions in a file .
* Call kernel - doc with following parameters :
* kernel - doc - docbook - nofunction function_name1 filename
2007-09-05 08:23:22 +04:00
* Function names are obtained from all the src files
2005-04-17 02:20:36 +04:00
* by find_export_symbols .
* intfunc uses - nofunction
* extfunc uses - function
*/
2009-09-18 23:49:23 +04:00
static void docfunctions ( char * filename , char * type )
2005-04-17 02:20:36 +04:00
{
int i , j ;
int symcnt = 0 ;
int idx = 0 ;
char * * vec ;
for ( i = 0 ; i < = symfilecnt ; i + + )
symcnt + = symfilelist [ i ] . symbolcnt ;
2007-10-25 02:08:48 +04:00
vec = malloc ( ( 2 + 2 * symcnt + 3 ) * sizeof ( char * ) ) ;
2005-04-17 02:20:36 +04:00
if ( vec = = NULL ) {
perror ( " docproc: " ) ;
exit ( 1 ) ;
}
vec [ idx + + ] = KERNELDOC ;
vec [ idx + + ] = DOCBOOK ;
2007-10-25 02:08:48 +04:00
vec [ idx + + ] = NODOCSECTIONS ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < symfilecnt ; i + + ) {
struct symfile * sym = & symfilelist [ i ] ;
for ( j = 0 ; j < sym - > symbolcnt ; j + + ) {
vec [ idx + + ] = type ;
2010-09-12 02:55:22 +04:00
consume_symbol ( sym - > symbollist [ j ] . name ) ;
2005-04-17 02:20:36 +04:00
vec [ idx + + ] = sym - > symbollist [ j ] . name ;
}
}
vec [ idx + + ] = filename ;
vec [ idx ] = NULL ;
printf ( " <!-- %s --> \n " , filename ) ;
exec_kernel_doc ( vec ) ;
fflush ( stdout ) ;
free ( vec ) ;
}
2009-09-18 23:49:23 +04:00
static void intfunc ( char * filename ) { docfunctions ( filename , NOFUNCTION ) ; }
static void extfunc ( char * filename ) { docfunctions ( filename , FUNCTION ) ; }
2005-04-17 02:20:36 +04:00
/*
2006-11-03 09:07:01 +03:00
* Document specific function ( s ) in a file .
2005-04-17 02:20:36 +04:00
* Call kernel - doc with the following parameters :
* kernel - doc - docbook - function function1 [ - function function2 ]
*/
2009-09-18 23:49:23 +04:00
static void singfunc ( char * filename , char * line )
2005-04-17 02:20:36 +04:00
{
char * vec [ 200 ] ; /* Enough for specific functions */
int i , idx = 0 ;
int startofsym = 1 ;
vec [ idx + + ] = KERNELDOC ;
vec [ idx + + ] = DOCBOOK ;
2007-09-05 08:23:22 +04:00
/* Split line up in individual parameters preceded by FUNCTION */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; line [ i ] ; i + + ) {
if ( isspace ( line [ i ] ) ) {
line [ i ] = ' \0 ' ;
startofsym = 1 ;
continue ;
}
if ( startofsym ) {
startofsym = 0 ;
vec [ idx + + ] = FUNCTION ;
vec [ idx + + ] = & line [ i ] ;
}
}
2010-09-12 02:55:22 +04:00
for ( i = 0 ; i < idx ; i + + ) {
if ( strcmp ( vec [ i ] , FUNCTION ) )
continue ;
consume_symbol ( vec [ i + 1 ] ) ;
}
2005-04-17 02:20:36 +04:00
vec [ idx + + ] = filename ;
vec [ idx ] = NULL ;
exec_kernel_doc ( vec ) ;
}
2007-10-25 02:08:48 +04:00
/*
* Insert specific documentation section from a file .
* Call kernel - doc with the following parameters :
* kernel - doc - docbook - function " doc section " filename
*/
2009-09-18 23:49:23 +04:00
static void docsect ( char * filename , char * line )
2007-10-25 02:08:48 +04:00
{
char * vec [ 6 ] ; /* kerneldoc -docbook -function "section" file NULL */
char * s ;
for ( s = line ; * s ; s + + )
if ( * s = = ' \n ' )
* s = ' \0 ' ;
2010-10-22 18:32:10 +04:00
if ( asprintf ( & s , " DOC: %s " , line ) < 0 ) {
perror ( " asprintf " ) ;
exit ( 1 ) ;
}
2010-09-12 02:55:22 +04:00
consume_symbol ( s ) ;
free ( s ) ;
2007-10-25 02:08:48 +04:00
vec [ 0 ] = KERNELDOC ;
vec [ 1 ] = DOCBOOK ;
vec [ 2 ] = FUNCTION ;
vec [ 3 ] = line ;
vec [ 4 ] = filename ;
vec [ 5 ] = NULL ;
exec_kernel_doc ( vec ) ;
}
2010-09-12 02:55:22 +04:00
static void find_all_symbols ( char * filename )
{
char * vec [ 4 ] ; /* kerneldoc -list file NULL */
pid_t pid ;
int ret , i , count , start ;
char real_filename [ PATH_MAX + 1 ] ;
int pipefd [ 2 ] ;
char * data , * str ;
size_t data_len = 0 ;
vec [ 0 ] = KERNELDOC ;
vec [ 1 ] = LIST ;
vec [ 2 ] = filename ;
vec [ 3 ] = NULL ;
if ( pipe ( pipefd ) ) {
perror ( " pipe " ) ;
exit ( 1 ) ;
}
switch ( pid = fork ( ) ) {
case - 1 :
perror ( " fork " ) ;
exit ( 1 ) ;
case 0 :
close ( pipefd [ 0 ] ) ;
dup2 ( pipefd [ 1 ] , 1 ) ;
memset ( real_filename , 0 , sizeof ( real_filename ) ) ;
strncat ( real_filename , kernsrctree , PATH_MAX ) ;
strncat ( real_filename , " / " KERNELDOCPATH KERNELDOC ,
PATH_MAX - strlen ( real_filename ) ) ;
execvp ( real_filename , vec ) ;
fprintf ( stderr , " exec " ) ;
perror ( real_filename ) ;
exit ( 1 ) ;
default :
close ( pipefd [ 1 ] ) ;
data = malloc ( 4096 ) ;
do {
while ( ( ret = read ( pipefd [ 0 ] ,
data + data_len ,
4096 ) ) > 0 ) {
data_len + = ret ;
data = realloc ( data , data_len + 4096 ) ;
}
} while ( ret = = - EAGAIN ) ;
if ( ret ! = 0 ) {
perror ( " read " ) ;
exit ( 1 ) ;
}
waitpid ( pid , & ret , 0 ) ;
}
if ( WIFEXITED ( ret ) )
exitstatus | = WEXITSTATUS ( ret ) ;
else
exitstatus = 0xff ;
count = 0 ;
/* poor man's strtok, but with counting */
for ( i = 0 ; i < data_len ; i + + ) {
if ( data [ i ] = = ' \n ' ) {
count + + ;
data [ i ] = ' \0 ' ;
}
}
start = all_list_len ;
all_list_len + = count ;
all_list = realloc ( all_list , sizeof ( char * ) * all_list_len ) ;
str = data ;
for ( i = 0 ; i < data_len & & start ! = all_list_len ; i + + ) {
if ( data [ i ] = = ' \0 ' ) {
all_list [ start ] = str ;
str = data + i + 1 ;
start + + ;
}
}
}
2005-04-17 02:20:36 +04:00
/*
* Parse file , calling action specific functions for :
* 1 ) Lines containing ! E
* 2 ) Lines containing ! I
* 3 ) Lines containing ! D
* 4 ) Lines containing ! F
2007-10-25 02:08:48 +04:00
* 5 ) Lines containing ! P
2010-09-12 02:55:22 +04:00
* 6 ) Lines containing ! C
* 7 ) Default lines - lines not matching the above
2005-04-17 02:20:36 +04:00
*/
2009-09-18 23:49:23 +04:00
static void parse_file ( FILE * infile )
2005-04-17 02:20:36 +04:00
{
char line [ MAXLINESZ ] ;
2005-06-26 01:59:22 +04:00
char * s ;
2007-09-05 08:23:22 +04:00
while ( fgets ( line , MAXLINESZ , infile ) ) {
2005-04-17 02:20:36 +04:00
if ( line [ 0 ] = = ' ! ' ) {
s = line + 2 ;
switch ( line [ 1 ] ) {
case ' E ' :
while ( * s & & ! isspace ( * s ) ) s + + ;
* s = ' \0 ' ;
externalfunctions ( line + 2 ) ;
break ;
case ' I ' :
while ( * s & & ! isspace ( * s ) ) s + + ;
* s = ' \0 ' ;
internalfunctions ( line + 2 ) ;
break ;
case ' D ' :
while ( * s & & ! isspace ( * s ) ) s + + ;
* s = ' \0 ' ;
symbolsonly ( line + 2 ) ;
break ;
case ' F ' :
/* filename */
while ( * s & & ! isspace ( * s ) ) s + + ;
* s + + = ' \0 ' ;
/* function names */
while ( isspace ( * s ) )
s + + ;
singlefunctions ( line + 2 , s ) ;
break ;
2007-10-25 02:08:48 +04:00
case ' P ' :
/* filename */
while ( * s & & ! isspace ( * s ) ) s + + ;
* s + + = ' \0 ' ;
/* DOC: section name */
while ( isspace ( * s ) )
s + + ;
docsection ( line + 2 , s ) ;
break ;
2010-09-12 02:55:22 +04:00
case ' C ' :
while ( * s & & ! isspace ( * s ) ) s + + ;
* s = ' \0 ' ;
if ( findall )
findall ( line + 2 ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
defaultline ( line ) ;
}
}
else {
defaultline ( line ) ;
}
}
fflush ( stdout ) ;
}
int main ( int argc , char * argv [ ] )
{
FILE * infile ;
2010-09-12 02:55:22 +04:00
int i ;
2007-10-09 10:25:18 +04:00
srctree = getenv ( " SRCTREE " ) ;
if ( ! srctree )
srctree = getcwd ( NULL , 0 ) ;
2009-05-31 20:05:34 +04:00
kernsrctree = getenv ( " KBUILD_SRC " ) ;
2009-06-19 11:06:54 +04:00
if ( ! kernsrctree | | ! * kernsrctree )
2009-05-31 20:05:34 +04:00
kernsrctree = srctree ;
2005-04-17 02:20:36 +04:00
if ( argc ! = 3 ) {
usage ( ) ;
exit ( 1 ) ;
}
/* Open file, exit on error */
infile = fopen ( argv [ 2 ] , " r " ) ;
if ( infile = = NULL ) {
fprintf ( stderr , " docproc: " ) ;
perror ( argv [ 2 ] ) ;
exit ( 2 ) ;
}
if ( strcmp ( " doc " , argv [ 1 ] ) = = 0 )
{
/* Need to do this in two passes.
* First pass is used to collect all symbols exported
2007-09-05 08:23:22 +04:00
* in the various files ;
2005-04-17 02:20:36 +04:00
* Second pass generate the documentation .
2007-09-05 08:23:22 +04:00
* This is required because some functions are declared
2005-04-17 02:20:36 +04:00
* and exported in different files : - ( (
*/
/* Collect symbols */
defaultline = noaction ;
internalfunctions = find_export_symbols ;
externalfunctions = find_export_symbols ;
symbolsonly = find_export_symbols ;
singlefunctions = noaction2 ;
2007-10-25 02:08:48 +04:00
docsection = noaction2 ;
2010-09-12 02:55:22 +04:00
findall = find_all_symbols ;
2005-04-17 02:20:36 +04:00
parse_file ( infile ) ;
/* Rewind to start from beginning of file again */
fseek ( infile , 0 , SEEK_SET ) ;
defaultline = printline ;
internalfunctions = intfunc ;
externalfunctions = extfunc ;
symbolsonly = printline ;
singlefunctions = singfunc ;
2007-10-25 02:08:48 +04:00
docsection = docsect ;
2010-09-12 02:55:22 +04:00
findall = NULL ;
2005-04-17 02:20:36 +04:00
parse_file ( infile ) ;
2010-09-12 02:55:22 +04:00
for ( i = 0 ; i < all_list_len ; i + + ) {
if ( ! all_list [ i ] )
continue ;
fprintf ( stderr , " Warning: didn't use docs for %s \n " ,
all_list [ i ] ) ;
}
2005-04-17 02:20:36 +04:00
}
else if ( strcmp ( " depend " , argv [ 1 ] ) = = 0 )
{
/* Create first part of dependency chain
* file . tmpl */
printf ( " %s \t " , argv [ 2 ] ) ;
defaultline = noaction ;
internalfunctions = adddep ;
externalfunctions = adddep ;
symbolsonly = adddep ;
singlefunctions = adddep2 ;
2007-10-25 02:08:48 +04:00
docsection = adddep2 ;
2010-09-12 02:55:22 +04:00
findall = adddep ;
2005-04-17 02:20:36 +04:00
parse_file ( infile ) ;
printf ( " \n " ) ;
}
else
{
fprintf ( stderr , " Unknown option: %s \n " , argv [ 1 ] ) ;
exit ( 1 ) ;
}
fclose ( infile ) ;
fflush ( stdout ) ;
return exitstatus ;
}