VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/usb/UsbTestServiceCfg.cpp@ 61239

Last change on this file since 61239 was 61024, checked in by vboxsync, 9 years ago

copyright header fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.2 KB
Line 
1/* $Id: UsbTestServiceCfg.cpp 61024 2016-05-18 07:45:51Z vboxsync $ */
2/** @file
3 * UsbTestServ - Remote USB test configuration and execution server, Config file API.
4 */
5
6/*
7 * Copyright (C) 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31
32#include <iprt/stream.h>
33#include <iprt/process.h>
34#include <iprt/string.h>
35#include <iprt/mem.h>
36#include <iprt/ctype.h>
37#include <iprt/message.h>
38
39#include "UsbTestServiceCfg.h"
40
41
42/*********************************************************************************************************************************
43* Constants And Macros, Structures and Typedefs *
44*********************************************************************************************************************************/
45
46/**
47 * Token type.
48 */
49typedef enum CFGTOKENTYPE
50{
51 /** Invalid token type. */
52 CFGTOKENTYPE_INVALID = 0,
53 /** Identifier. */
54 CFGTOKENTYPE_ID,
55 /** Comma. */
56 CFGTOKENTYPE_COMMA,
57 /** Equal sign. */
58 CFGTOKENTYPE_EQUAL,
59 /** Open curly brackets. */
60 CFGTOKENTYPE_CURLY_OPEN,
61 /** Closing curly brackets. */
62 CFGTOKENTYPE_CURLY_CLOSING,
63 /** End of file. */
64 CFGTOKENTYPE_EOF,
65 /** 32bit hack. */
66 CFGTOKENTYPE_32BIT_HACK = 0x7fffffff
67} CFGTOKENTYPE;
68/** Pointer to a token type. */
69typedef CFGTOKENTYPE *PCFGTOKENTYPE;
70/** Pointer to a const token type. */
71typedef const CFGTOKENTYPE *PCCFGTOKENTYPE;
72
73/**
74 * A token.
75 */
76typedef struct CFGTOKEN
77{
78 /** Type of the token. */
79 CFGTOKENTYPE enmType;
80 /** Line number of the token. */
81 unsigned iLine;
82 /** Starting character of the token in the stream. */
83 unsigned cchStart;
84 /** Type dependen token data. */
85 union
86 {
87 /** Data for the ID type. */
88 struct
89 {
90 /** Size of the id in characters, excluding the \0 terminator. */
91 size_t cchToken;
92 /** Token data, variable size (given by cchToken member). */
93 char achToken[1];
94 } Id;
95 } u;
96} CFGTOKEN;
97/** Pointer to a token. */
98typedef CFGTOKEN *PCFGTOKEN;
99/** Pointer to a const token. */
100typedef const CFGTOKEN *PCCFGTOKEN;
101
102/**
103 * Tokenizer instance data for the config data.
104 */
105typedef struct CFGTOKENIZER
106{
107 /** Config file handle. */
108 PRTSTREAM hStrmConfig;
109 /** String buffer for the current line we are operating in. */
110 char *pszLine;
111 /** Size of the string buffer. */
112 size_t cbLine;
113 /** Current position in the line. */
114 char *pszLineCurr;
115 /** Current line in the config file. */
116 unsigned iLine;
117 /** Current character of the line. */
118 unsigned cchCurr;
119 /** Flag whether the end of the config stream is reached. */
120 bool fEof;
121 /** Pointer to the next token in the stream (used to peek). */
122 PCFGTOKEN pTokenNext;
123} CFGTOKENIZER, *PCFGTOKENIZER;
124
125
126/*********************************************************************************************************************************
127* Internal Functions *
128*********************************************************************************************************************************/
129
130/**
131 * Free a config token.
132 *
133 * @returns nothing.
134 * @param pCfgTokenizer The config tokenizer.
135 * @param pToken The token to free.
136 */
137static void utsConfigTokenFree(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 utsConfigTokenizerReadNextLine(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 utsConfigTokenizerCreateToken(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 = utsConfigTokenizerReadNextLine(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 utsConfigTokenFree(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 utsConfigTokenFree(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 * @returns nothing.
324 * @param pCfgTokenizer The config tokenizer to destroy.
325 */
326static void utsConfigTokenizerDestroy(PCFGTOKENIZER pCfgTokenizer)
327{
328 if (pCfgTokenizer->pszLine)
329 RTMemFree(pCfgTokenizer->pszLine);
330 if (pCfgTokenizer->hStrmConfig)
331 RTStrmClose(pCfgTokenizer->hStrmConfig);
332 if (pCfgTokenizer->pTokenNext)
333 RTMemFree(pCfgTokenizer->pTokenNext);
334 RTMemFree(pCfgTokenizer);
335}
336
337/**
338 * Creates the config tokenizer from the given filename.
339 *
340 * @returns VBox status code.
341 * @param pszFilename Config filename.
342 * @param ppCfgTokenizer Where to store the pointer to the config tokenizer on
343 * success.
344 */
345static int utsConfigTokenizerCreate(const char *pszFilename, PCFGTOKENIZER *ppCfgTokenizer)
346{
347 int rc = VINF_SUCCESS;
348 PCFGTOKENIZER pCfgTokenizer = (PCFGTOKENIZER)RTMemAllocZ(sizeof(CFGTOKENIZER));
349
350 if (pCfgTokenizer)
351 {
352 pCfgTokenizer->iLine = 0;
353 pCfgTokenizer->cbLine = 128;
354 pCfgTokenizer->pszLine = (char *)RTMemAllocZ(pCfgTokenizer->cbLine);
355 if (pCfgTokenizer->pszLine)
356 {
357 rc = RTStrmOpen(pszFilename, "r", &pCfgTokenizer->hStrmConfig);
358 if (RT_SUCCESS(rc))
359 {
360 rc = utsConfigTokenizerReadNextLine(pCfgTokenizer);
361 if (RT_SUCCESS(rc))
362 rc = utsConfigTokenizerCreateToken(pCfgTokenizer, NULL,
363 &pCfgTokenizer->pTokenNext);
364 }
365 }
366 else
367 rc = VERR_NO_MEMORY;
368 }
369 else
370 rc = VERR_NO_MEMORY;
371
372 if (RT_SUCCESS(rc))
373 *ppCfgTokenizer = pCfgTokenizer;
374 else if ( RT_FAILURE(rc)
375 && pCfgTokenizer)
376 utsConfigTokenizerDestroy(pCfgTokenizer);
377
378 return rc;
379}
380
381/**
382 * Return the next token from the config stream.
383 *
384 * @returns VBox status code.
385 * @param pCfgTokenizer The config tokenizer.
386 * @param ppCfgToken Where to store the next token.
387 */
388static int utsConfigTokenizerGetNextToken(PCFGTOKENIZER pCfgTokenizer,
389 PCFGTOKEN *ppCfgToken)
390{
391 *ppCfgToken = pCfgTokenizer->pTokenNext;
392 return utsConfigTokenizerCreateToken(pCfgTokenizer, NULL, &pCfgTokenizer->pTokenNext);
393}
394
395/**
396 * Returns a stringified version of the token type.
397 *
398 * @returns Stringified version of the token type.
399 * @param enmType Token type.
400 */
401static const char *utsConfigTokenTypeToStr(CFGTOKENTYPE enmType)
402{
403 switch (enmType)
404 {
405 case CFGTOKENTYPE_COMMA:
406 return ",";
407 case CFGTOKENTYPE_EQUAL:
408 return "=";
409 case CFGTOKENTYPE_CURLY_OPEN:
410 return "{";
411 case CFGTOKENTYPE_CURLY_CLOSING:
412 return "}";
413 case CFGTOKENTYPE_EOF:
414 return "<EOF>";
415 case CFGTOKENTYPE_ID:
416 return "<Identifier>";
417 default:
418 AssertFailed();
419 return "<Invalid>";
420 }
421
422 AssertFailed();
423 return NULL;
424}
425
426/**
427 * Returns a stringified version of the token.
428 *
429 * @returns Stringified version of the token type.
430 * @param pToken Token.
431 */
432static const char *utsConfigTokenToString(PCFGTOKEN pToken)
433{
434 if (pToken->enmType == CFGTOKENTYPE_ID)
435 return pToken->u.Id.achToken;
436 else
437 return utsConfigTokenTypeToStr(pToken->enmType);
438}
439
440/**
441 * Returns the length of the token in characters (without zero terminator).
442 *
443 * @returns Token length.
444 * @param pToken Token.
445 */
446static size_t utsConfigTokenGetLength(PCFGTOKEN pToken)
447{
448 switch (pToken->enmType)
449 {
450 case CFGTOKENTYPE_COMMA:
451 case CFGTOKENTYPE_EQUAL:
452 case CFGTOKENTYPE_CURLY_OPEN:
453 case CFGTOKENTYPE_CURLY_CLOSING:
454 return 1;
455 case CFGTOKENTYPE_EOF:
456 return 0;
457 case CFGTOKENTYPE_ID:
458 return strlen(pToken->u.Id.achToken);
459 default:
460 AssertFailed();
461 return 0;
462 }
463
464 AssertFailed();
465 return 0;
466}
467
468/**
469 * Log unexpected token error.
470 *
471 * @returns nothing.
472 * @param pToken The token which caused the error.
473 * @param pszExpected String of the token which was expected.
474 * @param ppErrInfo Where to store the detailed error info.
475 */
476static void utsConfigTokenizerMsgUnexpectedToken(PCFGTOKEN pToken, const char *pszExpected,
477 PRTERRINFO *ppErrInfo)
478{
479 if (ppErrInfo)
480 {
481 PRTERRINFO pErrInfo = RTErrInfoAlloc(256);
482 if (RT_LIKELY(pErrInfo))
483 {
484 RTErrInfoSetF(pErrInfo, VERR_INVALID_STATE, "Unexpected token '%s' at %d:%d.%d, expected '%s'",
485 utsConfigTokenToString(pToken),
486 pToken->iLine, pToken->cchStart,
487 pToken->cchStart + utsConfigTokenGetLength(pToken) - 1, pszExpected);
488 *ppErrInfo = pErrInfo;
489 }
490 }
491}
492
493/**
494 * Verfies a token and consumes it.
495 *
496 * @returns VBox status code.
497 * @param pCfgTokenizer The config tokenizer.
498 * @param pszTokenCheck The token to check for.
499 * @param ppErrInfo Where to store the detailed error info.
500 */
501static int utsConfigTokenizerCheckAndConsume(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType,
502 PRTERRINFO *ppErrInfo)
503{
504 int rc = VINF_SUCCESS;
505 PCFGTOKEN pCfgToken = NULL;
506
507 rc = utsConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
508 if (RT_SUCCESS(rc))
509 {
510 if (pCfgToken->enmType != enmType)
511 {
512 utsConfigTokenizerMsgUnexpectedToken(pCfgToken, utsConfigTokenTypeToStr(enmType), ppErrInfo);
513 rc = VERR_INVALID_PARAMETER;
514 }
515
516 utsConfigTokenFree(pCfgTokenizer, pCfgToken);
517 }
518 return rc;
519}
520
521/**
522 * Consumes the next token in the stream.
523 *
524 * @returns VBox status code.
525 * @param pCfgTokenizer Tokenizer instance data.
526 */
527static int utsConfigTokenizerConsume(PCFGTOKENIZER pCfgTokenizer)
528{
529 int rc = VINF_SUCCESS;
530 PCFGTOKEN pCfgToken = NULL;
531
532 rc = utsConfigTokenizerGetNextToken(pCfgTokenizer, &pCfgToken);
533 if (RT_SUCCESS(rc))
534 utsConfigTokenFree(pCfgTokenizer, pCfgToken);
535
536 return rc;
537}
538
539/**
540 * Returns the start of the next token without consuming it.
541 *
542 * @returns The next token without consuming it.
543 * @param pCfgTokenizer Tokenizer instance data.
544 */
545DECLINLINE(PCFGTOKEN) utsConfigTokenizerPeek(PCFGTOKENIZER pCfgTokenizer)
546{
547 return pCfgTokenizer->pTokenNext;
548}
549
550/**
551 * Check whether the next token is equal to the given one.
552 *
553 * @returns true if the next token in the stream is equal to the given one
554 * false otherwise.
555 * @param pszToken The token to check for.
556 */
557DECLINLINE(bool) utsConfigTokenizerPeekIsEqual(PCFGTOKENIZER pCfgTokenizer, CFGTOKENTYPE enmType)
558{
559 PCFGTOKEN pToken = utsConfigTokenizerPeek(pCfgTokenizer);
560 return pToken->enmType == enmType;
561}
562
563/**
564 * Parse a key value node and returns the AST.
565 *
566 * @returns VBox status code.
567 * @param pCfgTokenizer The tokenizer for the config stream.
568 * @param pszKey The key for the pair.
569 * @param ppCfgAst Where to store the resulting AST on success.
570 * @param ppErrInfo Where to store the detailed error info.
571 */
572static int utsConfigParseValue(PCFGTOKENIZER pCfgTokenizer, const char *pszKey,
573 PCFGAST *ppCfgAst, PRTERRINFO *ppErrInfo)
574{
575 int rc = VINF_SUCCESS;
576 PCFGTOKEN pToken = NULL;
577
578 rc = utsConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
579 if ( RT_SUCCESS(rc)
580 && pToken->enmType == CFGTOKENTYPE_ID)
581 {
582 PCFGAST pCfgAst = NULL;
583
584 pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.KeyValue.aszValue[pToken->u.Id.cchToken + 1]));
585 if (!pCfgAst)
586 return VERR_NO_MEMORY;
587
588 pCfgAst->enmType = CFGASTNODETYPE_KEYVALUE;
589 pCfgAst->pszKey = RTStrDup(pszKey);
590 if (!pCfgAst->pszKey)
591 {
592 RTMemFree(pCfgAst);
593 return VERR_NO_MEMORY;
594 }
595
596 memcpy(pCfgAst->u.KeyValue.aszValue, pToken->u.Id.achToken, pToken->u.Id.cchToken);
597 pCfgAst->u.KeyValue.cchValue = pToken->u.Id.cchToken;
598 *ppCfgAst = pCfgAst;
599 }
600 else
601 {
602 utsConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token", ppErrInfo);
603 rc = VERR_INVALID_PARAMETER;
604 }
605
606 return rc;
607}
608
609/**
610 * Parses a compound node constructing the AST and returning it on success.
611 *
612 * @returns VBox status code.
613 * @param pCfgTokenizer The tokenizer for the config stream.
614 * @param pszScopeId The scope ID of the compound node.
615 * @param ppCfgAst Where to store the resulting AST on success.
616 */
617static int utsConfigParseCompoundNode(PCFGTOKENIZER pCfgTokenizer, const char *pszScopeId,
618 PCFGAST *ppCfgAst, PRTERRINFO *ppErrInfo)
619{
620 int rc = VINF_SUCCESS;
621 unsigned cAstNodesMax = 10;
622 unsigned idxAstNodeCur = 0;
623 PCFGAST pCfgAst = NULL;
624
625 pCfgAst = (PCFGAST)RTMemAllocZ(RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
626 if (!pCfgAst)
627 return VERR_NO_MEMORY;
628
629 pCfgAst->enmType = CFGASTNODETYPE_COMPOUND;
630 pCfgAst->u.Compound.cAstNodes = 0;
631 pCfgAst->pszKey = RTStrDup(pszScopeId);
632 if (!pCfgAst->pszKey)
633 {
634 RTMemFree(pCfgAst);
635 return VERR_NO_MEMORY;
636 }
637
638 do
639 {
640 PCFGTOKEN pToken = NULL;
641 PCFGAST pAstNode = NULL;
642
643 if ( utsConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING)
644 || utsConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_EOF))
645 break;
646
647 rc = utsConfigTokenizerGetNextToken(pCfgTokenizer, &pToken);
648 if ( RT_SUCCESS(rc)
649 && pToken->enmType == CFGTOKENTYPE_ID)
650 {
651 /* Next must be a = token in all cases at this place. */
652 rc = utsConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EQUAL, ppErrInfo);
653 if (RT_SUCCESS(rc))
654 {
655 /* Check whether this is a compound node. */
656 if (utsConfigTokenizerPeekIsEqual(pCfgTokenizer, CFGTOKENTYPE_CURLY_OPEN))
657 {
658 rc = utsConfigTokenizerConsume(pCfgTokenizer);
659 if (RT_SUCCESS(rc))
660 rc = utsConfigParseCompoundNode(pCfgTokenizer, pToken->u.Id.achToken,
661 &pAstNode, ppErrInfo);
662
663 if (RT_SUCCESS(rc))
664 rc = utsConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_CURLY_CLOSING, ppErrInfo);
665 }
666 else
667 rc = utsConfigParseValue(pCfgTokenizer, pToken->u.Id.achToken,
668 &pAstNode, ppErrInfo);
669 }
670 }
671 else if (RT_SUCCESS(rc))
672 {
673 utsConfigTokenizerMsgUnexpectedToken(pToken, "non reserved token", ppErrInfo);
674 rc = VERR_INVALID_PARAMETER;
675 }
676
677 /* Add to the current compound node. */
678 if (RT_SUCCESS(rc))
679 {
680 if (pCfgAst->u.Compound.cAstNodes >= cAstNodesMax)
681 {
682 cAstNodesMax += 10;
683
684 PCFGAST pCfgAstNew = (PCFGAST)RTMemRealloc(pCfgAst, RT_OFFSETOF(CFGAST, u.Compound.apAstNodes[cAstNodesMax]));
685 if (!pCfgAstNew)
686 rc = VERR_NO_MEMORY;
687 else
688 pCfgAst = pCfgAstNew;
689 }
690
691 if (RT_SUCCESS(rc))
692 {
693 pCfgAst->u.Compound.apAstNodes[pCfgAst->u.Compound.cAstNodes] = pAstNode;
694 pCfgAst->u.Compound.cAstNodes++;
695 }
696 }
697
698 utsConfigTokenFree(pCfgTokenizer, pToken);
699
700 } while (RT_SUCCESS(rc));
701
702 if (RT_SUCCESS(rc))
703 *ppCfgAst = pCfgAst;
704 else
705 utsConfigAstDestroy(pCfgAst);
706
707 return rc;
708}
709
710DECLHIDDEN(int) utsParseConfig(const char *pszFilename, PCFGAST *ppCfgAst, PRTERRINFO *ppErrInfo)
711{
712 PCFGTOKENIZER pCfgTokenizer = NULL;
713 int rc = VINF_SUCCESS;
714 PCFGAST pCfgAst = NULL;
715
716 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
717 AssertPtrReturn(ppCfgAst, VERR_INVALID_POINTER);
718
719 rc = utsConfigTokenizerCreate(pszFilename, &pCfgTokenizer);
720 if (RT_SUCCESS(rc))
721 {
722 rc = utsConfigParseCompoundNode(pCfgTokenizer, "", &pCfgAst, ppErrInfo);
723 if (RT_SUCCESS(rc))
724 rc = utsConfigTokenizerCheckAndConsume(pCfgTokenizer, CFGTOKENTYPE_EOF, ppErrInfo);
725 }
726
727 if (pCfgTokenizer)
728 utsConfigTokenizerDestroy(pCfgTokenizer);
729
730 if (RT_SUCCESS(rc))
731 *ppCfgAst = pCfgAst;
732
733 return rc;
734}
735
736DECLHIDDEN(void) utsConfigAstDestroy(PCFGAST pCfgAst)
737{
738 AssertPtrReturnVoid(pCfgAst);
739
740 switch (pCfgAst->enmType)
741 {
742 case CFGASTNODETYPE_KEYVALUE:
743 {
744 RTMemFree(pCfgAst);
745 break;
746 }
747 case CFGASTNODETYPE_COMPOUND:
748 {
749 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
750 utsConfigAstDestroy(pCfgAst->u.Compound.apAstNodes[i]);
751 RTMemFree(pCfgAst);
752 break;
753 }
754 case CFGASTNODETYPE_LIST:
755 default:
756 AssertMsgFailed(("Invalid AST node type %d\n", pCfgAst->enmType));
757 }
758}
759
760DECLHIDDEN(PCFGAST) utsConfigAstGetByName(PCFGAST pCfgAst, const char *pszName)
761{
762 if (!pCfgAst)
763 return NULL;
764
765 AssertReturn(pCfgAst->enmType == CFGASTNODETYPE_COMPOUND, NULL);
766
767 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
768 {
769 PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
770
771 if (!RTStrCmp(pNode->pszKey, pszName))
772 return pNode;
773 }
774
775 return NULL;
776}
777
Note: See TracBrowser for help on using the repository browser.

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