VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • 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 98103 2023-01-17 14:15:46Z 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 * @returns nothing.
135 * @param pCfgTokenizer The config tokenizer.
136 * @param pToken The token to free.
137 */
138static void autostartConfigTokenFree(PCFGTOKENIZER pCfgTokenizer, PCFGTOKEN pToken)
139{
140 NOREF(pCfgTokenizer);
141 RTMemFree(pToken);
142}
143
144/**
145 * Reads the next line from the config stream.
146 *
147 * @returns VBox status code.
148 * @param pCfgTokenizer The config tokenizer.
149 */
150static int autostartConfigTokenizerReadNextLine(PCFGTOKENIZER pCfgTokenizer)
151{
152 int rc = VINF_SUCCESS;
153
154 if (pCfgTokenizer->fEof)
155 return VERR_EOF;
156
157 do
158 {
159 rc = RTStrmGetLine(pCfgTokenizer->hStrmConfig, pCfgTokenizer->pszLine,
160 pCfgTokenizer->cbLine);
161 if (rc == VERR_BUFFER_OVERFLOW)
162 {
163 char *pszTmp;
164
165 pCfgTokenizer->cbLine += 128;
166 pszTmp = (char *)RTMemRealloc(pCfgTokenizer->pszLine, pCfgTokenizer->cbLine);
167 if (pszTmp)
168 pCfgTokenizer->pszLine = pszTmp;
169 else
170 rc = VERR_NO_MEMORY;
171 }
172 } while (rc == VERR_BUFFER_OVERFLOW);
173
174 if ( RT_SUCCESS(rc)
175 || rc == VERR_EOF)
176 {
177 pCfgTokenizer->iLine++;
178 pCfgTokenizer->cchCurr = 1;
179 pCfgTokenizer->pszLineCurr = pCfgTokenizer->pszLine;
180 if (rc == VERR_EOF)
181 pCfgTokenizer->fEof = true;
182 }
183
184 return rc;
185}
186
187/**
188 * Get the next token from the config stream and create a token structure.
189 *
190 * @returns VBox status code.
191 * @param pCfgTokenizer The config tokenizer data.
192 * @param pCfgTokenUse Allocated token structure to use or NULL to allocate
193 * a new one. It will bee freed if an error is encountered.
194 * @param ppCfgToken Where to store the pointer to the next token on success.
195 */
196static int autostartConfigTokenizerCreateToken(PCFGTOKENIZER pCfgTokenizer,
197 PCFGTOKEN pCfgTokenUse, PCFGTOKEN *ppCfgToken)
198{
199 const char *pszToken = NULL;
200 size_t cchToken = 1;
201 size_t cchAdvance = 0;
202 CFGTOKENTYPE enmType = CFGTOKENTYPE_INVALID;
203 int rc = VINF_SUCCESS;
204
205 for (;;)
206 {
207 pszToken = pCfgTokenizer->pszLineCurr;
208
209 /* Skip all spaces. */
210 while (RT_C_IS_BLANK(*pszToken))
211 {
212 pszToken++;
213 cchAdvance++;
214 }
215
216 /* Check if we have to read a new line. */
217 if ( *pszToken == '\0'
218 || *pszToken == '#')
219 {
220 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
221 if (rc == VERR_EOF)
222 {
223 enmType = CFGTOKENTYPE_EOF;
224 rc = VINF_SUCCESS;
225 break;
226 }
227 else if (RT_FAILURE(rc))
228 break;
229 /* start from the beginning. */
230 cchAdvance = 0;
231 }
232 else if (*pszToken == '=')
233 {
234 enmType = CFGTOKENTYPE_EQUAL;
235 break;
236 }
237 else if (*pszToken == ',')
238 {
239 enmType = CFGTOKENTYPE_COMMA;
240 break;
241 }
242 else if (*pszToken == '{')
243 {
244 enmType = CFGTOKENTYPE_CURLY_OPEN;
245 break;
246 }
247 else if (*pszToken == '}')
248 {
249 enmType = CFGTOKENTYPE_CURLY_CLOSING;
250 break;
251 }
252 else
253 {
254 const char *pszTmp = pszToken;
255 cchToken = 0;
256 enmType = CFGTOKENTYPE_ID;
257
258 /* Get the complete token. */
259 while ( RT_C_IS_ALNUM(*pszTmp)
260 || *pszTmp == '_'
261 || *pszTmp == '.')
262 {
263 pszTmp++;
264 cchToken++;
265 }
266 break;
267 }
268 }
269
270 Assert(RT_FAILURE(rc) || enmType != CFGTOKENTYPE_INVALID);
271
272 if (RT_SUCCESS(rc))
273 {
274 /* Free the given token if it is an ID or the current one is an ID token. */
275 if ( pCfgTokenUse
276 && ( pCfgTokenUse->enmType == CFGTOKENTYPE_ID
277 || enmType == CFGTOKENTYPE_ID))
278 {
279 autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
280 pCfgTokenUse = NULL;
281 }
282
283 if (!pCfgTokenUse)
284 {
285 size_t cbToken = sizeof(CFGTOKEN);
286 if (enmType == CFGTOKENTYPE_ID)
287 cbToken += (cchToken + 1) * sizeof(char);
288
289 pCfgTokenUse = (PCFGTOKEN)RTMemAllocZ(cbToken);
290 if (!pCfgTokenUse)
291 rc = VERR_NO_MEMORY;
292 }
293
294 if (RT_SUCCESS(rc))
295 {
296 /* Copy token data. */
297 pCfgTokenUse->enmType = enmType;
298 pCfgTokenUse->cchStart = pCfgTokenizer->cchCurr;
299 pCfgTokenUse->iLine = pCfgTokenizer->iLine;
300 if (enmType == CFGTOKENTYPE_ID)
301 {
302 pCfgTokenUse->u.Id.cchToken = cchToken;
303 memcpy(pCfgTokenUse->u.Id.achToken, pszToken, cchToken);
304 }
305 }
306 else if (pCfgTokenUse)
307 autostartConfigTokenFree(pCfgTokenizer, pCfgTokenUse);
308
309 if (RT_SUCCESS(rc))
310 {
311 /* Set new position in config stream. */
312 pCfgTokenizer->pszLineCurr += cchToken + cchAdvance;
313 pCfgTokenizer->cchCurr += cchToken + cchAdvance;
314 *ppCfgToken = pCfgTokenUse;
315 }
316 }
317
318 return rc;
319}
320
321/**
322 * Destroys the given config tokenizer.
323 *
324 * @returns nothing.
325 * @param pCfgTokenizer The config tokenizer to destroy.
326 */
327static void autostartConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
328{
329 if (pCfgTokenizer->pszLine)
330 RTMemFree(pCfgTokenizer->pszLine);
331 if (pCfgTokenizer->hStrmConfig)
332 RTStrmClose(pCfgTokenizer->hStrmConfig);
333 if (pCfgTokenizer->pTokenNext)
334 RTMemFree(pCfgTokenizer->pTokenNext);
335 RTMemFree(pCfgTokenizer);
336}
337
338/**
339 * Creates the config tokenizer from the given filename.
340 *
341 * @returns VBox status code.
342 * @param pszFilename Config filename.
343 * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
344 * success.
345 */
346static int autostartConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
347{
348 int rc = VINF_SUCCESS;
349 PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
350
351 if (pCfgTokenizer)
352 {
353 pCfgTokenizer->iLine = 0;
354 pCfgTokenizer->cbLine = 128;
355 pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
356 if (pCfgTokenizer->pszLine)
357 {
358 rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
359 if (RT_SUCCESS(rc))
360 {
361 rc = autostartConfigTokenizerReadNextLine(pCfgTokenizer);
362 if (RT_SUCCESS(rc))
363 rc = autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL,
364 &pCfgTokenizer->pTokenNext);
365 }
366 }
367 else
368 rc = VERR_NO_MEMORY;
369 }
370 else
371 rc = VERR_NO_MEMORY;
372
373 if (RT_SUCCESS(rc))
374 *ppCfgTokenizer = pCfgTokenizer;
375 else if ( RT_FAILURE(rc)
376 && pCfgTokenizer)
377 autostartConfigTokenizerDestroy(pCfgTokenizer);
378
379 return rc;
380}
381
382/**
383 * Return the next token from the config stream.
384 *
385 * @returns VBox status code.
386 * @param pCfgTokenizer The config tokenizer.
387 * @param ppCfgToken Where to store the next token.
388 */
389static int autostartConfigTokenizerGetNextToken(PCFGTOKENIZER pCfgTokenizer,
390 PCFGTOKEN *ppCfgToken)
391{
392 *ppCfgToken = pCfgTokenizer->pTokenNext;
393 return autostartConfigTokenizerCreateToken(pCfgTokenizer, NULL, &pCfgTokenizer->pTokenNext);
394}
395
396/**
397 * Returns a stringified version of the token type.
398 *
399 * @returns Stringified version of the token type.
400 * @param enmType Token type.
401 */
402static const char *autostartConfigTokenTypeToStr(CFGTOKENTYPE enmType)
403{
404 switch (enmType)
405 {
406 case CFGTOKENTYPE_COMMA:
407 return ",";
408 case CFGTOKENTYPE_EQUAL:
409 return "=";
410 case CFGTOKENTYPE_CURLY_OPEN:
411 return "{";
412 case CFGTOKENTYPE_CURLY_CLOSING:
413 return "}";
414 case CFGTOKENTYPE_EOF:
415 return "<EOF>";
416 case CFGTOKENTYPE_ID:
417 return "<Identifier>";
418 default:
419 AssertFailed();
420 return "<Invalid>";
421 }
422 /* not reached */
423}
424
425/**
426 * Returns a stringified version of the token.
427 *
428 * @returns Stringified version of the token type.
429 * @param pToken Token.
430 */
431static const char *autostartConfigTokenToString(PCFGTOKEN pToken)
432{
433 if (pToken->enmType == CFGTOKENTYPE_ID)
434 return pToken->u.Id.achToken;
435 else
436 return autostartConfigTokenTypeToStr(pToken->enmType);
437}
438
439/**
440 * Returns the length of the token in characters (without zero terminator).
441 *
442 * @returns Token length.
443 * @param pToken Token.
444 */
445static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken)
446{
447 switch (pToken->enmType)
448 {
449 case CFGTOKENTYPE_COMMA:
450 case CFGTOKENTYPE_EQUAL:
451 case CFGTOKENTYPE_CURLY_OPEN:
452 case CFGTOKENTYPE_CURLY_CLOSING:
453 return 1;
454 case CFGTOKENTYPE_EOF:
455 return 0;
456 case CFGTOKENTYPE_ID:
457 return strlen(pToken->u.Id.achToken);
458 default:
459 AssertFailed();
460 return 0;
461 }
462 /* not reached */
463}
464
465/**
466 * Log unexpected token error.
467 *
468 * @returns VBox status code (VERR_INVALID_PARAMETER).
469 * @param pToken The token which caused the error.
470 * @param pszExpected String of the token which was expected.
471 */
472static int autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected)
473{
474 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "Unexpected token '%s' at %d:%d.%d, expected '%s'",
475 autostartConfigTokenToString(pToken),
476 pToken->iLine, pToken->cchStart,
477 pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected);
478}
479
480/**
481 * Verfies a token and consumes it.
482 *
483 * @returns VBox status code.
484 * @param pCfgTokenizer The config tokenizer.
485 * @param pszTokenCheck The token to check for.
486 */
487static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
488{
489 int rc = VINF_SUCCESS;
490 PCFGTOKEN pCfgToken = NULL;
491
492 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
493 if (RT_SUCCESS(rc))
494 {
495 if (pCfgToken->enmType != enmType)
496 return autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType));
497
498 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
499 }
500 return rc;
501}
502
503/**
504 * Consumes the next token in the stream.
505 *
506 * @returns VBox status code.
507 * @param pCfgTokenizer Tokenizer instance data.
508 */
509static int autostartConfigTokenizerConsume(PCFGTOKENIZER pCfgTokenizer)
510{
511 int rc = VINF_SUCCESS;
512 PCFGTOKEN pCfgToken = NULL;
513
514 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
515 if (RT_SUCCESS(rc))
516 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
517
518 return rc;
519}
520
521/**
522 * Returns the start of the next token without consuming it.
523 *
524 * @returns The next token without consuming it.
525 * @param pCfgTokenizer Tokenizer instance data.
526 */
527DECLINLINE(PCFGTOKEN) autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer)
528{
529 return pCfgTokenizer->pTokenNext;
530}
531
532/**
533 * Check whether the next token is equal to the given one.
534 *
535 * @returns true if the next token in the stream is equal to the given one
536 * false otherwise.
537 * @param pszToken The token to check for.
538 */
539DECLINLINE(bool) autostartConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
540{
541 PCFGTOKEN pToken = autostartConfigTokenizerPeek(pCfgTokenizer);
542 return pToken->enmType == enmType;
543}
544
545/**
546 * Parse a key value node and returns the AST.
547 *
548 * @returns VBox status code.
549 * @param pCfgTokenizer The tokenizer for the config stream.
550 * @param pszKey The key for the pair.
551 * @param ppCfgAst Where to store the resulting AST on success.
552 */
553static int autostartConfigParseValue(PCFGTOKENIZER pCfgTokenizer, const char *pszKey,
554 PCFGAST *ppCfgAst)
555{
556 int rc = VINF_SUCCESS;
557 PCFGTOKEN pToken = NULL;
558
559 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
560 if ( RT_SUCCESS(rc)
561 && pToken->enmType == CFGTOKENTYPE_ID)
562 {
563 PCFGAST pCfgAst = NULL;
564
565 pCfgAst = (PCFGAST)RTMemAllocZ(RT_UOFFSETOF_DYN(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
566 if (!pCfgAst)
567 return VERR_NO_MEMORY;
568
569 pCfgAst->enmType = CFGASTNODETYPE_KEYVALUE;
570 pCfgAst->pszKey = RTStrDup(pszKey);
571 if (!pCfgAst->pszKey)
572 {
573 RTMemFree(pCfgAst);
574 return VERR_NO_MEMORY;
575 }
576
577 memcpy(pCfgAst->u.KeyValue.aszValue, pToken->u.Id.achToken, pToken->u.Id.cchToken);
578 pCfgAst->u.KeyValue.cchValue = pToken->u.Id.cchToken;
579 *ppCfgAst = pCfgAst;
580 }
581 else
582 rc = autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
583
584 return rc;
585}
586
587/**
588 * Parses a compound node constructing the AST and returning it on success.
589 *
590 * @returns VBox status code.
591 * @param pCfgTokenizer The tokenizer for the config stream.
592 * @param pszScopeId The scope ID of the compound node.
593 * @param ppCfgAst Where to store the resulting AST on success.
594 */
595static int autostartConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const char *pszScopeId,
596 PCFGAST *ppCfgAst)
597{
598 unsigned cAstNodesMax = 10;
599 PCFGAST pCfgAst = (PCFGAST)RTMemAllocZ(RT_UOFFSETOF_DYN(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
600 if (!pCfgAst)
601 return VERR_NO_MEMORY;
602
603 pCfgAst->enmType = CFGASTNODETYPE_COMPOUND;
604 pCfgAst->u.Compound.cAstNodes = 0;
605 pCfgAst->pszKey = RTStrDup(pszScopeId);
606 if (!pCfgAst->pszKey)
607 {
608 RTMemFree(pCfgAst);
609 return VERR_NO_MEMORY;
610 }
611
612 int rc = VINF_SUCCESS;
613 do
614 {
615 PCFGTOKEN pToken = NULL;
616 PCFGAST pAstNode = NULL;
617
618 if ( autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING)
619 || autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_EOF))
620 break;
621
622 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
623 if ( RT_SUCCESS(rc)
624 && pToken->enmType == CFGTOKENTYPE_ID)
625 {
626 /* Next must be a = token in all cases at this place. */
627 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EQUAL);
628 if (RT_SUCCESS(rc))
629 {
630 /* Check whether this is a compound node. */
631 if (autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_OPEN))
632 {
633 rc = autostartConfigTokenizerConsume(pCfgTokenizer);
634 if (RT_SUCCESS(rc))
635 rc = autostartConfigParseCompoundNode(pCfgTokenizer, pToken->u.Id.achToken,
636 &pAstNode);
637
638 if (RT_SUCCESS(rc))
639 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING);
640 }
641 else
642 rc = autostartConfigParseValue(pCfgTokenizer, pToken->u.Id.achToken,
643 &pAstNode);
644 }
645 }
646 else if (RT_SUCCESS(rc))
647 rc = autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
648
649 /* Add to the current compound node. */
650 if (RT_SUCCESS(rc))
651 {
652 if (pCfgAst->u.Compound.cAstNodes >= cAstNodesMax)
653 {
654 cAstNodesMax += 10;
655
656 PCFGAST pCfgAstNew = (PCFGAST)RTMemRealloc(pCfgAst, RT_UOFFSETOF_DYN(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
657 if (!pCfgAstNew)
658 rc = VERR_NO_MEMORY;
659 else
660 pCfgAst = pCfgAstNew;
661 }
662
663 if (RT_SUCCESS(rc))
664 {
665 pCfgAst->u.Compound.apAstNodes[pCfgAst->u.Compound.cAstNodes] = pAstNode;
666 pCfgAst->u.Compound.cAstNodes++;
667 }
668 }
669
670 autostartConfigTokenFree(pCfgTokenizer, pToken);
671
672 } while (RT_SUCCESS(rc));
673
674 if (RT_SUCCESS(rc))
675 *ppCfgAst = pCfgAst;
676 else
677 autostartConfigAstDestroy(pCfgAst);
678
679 return rc;
680}
681
682DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, PCFGAST *ppCfgAst)
683{
684 PCFGTOKENIZER pCfgTokenizer = NULL;
685 int rc = VINF_SUCCESS;
686 PCFGAST pCfgAst = NULL;
687
688 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
689 AssertPtrReturn(ppCfgAst, VERR_INVALID_POINTER);
690
691 rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
692 if (RT_SUCCESS(rc))
693 {
694 rc = autostartConfigParseCompoundNode(pCfgTokenizer, "", &pCfgAst);
695 if (RT_SUCCESS(rc))
696 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EOF);
697 }
698
699 if (pCfgTokenizer)
700 autostartConfigTokenizerDestroy(pCfgTokenizer);
701
702 if (RT_SUCCESS(rc))
703 *ppCfgAst = pCfgAst;
704
705 return rc;
706}
707
708DECLHIDDEN(void) autostartConfigAstDestroy(PCFGAST pCfgAst)
709{
710 AssertPtrReturnVoid(pCfgAst);
711
712 switch (pCfgAst->enmType)
713 {
714 case CFGASTNODETYPE_KEYVALUE:
715 {
716 RTMemFree(pCfgAst);
717 break;
718 }
719 case CFGASTNODETYPE_COMPOUND:
720 {
721 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
722 autostartConfigAstDestroy(pCfgAst->u.Compound.apAstNodes[i]);
723 RTMemFree(pCfgAst);
724 break;
725 }
726 case CFGASTNODETYPE_LIST:
727 RT_FALL_THROUGH();
728 default:
729 AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType));
730 }
731}
732
733DECLHIDDEN(PCFGAST) autostartConfigAstGetByName(PCFGAST pCfgAst, const char *pszName)
734{
735 if (!pCfgAst)
736 return NULL;
737
738 AssertReturn(pCfgAst->enmType == CFGASTNODETYPE_COMPOUND, NULL);
739
740 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
741 {
742 PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
743
744 if (!RTStrCmp(pNode->pszKey, pszName))
745 return pNode;
746 }
747
748 return NULL;
749}
750
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