X-Git-Url: http://git.thejh.net/?p=moctel.git;a=blobdiff_plain;f=gen_method_list.c;fp=gen_method_list.c;h=94572f80e0a1c6ecd024fb9829d7d89d438e4652;hp=0000000000000000000000000000000000000000;hb=73d712f1a25728984f7f9b81d7c3d02694648b4f;hpb=d20cca0688694acb09484232ada3aa4d13ac0954 diff --git a/gen_method_list.c b/gen_method_list.c new file mode 100644 index 0000000..94572f8 --- /dev/null +++ b/gen_method_list.c @@ -0,0 +1,177 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int out_fd; +char outbuf[10000]; +int outbuf_i = 0; + +void clear_out(void) { + if (write(out_fd, outbuf, outbuf_i) != outbuf_i) { + fprintf(stderr, "write to outfile did not succeed\n"); + exit(1); + } + outbuf_i = 0; +} + +void write_out(char *buf) { + size_t len = strlen(buf); + assert(len <= 500); + memcpy(outbuf+outbuf_i, buf, len); + outbuf_i += len; + if (outbuf_i > 9500) clear_out(); +} + +/* Maps a textfile into RAM. Returns a pointer to a read-only memory + area containing the 0-terminated data. len includes the nullbyte. + Free it using munmap. */ +char *map_textfile(char *path, size_t *len) { + int fd = open(path, O_RDONLY); + if (fd == -1) fprintf(stderr, "can't open %s: %m\n", path), exit(1); + + struct stat st; + if (fstat(fd, &st)) fprintf(stderr, "can't stat %s: %m\n", path), exit(1); + *len = st.st_size; + + char *data = mmap(NULL, *len+1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (data == MAP_FAILED) fprintf(stderr, "can't mmap %s: %m\n", path), exit(1); + + close(fd); + + if ((*len & 4095) == 0) { + // TODO: is this ok? does the previous mmap make sure that that address exists? + void *nulls_addr = mmap(data+*len, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + if (nulls_addr != data+*len) + fprintf(stderr, "the mmap trick for 0-termination failed :(\n"), exit(1); + *len += 1; + } + + return data; +} + +bool isichar(char c) { + return (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') || c=='_'; +} + +void handle_file(char *name, char *path) { + size_t data_len; + char *data = map_textfile(name, &data_len); + + bool wrote_path = false; + + /* look for stuff that looks like a method definition. this means: + * - line starts with a non-whitespace + * - there is a token (the name) followed by an opening paren + * - the next { is earlier than the next ; + */ + char *s = data; + while (1) { + char *lineend = strchr(s, '\n'); + if (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\0') goto next; + if (lineend) *lineend = '\0'; + char *paren = strchr(s, '('); + if (paren == NULL) goto next; + *lineend = '\n'; + char *next_curly = strchr(paren, '{'); + if (next_curly == NULL) goto next; + char *next_semicolon = strchr(paren, ';'); + if (next_semicolon != NULL && next_semicolon < next_curly) + goto next; /* declaration, not definition */ + char *nameend = paren; + while (nameend>s+1 && !isichar(nameend[-1])) nameend--; /* let nameend point behind the name */ + *nameend = '\0'; + char *name = nameend-1; + while (name>s && isichar(name[-1])) name--; + if (!wrote_path) { + write_out(path); + write_out(":"); + wrote_path = true; + } else { + write_out(","); + } + write_out(name); + +next: + if (!lineend) break; + s = lineend+1; + } + + munmap(data, data_len); + write_out("\n"); +} + +// Recursive code dir traversal function +void run(char *path) { + DIR *dir = opendir("."); + if (dir == NULL) perror("can't open ."), exit(1); + + struct dirent dent_buf; + struct dirent *cur_ent = NULL; + while (1) { + readdir_r(dir, &dent_buf, &cur_ent); + if (cur_ent == NULL) break; + if (cur_ent->d_name[0] == '.') continue; /* ., .. or hidden */ + + if (cur_ent->d_type == DT_UNKNOWN) { + fprintf(stderr, "your fs returned DT_UNKNOWN in readdir. it's allowed, but dumb." + "\nhandling that case would bloat the code, so... fix it if you need it.\n"); + exit(1); + } + + if (cur_ent->d_type == DT_DIR) { + if (strcmp(cur_ent->d_name, "debian") == 0) continue; + if (chdir(cur_ent->d_name)) + fprintf(stderr, "can't chdir into %s: %m\n", cur_ent->d_name), exit(1); + char *subpath = NULL; + asprintf(&subpath, "%s%s/", path, cur_ent->d_name); + if (subpath == NULL) + fprintf(stderr, "can't create subpath\n"), exit(1); + run(subpath); + free(subpath); + if (fchdir(dirfd(dir))) perror("can't chdir back"), exit(1); + } + + if (cur_ent->d_type == DT_REG) { + char *dot = strrchr(cur_ent->d_name, '.'); + if (dot == NULL) continue; + if (dot[1] != 'c' || dot[2] != '\0') continue; + // it's a C source file + char *subpath = NULL; + asprintf(&subpath, "%s%s", path, cur_ent->d_name); + if (subpath == NULL) + fprintf(stderr, "can't create subpath\n"), exit(1); + handle_file(cur_ent->d_name, subpath); + free(subpath); + } + } + + closedir(dir); +} + +int main(int argc, char **argv) { + if (argc != 3) { + fputs("bad invocation: want ./gen_method_list ", stderr); + exit(1); + } + char *kernel_src_path = argv[1]; + char *out_path = argv[2]; + + out_fd = open(out_path, O_WRONLY|O_CREAT|O_EXCL, 0755); + if (out_fd == -1) fprintf(stderr, "can't create outfile at %s: %m\n", out_path), exit(1); + + if (chdir(kernel_src_path)) + fprintf(stderr, "can't access kernel sources at %s: %m\n", kernel_src_path), exit(1); + + run(""); + clear_out(); +}