VirtualBox

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

Last change on this file since 62493 was 62493, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.4 KB
Line 
1/* $Id: VBoxAutostartCfg.cpp 62493 2016-07-22 18:44:18Z vboxsync $ */
2/** @file
3 * VBoxAutostart - VirtualBox Autostart service, configuration parser.
4 */
5
6/*
7 * Copyright (C) 2012-2016 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 unsigned 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 unsigned 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
413 AssertFailed();
414 return NULL;
415}
416
417/**
418 * Returns a stringified version of the token.
419 *
420 * @returns Stringified version of the token type.
421 * @param pToken Token.
422 */
423static const char *autostartConfigTokenToString(PCFGTOKEN pToken)
424{
425 if (pToken->enmType == CFGTOKENTYPE_ID)
426 return pToken->u.Id.achToken;
427 else
428 return autostartConfigTokenTypeToStr(pToken->enmType);
429}
430
431/**
432 * Returns the length of the token in characters (without zero terminator).
433 *
434 * @returns Token length.
435 * @param pToken Token.
436 */
437static size_t autostartConfigTokenGetLength(PCFGTOKEN pToken)
438{
439 switch (pToken->enmType)
440 {
441 case CFGTOKENTYPE_COMMA:
442 case CFGTOKENTYPE_EQUAL:
443 case CFGTOKENTYPE_CURLY_OPEN:
444 case CFGTOKENTYPE_CURLY_CLOSING:
445 return 1;
446 case CFGTOKENTYPE_EOF:
447 return 0;
448 case CFGTOKENTYPE_ID:
449 return strlen(pToken->u.Id.achToken);
450 default:
451 AssertFailed();
452 return 0;
453 }
454
455 AssertFailed();
456 return 0;
457}
458
459/**
460 * Log unexpected token error.
461 *
462 * @returns nothing.
463 * @param pToken The token which caused the error.
464 * @param pszExpected String of the token which was expected.
465 */
466static void autostartConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected)
467{
468 autostartSvcLogError("Unexpected token '%s' at %d:%d.%d, expected '%s'",
469 autostartConfigTokenToString(pToken),
470 pToken->iLine, pToken->cchStart,
471 pToken->cchStart + autostartConfigTokenGetLength(pToken) - 1, pszExpected);
472}
473
474/**
475 * Verfies a token and consumes it.
476 *
477 * @returns VBox status code.
478 * @param pCfgTokenizer The config tokenizer.
479 * @param pszTokenCheck The token to check for.
480 */
481static int autostartConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
482{
483 int rc = VINF_SUCCESS;
484 PCFGTOKEN pCfgToken = NULL;
485
486 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
487 if (RT_SUCCESS(rc))
488 {
489 if (pCfgToken->enmType != enmType)
490 {
491 autostartConfigTokenizerMsgUnexpectedToken(pCfgToken, autostartConfigTokenTypeToStr(enmType));
492 rc = VERR_INVALID_PARAMETER;
493 }
494
495 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
496 }
497 return rc;
498}
499
500/**
501 * Consumes the next token in the stream.
502 *
503 * @returns VBox status code.
504 * @param pCfgTokenizer Tokenizer instance data.
505 */
506static int autostartConfigTokenizerConsume(PCFGTOKENIZER pCfgTokenizer)
507{
508 int rc = VINF_SUCCESS;
509 PCFGTOKEN pCfgToken = NULL;
510
511 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
512 if (RT_SUCCESS(rc))
513 autostartConfigTokenFree(pCfgTokenizer, pCfgToken);
514
515 return rc;
516}
517
518/**
519 * Returns the start of the next token without consuming it.
520 *
521 * @returns The next token without consuming it.
522 * @param pCfgTokenizer Tokenizer instance data.
523 */
524DECLINLINE(PCFGTOKEN) autostartConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer)
525{
526 return pCfgTokenizer->pTokenNext;
527}
528
529/**
530 * Check whether the next token is equal to the given one.
531 *
532 * @returns true if the next token in the stream is equal to the given one
533 * false otherwise.
534 * @param pszToken The token to check for.
535 */
536DECLINLINE(bool) autostartConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
537{
538 PCFGTOKEN pToken = autostartConfigTokenizerPeek(pCfgTokenizer);
539 return pToken->enmType == enmType;
540}
541
542/**
543 * Parse a key value node and returns the AST.
544 *
545 * @returns VBox status code.
546 * @param pCfgTokenizer The tokenizer for the config stream.
547 * @param pszKey The key for the pair.
548 * @param ppCfgAst Where to store the resulting AST on success.
549 */
550static int autostartConfigParseValue(PCFGTOKENIZER pCfgTokenizer, const char *pszKey,
551 PCFGAST *ppCfgAst)
552{
553 int rc = VINF_SUCCESS;
554 PCFGTOKEN pToken = NULL;
555
556 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
557 if ( RT_SUCCESS(rc)
558 && pToken->enmType == CFGTOKENTYPE_ID)
559 {
560 PCFGAST pCfgAst = NULL;
561
562 pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
563 if (!pCfgAst)
564 return VERR_NO_MEMORY;
565
566 pCfgAst->enmType = CFGASTNODETYPE_KEYVALUE;
567 pCfgAst->pszKey = RTStrDup(pszKey);
568 if (!pCfgAst->pszKey)
569 {
570 RTMemFree(pCfgAst);
571 return VERR_NO_MEMORY;
572 }
573
574 memcpy(pCfgAst->u.KeyValue.aszValue, pToken->u.Id.achToken, pToken->u.Id.cchToken);
575 pCfgAst->u.KeyValue.cchValue = pToken->u.Id.cchToken;
576 *ppCfgAst = pCfgAst;
577 }
578 else
579 {
580 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
581 rc = VERR_INVALID_PARAMETER;
582 }
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 int rc = VINF_SUCCESS;
599 unsigned cAstNodesMax = 10;
600 unsigned idxAstNodeCur = 0;
601 PCFGAST pCfgAst = NULL;
602
603 pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
604 if (!pCfgAst)
605 return VERR_NO_MEMORY;
606
607 pCfgAst->enmType = CFGASTNODETYPE_COMPOUND;
608 pCfgAst->u.Compound.cAstNodes = 0;
609 pCfgAst->pszKey = RTStrDup(pszScopeId);
610 if (!pCfgAst->pszKey)
611 {
612 RTMemFree(pCfgAst);
613 return VERR_NO_MEMORY;
614 }
615
616 do
617 {
618 PCFGTOKEN pToken = NULL;
619 PCFGAST pAstNode = NULL;
620
621 if ( autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING)
622 || autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_EOF))
623 break;
624
625 rc = autostartConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
626 if ( RT_SUCCESS(rc)
627 && pToken->enmType == CFGTOKENTYPE_ID)
628 {
629 /* Next must be a = token in all cases at this place. */
630 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EQUAL);
631 if (RT_SUCCESS(rc))
632 {
633 /* Check whether this is a compound node. */
634 if (autostartConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_OPEN))
635 {
636 rc = autostartConfigTokenizerConsume(pCfgTokenizer);
637 if (RT_SUCCESS(rc))
638 rc = autostartConfigParseCompoundNode(pCfgTokenizer, pToken->u.Id.achToken,
639 &pAstNode);
640
641 if (RT_SUCCESS(rc))
642 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING);
643 }
644 else
645 rc = autostartConfigParseValue(pCfgTokenizer, pToken->u.Id.achToken,
646 &pAstNode);
647 }
648 }
649 else if (RT_SUCCESS(rc))
650 {
651 autostartConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token");
652 rc = VERR_INVALID_PARAMETER;
653 }
654
655 /* Add to the current compound node. */
656 if (RT_SUCCESS(rc))
657 {
658 if (pCfgAst->u.Compound.cAstNodes >= cAstNodesMax)
659 {
660 cAstNodesMax += 10;
661
662 PCFGAST pCfgAstNew = (PCFGAST)RTMemRealloc(pCfgAst, RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
663 if (!pCfgAstNew)
664 rc = VERR_NO_MEMORY;
665 else
666 pCfgAst = pCfgAstNew;
667 }
668
669 if (RT_SUCCESS(rc))
670 {
671 pCfgAst->u.Compound.apAstNodes[pCfgAst->u.Compound.cAstNodes] = pAstNode;
672 pCfgAst->u.Compound.cAstNodes++;
673 }
674 }
675
676 autostartConfigTokenFree(pCfgTokenizer, pToken);
677
678 } while (RT_SUCCESS(rc));
679
680 if (RT_SUCCESS(rc))
681 *ppCfgAst = pCfgAst;
682 else
683 autostartConfigAstDestroy(pCfgAst);
684
685 return rc;
686}
687
688DECLHIDDEN(int) autostartParseConfig(const char *pszFilename, PCFGAST *ppCfgAst)
689{
690 PCFGTOKENIZER pCfgTokenizer = NULL;
691 int rc = VINF_SUCCESS;
692 PCFGAST pCfgAst = NULL;
693
694 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
695 AssertPtrReturn(ppCfgAst, VERR_INVALID_POINTER);
696
697 rc = autostartConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
698 if (RT_SUCCESS(rc))
699 {
700 rc = autostartConfigParseCompoundNode(pCfgTokenizer, "", &pCfgAst);
701 if (RT_SUCCESS(rc))
702 rc = autostartConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EOF);
703 }
704
705 if (pCfgTokenizer)
706 autostartConfigTokenizerDestroy(pCfgTokenizer);
707
708 if (RT_SUCCESS(rc))
709 *ppCfgAst = pCfgAst;
710
711 return rc;
712}
713
714DECLHIDDEN(void) autostartConfigAstDestroy(PCFGAST pCfgAst)
715{
716 AssertPtrReturnVoid(pCfgAst);
717
718 switch (pCfgAst->enmType)
719 {
720 case CFGASTNODETYPE_KEYVALUE:
721 {
722 RTMemFree(pCfgAst);
723 break;
724 }
725 case CFGASTNODETYPE_COMPOUND:
726 {
727 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
728 autostartConfigAstDestroy(pCfgAst->u.Compound.apAstNodes[i]);
729 RTMemFree(pCfgAst);
730 break;
731 }
732 case CFGASTNODETYPE_LIST:
733 default:
734 AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType));
735 }
736}
737
738DECLHIDDEN(PCFGAST) autostartConfigAstGetByName(PCFGAST pCfgAst, const char *pszName)
739{
740 if (!pCfgAst)
741 return NULL;
742
743 AssertReturn(pCfgAst->enmType == CFGASTNODETYPE_COMPOUND, NULL);
744
745 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
746 {
747 PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
748
749 if (!RTStrCmp(pNode->pszKey, pszName))
750 return pNode;
751 }
752
753 return NULL;
754}
755
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