1 /* 2 * Copyright 2016 Michael Müller 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdarg.h> 20 #include <string.h> 21 22 #include "windef.h" 23 #include "winbase.h" 24 #include "winuser.h" 25 26 #include "inseng_private.h" 27 28 #include "wine/list.h" 29 30 struct inf_value 31 { 32 struct list entry; 33 char *key; 34 char *value; 35 36 struct inf_section *section; 37 }; 38 39 struct inf_section 40 { 41 struct list entry; 42 char *name; 43 struct list values; 44 45 struct inf_file *file; 46 }; 47 48 struct inf_file 49 { 50 char *content; 51 DWORD size; 52 struct list sections; 53 }; 54 55 static void inf_value_free(struct inf_value *value) 56 { 57 heap_free(value); 58 } 59 60 static void inf_section_free(struct inf_section *section) 61 { 62 struct inf_value *val, *val_next; 63 LIST_FOR_EACH_ENTRY_SAFE(val, val_next, §ion->values, struct inf_value, entry) 64 { 65 list_remove(&val->entry); 66 inf_value_free(val); 67 } 68 69 heap_free(section); 70 } 71 72 static const char *get_substitution(struct inf_file *inf, const char *name, int len) 73 { 74 struct inf_section *sec; 75 struct inf_value *value = NULL; 76 77 sec = inf_get_section(inf, "Strings"); 78 if (!sec) return NULL; 79 80 while (inf_section_next_value(sec, &value)) 81 { 82 if (strlen(value->key) == len && !strncasecmp(value->key, name, len)) 83 return value->value; 84 } 85 86 return NULL; 87 } 88 89 static int expand_variables_buffer(struct inf_file *inf, const char *str, char *output) 90 { 91 const char *p, *var_start = NULL; 92 int var_len = 0, len = 0; 93 const char *substitution; 94 95 for (p = str; *p; p++) 96 { 97 if (*p != '%') 98 { 99 if (var_start) 100 var_len++; 101 else 102 { 103 if (output) 104 *output++ = *p; 105 len++; 106 } 107 108 continue; 109 } 110 111 if (!var_start) 112 { 113 var_start = p; 114 var_len = 0; 115 116 continue; 117 } 118 119 if (!var_len) 120 { 121 /* just an escaped % */ 122 if (output) 123 *output++ = '%'; 124 len += 1; 125 126 var_start = NULL; 127 continue; 128 } 129 130 substitution = get_substitution(inf, var_start + 1, var_len); 131 if (!substitution) 132 { 133 if (output) 134 { 135 memcpy(output, var_start, var_len + 2); 136 output += var_len + 2; 137 } 138 len += var_len + 2; 139 } 140 else 141 { 142 int sub_len = strlen(substitution); 143 144 if (output) 145 { 146 memcpy(output, substitution, sub_len); 147 output += sub_len; 148 } 149 len += sub_len; 150 } 151 152 var_start = NULL; 153 } 154 155 if (output) *output = 0; 156 return len + 1; 157 } 158 159 static char *expand_variables(struct inf_file *inf, const char *str) 160 { 161 char *buffer; 162 int len; 163 164 len = expand_variables_buffer(inf, str, NULL); 165 buffer = heap_alloc(len); 166 if (!len) return NULL; 167 168 expand_variables_buffer(inf, str, buffer); 169 return buffer; 170 } 171 172 void inf_free(struct inf_file *inf) 173 { 174 struct inf_section *sec, *sec_next; 175 LIST_FOR_EACH_ENTRY_SAFE(sec, sec_next, &inf->sections, struct inf_section, entry) 176 { 177 list_remove(&sec->entry); 178 inf_section_free(sec); 179 } 180 181 heap_free(inf->content); 182 heap_free(inf); 183 } 184 185 BOOL inf_next_section(struct inf_file *inf, struct inf_section **sec) 186 { 187 struct list *next_entry, *cur_position; 188 189 if (*sec) 190 cur_position = &(*sec)->entry; 191 else 192 cur_position = &inf->sections; 193 194 next_entry = list_next(&inf->sections, cur_position); 195 if (!next_entry) return FALSE; 196 197 *sec = CONTAINING_RECORD(next_entry, struct inf_section, entry); 198 return TRUE; 199 } 200 201 struct inf_section *inf_get_section(struct inf_file *inf, const char *name) 202 { 203 struct inf_section *sec = NULL; 204 205 while (inf_next_section(inf, &sec)) 206 { 207 if (!strcasecmp(sec->name, name)) 208 return sec; 209 } 210 211 return NULL; 212 } 213 214 char *inf_section_get_name(struct inf_section *section) 215 { 216 return strdupA(section->name); 217 } 218 219 BOOL inf_section_next_value(struct inf_section *sec, struct inf_value **value) 220 { 221 struct list *next_entry, *cur_position; 222 223 if (*value) 224 cur_position = &(*value)->entry; 225 else 226 cur_position = &sec->values; 227 228 next_entry = list_next(&sec->values, cur_position); 229 if (!next_entry) return FALSE; 230 231 *value = CONTAINING_RECORD(next_entry, struct inf_value, entry); 232 return TRUE; 233 } 234 235 struct inf_value *inf_get_value(struct inf_section *sec, const char *key) 236 { 237 struct inf_value *value = NULL; 238 239 while (inf_section_next_value(sec, &value)) 240 { 241 if (!strcasecmp(value->key, key)) 242 return value; 243 } 244 245 return NULL; 246 } 247 248 char *inf_value_get_key(struct inf_value *value) 249 { 250 return strdupA(value->key); 251 } 252 253 char *inf_value_get_value(struct inf_value *value) 254 { 255 return expand_variables(value->section->file, value->value); 256 } 257 258 char *trim(char *str, char **last_chr, BOOL strip_quotes) 259 { 260 char *last; 261 262 for (; *str; str++) 263 { 264 if (*str != '\t' && *str != ' ') 265 break; 266 } 267 268 if (!*str) 269 { 270 if (last_chr) *last_chr = str; 271 return str; 272 } 273 274 last = str + strlen(str) - 1; 275 276 for (; last > str; last--) 277 { 278 if (*last != '\t' && *last != ' ') 279 break; 280 *last = 0; 281 } 282 283 if (strip_quotes && last != str) 284 { 285 if (*last == '"' && *str == '"') 286 { 287 str++; 288 *last = 0; 289 } 290 } 291 292 if (last_chr) *last_chr = last; 293 return str; 294 } 295 296 static char *get_next_line(char **str, char **last_chr) 297 { 298 BOOL in_next_line = FALSE; 299 char *start, *next; 300 301 start = *str; 302 if (!start || !*start) return NULL; 303 304 for (next = start; *next; next++) 305 { 306 if (*next == '\n' || *next == '\r') 307 { 308 *next = 0; 309 in_next_line = TRUE; 310 } 311 else if (in_next_line) 312 { 313 break; 314 } 315 } 316 317 *str = next; 318 return trim(start, last_chr, FALSE); 319 } 320 321 /* This function only fails in case of an memory allocation error 322 * and does not touch section in case the parsing failed. */ 323 static HRESULT inf_section_parse(struct inf_file *inf, char *line, char *last_chr, struct inf_section **section) 324 { 325 struct inf_section *sec; 326 char *comment; 327 char *name; 328 329 if (*line != '[') 330 return S_OK; 331 332 line++; 333 334 comment = strchr(line, ';'); 335 if (comment) 336 { 337 *comment = 0; 338 line = trim(line, &last_chr, FALSE); 339 } 340 341 if (*last_chr != ']') 342 return S_OK; 343 344 *last_chr = 0; 345 name = trim(line, NULL, FALSE); 346 if (!name) return S_OK; 347 348 sec = heap_alloc_zero(sizeof(*sec)); 349 if (!sec) return E_OUTOFMEMORY; 350 351 sec->name = name; 352 sec->file = inf; 353 list_init(&sec->values); 354 355 list_add_tail(&inf->sections, &sec->entry); 356 357 *section = sec; 358 return S_OK; 359 } 360 361 static HRESULT inf_value_parse(struct inf_section *sec, char *line) 362 { 363 struct inf_value *key_val; 364 char *key, *value, *del; 365 366 del = strchr(line, '='); 367 if (!del) return S_OK; 368 369 *del = 0; 370 key = line; 371 value = del + 1; 372 373 key = trim(key, NULL, FALSE); 374 value = trim(value, NULL, TRUE); 375 376 key_val = heap_alloc_zero(sizeof(*key_val)); 377 if (!key_val) return E_OUTOFMEMORY; 378 379 key_val->key = key; 380 key_val->value = value; 381 key_val->section = sec; 382 383 list_add_tail(&sec->values, &key_val->entry); 384 return S_OK; 385 } 386 387 static HRESULT inf_process_content(struct inf_file *inf) 388 { 389 struct inf_section *section = NULL; 390 char *content = inf->content; 391 char *line, *last_chr; 392 HRESULT hr = S_OK; 393 394 while (SUCCEEDED(hr) && (line = get_next_line(&content, &last_chr))) 395 { 396 if (*line == '[') 397 hr = inf_section_parse(inf, line, last_chr, §ion); 398 else if (strchr(line, '=') && section) 399 hr = inf_value_parse(section, line); 400 } 401 402 return hr; 403 } 404 405 HRESULT inf_load(const char *path, struct inf_file **inf_file) 406 { 407 LARGE_INTEGER file_size; 408 struct inf_file *inf; 409 HRESULT hr = E_FAIL; 410 HANDLE file; 411 DWORD read; 412 413 file = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 414 if (file == INVALID_HANDLE_VALUE) return E_FAIL; 415 416 inf = heap_alloc_zero(sizeof(*inf)); 417 if (!inf) goto error; 418 419 if (!GetFileSizeEx(file, &file_size)) 420 goto error; 421 422 inf->size = file_size.QuadPart; 423 424 inf->content = heap_alloc_zero(inf->size); 425 if (!inf->content) goto error; 426 427 list_init(&inf->sections); 428 429 if (!ReadFile(file, inf->content, inf->size, &read, NULL) || read != inf->size) 430 goto error; 431 432 hr = inf_process_content(inf); 433 if (FAILED(hr)) goto error; 434 435 CloseHandle(file); 436 *inf_file = inf; 437 return S_OK; 438 439 error: 440 if (inf) inf_free(inf); 441 CloseHandle(file); 442 return hr; 443 } 444