Back to home page

LXR

 
 

    


0001 /*
0002  *  docproc is a simple preprocessor for the template files
0003  *      used as placeholders for the kernel internal documentation.
0004  *  docproc is used for documentation-frontend and
0005  *      dependency-generator.
0006  *  The two usages have in common that they require
0007  *  some knowledge of the .tmpl syntax, therefore they
0008  *  are kept together.
0009  *
0010  *  documentation-frontend
0011  *      Scans the template file and call kernel-doc for
0012  *      all occurrences of ![EIF]file
0013  *      Beforehand each referenced file is scanned for
0014  *      any symbols that are exported via these macros:
0015  *          EXPORT_SYMBOL(), EXPORT_SYMBOL_GPL(), &
0016  *          EXPORT_SYMBOL_GPL_FUTURE()
0017  *      This is used to create proper -function and
0018  *      -nofunction arguments in calls to kernel-doc.
0019  *      Usage: docproc doc file.tmpl
0020  *
0021  *  dependency-generator:
0022  *      Scans the template file and list all files
0023  *      referenced in a format recognized by make.
0024  *      Usage:  docproc depend file.tmpl
0025  *      Writes dependency information to stdout
0026  *      in the following format:
0027  *      file.tmpl src.c src2.c
0028  *      The filenames are obtained from the following constructs:
0029  *      !Efilename
0030  *      !Ifilename
0031  *      !Dfilename
0032  *      !Ffilename
0033  *      !Pfilename
0034  *
0035  */
0036 
0037 #define _GNU_SOURCE
0038 #include <stdio.h>
0039 #include <stdlib.h>
0040 #include <string.h>
0041 #include <ctype.h>
0042 #include <unistd.h>
0043 #include <limits.h>
0044 #include <errno.h>
0045 #include <getopt.h>
0046 #include <sys/types.h>
0047 #include <sys/wait.h>
0048 #include <time.h>
0049 
0050 /* exitstatus is used to keep track of any failing calls to kernel-doc,
0051  * but execution continues. */
0052 int exitstatus = 0;
0053 
0054 typedef void DFL(char *);
0055 DFL *defaultline;
0056 
0057 typedef void FILEONLY(char * file);
0058 FILEONLY *internalfunctions;
0059 FILEONLY *externalfunctions;
0060 FILEONLY *symbolsonly;
0061 FILEONLY *findall;
0062 
0063 typedef void FILELINE(char * file, char * line);
0064 FILELINE * singlefunctions;
0065 FILELINE * entity_system;
0066 FILELINE * docsection;
0067 
0068 #define MAXLINESZ     2048
0069 #define MAXFILES      250
0070 #define KERNELDOCPATH "scripts/"
0071 #define KERNELDOC     "kernel-doc"
0072 #define DOCBOOK       "-docbook"
0073 #define RST           "-rst"
0074 #define LIST          "-list"
0075 #define FUNCTION      "-function"
0076 #define NOFUNCTION    "-nofunction"
0077 #define NODOCSECTIONS "-no-doc-sections"
0078 #define SHOWNOTFOUND  "-show-not-found"
0079 
0080 enum file_format {
0081     FORMAT_AUTO,
0082     FORMAT_DOCBOOK,
0083     FORMAT_RST,
0084 };
0085 
0086 static enum file_format file_format = FORMAT_AUTO;
0087 
0088 #define KERNELDOC_FORMAT    (file_format == FORMAT_RST ? RST : DOCBOOK)
0089 
0090 static char *srctree, *kernsrctree;
0091 
0092 static char **all_list = NULL;
0093 static int all_list_len = 0;
0094 
0095 static void consume_symbol(const char *sym)
0096 {
0097     int i;
0098 
0099     for (i = 0; i < all_list_len; i++) {
0100         if (!all_list[i])
0101             continue;
0102         if (strcmp(sym, all_list[i]))
0103             continue;
0104         all_list[i] = NULL;
0105         break;
0106     }
0107 }
0108 
0109 static void usage (void)
0110 {
0111     fprintf(stderr, "Usage: docproc [{--docbook|--rst}] {doc|depend} file\n");
0112     fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
0113     fprintf(stderr, "doc: frontend when generating kernel documentation\n");
0114     fprintf(stderr, "depend: generate list of files referenced within file\n");
0115     fprintf(stderr, "Environment variable SRCTREE: absolute path to sources.\n");
0116     fprintf(stderr, "                     KBUILD_SRC: absolute path to kernel source tree.\n");
0117 }
0118 
0119 /*
0120  * Execute kernel-doc with parameters given in svec
0121  */
0122 static void exec_kernel_doc(char **svec)
0123 {
0124     pid_t pid;
0125     int ret;
0126     char real_filename[PATH_MAX + 1];
0127     /* Make sure output generated so far are flushed */
0128     fflush(stdout);
0129     switch (pid=fork()) {
0130         case -1:
0131             perror("fork");
0132             exit(1);
0133         case  0:
0134             memset(real_filename, 0, sizeof(real_filename));
0135             strncat(real_filename, kernsrctree, PATH_MAX);
0136             strncat(real_filename, "/" KERNELDOCPATH KERNELDOC,
0137                     PATH_MAX - strlen(real_filename));
0138             execvp(real_filename, svec);
0139             fprintf(stderr, "exec ");
0140             perror(real_filename);
0141             exit(1);
0142         default:
0143             waitpid(pid, &ret ,0);
0144     }
0145     if (WIFEXITED(ret))
0146         exitstatus |= WEXITSTATUS(ret);
0147     else
0148         exitstatus = 0xff;
0149 }
0150 
0151 /* Types used to create list of all exported symbols in a number of files */
0152 struct symbols
0153 {
0154     char *name;
0155 };
0156 
0157 struct symfile
0158 {
0159     char *filename;
0160     struct symbols *symbollist;
0161     int symbolcnt;
0162 };
0163 
0164 struct symfile symfilelist[MAXFILES];
0165 int symfilecnt = 0;
0166 
0167 static void add_new_symbol(struct symfile *sym, char * symname)
0168 {
0169     sym->symbollist =
0170       realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
0171     sym->symbollist[sym->symbolcnt++].name = strdup(symname);
0172 }
0173 
0174 /* Add a filename to the list */
0175 static struct symfile * add_new_file(char * filename)
0176 {
0177     symfilelist[symfilecnt++].filename = strdup(filename);
0178     return &symfilelist[symfilecnt - 1];
0179 }
0180 
0181 /* Check if file already are present in the list */
0182 static struct symfile * filename_exist(char * filename)
0183 {
0184     int i;
0185     for (i=0; i < symfilecnt; i++)
0186         if (strcmp(symfilelist[i].filename, filename) == 0)
0187             return &symfilelist[i];
0188     return NULL;
0189 }
0190 
0191 /*
0192  * List all files referenced within the template file.
0193  * Files are separated by tabs.
0194  */
0195 static void adddep(char * file)        { printf("\t%s", file); }
0196 static void adddep2(char * file, char * line)     { line = line; adddep(file); }
0197 static void noaction(char * line)          { line = line; }
0198 static void noaction2(char * file, char * line)   { file = file; line = line; }
0199 
0200 /* Echo the line without further action */
0201 static void printline(char * line)               { printf("%s", line); }
0202 
0203 /*
0204  * Find all symbols in filename that are exported with EXPORT_SYMBOL &
0205  * EXPORT_SYMBOL_GPL (& EXPORT_SYMBOL_GPL_FUTURE implicitly).
0206  * All symbols located are stored in symfilelist.
0207  */
0208 static void find_export_symbols(char * filename)
0209 {
0210     FILE * fp;
0211     struct symfile *sym;
0212     char line[MAXLINESZ];
0213     if (filename_exist(filename) == NULL) {
0214         char real_filename[PATH_MAX + 1];
0215         memset(real_filename, 0, sizeof(real_filename));
0216         strncat(real_filename, srctree, PATH_MAX);
0217         strncat(real_filename, "/", PATH_MAX - strlen(real_filename));
0218         strncat(real_filename, filename,
0219                 PATH_MAX - strlen(real_filename));
0220         sym = add_new_file(filename);
0221         fp = fopen(real_filename, "r");
0222         if (fp == NULL) {
0223             fprintf(stderr, "docproc: ");
0224             perror(real_filename);
0225             exit(1);
0226         }
0227         while (fgets(line, MAXLINESZ, fp)) {
0228             char *p;
0229             char *e;
0230             if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
0231                 ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
0232                 /* Skip EXPORT_SYMBOL{_GPL} */
0233                 while (isalnum(*p) || *p == '_')
0234                     p++;
0235                 /* Remove parentheses & additional whitespace */
0236                 while (isspace(*p))
0237                     p++;
0238                 if (*p != '(')
0239                     continue; /* Syntax error? */
0240                 else
0241                     p++;
0242                 while (isspace(*p))
0243                     p++;
0244                 e = p;
0245                 while (isalnum(*e) || *e == '_')
0246                     e++;
0247                 *e = '\0';
0248                 add_new_symbol(sym, p);
0249             }
0250         }
0251         fclose(fp);
0252     }
0253 }
0254 
0255 /*
0256  * Document all external or internal functions in a file.
0257  * Call kernel-doc with following parameters:
0258  * kernel-doc [-docbook|-rst] -nofunction function_name1 filename
0259  * Function names are obtained from all the src files
0260  * by find_export_symbols.
0261  * intfunc uses -nofunction
0262  * extfunc uses -function
0263  */
0264 static void docfunctions(char * filename, char * type)
0265 {
0266     int i,j;
0267     int symcnt = 0;
0268     int idx = 0;
0269     char **vec;
0270 
0271     for (i=0; i <= symfilecnt; i++)
0272         symcnt += symfilelist[i].symbolcnt;
0273     vec = malloc((2 + 2 * symcnt + 3) * sizeof(char *));
0274     if (vec == NULL) {
0275         perror("docproc: ");
0276         exit(1);
0277     }
0278     vec[idx++] = KERNELDOC;
0279     vec[idx++] = KERNELDOC_FORMAT;
0280     vec[idx++] = NODOCSECTIONS;
0281     for (i=0; i < symfilecnt; i++) {
0282         struct symfile * sym = &symfilelist[i];
0283         for (j=0; j < sym->symbolcnt; j++) {
0284             vec[idx++]     = type;
0285             consume_symbol(sym->symbollist[j].name);
0286             vec[idx++] = sym->symbollist[j].name;
0287         }
0288     }
0289     vec[idx++]     = filename;
0290     vec[idx] = NULL;
0291     if (file_format == FORMAT_RST)
0292         printf(".. %s\n", filename);
0293     else
0294         printf("<!-- %s -->\n", filename);
0295     exec_kernel_doc(vec);
0296     fflush(stdout);
0297     free(vec);
0298 }
0299 static void intfunc(char * filename) {  docfunctions(filename, NOFUNCTION); }
0300 static void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }
0301 
0302 /*
0303  * Document specific function(s) in a file.
0304  * Call kernel-doc with the following parameters:
0305  * kernel-doc -docbook -function function1 [-function function2]
0306  */
0307 static void singfunc(char * filename, char * line)
0308 {
0309     char *vec[200]; /* Enough for specific functions */
0310     int i, idx = 0;
0311     int startofsym = 1;
0312     vec[idx++] = KERNELDOC;
0313     vec[idx++] = KERNELDOC_FORMAT;
0314     vec[idx++] = SHOWNOTFOUND;
0315 
0316     /* Split line up in individual parameters preceded by FUNCTION */
0317     for (i=0; line[i]; i++) {
0318         if (isspace(line[i])) {
0319             line[i] = '\0';
0320             startofsym = 1;
0321             continue;
0322         }
0323         if (startofsym) {
0324             startofsym = 0;
0325             vec[idx++] = FUNCTION;
0326             vec[idx++] = &line[i];
0327         }
0328     }
0329     for (i = 0; i < idx; i++) {
0330         if (strcmp(vec[i], FUNCTION))
0331             continue;
0332         consume_symbol(vec[i + 1]);
0333     }
0334     vec[idx++] = filename;
0335     vec[idx] = NULL;
0336     exec_kernel_doc(vec);
0337 }
0338 
0339 /*
0340  * Insert specific documentation section from a file.
0341  * Call kernel-doc with the following parameters:
0342  * kernel-doc -docbook -function "doc section" filename
0343  */
0344 static void docsect(char *filename, char *line)
0345 {
0346     /* kerneldoc -docbook -show-not-found -function "section" file NULL */
0347     char *vec[7];
0348     char *s;
0349 
0350     for (s = line; *s; s++)
0351         if (*s == '\n')
0352             *s = '\0';
0353 
0354     if (asprintf(&s, "DOC: %s", line) < 0) {
0355         perror("asprintf");
0356         exit(1);
0357     }
0358     consume_symbol(s);
0359     free(s);
0360 
0361     vec[0] = KERNELDOC;
0362     vec[1] = KERNELDOC_FORMAT;
0363     vec[2] = SHOWNOTFOUND;
0364     vec[3] = FUNCTION;
0365     vec[4] = line;
0366     vec[5] = filename;
0367     vec[6] = NULL;
0368     exec_kernel_doc(vec);
0369 }
0370 
0371 static void find_all_symbols(char *filename)
0372 {
0373     char *vec[4]; /* kerneldoc -list file NULL */
0374     pid_t pid;
0375     int ret, i, count, start;
0376     char real_filename[PATH_MAX + 1];
0377     int pipefd[2];
0378     char *data, *str;
0379     size_t data_len = 0;
0380 
0381     vec[0] = KERNELDOC;
0382     vec[1] = LIST;
0383     vec[2] = filename;
0384     vec[3] = NULL;
0385 
0386     if (pipe(pipefd)) {
0387         perror("pipe");
0388         exit(1);
0389     }
0390 
0391     switch (pid=fork()) {
0392         case -1:
0393             perror("fork");
0394             exit(1);
0395         case  0:
0396             close(pipefd[0]);
0397             dup2(pipefd[1], 1);
0398             memset(real_filename, 0, sizeof(real_filename));
0399             strncat(real_filename, kernsrctree, PATH_MAX);
0400             strncat(real_filename, "/" KERNELDOCPATH KERNELDOC,
0401                     PATH_MAX - strlen(real_filename));
0402             execvp(real_filename, vec);
0403             fprintf(stderr, "exec ");
0404             perror(real_filename);
0405             exit(1);
0406         default:
0407             close(pipefd[1]);
0408             data = malloc(4096);
0409             do {
0410                 while ((ret = read(pipefd[0],
0411                            data + data_len,
0412                            4096)) > 0) {
0413                     data_len += ret;
0414                     data = realloc(data, data_len + 4096);
0415                 }
0416             } while (ret == -EAGAIN);
0417             if (ret != 0) {
0418                 perror("read");
0419                 exit(1);
0420             }
0421             waitpid(pid, &ret ,0);
0422     }
0423     if (WIFEXITED(ret))
0424         exitstatus |= WEXITSTATUS(ret);
0425     else
0426         exitstatus = 0xff;
0427 
0428     count = 0;
0429     /* poor man's strtok, but with counting */
0430     for (i = 0; i < data_len; i++) {
0431         if (data[i] == '\n') {
0432             count++;
0433             data[i] = '\0';
0434         }
0435     }
0436     start = all_list_len;
0437     all_list_len += count;
0438     all_list = realloc(all_list, sizeof(char *) * all_list_len);
0439     str = data;
0440     for (i = 0; i < data_len && start != all_list_len; i++) {
0441         if (data[i] == '\0') {
0442             all_list[start] = str;
0443             str = data + i + 1;
0444             start++;
0445         }
0446     }
0447 }
0448 
0449 /*
0450  * Terminate s at first space, if any. If there was a space, return pointer to
0451  * the character after that. Otherwise, return pointer to the terminating NUL.
0452  */
0453 static char *chomp(char *s)
0454 {
0455     while (*s && !isspace(*s))
0456         s++;
0457 
0458     if (*s)
0459         *s++ = '\0';
0460 
0461     return s;
0462 }
0463 
0464 /* Return pointer to directive content, or NULL if not a directive. */
0465 static char *is_directive(char *line)
0466 {
0467     if (file_format == FORMAT_DOCBOOK && line[0] == '!')
0468         return line + 1;
0469     else if (file_format == FORMAT_RST && !strncmp(line, ".. !", 4))
0470         return line + 4;
0471 
0472     return NULL;
0473 }
0474 
0475 /*
0476  * Parse file, calling action specific functions for:
0477  * 1) Lines containing !E
0478  * 2) Lines containing !I
0479  * 3) Lines containing !D
0480  * 4) Lines containing !F
0481  * 5) Lines containing !P
0482  * 6) Lines containing !C
0483  * 7) Default lines - lines not matching the above
0484  */
0485 static void parse_file(FILE *infile)
0486 {
0487     char line[MAXLINESZ];
0488     char *p, *s;
0489     while (fgets(line, MAXLINESZ, infile)) {
0490         p = is_directive(line);
0491         if (!p) {
0492             defaultline(line);
0493             continue;
0494         }
0495 
0496         switch (*p++) {
0497         case 'E':
0498             chomp(p);
0499             externalfunctions(p);
0500             break;
0501         case 'I':
0502             chomp(p);
0503             internalfunctions(p);
0504             break;
0505         case 'D':
0506             chomp(p);
0507             symbolsonly(p);
0508             break;
0509         case 'F':
0510             /* filename */
0511             s = chomp(p);
0512             /* function names */
0513             while (isspace(*s))
0514                 s++;
0515             singlefunctions(p, s);
0516             break;
0517         case 'P':
0518             /* filename */
0519             s = chomp(p);
0520             /* DOC: section name */
0521             while (isspace(*s))
0522                 s++;
0523             docsection(p, s);
0524             break;
0525         case 'C':
0526             chomp(p);
0527             if (findall)
0528                 findall(p);
0529             break;
0530         default:
0531             defaultline(line);
0532         }
0533     }
0534     fflush(stdout);
0535 }
0536 
0537 /*
0538  * Is this a RestructuredText template?  Answer the question by seeing if its
0539  * name ends in ".rst".
0540  */
0541 static int is_rst(const char *file)
0542 {
0543     char *dot = strrchr(file, '.');
0544 
0545     return dot && !strcmp(dot + 1, "rst");
0546 }
0547 
0548 enum opts {
0549     OPT_DOCBOOK,
0550     OPT_RST,
0551     OPT_HELP,
0552 };
0553 
0554 int main(int argc, char *argv[])
0555 {
0556     const char *subcommand, *filename;
0557     FILE * infile;
0558     int i;
0559 
0560     srctree = getenv("SRCTREE");
0561     if (!srctree)
0562         srctree = getcwd(NULL, 0);
0563     kernsrctree = getenv("KBUILD_SRC");
0564     if (!kernsrctree || !*kernsrctree)
0565         kernsrctree = srctree;
0566 
0567     for (;;) {
0568         int c;
0569         struct option opts[] = {
0570             { "docbook",    no_argument, NULL, OPT_DOCBOOK },
0571             { "rst",    no_argument, NULL, OPT_RST },
0572             { "help",   no_argument, NULL, OPT_HELP },
0573             {}
0574         };
0575 
0576         c = getopt_long_only(argc, argv, "", opts, NULL);
0577         if (c == -1)
0578             break;
0579 
0580         switch (c) {
0581         case OPT_DOCBOOK:
0582             file_format = FORMAT_DOCBOOK;
0583             break;
0584         case OPT_RST:
0585             file_format = FORMAT_RST;
0586             break;
0587         case OPT_HELP:
0588             usage();
0589             return 0;
0590         default:
0591         case '?':
0592             usage();
0593             return 1;
0594         }
0595     }
0596 
0597     argc -= optind;
0598     argv += optind;
0599 
0600     if (argc != 2) {
0601         usage();
0602         exit(1);
0603     }
0604 
0605     subcommand = argv[0];
0606     filename = argv[1];
0607 
0608     if (file_format == FORMAT_AUTO)
0609         file_format = is_rst(filename) ? FORMAT_RST : FORMAT_DOCBOOK;
0610 
0611     /* Open file, exit on error */
0612     infile = fopen(filename, "r");
0613     if (infile == NULL) {
0614         fprintf(stderr, "docproc: ");
0615         perror(filename);
0616         exit(2);
0617     }
0618 
0619     if (strcmp("doc", subcommand) == 0) {
0620         if (file_format == FORMAT_RST) {
0621             time_t t = time(NULL);
0622             printf(".. generated from %s by docproc %s\n",
0623                    filename, ctime(&t));
0624         }
0625 
0626         /* Need to do this in two passes.
0627          * First pass is used to collect all symbols exported
0628          * in the various files;
0629          * Second pass generate the documentation.
0630          * This is required because some functions are declared
0631          * and exported in different files :-((
0632          */
0633         /* Collect symbols */
0634         defaultline       = noaction;
0635         internalfunctions = find_export_symbols;
0636         externalfunctions = find_export_symbols;
0637         symbolsonly       = find_export_symbols;
0638         singlefunctions   = noaction2;
0639         docsection        = noaction2;
0640         findall           = find_all_symbols;
0641         parse_file(infile);
0642 
0643         /* Rewind to start from beginning of file again */
0644         fseek(infile, 0, SEEK_SET);
0645         defaultline       = printline;
0646         internalfunctions = intfunc;
0647         externalfunctions = extfunc;
0648         symbolsonly       = printline;
0649         singlefunctions   = singfunc;
0650         docsection        = docsect;
0651         findall           = NULL;
0652 
0653         parse_file(infile);
0654 
0655         for (i = 0; i < all_list_len; i++) {
0656             if (!all_list[i])
0657                 continue;
0658             fprintf(stderr, "Warning: didn't use docs for %s\n",
0659                 all_list[i]);
0660         }
0661     } else if (strcmp("depend", subcommand) == 0) {
0662         /* Create first part of dependency chain
0663          * file.tmpl */
0664         printf("%s\t", filename);
0665         defaultline       = noaction;
0666         internalfunctions = adddep;
0667         externalfunctions = adddep;
0668         symbolsonly       = adddep;
0669         singlefunctions   = adddep2;
0670         docsection        = adddep2;
0671         findall           = adddep;
0672         parse_file(infile);
0673         printf("\n");
0674     } else {
0675         fprintf(stderr, "Unknown option: %s\n", subcommand);
0676         exit(1);
0677     }
0678     fclose(infile);
0679     fflush(stdout);
0680     return exitstatus;
0681 }