diff options
-rw-r--r-- | scripts/docproc.c | 221 | ||||
-rwxr-xr-x | scripts/kernel-doc | 315 |
2 files changed, 430 insertions, 106 deletions
diff --git a/scripts/docproc.c b/scripts/docproc.c index e267e621..0a12593 100644 --- a/scripts/docproc.c +++ b/scripts/docproc.c @@ -42,8 +42,10 @@ #include <unistd.h> #include <limits.h> #include <errno.h> +#include <getopt.h> #include <sys/types.h> #include <sys/wait.h> +#include <time.h> /* exitstatus is used to keep track of any failing calls to kernel-doc, * but execution continues. */ @@ -68,12 +70,23 @@ FILELINE * docsection; #define KERNELDOCPATH "scripts/" #define KERNELDOC "kernel-doc" #define DOCBOOK "-docbook" +#define RST "-rst" #define LIST "-list" #define FUNCTION "-function" #define NOFUNCTION "-nofunction" #define NODOCSECTIONS "-no-doc-sections" #define SHOWNOTFOUND "-show-not-found" +enum file_format { + FORMAT_AUTO, + FORMAT_DOCBOOK, + FORMAT_RST, +}; + +static enum file_format file_format = FORMAT_AUTO; + +#define KERNELDOC_FORMAT (file_format == FORMAT_RST ? RST : DOCBOOK) + static char *srctree, *kernsrctree; static char **all_list = NULL; @@ -95,7 +108,7 @@ static void consume_symbol(const char *sym) static void usage (void) { - fprintf(stderr, "Usage: docproc {doc|depend} file\n"); + fprintf(stderr, "Usage: docproc [{--docbook|--rst}] {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"); @@ -242,7 +255,7 @@ static void find_export_symbols(char * filename) /* * Document all external or internal functions in a file. * Call kernel-doc with following parameters: - * kernel-doc -docbook -nofunction function_name1 filename + * kernel-doc [-docbook|-rst] -nofunction function_name1 filename * Function names are obtained from all the src files * by find_export_symbols. * intfunc uses -nofunction @@ -263,7 +276,7 @@ static void docfunctions(char * filename, char * type) exit(1); } vec[idx++] = KERNELDOC; - vec[idx++] = DOCBOOK; + vec[idx++] = KERNELDOC_FORMAT; vec[idx++] = NODOCSECTIONS; for (i=0; i < symfilecnt; i++) { struct symfile * sym = &symfilelist[i]; @@ -275,7 +288,10 @@ static void docfunctions(char * filename, char * type) } vec[idx++] = filename; vec[idx] = NULL; - printf("<!-- %s -->\n", filename); + if (file_format == FORMAT_RST) + printf(".. %s\n", filename); + else + printf("<!-- %s -->\n", filename); exec_kernel_doc(vec); fflush(stdout); free(vec); @@ -294,7 +310,7 @@ static void singfunc(char * filename, char * line) int i, idx = 0; int startofsym = 1; vec[idx++] = KERNELDOC; - vec[idx++] = DOCBOOK; + vec[idx++] = KERNELDOC_FORMAT; vec[idx++] = SHOWNOTFOUND; /* Split line up in individual parameters preceded by FUNCTION */ @@ -343,7 +359,7 @@ static void docsect(char *filename, char *line) free(s); vec[0] = KERNELDOC; - vec[1] = DOCBOOK; + vec[1] = KERNELDOC_FORMAT; vec[2] = SHOWNOTFOUND; vec[3] = FUNCTION; vec[4] = line; @@ -431,6 +447,32 @@ static void find_all_symbols(char *filename) } /* + * Terminate s at first space, if any. If there was a space, return pointer to + * the character after that. Otherwise, return pointer to the terminating NUL. + */ +static char *chomp(char *s) +{ + while (*s && !isspace(*s)) + s++; + + if (*s) + *s++ = '\0'; + + return s; +} + +/* Return pointer to directive content, or NULL if not a directive. */ +static char *is_directive(char *line) +{ + if (file_format == FORMAT_DOCBOOK && line[0] == '!') + return line + 1; + else if (file_format == FORMAT_RST && !strncmp(line, ".. !", 4)) + return line + 4; + + return NULL; +} + +/* * Parse file, calling action specific functions for: * 1) Lines containing !E * 2) Lines containing !I @@ -443,63 +485,75 @@ static void find_all_symbols(char *filename) static void parse_file(FILE *infile) { char line[MAXLINESZ]; - char * s; + char *p, *s; while (fgets(line, MAXLINESZ, infile)) { - 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; - case 'P': - /* filename */ - while (*s && !isspace(*s)) s++; - *s++ = '\0'; - /* DOC: section name */ - while (isspace(*s)) - s++; - docsection(line + 2, s); - break; - case 'C': - while (*s && !isspace(*s)) s++; - *s = '\0'; - if (findall) - findall(line+2); - break; - default: - defaultline(line); - } - } else { + p = is_directive(line); + if (!p) { + defaultline(line); + continue; + } + + switch (*p++) { + case 'E': + chomp(p); + externalfunctions(p); + break; + case 'I': + chomp(p); + internalfunctions(p); + break; + case 'D': + chomp(p); + symbolsonly(p); + break; + case 'F': + /* filename */ + s = chomp(p); + /* function names */ + while (isspace(*s)) + s++; + singlefunctions(p, s); + break; + case 'P': + /* filename */ + s = chomp(p); + /* DOC: section name */ + while (isspace(*s)) + s++; + docsection(p, s); + break; + case 'C': + chomp(p); + if (findall) + findall(p); + break; + default: defaultline(line); } } fflush(stdout); } +/* + * Is this a RestructuredText template? Answer the question by seeing if its + * name ends in ".rst". + */ +static int is_rst(const char *file) +{ + char *dot = strrchr(file, '.'); + + return dot && !strcmp(dot + 1, "rst"); +} + +enum opts { + OPT_DOCBOOK, + OPT_RST, + OPT_HELP, +}; int main(int argc, char *argv[]) { + const char *subcommand, *filename; FILE * infile; int i; @@ -509,19 +563,66 @@ int main(int argc, char *argv[]) kernsrctree = getenv("KBUILD_SRC"); if (!kernsrctree || !*kernsrctree) kernsrctree = srctree; - if (argc != 3) { + + for (;;) { + int c; + struct option opts[] = { + { "docbook", no_argument, NULL, OPT_DOCBOOK }, + { "rst", no_argument, NULL, OPT_RST }, + { "help", no_argument, NULL, OPT_HELP }, + {} + }; + + c = getopt_long_only(argc, argv, "", opts, NULL); + if (c == -1) + break; + + switch (c) { + case OPT_DOCBOOK: + file_format = FORMAT_DOCBOOK; + break; + case OPT_RST: + file_format = FORMAT_RST; + break; + case OPT_HELP: + usage(); + return 0; + default: + case '?': + usage(); + return 1; + } + } + + argc -= optind; + argv += optind; + + if (argc != 2) { usage(); exit(1); } + + subcommand = argv[0]; + filename = argv[1]; + + if (file_format == FORMAT_AUTO) + file_format = is_rst(filename) ? FORMAT_RST : FORMAT_DOCBOOK; + /* Open file, exit on error */ - infile = fopen(argv[2], "r"); + infile = fopen(filename, "r"); if (infile == NULL) { fprintf(stderr, "docproc: "); - perror(argv[2]); + perror(filename); exit(2); } - if (strcmp("doc", argv[1]) == 0) { + if (strcmp("doc", subcommand) == 0) { + if (file_format == FORMAT_RST) { + time_t t = time(NULL); + printf(".. generated from %s by docproc %s\n", + filename, ctime(&t)); + } + /* Need to do this in two passes. * First pass is used to collect all symbols exported * in the various files; @@ -557,10 +658,10 @@ int main(int argc, char *argv[]) fprintf(stderr, "Warning: didn't use docs for %s\n", all_list[i]); } - } else if (strcmp("depend", argv[1]) == 0) { + } else if (strcmp("depend", subcommand) == 0) { /* Create first part of dependency chain * file.tmpl */ - printf("%s\t", argv[2]); + printf("%s\t", filename); defaultline = noaction; internalfunctions = adddep; externalfunctions = adddep; @@ -571,7 +672,7 @@ int main(int argc, char *argv[]) parse_file(infile); printf("\n"); } else { - fprintf(stderr, "Unknown option: %s\n", argv[1]); + fprintf(stderr, "Unknown option: %s\n", subcommand); exit(1); } fclose(infile); diff --git a/scripts/kernel-doc b/scripts/kernel-doc index c37255b..2fc8fad 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -39,41 +39,44 @@ use strict; # 25/07/2012 - Added support for HTML5 # -- Dan Luedtke <mail@danrl.de> -# -# This will read a 'c' file and scan for embedded comments in the -# style of gnome comments (+minor extensions - see below). -# - -# Note: This only supports 'c'. - -# usage: -# kernel-doc [ -docbook | -html | -html5 | -text | -man | -list ] -# [ -no-doc-sections ] -# [ -function funcname [ -function funcname ...] ] -# c file(s)s > outputfile -# or -# [ -nofunction funcname [ -function funcname ...] ] -# c file(s)s > outputfile -# -# Set output format using one of -docbook -html -html5 -text or -man. -# Default is man. -# The -list format is for internal use by docproc. -# -# -no-doc-sections -# Do not output DOC: sections -# -# -function funcname -# If set, then only generate documentation for the given function(s) or -# DOC: section titles. All other functions and DOC: sections are ignored. -# -# -nofunction funcname -# If set, then only generate documentation for the other function(s)/DOC: -# sections. Cannot be used together with -function (yes, that's a bug -- -# perl hackers can fix it 8)) -# -# c files - list of 'c' files to process -# -# All output goes to stdout, with errors to stderr. +sub usage { + my $message = <<"EOF"; +Usage: $0 [OPTION ...] FILE ... + +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by "/**" opening comment mark. See +Documentation/kernel-doc-nano-HOWTO.txt for the documentation comment syntax. + +Output format selection (mutually exclusive): + -docbook Output DocBook format. + -html Output HTML format. + -html5 Output HTML5 format. + -list Output symbol list format. This is for use by docproc. + -man Output troff manual page format. This is the default. + -rst Output reStructuredText format. + -text Output plain text format. + +Output selection (mutually exclusive): + -function NAME Only output documentation for the given function(s) + or DOC: section title(s). All other functions and DOC: + sections are ignored. May be specified multiple times. + -nofunction NAME Do NOT output documentation for the given function(s); + only output documentation for the other functions and + DOC: sections. May be specified multiple times. + +Output selection modifiers: + -no-doc-sections Do not output DOC: sections. + +Other parameters: + -v Verbose output, more warnings and other information. + -h Print this help. + +EOF + print $message; + exit 1; +} # # format of comments. @@ -201,6 +204,8 @@ my $type_param = '\@(\w+)'; my $type_struct = '\&((struct\s*)*[_\w]+)'; my $type_struct_xml = '\\&((struct\s*)*[_\w]+)'; my $type_env = '(\$\w+)'; +my $type_enum_full = '\&(enum)\s*([_\w]+)'; +my $type_struct_full = '\&(struct)\s*([_\w]+)'; # Output conversion substitutions. # One for each output format @@ -266,6 +271,17 @@ my @highlights_text = ( ); my $blankline_text = ""; +# rst-mode +my @highlights_rst = ( + [$type_constant, "``\$1``"], + [$type_func, "\\:c\\:func\\:`\$1`"], + [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], + [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], + [$type_struct, "\\:c\\:type\\:`struct \$1 <\$1>`"], + [$type_param, "**\$1**"] + ); +my $blankline_rst = "\n"; + # list mode my @highlights_list = ( [$type_constant, "\$1"], @@ -402,6 +418,10 @@ while ($ARGV[0] =~ m/^-(.*)/) { $output_mode = "text"; @highlights = @highlights_text; $blankline = $blankline_text; + } elsif ($cmd eq "-rst") { + $output_mode = "rst"; + @highlights = @highlights_rst; + $blankline = $blankline_rst; } elsif ($cmd eq "-docbook") { $output_mode = "xml"; @highlights = @highlights_xml; @@ -437,17 +457,6 @@ while ($ARGV[0] =~ m/^-(.*)/) { # continue execution near EOF; -sub usage { - print "Usage: $0 [ -docbook | -html | -html5 | -text | -man | -list ]\n"; - print " [ -no-doc-sections ]\n"; - print " [ -function funcname [ -function funcname ...] ]\n"; - print " [ -nofunction funcname [ -nofunction funcname ...] ]\n"; - print " [ -v ]\n"; - print " c source file(s) > outputfile\n"; - print " -v : verbose output, more warnings & other info listed\n"; - exit 1; -} - # get kernel version from env sub get_kernel_version() { my $version = 'unknown kernel version'; @@ -1713,6 +1722,208 @@ sub output_blockhead_text(%) { } } +## +# output in restructured text +# + +# +# This could use some work; it's used to output the DOC: sections, and +# starts by putting out the name of the doc section itself, but that tends +# to duplicate a header already in the template file. +# +sub output_blockhead_rst(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + + foreach $section (@{$args{'sectionlist'}}) { + print "**$section**\n\n"; + output_highlight_rst($args{'sections'}{$section}); + print "\n"; + } +} + +sub output_highlight_rst { + my $contents = join "\n",@_; + my $line; + + # undo the evil effects of xml_escape() earlier + $contents = xml_unescape($contents); + + eval $dohighlight; + die $@ if $@; + + foreach $line (split "\n", $contents) { + if ($line eq "") { + print $lineprefix, $blankline; + } else { + $line =~ s/\\\\\\/\&/g; + print $lineprefix, $line; + } + print "\n"; + } +} + +sub output_function_rst(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $start; + + print ".. c:function:: "; + if ($args{'functiontype'} ne "") { + $start = $args{'functiontype'} . " " . $args{'function'} . " ("; + } else { + $start = $args{'function'} . " ("; + } + print $start; + + my $count = 0; + foreach my $parameter (@{$args{'parameterlist'}}) { + if ($count ne 0) { + print ", "; + } + $count++; + $type = $args{'parametertypes'}{$parameter}; + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print $1 . $parameter . ") (" . $2; + } else { + print $type . " " . $parameter; + } + } + print ")\n\n " . $args{'purpose'} . "\n\n"; + + print ":Parameters:\n\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + my $parameter_name = $parameter; + #$parameter_name =~ s/\[.*//; + $type = $args{'parametertypes'}{$parameter}; + + if ($type ne "") { + print " ``$type $parameter``\n"; + } else { + print " ``$parameter``\n"; + } + if ($args{'parameterdescs'}{$parameter_name} ne $undescribed) { + my $oldprefix = $lineprefix; + $lineprefix = " "; + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); + $lineprefix = $oldprefix; + } else { + print "\n _undescribed_\n"; + } + print "\n"; + } + output_section_rst(@_); +} + +sub output_section_rst(%) { + my %args = %{$_[0]}; + my $section; + my $oldprefix = $lineprefix; + $lineprefix = " "; + + foreach $section (@{$args{'sectionlist'}}) { + print ":$section:\n\n"; + output_highlight_rst($args{'sections'}{$section}); + print "\n"; + } + print "\n"; + $lineprefix = $oldprefix; +} + +sub output_enum_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $count; + my $name = "enum " . $args{'enum'}; + + print "\n\n.. c:type:: " . $name . "\n\n"; + print " " . $args{'purpose'} . "\n\n"; + + print "..\n\n:Constants:\n\n"; + my $oldprefix = $lineprefix; + $lineprefix = " "; + foreach $parameter (@{$args{'parameterlist'}}) { + print " `$parameter`\n"; + if ($args{'parameterdescs'}{$parameter} ne $undescribed) { + output_highlight_rst($args{'parameterdescs'}{$parameter}); + } else { + print " undescribed\n"; + } + print "\n"; + } + $lineprefix = $oldprefix; + output_section_rst(@_); +} + +sub output_typedef_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $count; + my $name = "typedef " . $args{'typedef'}; + + ### FIXME: should the name below contain "typedef" or not? + print "\n\n.. c:type:: " . $name . "\n\n"; + print " " . $args{'purpose'} . "\n\n"; + + output_section_rst(@_); +} + +sub output_struct_rst(%) { + my %args = %{$_[0]}; + my ($parameter); + my $name = $args{'type'} . " " . $args{'struct'}; + + print "\n\n.. c:type:: " . $name . "\n\n"; + print " " . $args{'purpose'} . "\n\n"; + + print ":Definition:\n\n"; + print " ::\n\n"; + print " " . $args{'type'} . " " . $args{'struct'} . " {\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + if ($parameter =~ /^#/) { + print " " . "$parameter\n"; + next; + } + + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + $type = $args{'parametertypes'}{$parameter}; + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print " $1 $parameter) ($2);\n"; + } elsif ($type =~ m/^(.*?)\s*(:.*)/) { + # bitfield + print " $1 $parameter$2;\n"; + } else { + print " " . $type . " " . $parameter . ";\n"; + } + } + print " };\n\n"; + + print ":Members:\n\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + ($parameter =~ /^#/) && next; + + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + $type = $args{'parametertypes'}{$parameter}; + print " `$type $parameter`" . "\n"; + my $oldprefix = $lineprefix; + $lineprefix = " "; + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); + $lineprefix = $oldprefix; + print "\n"; + } + print "\n"; + output_section_rst(@_); +} + + ## list mode output functions sub output_function_list(%) { @@ -2414,6 +2625,18 @@ sub xml_escape($) { return $text; } +# xml_unescape: reverse the effects of xml_escape +sub xml_unescape($) { + my $text = shift; + if (($output_mode eq "text") || ($output_mode eq "man")) { + return $text; + } + $text =~ s/\\\\\\amp;/\&/g; + $text =~ s/\\\\\\lt;/</g; + $text =~ s/\\\\\\gt;/>/g; + return $text; +} + # convert local escape strings to html # local escape strings look like: '\\\\menmonic:' (that's 4 backslashes) sub local_unescape($) { |