1#include <assert.h>
2#include <ctype.h>
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "../../include/grubmenu.h"
9
10static int
11skip_line(FILE *infile) {
12    int c;
13
14    do {
15        c= fgetc(infile);
16        if(c < 0) return c;
17    } while(c != '\n');
18
19    return 0;
20}
21
22static int
23skip_whitespace(FILE *infile, int multiline) {
24    int c;
25    do {
26        c = fgetc(infile);
27        if(c < 0) return c;
28        if(!multiline && c == '\n') break;
29    } while(isspace(c));
30    ungetc(c, infile);
31
32    return 0;
33}
34
35static int
36peek(FILE *f) {
37    int c= fgetc(f);
38    if(c >= 0) ungetc(c, f);
39    return c;
40}
41
42#define MAXBUF 255
43
44static int
45read_token(FILE *infile, char *buf) {
46    int c= peek(infile);
47    if(c == EOF) return 0;
48    if(c < 0) return c;
49
50    while(isspace(c) || c == '#') {
51        skip_whitespace(infile, 1);
52        if(c == '#') skip_line(infile);
53        c= peek(infile);
54        if(c == EOF) return 0;
55        if(c < 0) return c;
56    }
57
58    size_t i= 0;
59    c= fgetc(infile);
60    while(!isspace(c)) {
61        assert(i < MAXBUF);
62        buf[i]= c;
63        i++;
64        c= fgetc(infile);
65        if(c == EOF) break;
66        if(c < 0) return c;
67    }
68    if(c >= 0) ungetc(c, infile);
69
70    buf[i]= '\0';
71    return i + 1;
72}
73
74static int
75read_line(FILE *infile, char *buf) {
76    int c;
77
78    int len= skip_whitespace(infile, 0);
79    if(len < 0) return len;
80
81    size_t i= 0;
82    do {
83        c= fgetc(infile);
84        if(c < 0) return c;
85        assert(i < MAXBUF);
86        if(c != '\n') {
87            buf[i]= c;
88            i++;
89        }
90    } while(c != '\n');
91
92    buf[i]= '\0';
93    return i + 1;
94}
95
96typedef int (*reader_t)(FILE *, char *);
97
98static int
99read_string(FILE *f, reader_t reader, char **bufptr) {
100    char buf[MAXBUF+1];
101
102    size_t len= reader(f, buf);
103    if(len < 0) return len;
104
105    *bufptr= malloc(len);
106    if(!*bufptr) {
107        perror("malloc");
108        return 0;
109    }
110
111    memcpy(*bufptr, buf, len);
112    return len;
113}
114
115struct menu_lst *
116read_menu_lst(const char *path) {
117    int len;
118
119    FILE *infile= fopen(path, "r");
120    if(!infile) {
121        perror("fopen");
122        return NULL;
123    }
124
125    struct menu_lst *menu= calloc(sizeof(struct menu_lst), 1);
126    if(!menu) {
127        perror("malloc");
128        return NULL;
129    }
130
131    char cmd[MAXBUF+1];
132    while((len= read_token(infile, cmd)) > 0) {
133        if(!strcmp(cmd, "title")) {
134            len= read_string(infile, read_token, &menu->title);
135            if(len <= 0) {
136                fprintf(stderr, "Missing title value.\n");
137                break;
138            }
139        }
140        else if(!strcmp(cmd, "kernel")) {
141            len= read_string(infile, read_token, &menu->kernel.path);
142            if(len <= 0) {
143                fprintf(stderr, "Missing kernel path.\n");
144                break;
145            }
146
147            len= read_string(infile, read_line, &menu->kernel.args);
148            if(len <= 0) {
149                fprintf(stderr, "Missing kernel arguments.\n");
150                break;
151            }
152        }
153        else if(!strcmp(cmd, "image")) {
154            len= read_string(infile, read_line, &menu->image);
155            if(len <= 0) {
156                fprintf(stderr, "Missing image specifier.\n");
157                break;
158            }
159        }
160        // handle "module" and "modulenounzip"
161        else if(!strncmp(cmd, "module", 6)) {
162            menu->nmodules++;
163
164            menu->modules=
165                realloc(menu->modules,
166                        menu->nmodules * sizeof(struct menu_module));
167            if(!menu->modules) {
168                perror("realloc");
169                return NULL;
170            }
171
172            struct menu_module *mod= &menu->modules[menu->nmodules - 1];
173
174            len= read_string(infile, read_token, &mod->path);
175            if(len <= 0) {
176                fprintf(stderr, "Missing module path.\n");
177                break;
178            }
179
180            len= read_string(infile, read_line, &mod->args);
181            if(len <= 0) {
182                fprintf(stderr, "Missing module arguments.\n");
183                break;
184            }
185        }
186        else if(!strcmp(cmd, "mmap")) {
187            menu->mmap_len++;
188
189            menu->mmap=
190                realloc(menu->mmap,
191                        menu->mmap_len * sizeof(struct menu_mmap_entry));
192            if(!menu->mmap) {
193                perror("realloc");
194                return NULL;
195            }
196
197            struct menu_mmap_entry *entry= &menu->mmap[menu->mmap_len - 1];
198
199            len= read_string(infile, read_token, &entry->name);
200            if(len <= 0) {
201                fprintf(stderr, "Missing MMAP entry name.\n");
202                break;
203            }
204
205            char buf[MAXBUF+1];
206            len= read_token(infile, buf);
207            if(len <= 0) {
208                fprintf(stderr, "Missing MMAP start address.\n");
209                break;
210            }
211            errno= 0;
212            entry->base= strtoull(buf, NULL, 0);
213            if(errno) {
214                fprintf(stderr, "Invalid MMAP start address.\n");
215                break;
216            }
217
218            len= read_token(infile, buf);
219            if(len <= 0) {
220                fprintf(stderr, "Missing MMAP length.\n");
221                break;
222            }
223            errno= 0;
224            entry->length= strtoull(buf, NULL, 0);
225            if(errno) {
226                fprintf(stderr, "Invalid MMAP length.\n");
227                break;
228            }
229
230            len= read_token(infile, buf);
231            if(len <= 0) {
232                fprintf(stderr, "Missing MMAP ID.\n");
233                break;
234            }
235            errno= 0;
236            entry->type= strtoul(buf, NULL, 0);
237            if(errno) {
238                fprintf(stderr, "Invalid MMAP ID.\n");
239                break;
240            }
241        }
242        else if(!strcmp(cmd, "timeout")) {
243            char buf[MAXBUF+1];
244            len= read_token(infile, buf);
245            if(len <= 0) {
246                fprintf(stderr, "Missing timeout value.\n");
247                break;
248            }
249            errno= 0;
250            menu->timeout= strtoul(buf, NULL, 0);
251            if(errno) {
252                fprintf(stderr, "Invalid timeout value.\n");
253                break;
254            }
255        }
256        else if(!strcmp(cmd, "bootdriver")) {
257            len= read_string(infile, read_token, &menu->boot_driver.path);
258            if(len <= 0) {
259                fprintf(stderr, "Missing timeout value.\n");
260                break;
261            }
262        }
263        else if(!strcmp(cmd, "cpudriver")) {
264            len= read_string(infile, read_token, &menu->kernel.path);
265            if(len <= 0) {
266                fprintf(stderr, "Missing timeout value.\n");
267                break;
268            }
269            len= read_string(infile, read_line, &menu->kernel.args);
270            if(len <= 0) {
271                fprintf(stderr, "Missing module arguments.\n");
272                break;
273            }
274        }
275        else if(!strcmp(cmd, "root")) {
276            char buf[MAXBUF+1];
277            len= read_token(infile, buf);
278            if(len <= 0) {
279                fprintf(stderr, "Missing timeout value.\n");
280                break;
281            }
282        }
283        else {
284            fprintf(stderr, "Unknown command: %s\n", cmd);
285        }
286    }
287    if(len < 0) {
288        perror("read_token");
289        return NULL;
290    }
291
292    if(fclose(infile)) {
293        perror("fclose");
294        return NULL;
295    }
296
297    return menu;
298}
299