VirtualBox

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

最後變更 在這個檔案從76346是 76346,由 vboxsync 提交於 6 年 前

*: Preparing for iprt/string.h, iprt/json.h and iprt/serialport.h no longer including iprt/err.h and string.h no longer including latin1.h (it needs err.h). bugref:9344

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

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