VirtualBox

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

Last change on this file since 106042 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

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