19 void clear_out(void) {
20 if (write(out_fd, outbuf, outbuf_i) != outbuf_i) {
21 fprintf(stderr, "write to outfile did not succeed\n");
27 void write_out(char *buf) {
28 size_t len = strlen(buf);
30 memcpy(outbuf+outbuf_i, buf, len);
32 if (outbuf_i > 9500) clear_out();
35 /* Maps a textfile into RAM. Returns a pointer to a read-only memory
36 area containing the 0-terminated data. len includes the nullbyte.
37 Free it using munmap. */
38 char *map_textfile(char *path, size_t *len) {
39 int fd = open(path, O_RDONLY);
40 if (fd == -1) fprintf(stderr, "can't open %s: %m\n", path), exit(1);
43 if (fstat(fd, &st)) fprintf(stderr, "can't stat %s: %m\n", path), exit(1);
46 char *data = mmap(NULL, *len+1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
47 if (data == MAP_FAILED) fprintf(stderr, "can't mmap %s: %m\n", path), exit(1);
51 if ((*len & 4095) == 0) {
52 // TODO: is this ok? does the previous mmap make sure that that address exists?
53 void *nulls_addr = mmap(data+*len, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
54 if (nulls_addr != data+*len)
55 fprintf(stderr, "the mmap trick for 0-termination failed :(\n"), exit(1);
62 bool isichar(char c) {
63 return (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') || c=='_';
66 void handle_file(char *name, char *path) {
68 char *data = map_textfile(name, &data_len);
70 bool wrote_path = false;
72 /* look for stuff that looks like a method definition. this means:
73 * - line starts with a non-whitespace
74 * - there is a token (the name) followed by an opening paren
75 * - the next { is earlier than the next ;
79 char *lineend = strchr(s, '\n');
80 if (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\0') goto next;
81 if (lineend) *lineend = '\0';
82 char *paren = strchr(s, '(');
83 if (paren == NULL) goto next;
85 char *next_curly = strchr(paren, '{');
86 if (next_curly == NULL) goto next;
87 char *next_semicolon = strchr(paren, ';');
88 if (next_semicolon != NULL && next_semicolon < next_curly)
89 goto next; /* declaration, not definition */
90 char *nameend = paren;
91 while (nameend>s+1 && !isichar(nameend[-1])) nameend--; /* let nameend point behind the name */
93 char *name = nameend-1;
94 while (name>s && isichar(name[-1])) name--;
109 munmap(data, data_len);
113 // Recursive code dir traversal function
114 void run(char *path) {
115 DIR *dir = opendir(".");
116 if (dir == NULL) perror("can't open ."), exit(1);
118 struct dirent dent_buf;
119 struct dirent *cur_ent = NULL;
121 readdir_r(dir, &dent_buf, &cur_ent);
122 if (cur_ent == NULL) break;
123 if (cur_ent->d_name[0] == '.') continue; /* ., .. or hidden */
125 if (cur_ent->d_type == DT_UNKNOWN) {
126 fprintf(stderr, "your fs returned DT_UNKNOWN in readdir. it's allowed, but dumb."
127 "\nhandling that case would bloat the code, so... fix it if you need it.\n");
131 if (cur_ent->d_type == DT_DIR) {
132 if (strcmp(cur_ent->d_name, "debian") == 0) continue;
133 if (chdir(cur_ent->d_name))
134 fprintf(stderr, "can't chdir into %s: %m\n", cur_ent->d_name), exit(1);
135 char *subpath = NULL;
136 asprintf(&subpath, "%s%s/", path, cur_ent->d_name);
138 fprintf(stderr, "can't create subpath\n"), exit(1);
141 if (fchdir(dirfd(dir))) perror("can't chdir back"), exit(1);
144 if (cur_ent->d_type == DT_REG) {
145 char *dot = strrchr(cur_ent->d_name, '.');
146 if (dot == NULL) continue;
147 if (dot[1] != 'c' || dot[2] != '\0') continue;
148 // it's a C source file
149 char *subpath = NULL;
150 asprintf(&subpath, "%s%s", path, cur_ent->d_name);
152 fprintf(stderr, "can't create subpath\n"), exit(1);
153 handle_file(cur_ent->d_name, subpath);
161 int main(int argc, char **argv) {
163 fputs("bad invocation: want ./gen_method_list <kernel_src> <outpath>", stderr);
166 char *kernel_src_path = argv[1];
167 char *out_path = argv[2];
169 out_fd = open(out_path, O_WRONLY|O_CREAT|O_EXCL, 0755);
170 if (out_fd == -1) fprintf(stderr, "can't create outfile at %s: %m\n", out_path), exit(1);
172 if (chdir(kernel_src_path))
173 fprintf(stderr, "can't access kernel sources at %s: %m\n", kernel_src_path), exit(1);