VirtualBox

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

Last change on this file since 43187 was 42732, checked in by vboxsync, 12 years ago

VBoxAutostart: Rewritten config parser for more flexibility in the future, changes config format

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette