linux/scripts/basic/docproc.c
<<
>>
Prefs
   1/*
   2 *      docproc is a simple preprocessor for the template files
   3 *      used as placeholders for the kernel internal documentation.
   4 *      docproc is used for documentation-frontend and
   5 *      dependency-generator.
   6 *      The two usages have in common that they require
   7 *      some knowledge of the .tmpl syntax, therefore they
   8 *      are kept together.
   9 *
  10 *      documentation-frontend
  11 *              Scans the template file and call kernel-doc for
  12 *              all occurrences of ![EIF]file
  13 *              Beforehand each referenced file is scanned for
  14 *              any symbols that are exported via these macros:
  15 *                      EXPORT_SYMBOL(), EXPORT_SYMBOL_GPL(), &
  16 *                      EXPORT_SYMBOL_GPL_FUTURE()
  17 *              This is used to create proper -function and
  18 *              -nofunction arguments in calls to kernel-doc.
  19 *              Usage: docproc doc file.tmpl
  20 *
  21 *      dependency-generator:
  22 *              Scans the template file and list all files
  23 *              referenced in a format recognized by make.
  24 *              Usage:  docproc depend file.tmpl
  25 *              Writes dependency information to stdout
  26 *              in the following format:
  27 *              file.tmpl src.c src2.c
  28 *              The filenames are obtained from the following constructs:
  29 *              !Efilename
  30 *              !Ifilename
  31 *              !Dfilename
  32 *              !Ffilename
  33 *              !Pfilename
  34 *
  35 */
  36
  37#include <stdio.h>
  38#include <stdlib.h>
  39#include <string.h>
  40#include <ctype.h>
  41#include <unistd.h>
  42#include <limits.h>
  43#include <sys/types.h>
  44#include <sys/wait.h>
  45
  46/* exitstatus is used to keep track of any failing calls to kernel-doc,
  47 * but execution continues. */
  48int exitstatus = 0;
  49
  50typedef void DFL(char *);
  51DFL *defaultline;
  52
  53typedef void FILEONLY(char * file);
  54FILEONLY *internalfunctions;
  55FILEONLY *externalfunctions;
  56FILEONLY *symbolsonly;
  57
  58typedef void FILELINE(char * file, char * line);
  59FILELINE * singlefunctions;
  60FILELINE * entity_system;
  61FILELINE * docsection;
  62
  63#define MAXLINESZ     2048
  64#define MAXFILES      250
  65#define KERNELDOCPATH "scripts/"
  66#define KERNELDOC     "kernel-doc"
  67#define DOCBOOK       "-docbook"
  68#define FUNCTION      "-function"
  69#define NOFUNCTION    "-nofunction"
  70#define NODOCSECTIONS "-no-doc-sections"
  71
  72char *srctree;
  73
  74void usage (void)
  75{
  76        fprintf(stderr, "Usage: docproc {doc|depend} file\n");
  77        fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
  78        fprintf(stderr, "doc: frontend when generating kernel documentation\n");
  79        fprintf(stderr, "depend: generate list of files referenced within file\n");
  80        fprintf(stderr, "Environment variable SRCTREE: absolute path to kernel source tree.\n");
  81}
  82
  83/*
  84 * Execute kernel-doc with parameters given in svec
  85 */
  86void exec_kernel_doc(char **svec)
  87{
  88        pid_t pid;
  89        int ret;
  90        char real_filename[PATH_MAX + 1];
  91        /* Make sure output generated so far are flushed */
  92        fflush(stdout);
  93        switch (pid=fork()) {
  94                case -1:
  95                        perror("fork");
  96                        exit(1);
  97                case  0:
  98                        memset(real_filename, 0, sizeof(real_filename));
  99                        strncat(real_filename, srctree, PATH_MAX);
 100                        strncat(real_filename, KERNELDOCPATH KERNELDOC,
 101                                        PATH_MAX - strlen(real_filename));
 102                        execvp(real_filename, svec);
 103                        fprintf(stderr, "exec ");
 104                        perror(real_filename);
 105                        exit(1);
 106                default:
 107                        waitpid(pid, &ret ,0);
 108        }
 109        if (WIFEXITED(ret))
 110                exitstatus |= WEXITSTATUS(ret);
 111        else
 112                exitstatus = 0xff;
 113}
 114
 115/* Types used to create list of all exported symbols in a number of files */
 116struct symbols
 117{
 118        char *name;
 119};
 120
 121struct symfile
 122{
 123        char *filename;
 124        struct symbols *symbollist;
 125        int symbolcnt;
 126};
 127
 128struct symfile symfilelist[MAXFILES];
 129int symfilecnt = 0;
 130
 131void add_new_symbol(struct symfile *sym, char * symname)
 132{
 133        sym->symbollist =
 134          realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
 135        sym->symbollist[sym->symbolcnt++].name = strdup(symname);
 136}
 137
 138/* Add a filename to the list */
 139struct symfile * add_new_file(char * filename)
 140{
 141        symfilelist[symfilecnt++].filename = strdup(filename);
 142        return &symfilelist[symfilecnt - 1];
 143}
 144
 145/* Check if file already are present in the list */
 146struct symfile * filename_exist(char * filename)
 147{
 148        int i;
 149        for (i=0; i < symfilecnt; i++)
 150                if (strcmp(symfilelist[i].filename, filename) == 0)
 151                        return &symfilelist[i];
 152        return NULL;
 153}
 154
 155/*
 156 * List all files referenced within the template file.
 157 * Files are separated by tabs.
 158 */
 159void adddep(char * file)                   { printf("\t%s", file); }
 160void adddep2(char * file, char * line)     { line = line; adddep(file); }
 161void noaction(char * line)                 { line = line; }
 162void noaction2(char * file, char * line)   { file = file; line = line; }
 163
 164/* Echo the line without further action */
 165void printline(char * line)               { printf("%s", line); }
 166
 167/*
 168 * Find all symbols in filename that are exported with EXPORT_SYMBOL &
 169 * EXPORT_SYMBOL_GPL (& EXPORT_SYMBOL_GPL_FUTURE implicitly).
 170 * All symbols located are stored in symfilelist.
 171 */
 172void find_export_symbols(char * filename)
 173{
 174        FILE * fp;
 175        struct symfile *sym;
 176        char line[MAXLINESZ];
 177        if (filename_exist(filename) == NULL) {
 178                char real_filename[PATH_MAX + 1];
 179                memset(real_filename, 0, sizeof(real_filename));
 180                strncat(real_filename, srctree, PATH_MAX);
 181                strncat(real_filename, filename,
 182                                PATH_MAX - strlen(real_filename));
 183                sym = add_new_file(filename);
 184                fp = fopen(real_filename, "r");
 185                if (fp == NULL)
 186                {
 187                        fprintf(stderr, "docproc: ");
 188                        perror(real_filename);
 189                        exit(1);
 190                }
 191                while (fgets(line, MAXLINESZ, fp)) {
 192                        char *p;
 193                        char *e;
 194                        if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != NULL) ||
 195                            ((p = strstr(line, "EXPORT_SYMBOL")) != NULL)) {
 196                                /* Skip EXPORT_SYMBOL{_GPL} */
 197                                while (isalnum(*p) || *p == '_')
 198                                        p++;
 199                                /* Remove parentheses & additional whitespace */
 200                                while (isspace(*p))
 201                                        p++;
 202                                if (*p != '(')
 203                                        continue; /* Syntax error? */
 204                                else
 205                                        p++;
 206                                while (isspace(*p))
 207                                        p++;
 208                                e = p;
 209                                while (isalnum(*e) || *e == '_')
 210                                        e++;
 211                                *e = '\0';
 212                                add_new_symbol(sym, p);
 213                        }
 214                }
 215                fclose(fp);
 216        }
 217}
 218
 219/*
 220 * Document all external or internal functions in a file.
 221 * Call kernel-doc with following parameters:
 222 * kernel-doc -docbook -nofunction function_name1 filename
 223 * Function names are obtained from all the src files
 224 * by find_export_symbols.
 225 * intfunc uses -nofunction
 226 * extfunc uses -function
 227 */
 228void docfunctions(char * filename, char * type)
 229{
 230        int i,j;
 231        int symcnt = 0;
 232        int idx = 0;
 233        char **vec;
 234
 235        for (i=0; i <= symfilecnt; i++)
 236                symcnt += symfilelist[i].symbolcnt;
 237        vec = malloc((2 + 2 * symcnt + 3) * sizeof(char *));
 238        if (vec == NULL) {
 239                perror("docproc: ");
 240                exit(1);
 241        }
 242        vec[idx++] = KERNELDOC;
 243        vec[idx++] = DOCBOOK;
 244        vec[idx++] = NODOCSECTIONS;
 245        for (i=0; i < symfilecnt; i++) {
 246                struct symfile * sym = &symfilelist[i];
 247                for (j=0; j < sym->symbolcnt; j++) {
 248                        vec[idx++]     = type;
 249                        vec[idx++] = sym->symbollist[j].name;
 250                }
 251        }
 252        vec[idx++]     = filename;
 253        vec[idx] = NULL;
 254        printf("<!-- %s -->\n", filename);
 255        exec_kernel_doc(vec);
 256        fflush(stdout);
 257        free(vec);
 258}
 259void intfunc(char * filename) { docfunctions(filename, NOFUNCTION); }
 260void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }
 261
 262/*
 263 * Document specific function(s) in a file.
 264 * Call kernel-doc with the following parameters:
 265 * kernel-doc -docbook -function function1 [-function function2]
 266 */
 267void singfunc(char * filename, char * line)
 268{
 269        char *vec[200]; /* Enough for specific functions */
 270        int i, idx = 0;
 271        int startofsym = 1;
 272        vec[idx++] = KERNELDOC;
 273        vec[idx++] = DOCBOOK;
 274
 275        /* Split line up in individual parameters preceded by FUNCTION */
 276        for (i=0; line[i]; i++) {
 277                if (isspace(line[i])) {
 278                        line[i] = '\0';
 279                        startofsym = 1;
 280                        continue;
 281                }
 282                if (startofsym) {
 283                        startofsym = 0;
 284                        vec[idx++] = FUNCTION;
 285                        vec[idx++] = &line[i];
 286                }
 287        }
 288        vec[idx++] = filename;
 289        vec[idx] = NULL;
 290        exec_kernel_doc(vec);
 291}
 292
 293/*
 294 * Insert specific documentation section from a file.
 295 * Call kernel-doc with the following parameters:
 296 * kernel-doc -docbook -function "doc section" filename
 297 */
 298void docsect(char *filename, char *line)
 299{
 300        char *vec[6]; /* kerneldoc -docbook -function "section" file NULL */
 301        char *s;
 302
 303        for (s = line; *s; s++)
 304                if (*s == '\n')
 305                        *s = '\0';
 306
 307        vec[0] = KERNELDOC;
 308        vec[1] = DOCBOOK;
 309        vec[2] = FUNCTION;
 310        vec[3] = line;
 311        vec[4] = filename;
 312        vec[5] = NULL;
 313        exec_kernel_doc(vec);
 314}
 315
 316/*
 317 * Parse file, calling action specific functions for:
 318 * 1) Lines containing !E
 319 * 2) Lines containing !I
 320 * 3) Lines containing !D
 321 * 4) Lines containing !F
 322 * 5) Lines containing !P
 323 * 6) Default lines - lines not matching the above
 324 */
 325void parse_file(FILE *infile)
 326{
 327        char line[MAXLINESZ];
 328        char * s;
 329        while (fgets(line, MAXLINESZ, infile)) {
 330                if (line[0] == '!') {
 331                        s = line + 2;
 332                        switch (line[1]) {
 333                                case 'E':
 334                                        while (*s && !isspace(*s)) s++;
 335                                        *s = '\0';
 336                                        externalfunctions(line+2);
 337                                        break;
 338                                case 'I':
 339                                        while (*s && !isspace(*s)) s++;
 340                                        *s = '\0';
 341                                        internalfunctions(line+2);
 342                                        break;
 343                                case 'D':
 344                                        while (*s && !isspace(*s)) s++;
 345                                        *s = '\0';
 346                                        symbolsonly(line+2);
 347                                        break;
 348                                case 'F':
 349                                        /* filename */
 350                                        while (*s && !isspace(*s)) s++;
 351                                        *s++ = '\0';
 352                                        /* function names */
 353                                        while (isspace(*s))
 354                                                s++;
 355                                        singlefunctions(line +2, s);
 356                                        break;
 357                                case 'P':
 358                                        /* filename */
 359                                        while (*s && !isspace(*s)) s++;
 360                                        *s++ = '\0';
 361                                        /* DOC: section name */
 362                                        while (isspace(*s))
 363                                                s++;
 364                                        docsection(line + 2, s);
 365                                        break;
 366                                default:
 367                                        defaultline(line);
 368                        }
 369                }
 370                else {
 371                        defaultline(line);
 372                }
 373        }
 374        fflush(stdout);
 375}
 376
 377
 378int main(int argc, char *argv[])
 379{
 380        FILE * infile;
 381
 382        srctree = getenv("SRCTREE");
 383        if (!srctree)
 384                srctree = getcwd(NULL, 0);
 385        if (argc != 3) {
 386                usage();
 387                exit(1);
 388        }
 389        /* Open file, exit on error */
 390        infile = fopen(argv[2], "r");
 391        if (infile == NULL) {
 392                fprintf(stderr, "docproc: ");
 393                perror(argv[2]);
 394                exit(2);
 395        }
 396
 397        if (strcmp("doc", argv[1]) == 0)
 398        {
 399                /* Need to do this in two passes.
 400                 * First pass is used to collect all symbols exported
 401                 * in the various files;
 402                 * Second pass generate the documentation.
 403                 * This is required because some functions are declared
 404                 * and exported in different files :-((
 405                 */
 406                /* Collect symbols */
 407                defaultline       = noaction;
 408                internalfunctions = find_export_symbols;
 409                externalfunctions = find_export_symbols;
 410                symbolsonly       = find_export_symbols;
 411                singlefunctions   = noaction2;
 412                docsection        = noaction2;
 413                parse_file(infile);
 414
 415                /* Rewind to start from beginning of file again */
 416                fseek(infile, 0, SEEK_SET);
 417                defaultline       = printline;
 418                internalfunctions = intfunc;
 419                externalfunctions = extfunc;
 420                symbolsonly       = printline;
 421                singlefunctions   = singfunc;
 422                docsection        = docsect;
 423
 424                parse_file(infile);
 425        }
 426        else if (strcmp("depend", argv[1]) == 0)
 427        {
 428                /* Create first part of dependency chain
 429                 * file.tmpl */
 430                printf("%s\t", argv[2]);
 431                defaultline       = noaction;
 432                internalfunctions = adddep;
 433                externalfunctions = adddep;
 434                symbolsonly       = adddep;
 435                singlefunctions   = adddep2;
 436                docsection        = adddep2;
 437                parse_file(infile);
 438                printf("\n");
 439        }
 440        else
 441        {
 442                fprintf(stderr, "Unknown option: %s\n", argv[1]);
 443                exit(1);
 444        }
 445        fclose(infile);
 446        fflush(stdout);
 447        return exitstatus;
 448}
 449