cregit-Linux how code gets into the kernel

Release 4.7 scripts/docproc.c

Directory: scripts
/*
 *      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
 *              Beforehand each referenced file is scanned for
 *              any symbols that are exported via these macros:
 *                      EXPORT_SYMBOL(), EXPORT_SYMBOL_GPL(), &
 *                      EXPORT_SYMBOL_GPL_FUTURE()
 *              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
 *              !Pfilename
 *
 */


#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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. */

int exitstatus = 0;


typedef void DFL(char *);

DFL *defaultline;


typedef void FILEONLY(char * file);

FILEONLY *internalfunctions;

FILEONLY *externalfunctions;

FILEONLY *symbolsonly;

FILEONLY *findall;


typedef void FILELINE(char * file, char * line);

FILELINE * singlefunctions;

FILELINE * entity_system;

FILELINE * docsection;


#define MAXLINESZ     2048

#define MAXFILES      250

#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;

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; } }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg59100.00%1100.00%
Total59100.00%1100.00%


static void usage (void) { 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"); fprintf(stderr, "Environment variable SRCTREE: absolute path to sources.\n"); fprintf(stderr, " KBUILD_SRC: absolute path to kernel source tree.\n"); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg3468.00%120.00%
jiri slabyjiri slaby816.00%120.00%
rob landleyrob landley612.00%120.00%
trevor keithtrevor keith12.00%120.00%
jani nikulajani nikula12.00%120.00%
Total50100.00%5100.00%

/* * Execute kernel-doc with parameters given in svec */
static void exec_kernel_doc(char **svec) { pid_t pid; int ret; char real_filename[PATH_MAX + 1]; /* Make sure output generated so far are flushed */ fflush(stdout); switch (pid=fork()) { case -1: perror("fork"); exit(1); case 0: 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, svec); fprintf(stderr, "exec "); perror(real_filename); exit(1); default: waitpid(pid, &ret ,0); } if (WIFEXITED(ret)) exitstatus |= WEXITSTATUS(ret); else exitstatus = 0xff; }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg7351.41%228.57%
tom rinitom rini4833.80%114.29%
pre-gitpre-git1611.27%114.29%
jiri slabyjiri slaby21.41%114.29%
linus torvaldslinus torvalds21.41%114.29%
trevor keithtrevor keith10.70%114.29%
Total142100.00%7100.00%

/* 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;
static void add_new_symbol(struct symfile *sym, char * symname) { sym->symbollist = realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *)); sym->symbollist[sym->symbolcnt++].name = strdup(symname); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg5392.98%133.33%
pre-gitpre-git35.26%133.33%
trevor keithtrevor keith11.75%133.33%
Total57100.00%3100.00%

/* Add a filename to the list */
static struct symfile * add_new_file(char * filename) { symfilelist[symfilecnt++].filename = strdup(filename); return &symfilelist[symfilecnt - 1]; }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg2985.29%133.33%
pre-gitpre-git411.76%133.33%
trevor keithtrevor keith12.94%133.33%
Total34100.00%3100.00%

/* Check if file already are present in the list */
static struct symfile * filename_exist(char * filename) { int i; for (i=0; i < symfilecnt; i++) if (strcmp(symfilelist[i].filename, filename) == 0) return &symfilelist[i]; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg3564.81%133.33%
pre-gitpre-git1833.33%133.33%
trevor keithtrevor keith11.85%133.33%
Total54100.00%3100.00%

/* * List all files referenced within the template file. * Files are separated by tabs. */
static void adddep(char * file) { printf("\t%s", file); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg1694.12%150.00%
trevor keithtrevor keith15.88%150.00%
Total17100.00%2100.00%


static void adddep2(char * file, char * line) { line = line; adddep(file); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg2295.65%150.00%
trevor keithtrevor keith14.35%150.00%
Total23100.00%2100.00%


static void noaction(char * line) { line = line; }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg1392.86%150.00%
trevor keithtrevor keith17.14%150.00%
Total14100.00%2100.00%


static void noaction2(char * file, char * line) { file = file; line = line; }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg2195.45%150.00%
trevor keithtrevor keith14.55%150.00%
Total22100.00%2100.00%

/* Echo the line without further action */
static void printline(char * line) { printf("%s", line); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg1694.12%150.00%
trevor keithtrevor keith15.88%150.00%
Total17100.00%2100.00%

/* * Find all symbols in filename that are exported with EXPORT_SYMBOL & * EXPORT_SYMBOL_GPL (& EXPORT_SYMBOL_GPL_FUTURE implicitly). * All symbols located are stored in symfilelist. */
static void find_export_symbols(char * filename) { 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)); strncat(real_filename, srctree, PATH_MAX); strncat(real_filename, "/", PATH_MAX - strlen(real_filename)); 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); exit(1); } while (fgets(line, MAXLINESZ, fp)) { char *p; char *e; if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) || ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) { /* Skip EXPORT_SYMBOL{_GPL} */ while (isalnum(*p) || *p == '_') p++; /* Remove parentheses & additional whitespace */ 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); } }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg21175.63%114.29%
tom rinitom rini4415.77%114.29%
jiri slabyjiri slaby145.02%114.29%
henrik kretzschmarhenrik kretzschmar51.79%114.29%
randy dunlaprandy dunlap31.08%114.29%
trevor keithtrevor keith10.36%114.29%
rob landleyrob landley10.36%114.29%
Total279100.00%7100.00%

/* * Document all external or internal functions in a file. * Call kernel-doc with following parameters: * kernel-doc [-docbook|-rst] -nofunction function_name1 filename * Function names are obtained from all the src files * by find_export_symbols. * intfunc uses -nofunction * extfunc uses -function */
static void docfunctions(char * filename, char * type) { int i,j; int symcnt = 0; int idx = 0; char **vec; for (i=0; i <= symfilecnt; i++) symcnt += symfilelist[i].symbolcnt; vec = malloc((2 + 2 * symcnt + 3) * sizeof(char *)); if (vec == NULL) { perror("docproc: "); exit(1); } vec[idx++] = KERNELDOC; vec[idx++] = KERNELDOC_FORMAT; vec[idx++] = NODOCSECTIONS; for (i=0; i < symfilecnt; i++) { struct symfile * sym = &symfilelist[i]; for (j=0; j < sym->symbolcnt; j++) { vec[idx++] = type; consume_symbol(sym->symbollist[j].name); vec[idx++] = sym->symbollist[j].name; } } 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); free(vec); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg21185.08%120.00%
johannes bergjohannes berg218.47%240.00%
jani nikulajani nikula156.05%120.00%
trevor keithtrevor keith10.40%120.00%
Total248100.00%5100.00%


static void intfunc(char * filename) { docfunctions(filename, NOFUNCTION); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg1694.12%150.00%
trevor keithtrevor keith15.88%150.00%
Total17100.00%2100.00%


static void extfunc(char * filename) { docfunctions(filename, FUNCTION); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg1694.12%150.00%
trevor keithtrevor keith15.88%150.00%
Total17100.00%2100.00%

/* * Document specific function(s) in a file. * Call kernel-doc with the following parameters: * kernel-doc -docbook -function function1 [-function function2] */
static void singfunc(char * filename, char * line) { char *vec[200]; /* Enough for specific functions */ int i, idx = 0; int startofsym = 1; vec[idx++] = KERNELDOC; vec[idx++] = KERNELDOC_FORMAT; vec[idx++] = SHOWNOTFOUND; /* Split line up in individual parameters preceded by FUNCTION */ 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]; } } for (i = 0; i < idx; i++) { if (strcmp(vec[i], FUNCTION)) continue; consume_symbol(vec[i + 1]); } vec[idx++] = filename; vec[idx] = NULL; exec_kernel_doc(vec); }

Contributors

PersonTokensPropCommitsCommitProp
sam ravnborgsam ravnborg11159.36%114.29%
johannes bergjohannes berg4624.60%228.57%
pre-gitpre-git2714.44%114.29%
jani nikulajani nikula10.53%114.29%
randy dunlaprandy dunlap10.53%114.29%
trevor keithtrevor keith10.53%114.29%
Total187100.00%7100.00%

/* * Insert specific documentation section from a file. * Call kernel-doc with the following parameters: * kernel-doc -docbook -function "doc section" filename */
static void docsect(char *filename, char *line) { /* kerneldoc -docbook -show-not-found -function "section" file NULL */ char *vec[7]; char *s; for (s = line; *s; s++) if (*s == '\n') *s = '\0'; if (asprintf(&s, "DOC: %s", line) < 0) { perror("asprintf"); exit(1); } consume_symbol(s); free(s); vec[0] = KERNELDOC; vec[1] = KERNELDOC_FORMAT; vec[2] = SHOWNOTFOUND; vec[3] = FUNCTION; vec[4] = line; vec[5] = filename; vec[6] = NULL; exec_kernel_doc(vec); }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg12287.14%350.00%
namhyung kimnamhyung kim1611.43%116.67%
trevor keithtrevor keith10.71%116.67%
jani nikulajani nikula10.71%116.67%
Total140100.00%6100.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++; } } }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg40694.20%133.33%
sam ravnborgsam ravnborg245.57%133.33%
trevor keithtrevor keith10.23%133.33%
Total431100.00%3100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
jani nikulajani nikula40100.00%1100.00%
Total40100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
jani nikulajani nikula54100.00%2100.00%
Total54100.00%2100.00%

/* * Parse file, calling action specific functions for: * 1) Lines containing !E * 2) Lines containing !I * 3) Lines containing !D * 4) Lines containing !F * 5) Lines containing !P * 6) Lines containing !C * 7) Default lines - lines not matching the above */
static void parse_file(FILE *infile) { char line[MAXLINESZ]; char *p, *s; while (fgets(line, MAXLINESZ, infile)) { 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); }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg8643.65%228.57%
sam ravnborgsam ravnborg6734.01%114.29%
jani nikulajani nikula4221.32%342.86%
pre-gitpre-git21.02%114.29%
Total197100.00%7100.00%

/* * 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"); }

Contributors

PersonTokensPropCommitsCommitProp
jani nikulajani nikula35100.00%1100.00%
Total35100.00%1100.00%

enum opts { OPT_DOCBOOK, OPT_RST, OPT_HELP, };
int main(int argc, char *argv[]) { const char *subcommand, *filename; FILE * infile; int i; srctree = getenv("SRCTREE"); if (!srctree) srctree = getcwd(NULL, 0); kernsrctree = getenv("KBUILD_SRC"); if (!kernsrctree || !*kernsrctree) kernsrctree = srctree; 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(filename, "r"); if (infile == NULL) { fprintf(stderr, "docproc: "); perror(filename); exit(2); } 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; * Second pass generate the documentation. * This is required because some functions are declared * and exported in different files :-(( */ /* Collect symbols */ defaultline = noaction; internalfunctions = find_export_symbols; externalfunctions = find_export_symbols; symbolsonly = find_export_symbols; singlefunctions = noaction2; docsection = noaction2; findall = find_all_symbols; 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; docsection = docsect; findall = NULL; parse_file(infile); 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]); } } else if (strcmp("depend", subcommand) == 0) { /* Create first part of dependency chain * file.tmpl */ printf("%s\t", filename); defaultline = noaction; internalfunctions = adddep; externalfunctions = adddep; symbolsonly = adddep; singlefunctions = adddep2; docsection = adddep2; findall = adddep; parse_file(infile); printf("\n"); } else { fprintf(stderr, "Unknown option: %s\n", subcommand); exit(1)