VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostartCfg.cpp@ 74942

Last change on this file since 74942 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.3 KB
Line 
1/* $Id: VBoxAutostartCfg.cpp 73097 2018-07-12 21:06:33Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service, configuration parser.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22
23#include <iprt/stream.h>
24#include <iprt/process.h>
25#include <iprt/string.h>
26#include <iprt/mem.h>
27#include <iprt/ctype.h>
28#include <iprt/message.h>
29
30#include "VBoxAutostart.h"
31
32
33/*********************************************************************************************************************************
34* Constants And Macros, Structures and Typedefs *
35*********************************************************************************************************************************/
36
37/**
38 * Token type.
39 */
40typedef enum CFGTOKENTYPE
41{
42 /** Invalid token type. */
43 CFGTOKENTYPE_INVALID = 0,
44 /** Identifier. */
45 CFGTOKENTYPE_ID,
46 /** Comma. */
47 CFGTOKENTYPE_COMMA,
48 /** Equal sign. */
49 CFGTOKENTYPE_EQUAL,
50 /** Open curly brackets. */
51 CFGTOKENTYPE_CURLY_OPEN,
52 /** Closing curly brackets. */
53 CFGTOKENTYPE_CURLY_CLOSING,
54 /** End of file. */
55 CFGTOKENTYPE_EOF,
56 /** 32bit hack. */
57 CFGTOKENTYPE_32BIT_HACK = 0x7fffffff
58} CFGTOKENTYPE;
59/** Pointer to a token type. */
60typedef CFGTOKENTYPE *PCFGTOKENTYPE;
61/** Pointer to a const token type. */
62typedef const CFGTOKENTYPE *PCCFGTOKENTYPE;
63
64/**
65 * A token.
66 */
67typedef struct CFGTOKEN
68{
69 /** Type of the token. */
70 CFGTOKENTYPE enmType;
71 /** Line number of the token. */
72 unsigned iLine;
73 /** Starting character of the token in the stream. */
74 size_t cchStart;
75 /** Type dependen token data. */
76 union
77 {
78 /** Data for the ID type. */
79 struct
80 {
81 /** Size of the id in characters, excluding the \0 terminator. */
82 size_t cchToken;
83 /** Token data, variable size (given by cchToken member). */
84 char achToken[1];
85 } Id;
86 } u;
87} CFGTOKEN;
88/** Pointer to a token. */
89typedef CFGTOKEN *PCFGTOKEN;
90/** Pointer to a const token. */
91typedef const CFGTOKEN *PCCFGTOKEN;
92
93/**
94 * Tokenizer instance data for the config data.
95 */
96typedef struct CFGTOKENIZER
97{
98 /** Config file handle. */
99 PRTSTREAM hStrmConfig;
100 /** String buffer for the current line we are operating in. */
101 char *pszLine;
102 /** Size of the string buffer. */
103 size_t cbLine;
104 /** Current position in the line. */
105 char *pszLineCurr;
106 /** Current line in the config file. */
107 unsigned iLine;
108 /** Current character of the line. */
109 size_t cchCurr;
110 /** Flag whether the end of the config stream is reached. */
111 bool fEof;
112 /** Pointer to the next token in the stream (used to peek). */
113 PCFGTOKEN pTokenNext;
114} CFGTOKENIZER, *PCFGTOKENIZER;
115
116
117/*********************************************************************************************************************************
118* Internal Functions *
119*********************************************************************************************************************************/
120
121/**
122 * Free a config token.
123 *
124 * @returns nothing.
125 * @param pCfgTokenizer The config tokenizer.
126 * @param pToken The token to free.
127 */
128static void autostartConfigTokenFree(PCFGTOKENIZER pCfgTokenizer, PCFGTOKEN pToken)
129{
130 NOREF(pCfgTokenizer);
131 RTMemFree(pToken);
132}
133
134/**
135 * Reads the next line from the config stream.
136 *
137 * @returns VBox status code.
138 * @param pCfgTokenizer The config tokenizer.
139 */
140static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
141{
142 int rc = VINF_SUCCESS;
143
144 if (pCfgTokenizer->fEof)
145 return VERR_EOF;
146
147 do
148 {
149 rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
150 pCfgTokenizer->cbLine);
151 if (rc == VERR_BUFFER_OVERFLOW)
152 {
153 char *pszTmp;
154
155 pCfgTokenizer->cbLine += 128;
156 pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
157 if (pszTmp)
158 pCfgTokenizer->pszLine = pszTmp;
159 else
160 rc = VERR_NO_MEMORY;
161 }
162 } while (rc == VERR_BUFFER_OVERFLOW);
163
164 if ( RT_SUCCESS(rc)
165 || rc == VERR_EOF)
166 {
167 pCfgTokenizer->iLine++;
168 pCfgTokenizer->cchCurr = 1;
169 pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
170 if (rc == VERR_EOF)
171 pCfgTokenizer->fEof = true;
172 }
173
174 return rc;
175}
176
177/**
178 * Get the next token from the config stream and create a token structure.
179 *
180 * @returns VBox status code.
181 * @param pCfgTokenizer The config tokenizer data.
182 * @param pCfgTokenUse Allocated token structure to use or NULL to allocate
183 * a new one. It will bee freed if an error is encountered.
184 * @param ppCfgToken Where to store the pointer to the next token on success.
185 */
186static int autostartConfigTokenizerCreateToken(PCFGTOKENIZER pCfgTokenizer,
187 PCFGTOKEN pCfgTokenUse, PCFGTOKEN *ppCfgToken)
188{
189 const char *pszToken = NULL;
190 size_t cchToken = 1;
191 size_t cchAdvance = 0;
192 CFGTOKENTYPE enmType = CFGTOKENTYPE_INVALID;
193 int rc = VINF_SUCCESS;
194
195 for (;;)
196 {
197 pszToken = pCfgTokenizer->pszLineCurr;
198
199 /* Skip all spaces. */
200 while (RT_C_IS_BLANK(*pszToken))
201 {
202 pszToken++;
203 cchAdvance++;
204 }
205
206 /* Check if we have to read a new line. */
207 if ( *pszToken == '\0'
208 || *pszToken == '#')
209 {
210 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
211 if (rc == VERR_EOF)
212 {
213 enmType = CFGTOKENTYPE_EOF;
214 rc = VINF_SUCCESS;
215 break;
216 }
217 else if (RT_FAILURE(rc))
218 break;
219 /* start from the beginning. */
220 cchAdvance = 0;
221 }
222 else if (*pszToken == '=')
223 {
224 enmType = CFGTOKENTYPE_EQUAL;
225 break;
226 }
227 else if (*pszToken == ',')
228 {
229 enmType = CFGTOKENTYPE_COMMA;
230 break;
231 }
232 else if (*pszToken == '{')
233 {
234 enmType = CFGTOKENTYPE_CURLY_OPEN;
235 break;
236 }
237 else if (*pszToken == '}')
238 {
239 enmType = CFGTOKENTYPE_CURLY_CLOSING;
240 break;
241 }
242 else
243 {
244 const char *pszTmp = pszToken;
245 cchToken = 0;
246 enmType = CFGTOKENTYPE_ID;
247
248 /* Get the complete token. */
249 while ( RT_C_IS_ALNUM(*pszTmp)
250 || *pszTmp == '_'
251 || *pszTmp == '.')
252 {
253 pszTmp++;
254 cchToken++;
255 }
256 break;
257 }
258 }
259
260 Assert(RT_FAILURE(rc) || enmType != CFGTOKENTYPE_INVALID);
261
262 if (RT_SUCCESS(rc))
263 {
264 /* Free the given token if it is an ID or the current one is an ID token. */
265 if ( pCfgTokenUse
266 && ( pCfgTokenUse->enmType == CFGTOKENTYPE_ID
267 || enmType == CFGTOKENTYPE_ID))
268 {
269 autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
270 pCfgTokenUse = NULL;
271 }
272
273 if (!pCfgTokenUse)
274 {
275 size_t cbToken = sizeof(CFGTOKEN);
276 if (enmType == CFGTOKENTYPE_ID)
277 cbToken += (cchToken + 1) * sizeof(char);
278
279 pCfgTokenUse = (PCFGTOKEN)RTMemAllocZ(cbToken);
280 if (!pCfgTokenUse)
281 rc = VERR_NO_MEMORY;
282 }
283
284 if (RT_SUCCESS(rc))
285 {
286 /* Copy token data. */
287 pCfgTokenUse->enmType = enmType;
288 pCfgTokenUse->cchStart = pCfgTokenizer->cchCurr;
289 pCfgTokenUse->iLine = pCfgTokenizer->iLine;
290 if (enmType == CFGTOKENTYPE_ID)
291 {
292 pCfgTokenUse->u.Id.cchToken = cchToken;
293 memcpy(pCfgTokenUse->u.Id.achToken, pszToken, cchToken);
294 }
295 }
296 else if (pCfgTokenUse)
297 autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
298
299 if (RT_SUCCESS(rc))
300 {
301 /* Set new position in config stream. */
302 pCfgTokenizer->pszLineCurr += cchToken + cchAdvance;
303 pCfgTokenizer->cchCurr += cchToken + cchAdvance;
304 *ppCfgToken = pCfgTokenUse;
305 }
306 }
307
308 return rc;
309}
310
311/**
312 * Destroys the given config tokenizer.
313 *
314 * @returns nothing.
315 * @param pCfgTokenizer The config tokenizer to destroy.
316 */
317static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
318{
319 if (pCfgTokenizer->pszLine)
320 RTMemFree(pCfgTokenizer->pszLine);
321 if (pCfgTokenizer->hStrmConfig)
322 RTStrmClose(pCfgTokenizer->hStrmConfig);
323 if (pCfgTokenizer->pTokenNext)
324 RTMemFree(pCfgTokenizer->pTokenNext);
325 RTMemFree(pCfgTokenizer);
326}
327
328/**
329 * Creates the config tokenizer from the given filename.
330 *
331 * @returns VBox status code.
332 * @param pszFilename Config filename.
333 * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
334 * success.
335 */
336static int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
337{
338 int rc = VINF_SUCCESS;
339 PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
340
341 if (pCfgTokenizer)
342 {
343 pCfgTokenizer->iLine = 0;
344 pCfgTokenizer->cbLine = 128;
345 pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
346 if (pCfgTokenizer->pszLine)
347 {
348 rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
349 if (RT_SUCCESS(rc))
350 {
351 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
352 if (RT_SUCCESS(rc))
353 rc = autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL,
354 &pCfgTokenizer->pTokenNext);
355 }
356 }
357 else
358 rc = VERR_NO_MEMORY;
359 }
360 else
361 rc = VERR_NO_MEMORY;
362
363 if (RT_SUCCESS(rc))
364 *ppCfgTokenizer = pCfgTokenizer;
365 else if ( RT_FAILURE(rc)
366 && pCfgTokenizer)
367 autostartConfigTokenizerDestroy(pCfgTokenizer);
368
369 return rc;
370}
371
372/**
373 * Return the next token from the config stream.
374 *
375 * @returns VBox status code.
376 * @param pCfgTokenizer The config tokenizer.
377 * @param ppCfgToken Where to store the next token.
378 */
379static int autostartConfigTokenizerGetNextToken(PCFGTOKENIZER pCfgTokenizer,
380 PCFGTOKEN *ppCfgToken)
381{
382 *ppCfgToken = pCfgTokenizer->pTokenNext;
383 return autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL, &pCfgTokenizer->pTokenNext);
384}
385
386/**
387 * Returns a stringified version of the token type.
388 *
389 * @returns Stringified version of the token type.
390 * @param enmType Token type.
391 */
392static const char *autostartConfigTokenTypeToStr(CFGTOKENTYPE enmType)
393{
394 switch (enmType)
395 {
396 case CFGTOKENTYPE_COMMA:
397 return ",";
398 case CFGTOKENTYPE_EQUAL:
399 return "=";
400 case CFGTOKENTYPE_CURLY_OPEN:
401 return "{";
402 case CFGTOKENTYPE_CURLY_CLOSING:
403 return "}";
404 case CFGTOKENTYPE_EOF:
405 return "<EOF>";
406 case CFGTOKENTYPE_ID:
407 return "<Identifier>";
408 default:
409 AssertFailed();
410 return "<Invalid>";
411 }
412 /* not reached */
413}
414
415/**
416 * Returns a stringified version of the token.
417 *
418 * @returns Stringified version of the token type.
419 * @param pToken Token.
420 */
421static const char *autostartConfigTokenToString(PCFGTOKEN pToken)
422{
423 if (pToken->enmType == CFGTOKENTYPE_ID)
424 return pToken->u.Id.achToken;
425 else
426 return autostartConfigTokenTypeToStr(pToken->enmType);
427}
428
429/**
430 * Returns the length of the token in characters (without zero terminator).
431 *
432 * @returns Token length.
433 * @param pToken Token.
434 */
435static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken)
436{
437 switch (pToken->enmType)
438 {
439 case CFGTOKENTYPE_COMMA:
440 case CFGTOKENTYPE_EQUAL:
441 case CFGTOKENTYPE_CURLY_OPEN:
442 case CFGTOKENTYPE_CURLY_CLOSING:
443 return 1;
444 case CFGTOKENTYPE_EOF:
445 return 0;
446 case CFGTOKENTYPE_ID:
447 return strlen(pToken->u.Id.achToken);
448 default:
449 AssertFailed();
450 return 0;
451 }
452 /* not reached */
453}
454
455/**
456 * Log unexpected token error.
457 *
458 * @returns nothing.
459 * @param pToken The token which caused the error.
460 * @param pszExpected String of the token which was expected.
461 */
462static void autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected)
463{
464 autostartSvcLogError("Unexpected token '%s' at %d:%d.%d, expected '%s'",
465 autostartConfigTokenToString(pToken),
466 pToken->iLine, pToken->cchStart,
467 pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected);
468}
469
470/**
471 * Verfies a token and consumes it.
472 *
473 * @returns VBox status code.
474 * @param pCfgTokenizer The config tokenizer.
475 * @param pszTokenCheck The token to check for.
476 */
477static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
478{
479 int rc = VINF_SUCCESS;
480 PCFGTOKEN pCfgToken = NULL;
481
482 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
483 if (RT_SUCCESS(rc))
484 {
485 if (pCfgToken->enmType != enmType)
486 {
487 autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType));
488 rc = VERR_INVALID_PARAMETER;
489 }
490
491 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
492 }
493 return rc;
494}
495
496/**
497 * Consumes the next token in the stream.
498 *
499 * @returns VBox status code.
500 * @param pCfgTokenizer Tokenizer instance data.
501 */
502static int autostartConfigTokenizerConsume(PCFGTOKENIZER pCfgTokenizer)
503{
504 int rc = VINF_SUCCESS;
505 PCFGTOKEN pCfgToken = NULL;
506
507 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
508 if (RT_SUCCESS(rc))
509 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
510
511 return rc;
512}
513
514/**
515 * Returns the start of the next token without consuming it.
516 *
517 * @returns The next token without consuming it.
518 * @param pCfgTokenizer Tokenizer instance data.
519 */
520DECLINLINE(PCFGTOKEN) autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer)
521{
522 return pCfgTokenizer->pTokenNext;
523}
524
525/**
526 * Check whether the next token is equal to the given one.
527 *
528 * @returns true if the next token in the stream is equal to the given one
529 * false otherwise.
530 * @param pszToken The token to check for.
531 */
532DECLINLINE(bool) autostartConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
533{
534 PCFGTOKEN pToken = autostartConfigTokenizerPeek(pCfgTokenizer);
535 return pToken->enmType == enmType;
536}
537
538/**
539 * Parse a key value node and returns the AST.
540 *
541 * @returns VBox status code.
542 * @param pCfgTokenizer The tokenizer for the config stream.
543 * @param pszKey The key for the pair.
544 * @param ppCfgAst Where to store the resulting AST on success.
545 */
546static int autostartConfigParseValue(PCFGTOKENIZER pCfgTokenizer, const char *pszKey,
547 PCFGAST *ppCfgAst)
548{
549 int rc = VINF_SUCCESS;
550 PCFGTOKEN pToken = NULL;
551
552 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
553 if ( RT_SUCCESS(rc)
554 && pToken->enmType == CFGTOKENTYPE_ID)
555 {
556 PCFGAST pCfgAst = NULL;
557
558 pCfgAst = (PCFGAST)RTMemAllocZ(RT_UOFFSETOF_DYN(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
559 if (!pCfgAst)
560 return VERR_NO_MEMORY;
561
562 pCfgAst->enmType = CFGASTNODETYPE_KEYVALUE;
563 pCfgAst->pszKey = RTStrDup(pszKey);
564 if (!pCfgAst->pszKey)
565 {
566 RTMemFree(pCfgAst);
567 return VERR_NO_MEMORY;
568 }
569
570 memcpy(pCfgAst->u.KeyValue.aszValue, pToken->u.Id.achToken, pToken->u.Id.cchToken);
571 pCfgAst->u.KeyValue.cchValue = pToken->u.Id.cchToken;
572 *ppCfgAst = pCfgAst;
573 }
574 else
575 {
576 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
577 rc = VERR_INVALID_PARAMETER;
578 }
579
580 return rc;
581}
582
583/**
584 * Parses a compound node constructing the AST and returning it on success.
585 *
586 * @returns VBox status code.
587 * @param pCfgTokenizer The tokenizer for the config stream.
588 * @param pszScopeId The scope ID of the compound node.
589 * @param ppCfgAst Where to store the resulting AST on success.
590 */
591static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const char *pszScopeId,
592 PCFGAST *ppCfgAst)
593{
594 unsigned cAstNodesMax = 10;
595 PCFGAST pCfgAst = (PCFGAST)RTMemAllocZ(RT_UOFFSETOF_DYN(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
596 if (!pCfgAst)
597 return VERR_NO_MEMORY;
598
599 pCfgAst->enmType = CFGASTNODETYPE_COMPOUND;
600 pCfgAst->u.Compound.cAstNodes = 0;
601 pCfgAst->pszKey = RTStrDup(pszScopeId);
602 if (!pCfgAst->pszKey)
603 {
604 RTMemFree(pCfgAst);
605 return VERR_NO_MEMORY;
606 }
607
608 int rc = VINF_SUCCESS;
609 do
610 {
611 PCFGTOKEN pToken = NULL;
612 PCFGAST pAstNode = NULL;
613
614 if ( autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING)
615 || autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_EOF))
616 break;
617
618 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
619 if ( RT_SUCCESS(rc)
620 && pToken->enmType == CFGTOKENTYPE_ID)
621 {
622 /* Next must be a = token in all cases at this place. */
623 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EQUAL);
624 if (RT_SUCCESS(rc))
625 {
626 /* Check whether this is a compound node. */
627 if (autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_OPEN))
628 {
629 rc = autostartConfigTokenizerConsume(pCfgTokenizer);
630 if (RT_SUCCESS(rc))
631 rc = autostartConfigParseCompoundNode(pCfgTokenizer, pToken->u.Id.achToken,
632 &pAstNode);
633
634 if (RT_SUCCESS(rc))
635 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING);
636 }
637 else
638 rc = autostartConfigParseValue(pCfgTokenizer, pToken->u.Id.achToken,
639 &pAstNode);
640 }
641 }
642 else if (RT_SUCCESS(rc))
643 {
644 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
645 rc = VERR_INVALID_PARAMETER;
646 }
647
648 /* Add to the current compound node. */
649 if (RT_SUCCESS(rc))
650 {
651 if (pCfgAst->u.Compound.cAstNodes >= cAstNodesMax)
652 {
653 cAstNodesMax += 10;
654
655 PCFGAST pCfgAstNew = (PCFGAST)RTMemRealloc(pCfgAst, RT_UOFFSETOF_DYN(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
656 if (!pCfgAstNew)
657 rc = VERR_NO_MEMORY;
658 else
659 pCfgAst = pCfgAstNew;
660 }
661
662 if (RT_SUCCESS(rc))
663 {
664 pCfgAst->u.Compound.apAstNodes[pCfgAst->u.Compound.cAstNodes] = pAstNode;
665 pCfgAst->u.Compound.cAstNodes++;
666 }
667 }
668
669 autostartConfigTokenFree(pCfgTokenizer, pToken);
670
671 } while (RT_SUCCESS(rc));
672
673 if (RT_SUCCESS(rc))
674 *ppCfgAst = pCfgAst;
675 else
676 autostartConfigAstDestroy(pCfgAst);
677
678 return rc;
679}
680
681DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, PCFGAST *ppCfgAst)
682{
683 PCFGTOKENIZER pCfgTokenizer = NULL;
684 int rc = VINF_SUCCESS;
685 PCFGAST pCfgAst = NULL;
686
687 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
688 AssertPtrReturn(ppCfgAst, VERR_INVALID_POINTER);
689
690 rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
691 if (RT_SUCCESS(rc))
692 {
693 rc = autostartConfigParseCompoundNode(pCfgTokenizer, "", &pCfgAst);
694 if (RT_SUCCESS(rc))
695 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EOF);
696 }
697
698 if (pCfgTokenizer)
699 autostartConfigTokenizerDestroy(pCfgTokenizer);
700
701 if (RT_SUCCESS(rc))
702 *ppCfgAst = pCfgAst;
703
704 return rc;
705}
706
707DECLHIDDEN(void) autostartConfigAstDestroy(PCFGAST pCfgAst)
708{
709 AssertPtrReturnVoid(pCfgAst);
710
711 switch (pCfgAst->enmType)
712 {
713 case CFGASTNODETYPE_KEYVALUE:
714 {
715 RTMemFree(pCfgAst);
716 break;
717 }
718 case CFGASTNODETYPE_COMPOUND:
719 {
720 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
721 autostartConfigAstDestroy(pCfgAst->u.Compound.apAstNodes[i]);
722 RTMemFree(pCfgAst);
723 break;
724 }
725 case CFGASTNODETYPE_LIST:
726 default:
727 AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType));
728 }
729}
730
731DECLHIDDEN(PCFGAST) autostartConfigAstGetByName(PCFGAST pCfgAst, const char *pszName)
732{
733 if (!pCfgAst)
734 return NULL;
735
736 AssertReturn(pCfgAst->enmType == CFGASTNODETYPE_COMPOUND, NULL);
737
738 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
739 {
740 PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
741
742 if (!RTStrCmp(pNode->pszKey, pszName))
743 return pNode;
744 }
745
746 return NULL;
747}
748
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use