1/* Parse a Hagfish configuration file - which in turn is roughly a GRUB 2 * menu.lst. */ 3 4#include <assert.h> 5#include <ctype.h> 6#include <errno.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10 11#include "config.h" 12 13static inline int 14iscomment(char c) { 15 return c == '#'; 16} 17 18static inline int 19istoken(char c) { 20 return !isspace(c) && !iscomment(c); 21} 22 23static size_t 24skip_whitespace(const char *buf, size_t size, size_t start, int skip_newlines) { 25 assert(start < size); 26 size_t i; 27 28 for(i= start; 29 i < size && ((isspace(buf[i]) && buf[i] != '\n') || 30 (skip_newlines && buf[i] == '\n')); 31 i++); 32 33 assert(start <= i); 34 assert(i <= size); 35 assert(i == size || 36 !isspace(buf[i]) || 37 (!skip_newlines && buf[i] == '\n')); 38 return i; 39} 40 41static size_t 42find_eol(const char *buf, size_t size, size_t start) { 43 assert(start < size); 44 size_t i; 45 46 for(i= start; i < size && buf[i] != '\n'; i++); 47 48 assert(start <= i); 49 assert(i <= size); 50 assert(i == size || buf[i] == '\n'); 51 return i; 52} 53 54static size_t 55find_token(const char *buf, size_t size, size_t start, int skip_newlines) { 56 assert(start < size); 57 size_t i= start; 58 59 while(i < size && !istoken(buf[i])) { 60 if(isspace(buf[i])) { 61 /* Skip whitespace. */ 62 i= skip_whitespace(buf, size, i, skip_newlines); 63 } 64 else { 65 /* Find the newline. */ 66 i= find_eol(buf, size, i); 67 /* Skip over it, if not at EOF. */ 68 if(i < size) i++; 69 } 70 } 71 72 assert(start <= i); 73 assert(i <= size); 74 assert(i == size || istoken(buf[i])); 75 return i; 76} 77 78static size_t 79get_token(const char *buf, size_t size, size_t start) { 80 assert(start < size); 81 assert(istoken(buf[start])); 82 size_t i; 83 84 for(i= start; i < size && istoken(buf[i]); i++); 85 86 assert(start < i); 87 assert(i <= size); 88 assert(istoken(buf[i-1])); 89 return i; 90} 91 92static int 93get_cmdline(const char *buf, size_t size, size_t *cursor, 94 size_t *cstart, size_t *clen, size_t *astart, size_t *alen) { 95 assert(*cursor < size); 96 *cursor= find_token(buf, size, *cursor, 0); 97 if(!istoken(buf[*cursor])) { 98 fprintf(stderr, "Missing command line\n"); 99 return 0; 100 } 101 *astart= *cstart= *cursor; /* Path starts here. */ 102 *cursor= get_token(buf, size, *cursor); 103 *clen= *cursor - *cstart; /* Path ends here. */ 104 assert(*clen <= size - *cstart); 105 *cursor= find_eol(buf, size, *cursor); 106 *alen= *cursor - *astart; 107 assert(*alen <= size - *astart); /* Arguments end here. */ 108 109 return 1; 110} 111 112struct config * 113parse_config(char *buf, size_t size) { 114 size_t cursor= 0; 115 struct config *cfg; 116 117 cfg= calloc(1, sizeof(struct config)); 118 if(!cfg) { 119 fprintf(stderr, "calloc: %s\n", strerror(errno)); 120 goto parse_fail; 121 } 122 cfg->buf= buf; 123 cfg->stack_size= DEFAULT_STACK_SIZE; 124 125 while(cursor < size) { 126 cursor= find_token(buf, size, cursor, 1); 127 if(cursor < size) { 128 size_t tstart= cursor, tlen; 129 130 assert(istoken(buf[cursor])); 131 cursor= get_token(buf, size, cursor); 132 tlen= cursor - tstart; 133 assert(tlen <= size - cursor); 134 135 if(!strncmp("title", buf+tstart, 5)) { 136 /* Ignore the title. */ 137 assert(cursor < size); 138 cursor= find_eol(buf, size, cursor); 139 } 140 else if(!strncmp("stack", buf+tstart, 5)) { 141 char arg[10]; 142 size_t astart, alen; 143 144 cursor= skip_whitespace(buf, size, cursor, 0); 145 if(!istoken(buf[cursor])) { 146 fprintf(stderr, "Expected stack size\n"); 147 goto parse_fail; 148 } 149 astart= cursor; 150 151 cursor= get_token(buf, size, cursor); 152 alen= cursor - astart; 153 assert(alen <= size - cursor); 154 155 if(alen > 9) { 156 fprintf(stderr, "Stack size field too long\n"); 157 goto parse_fail; 158 } 159 160 memcpy(arg, buf+astart, alen); 161 arg[alen]= '\0'; 162 cfg->stack_size= strtoul(arg, NULL, 10); 163 } 164 else if(!strncmp("kernel", buf+tstart, 6)) { 165 if(cfg->kernel) { 166 fprintf(stderr, "Kernel defined twice\n"); 167 goto parse_fail; 168 } 169 170 cfg->kernel= calloc(1, sizeof(struct component_config)); 171 if(!cfg->kernel) { 172 fprintf(stderr, "calloc: %s\n", strerror(errno)); 173 goto parse_fail; 174 } 175 176 /* Grab the command line. */ 177 if(!get_cmdline(buf, size, &cursor, 178 &cfg->kernel->path_start, 179 &cfg->kernel->path_len, 180 &cfg->kernel->args_start, 181 &cfg->kernel->args_len)) 182 goto parse_fail; 183 } 184 else if(!strncmp("module", buf+tstart, 6)) { 185 struct component_config *module= 186 calloc(1, sizeof(struct component_config)); 187 if(!module) { 188 fprintf(stderr, "calloc, %s\n", strerror(errno)); 189 goto parse_fail; 190 } 191 192 /* Grab the command line. */ 193 if(!get_cmdline(buf, size, &cursor, 194 &module->path_start, 195 &module->path_len, 196 &module->args_start, 197 &module->args_len)) 198 goto parse_fail; 199 200 if(cfg->first_module) { 201 assert(cfg->last_module); 202 cfg->last_module->next= module; 203 cfg->last_module= module; 204 } 205 else { 206 assert(!cfg->last_module); 207 cfg->first_module= module; 208 cfg->last_module= module; 209 } 210 } 211 else { 212 fprintf(stderr, 213 "Unrecognised entry \"%.*s\", skipping line.\n", 214 (int)tlen, buf + tstart); 215 cursor= find_eol(buf, size, cursor); 216 } 217 } 218 } 219 220 if(!cfg->kernel) { 221 fprintf(stderr, "No kernel image specified\n"); 222 goto parse_fail; 223 } 224 225 return cfg; 226 227parse_fail: 228 if(cfg) { 229 if(cfg->kernel) free(cfg->kernel); 230 231 struct component_config *cmp= cfg->first_module; 232 while(cmp) { 233 struct component_config *next= cmp->next; 234 free(cmp); 235 cmp= next; 236 } 237 238 free(cfg); 239 } 240 return NULL; 241} 242