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