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