VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/inifile.cpp@ 100249

Last change on this file since 100249 was 99758, checked in by vboxsync, 19 months ago

IPRT: Make doxygen 1.9.6 happy. Mostly removing duplicate docs (iprt is documented in the header files). bugref:10442

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.3 KB
Line 
1/* $Id: inifile.cpp 99758 2023-05-11 21:37:59Z vboxsync $ */
2/** @file
3 * IPRT - INI-file parser.
4 */
5
6/*
7 * Copyright (C) 2017-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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/inifile.h>
42#include "internal/iprt.h"
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/ctype.h>
47#include <iprt/err.h>
48#include <iprt/latin1.h>
49#include <iprt/mem.h>
50#include <iprt/string.h>
51#include <iprt/utf16.h>
52#include <iprt/vfs.h>
53
54#include "internal/magics.h"
55
56
57/*********************************************************************************************************************************
58* Defined Constants And Macros *
59*********************************************************************************************************************************/
60/** @def RTINIFILE_MAX_SIZE
61 * The maximum INI-file size we accept loading. */
62#if ARCH_BITS > 32
63# define RTINIFILE_MAX_SIZE (_64M - 2U)
64#elif ARCH_BITS > 16
65# define RTINIFILE_MAX_SIZE (_16M - 2U)
66#else
67# define RTINIFILE_MAX_SIZE (_64K - 2U)
68#endif
69
70/** @def RTINIFILE_MAX_SECTIONS
71 * The maximum number of sections we accept in an INI-file. */
72#if ARCH_BITS > 32
73# define RTINIFILE_MAX_SECTIONS (_1M)
74#elif ARCH_BITS > 16
75# define RTINIFILE_MAX_SECTIONS (_256K)
76#else
77# define RTINIFILE_MAX_SECTIONS (_1K)
78#endif
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84/**
85 * File encoding types.
86 */
87typedef enum RTINIFILEENCODING
88{
89 /** The customary invalid zero value. */
90 RTINIFILEENCODING_INVALID = 0,
91 /** We treat this as latin-1. */
92 RTINIFILEENCODING_ANSI,
93 /** UTF-8. */
94 RTINIFILEENCODING_UTF8,
95 /** Little endian UTF-16. */
96 RTINIFILEENCODING_UTF16LE,
97 /** Big endian UTF-16. */
98 RTINIFILEENCODING_UTF16BE,
99 /** End of valid encoding types. */
100 RTINIFILEENCODING_END
101} RTINIFILEENCODING;
102
103
104/**
105 * Preparsed section info.
106 */
107typedef struct RTINIFILESECTION
108{
109 /** The section name offset (byte). */
110 uint32_t offName;
111 /** The section length in bytes starting with the name. */
112 uint32_t cchSection;
113 /** The UTF-8 length of the section name. */
114 uint32_t cchName;
115 /** Offset into the section where to start looking for values. */
116 uint32_t cchSkipToValues : 24;
117 /** @todo use 4 bits for flags and stuff. like escaped name. */
118} RTINIFILESECTION;
119/** Pointer to preparsed section info. */
120typedef RTINIFILESECTION *PRTINIFILESECTION;
121
122
123/**
124 * INI-file instance data.
125 */
126typedef struct RTINIFILEINT
127{
128 /** Magic value (RTINIFILEINT_MAGIC). */
129 uint32_t u32Magic;
130 /** Reference counter. */
131 uint32_t volatile cRefs;
132 /** The file we're working on. */
133 RTVFSFILE hVfsFile;
134 /** Flags, RTINIFILE_F_XXX. */
135 uint32_t fFlags;
136
137 /** The original file encoding. */
138 RTINIFILEENCODING enmEncoding;
139 /** Pointer to the file content (converted to UTF-8). */
140 char *pszFile;
141 /** The file size. */
142 uint32_t cbFile;
143 /** Number of sections. */
144 uint32_t cSections;
145 /** Sections in the loaded file. */
146 PRTINIFILESECTION paSections;
147
148} RTINIFILEINT;
149/** Pointer to an INI-file instance. */
150typedef RTINIFILEINT *PRTINIFILEINT;
151
152
153static int rtIniFileLoad(PRTINIFILEINT pThis)
154{
155 /*
156 * Load the entire file into memory, ensuring two terminating zeros.
157 */
158 uint64_t cbFile;
159 int rc = RTVfsFileQuerySize(pThis->hVfsFile, &cbFile);
160 AssertRCReturn(rc, rc);
161
162 if (cbFile > RTINIFILE_MAX_SIZE)
163 return VERR_TOO_MUCH_DATA;
164 if (cbFile == 0)
165 return VINF_SUCCESS; /* Nothing to do. */
166
167 pThis->cbFile = (uint32_t)cbFile;
168 pThis->pszFile = (char *)RTMemAllocZ(pThis->cbFile + 2);
169 if (!pThis->pszFile)
170 return VERR_NO_MEMORY;
171
172 rc = RTVfsFileReadAt(pThis->hVfsFile, 0, pThis->pszFile, pThis->cbFile, NULL);
173 AssertRCReturn(rc, rc);
174
175 /*
176 * Detect encoding and convert to BOM prefixed UTF-8.
177 */
178 if ( (uint8_t)pThis->pszFile[0] == UINT8_C(0xef)
179 && (uint8_t)pThis->pszFile[1] == UINT8_C(0xbb)
180 && (uint8_t)pThis->pszFile[2] == UINT8_C(0xbf))
181 {
182 pThis->enmEncoding = RTINIFILEENCODING_UTF8;
183 rc = RTStrValidateEncoding(&pThis->pszFile[3]);
184 if (RT_FAILURE(rc))
185 return rc;
186 }
187 else
188 {
189 size_t cchUtf8;
190 if ( (uint8_t)pThis->pszFile[0] == UINT8_C(0xfe)
191 && (uint8_t)pThis->pszFile[1] == UINT8_C(0xff))
192 {
193 pThis->enmEncoding = RTINIFILEENCODING_UTF16BE;
194 rc = RTUtf16BigCalcUtf8LenEx((PCRTUTF16)&pThis->pszFile[2], RTSTR_MAX, &cchUtf8);
195 }
196 else if ( (uint8_t)pThis->pszFile[0] == UINT8_C(0xff)
197 && (uint8_t)pThis->pszFile[1] == UINT8_C(0xfe))
198 {
199 pThis->enmEncoding = RTINIFILEENCODING_UTF16LE;
200 rc = RTUtf16LittleCalcUtf8LenEx((PCRTUTF16)&pThis->pszFile[2], RTSTR_MAX, &cchUtf8);
201 }
202 else
203 {
204 pThis->enmEncoding = RTINIFILEENCODING_ANSI;
205 rc = RTLatin1CalcUtf8LenEx(pThis->pszFile, RTSTR_MAX, &cchUtf8);
206 }
207 if (RT_FAILURE(rc))
208 return rc;
209
210 char *pszUtf8Bom = (char *)RTMemAllocZ(3 + cchUtf8 + 1);
211 if (!pszUtf8Bom)
212 return VERR_NO_MEMORY;
213 pszUtf8Bom[0] = '\xEF';
214 pszUtf8Bom[1] = '\xBB';
215 pszUtf8Bom[2] = '\xBF';
216
217 char *pszUtf8 = pszUtf8Bom + 3;
218 if (pThis->enmEncoding == RTINIFILEENCODING_UTF16BE)
219 rc = RTUtf16BigToUtf8Ex((PCRTUTF16)&pThis->pszFile[2], RTSTR_MAX, &pszUtf8, cchUtf8 + 1, NULL);
220 else if (pThis->enmEncoding == RTINIFILEENCODING_UTF16LE)
221 rc = RTUtf16LittleToUtf8Ex((PCRTUTF16)&pThis->pszFile[2], RTSTR_MAX, &pszUtf8, cchUtf8 + 1, NULL);
222 else
223 rc = RTLatin1ToUtf8Ex(pThis->pszFile, RTSTR_MAX, &pszUtf8, cchUtf8 + 1, NULL);
224 AssertRCReturnStmt(rc, RTMemFree(pszUtf8Bom), rc);
225
226 RTMemFree(pThis->pszFile);
227 pThis->pszFile = pszUtf8Bom;
228 pThis->cbFile = 3 + (uint32_t)cchUtf8;
229 }
230
231 /*
232 * Do a rough section count.
233 * Section zero is for unsectioned values at the start of the file.
234 */
235 uint32_t cSections = 1;
236 const char *psz = pThis->pszFile + 3;
237 char ch;
238 while ((ch = *psz) != '\0')
239 {
240 while (RT_C_IS_SPACE(ch))
241 ch = *++psz;
242 if (ch == '[')
243 cSections++;
244
245 /* next line. */
246 psz = strchr(psz, '\n');
247 if (psz)
248 psz++;
249 else
250 break;
251 }
252 if (cSections > RTINIFILE_MAX_SECTIONS)
253 return VERR_TOO_MUCH_DATA;
254
255 /*
256 * Allocation section array and do the preparsing.
257 */
258 pThis->paSections = (PRTINIFILESECTION)RTMemAllocZ(sizeof(pThis->paSections[0]) * cSections);
259 if (!pThis->paSections)
260 return VERR_NO_MEMORY;
261
262 uint32_t iSection = 0;
263 pThis->paSections[0].offName = 3;
264 pThis->paSections[0].cchName = 0;
265 pThis->paSections[0].cchSkipToValues = 0;
266 psz = pThis->pszFile + 3;
267 while ((ch = *psz) != '\0')
268 {
269 const char *const pszLine = psz;
270
271 while (RT_C_IS_SPACE(ch))
272 ch = *++psz;
273 if (ch == '[')
274 {
275 /* Complete previous section. */
276 pThis->paSections[iSection].cchSection = (uint32_t)(pszLine - &pThis->pszFile[pThis->paSections[iSection].offName]);
277
278 /* New section. */
279 iSection++;
280 AssertReturn(iSection < cSections, VERR_INTERNAL_ERROR_3);
281 const char * const pszName = ++psz;
282 pThis->paSections[iSection].offName = (uint32_t)(psz - pThis->pszFile);
283
284 /* Figure the name length. We're very very relaxed about terminating bracket. */
285 while ((ch = *psz) != '\0' && ch != ']' && ch != '\r' && ch != '\n')
286 psz++;
287 pThis->paSections[iSection].cchName = (uint32_t)(psz - pszName);
288
289 /* Set skip count to the start of the next line. */
290 while (ch != '\0' && ch != '\n')
291 ch = *++psz;
292 pThis->paSections[iSection].cchSkipToValues = (uint32_t)(psz - pszName + 1);
293
294 if (ch == '\n')
295 psz++;
296 else
297 break;
298 }
299 else
300 {
301 psz = strchr(psz, '\n');
302 if (psz)
303 psz++;
304 else
305 break;
306 }
307 }
308
309 /* Complete the final section. */
310 pThis->paSections[iSection].cchSection = pThis->cbFile - pThis->paSections[iSection].offName;
311 pThis->cSections = iSection + 1;
312
313 return VINF_SUCCESS;
314}
315
316
317RTDECL(int) RTIniFileCreateFromVfsFile(PRTINIFILE phIniFile, RTVFSFILE hVfsFile, uint32_t fFlags)
318{
319 /*
320 * Validate input, retaining a reference to the file.
321 */
322 AssertPtrReturn(phIniFile, VERR_INVALID_POINTER);
323 AssertReturn(!(fFlags & ~RTINIFILE_F_VALID_MASK), VERR_INVALID_FLAGS);
324
325 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
326 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
327
328 /*
329 * Create an instance.
330 */
331 PRTINIFILEINT pThis = (PRTINIFILEINT)RTMemAllocZ(sizeof(*pThis));
332 if (pThis)
333 {
334 pThis->u32Magic = RTINIFILE_MAGIC;
335 pThis->cRefs = 1;
336 pThis->hVfsFile = hVfsFile;
337 pThis->fFlags = fFlags;
338
339 int rc = rtIniFileLoad(pThis);
340 if (RT_SUCCESS(rc))
341 {
342
343 *phIniFile = pThis;
344 return VINF_SUCCESS;
345 }
346 RTIniFileRelease(pThis);
347 return rc;
348 }
349 RTVfsFileRelease(hVfsFile);
350 return VERR_NO_MEMORY;
351}
352
353
354RTDECL(uint32_t) RTIniFileRetain(RTINIFILE hIniFile)
355{
356 PRTINIFILEINT pThis = hIniFile;
357 AssertPtrReturn(pThis, UINT32_MAX);
358 AssertReturn(pThis->u32Magic == RTINIFILE_MAGIC, UINT32_MAX);
359
360 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
361 Assert(cRefs > 1);
362 Assert(cRefs < _64K);
363 return cRefs;
364}
365
366
367RTDECL(uint32_t) RTIniFileRelease(RTINIFILE hIniFile)
368{
369 if (hIniFile == NIL_RTINIFILE)
370 return 0;
371 PRTINIFILEINT pThis = hIniFile;
372 AssertPtrReturn(pThis, UINT32_MAX);
373 AssertReturn(pThis->u32Magic == RTINIFILE_MAGIC, UINT32_MAX);
374
375 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
376 Assert(cRefs < _64K);
377 if (cRefs == 0)
378 {
379 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTINIFILE_MAGIC_DEAD, RTINIFILE_MAGIC), UINT32_MAX);
380 RTMemFree(pThis->paSections);
381 pThis->paSections = NULL;
382 RTMemFree(pThis->pszFile);
383 pThis->pszFile = NULL;
384 RTVfsFileRelease(pThis->hVfsFile);
385 pThis->hVfsFile = NIL_RTVFSFILE;
386 RTMemFree(pThis);
387 }
388 return cRefs;
389}
390
391
392/**
393 * Worker for RTIniFileQueryValue.
394 */
395static int rtIniFileQueryValueInSection(PRTINIFILEINT pThis, PRTINIFILESECTION pSection, const char *pszKey, size_t cchKey,
396 char *pszValue, size_t cbValue, size_t *pcbActual)
397{
398 /*
399 * Scan the section, looking for the matching key.
400 */
401 Assert(pSection->cchSkipToValues <= pSection->cchSection);
402 const char * const pszEnd = &pThis->pszFile[pSection->offName + pSection->cchSection];
403 const char * pszNext = pszEnd;
404 for (const char *psz = &pThis->pszFile[pSection->offName + pSection->cchSkipToValues];
405 (uintptr_t)psz < (uintptr_t)pszEnd;
406 psz = pszNext)
407 {
408 /* Find start of next line so we can use 'continue' to skip a line. */
409 pszNext = strchr(psz, '\n');
410 if (pszNext)
411 pszNext++;
412 else
413 pszNext = pszEnd;
414
415 /* Skip leading spaces. */
416 char ch;
417 while ((ch = *psz) != '\0' && RT_C_IS_SPACE(ch))
418 psz++;
419 if ( ch != ';' /* comment line */
420 && ch != '\n' /* empty line */
421 && ch != '\r' /* empty line */
422 && (uintptr_t)psz < (uintptr_t)pszEnd)
423 {
424 /* Find end of key name, if any. */
425 const char *pszCurKey = psz;
426 size_t cchCurKey;
427 const char *pszEqual;
428 if (ch != '=')
429 {
430 /** @todo deal with escaped equal signs? */
431 pszEqual = strchr(psz, '=');
432 if (pszEqual)
433 {
434 if ((uintptr_t)pszEqual < (uintptr_t)pszNext)
435 cchCurKey = pszEqual - pszCurKey;
436 else
437 continue;
438 }
439 else
440 break;
441
442 /* Strip trailing spaces from the current key name. */
443 while (cchCurKey > 0 && RT_C_IS_SPACE(pszCurKey[cchCurKey - 1]))
444 cchCurKey--;
445 }
446 else
447 {
448 cchCurKey = 0;
449 pszEqual = psz;
450 }
451
452 /* Match the keys. */
453 /** @todo escape sequences? */
454 if ( cchCurKey == cchKey
455 && RTStrNICmp(pszCurKey, pszKey, cchKey) == 0)
456 {
457 /*
458 * Copy out the return value, without quotes.
459 */
460
461 /* Skip leading blanks. */
462 psz = pszEqual + 1;
463 while ((ch = *psz) && RT_C_IS_SPACE(ch) && ch != '\n')
464 psz++;
465
466 /* Strip trailing spaces. */
467 size_t cchCurValue = pszNext - psz;
468 while (cchCurValue > 1 && RT_C_IS_SPACE(psz[cchCurValue - 1]))
469 cchCurValue--;
470
471 /* Strip quotes. */
472 if ( cchCurValue > 2
473 && ( (ch = *psz) == '"'
474 || ch == '\'' )
475 && psz[cchCurValue - 1] == ch)
476 {
477 cchCurValue -= 2;
478 psz++;
479 }
480
481 /* Do the copying. */
482 if (cchCurValue < cbValue)
483 {
484 memcpy(pszValue, psz, cchCurValue);
485 pszValue[cchCurValue] = '\0';
486 if (pcbActual)
487 *pcbActual = cchCurValue;
488 return VINF_SUCCESS;
489 }
490
491 if (cbValue > 0)
492 {
493 memcpy(pszValue, psz, cbValue - 1);
494 pszValue[cbValue - 1] = '\0';
495 }
496 if (pcbActual)
497 *pcbActual = cchCurValue + 1;
498 return VERR_BUFFER_OVERFLOW;
499 }
500 }
501 }
502 return VERR_NOT_FOUND;
503}
504
505
506RTDECL(int) RTIniFileQueryValue(RTINIFILE hIniFile, const char *pszSection, const char *pszKey,
507 char *pszValue, size_t cbValue, size_t *pcbActual)
508{
509 /*
510 * Validate input.
511 */
512 PRTINIFILEINT pThis = hIniFile;
513 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
514 AssertReturn(pThis->u32Magic == RTINIFILE_MAGIC, VERR_INVALID_HANDLE);
515 AssertPtrNullReturn(pszSection, VERR_INVALID_POINTER);
516 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
517 size_t const cchKey = strlen(pszKey);
518 if (cbValue)
519 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
520 AssertPtrNullReturn(pcbActual, VERR_INVALID_POINTER);
521
522 /*
523 * Search relevant sections.
524 */
525 int rc;
526 if (pszSection == NULL)
527 rc = rtIniFileQueryValueInSection(pThis, &pThis->paSections[0], pszKey, cchKey, pszValue, cbValue, pcbActual);
528 else
529 {
530 rc = VERR_NOT_FOUND;
531 uint32_t const cchSection = (uint32_t)strlen(pszSection);
532 for (uint32_t iSection = 1; iSection < pThis->cSections; iSection++)
533 if ( pThis->paSections[iSection].cchName == cchSection
534 && RTStrNICmp(&pThis->pszFile[pThis->paSections[iSection].offName], pszSection, cchSection) == 0)
535 {
536 rc = rtIniFileQueryValueInSection(pThis, &pThis->paSections[iSection], pszKey, cchKey,
537 pszValue, cbValue, pcbActual);
538 if (rc != VERR_NOT_FOUND)
539 break;
540 }
541 }
542 return rc;
543}
544
545
546/**
547 * Worker for RTIniFileQueryPair.
548 *
549 * This can also be used to count the number of pairs in a section.
550 */
551static int rtIniFileQueryPairInSection(PRTINIFILEINT pThis, PRTINIFILESECTION pSection, uint32_t *pidxPair,
552 char *pszKey, size_t cbKey, size_t *pcbKeyActual,
553 char *pszValue, size_t cbValue, size_t *pcbValueActual)
554{
555 uint32_t idxPair = *pidxPair;
556
557 /*
558 * Scan the section, looking for the matching key.
559 */
560 Assert(pSection->cchSkipToValues <= pSection->cchSection);
561 const char * const pszEnd = &pThis->pszFile[pSection->offName + pSection->cchSection];
562 const char * pszNext = pszEnd;
563 for (const char *psz = &pThis->pszFile[pSection->offName + pSection->cchSkipToValues];
564 (uintptr_t)psz < (uintptr_t)pszEnd;
565 psz = pszNext)
566 {
567 /* Find start of next line so we can use 'continue' to skip a line. */
568 pszNext = strchr(psz, '\n');
569 if (pszNext)
570 pszNext++;
571 else
572 pszNext = pszEnd;
573
574 /* Skip leading spaces. */
575 char ch;
576 while ((ch = *psz) != '\0' && RT_C_IS_SPACE(ch))
577 psz++;
578 if ( ch != ';' /* comment line */
579 && ch != '\n' /* empty line */
580 && ch != '\r' /* empty line */
581 && (uintptr_t)psz < (uintptr_t)pszEnd)
582 {
583 /* Find end of key name, if any. */
584 const char *pszCurKey = psz;
585 size_t cchCurKey;
586 const char *pszEqual;
587 if (ch != '=')
588 {
589 /** @todo deal with escaped equal signs? */
590 pszEqual = strchr(psz, '=');
591 if (pszEqual)
592 {
593 if ((uintptr_t)pszEqual < (uintptr_t)pszNext)
594 cchCurKey = pszEqual - pszCurKey;
595 else
596 continue;
597 }
598 else
599 break;
600 }
601 else
602 {
603 cchCurKey = 0;
604 pszEqual = psz;
605 }
606
607 /* Is this the pair we're looking for? */
608 if (idxPair > 0)
609 idxPair--;
610 else
611 {
612 /*
613 * Yes it's the stuff we're looking for.
614 * Prepare the the return stuff.
615 */
616
617 /* Strip trailing spaces from the key name. */
618 while (cchCurKey > 0 && RT_C_IS_SPACE(pszCurKey[cchCurKey - 1]))
619 cchCurKey--;
620
621 /* Skip leading blanks from the value. */
622 psz = pszEqual + 1;
623 while ((ch = *psz) && RT_C_IS_SPACE(ch) && ch != '\n')
624 psz++;
625
626 /* Strip trailing spaces from the value. */
627 size_t cchCurValue = pszNext - psz;
628 while (cchCurValue > 1 && RT_C_IS_SPACE(psz[cchCurValue - 1]))
629 cchCurValue--;
630
631 /* Strip value quotes. */
632 if ( cchCurValue > 2
633 && ( (ch = *psz) == '"'
634 || ch == '\'' )
635 && psz[cchCurValue - 1] == ch)
636 {
637 cchCurValue -= 2;
638 psz++;
639 }
640
641 /*
642 * Copy the stuff out.
643 */
644 if ( cchCurValue < cbValue
645 && cchCurKey < cbKey)
646 {
647 memcpy(pszKey, pszCurKey, cchCurKey);
648 pszKey[cchCurKey] = '\0';
649 if (pcbKeyActual)
650 *pcbKeyActual = cchCurKey;
651
652 memcpy(pszValue, psz, cchCurValue);
653 pszValue[cchCurValue] = '\0';
654 if (pcbValueActual)
655 *pcbValueActual = cchCurValue;
656
657 *pidxPair = 0;
658 return VINF_SUCCESS;
659 }
660
661 /* Buffer overflow. Copy out what we can. */
662 if (cbKey > 0)
663 {
664 if (cchCurKey < cbKey)
665 cbKey = cchCurKey + 1;
666 memcpy(pszKey, pszCurKey, cbKey - 1);
667 pszKey[cbKey - 1] = '\0';
668 }
669 if (pcbKeyActual)
670 *pcbKeyActual = cchCurKey + 1;
671
672 if (cbValue > 0)
673 {
674 if (cchCurValue < cbValue)
675 cbValue = cchCurValue + 1;
676 memcpy(pszValue, psz, cbValue - 1);
677 pszValue[cbValue - 1] = '\0';
678 }
679 if (pcbValueActual)
680 *pcbValueActual = cchCurValue + 1;
681
682 *pidxPair = 0;
683 return VERR_BUFFER_OVERFLOW;
684 }
685 }
686 }
687 *pidxPair = idxPair;
688 return VERR_NOT_FOUND;
689}
690
691
692RTDECL(int) RTIniFileQueryPair(RTINIFILE hIniFile, const char *pszSection, uint32_t idxPair,
693 char *pszKey, size_t cbKey, size_t *pcbKeyActual,
694 char *pszValue, size_t cbValue, size_t *pcbValueActual)
695{
696 /*
697 * Validate input.
698 */
699 PRTINIFILEINT pThis = hIniFile;
700 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
701 AssertReturn(pThis->u32Magic == RTINIFILE_MAGIC, VERR_INVALID_HANDLE);
702 AssertPtrNullReturn(pszSection, VERR_INVALID_POINTER);
703 if (cbKey)
704 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
705 AssertPtrNullReturn(pcbKeyActual, VERR_INVALID_POINTER);
706 if (cbValue)
707 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
708 AssertPtrNullReturn(pcbValueActual, VERR_INVALID_POINTER);
709
710 /*
711 * Search relevant sections.
712 */
713 int rc;
714 if (pszSection == NULL)
715 rc = rtIniFileQueryPairInSection(pThis, &pThis->paSections[0], &idxPair,
716 pszKey, cbKey, pcbKeyActual, pszValue, cbValue, pcbValueActual);
717 else
718 {
719 rc = VERR_NOT_FOUND;
720 uint32_t const cchSection = (uint32_t)strlen(pszSection);
721 for (uint32_t iSection = 1; iSection < pThis->cSections; iSection++)
722 if ( pThis->paSections[iSection].cchName == cchSection
723 && RTStrNICmp(&pThis->pszFile[pThis->paSections[iSection].offName], pszSection, cchSection) == 0)
724 {
725 rc = rtIniFileQueryPairInSection(pThis, &pThis->paSections[iSection], &idxPair,
726 pszKey, cbKey, pcbKeyActual, pszValue, cbValue, pcbValueActual);
727 if (rc != VERR_NOT_FOUND)
728 break;
729 }
730 }
731 return rc;
732}
733
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