Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 022eedee authored by Jonathan Corbet's avatar Jonathan Corbet
Browse files

Merge branch 'jani-rest' into docs-next

Patch series from Jani Nikula:

> Jon, I was hoping we could consider nudging things forward a bit in the
> kernel-doc and docproc reStructuredText front already in 4.7. I know
> it's a bit close to the merge window, but this should not interfere with
> anything else, and some of it are just trivial cleanups that I've been
> carrying around locally.
>
> Obviously this doesn't actually add anything that uses them yet, but I
> think it would be helpful to have some common base in to ease
> collaboration.
parents b79ef07d 0e95abf9
Loading
Loading
Loading
Loading
+161 −60
Original line number Diff line number Diff line
@@ -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,6 +288,9 @@ static void docfunctions(char * filename, char * type)
	}
	vec[idx++]     = filename;
	vec[idx] = NULL;
	if (file_format == FORMAT_RST)
		printf(".. %s\n", filename);
	else
		printf("<!-- %s -->\n", filename);
	exec_kernel_doc(vec);
	fflush(stdout);
@@ -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;
@@ -430,6 +446,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
@@ -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]) {
		p = is_directive(line);
		if (!p) {
			defaultline(line);
			continue;
		}

		switch (*p++) {
		case 'E':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
					externalfunctions(line+2);
			chomp(p);
			externalfunctions(p);
			break;
		case 'I':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
					internalfunctions(line+2);
			chomp(p);
			internalfunctions(p);
			break;
		case 'D':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
					symbolsonly(line+2);
			chomp(p);
			symbolsonly(p);
			break;
		case 'F':
			/* filename */
					while (*s && !isspace(*s)) s++;
					*s++ = '\0';
			s = chomp(p);
			/* function names */
			while (isspace(*s))
				s++;
					singlefunctions(line +2, s);
			singlefunctions(p, s);
			break;
		case 'P':
			/* filename */
					while (*s && !isspace(*s)) s++;
					*s++ = '\0';
			s = chomp(p);
			/* DOC: section name */
			while (isspace(*s))
				s++;
					docsection(line + 2, s);
			docsection(p, s);
			break;
		case 'C':
					while (*s && !isspace(*s)) s++;
					*s = '\0';
			chomp(p);
			if (findall)
						findall(line+2);
				findall(p);
			break;
		default:
			defaultline(line);
		}
		} else {
			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);
+269 −46
Original line number Diff line number Diff line
@@ -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 = '\\&amp;((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($) {