VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-cmn-StrFormatV.c@ 61533

Last change on this file since 61533 was 60774, checked in by vboxsync, 9 years ago

bs3kit: update / iret

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.2 KB
Line 
1/* $Id: bs3-cmn-StrFormatV.c 60774 2016-05-02 00:04:01Z vboxsync $ */
2/** @file
3 * BS3Kit - Bs3StrFormatV
4 */
5
6/*
7 * Copyright (C) 2007-2015 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#include "bs3kit-template-header.h"
32#include <iprt/ctype.h>
33
34
35/*********************************************************************************************************************************
36* Defined Constants And Macros *
37*********************************************************************************************************************************/
38#define STR_F_CAPITAL 0x0001
39#define STR_F_LEFT 0x0002
40#define STR_F_ZEROPAD 0x0004
41#define STR_F_SPECIAL 0x0008
42#define STR_F_VALSIGNED 0x0010
43#define STR_F_PLUS 0x0020
44#define STR_F_BLANK 0x0040
45#define STR_F_WIDTH 0x0080
46#define STR_F_PRECISION 0x0100
47#define STR_F_THOUSAND_SEP 0x0200
48#define STR_F_NEGATIVE 0x0400 /**< Used to indicated '-' must be printed. */
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/** Size of the temporary buffer. */
55#define BS3FMT_TMP_SIZE 64
56
57/**
58 * BS3kit string format state.
59 */
60typedef struct BS3FMTSTATE
61{
62 /** The output function. */
63 PFNBS3STRFORMATOUTPUT pfnOutput;
64 /** User argument for pfnOutput. */
65 void BS3_FAR *pvUser;
66
67 /** STR_F_XXX flags. */
68 unsigned fFlags;
69 /** The width when STR_F_WIDTH is specific. */
70 int cchWidth;
71 /** The width when STR_F_PRECISION is specific. */
72 int cchPrecision;
73 /** The number format base. */
74 unsigned uBase;
75 /** Temporary buffer. */
76 char szTmp[BS3FMT_TMP_SIZE];
77} BS3FMTSTATE;
78/** Pointer to a BS3Kit string formatter state. */
79typedef BS3FMTSTATE *PBS3FMTSTATE;
80
81
82
83/*********************************************************************************************************************************
84* Internal Functions *
85*********************************************************************************************************************************/
86#if ARCH_BITS != 64
87static size_t bs3StrFormatU32(PBS3FMTSTATE pState, uint32_t uValue);
88#endif
89
90
91
92/**
93 * Formats a number string.
94 *
95 * @returns Number of chars printed.
96 * @param pState The string formatter state.
97 * @param pszNumber The formatted number string.
98 * @param cchNumber The length of the number.
99 */
100static size_t bs3StrFormatNumberString(PBS3FMTSTATE pState, char const *pszNumber, size_t cchNumber)
101{
102 /*
103 * Calc the length of the core number with prefixes.
104 */
105 size_t cchActual = 0;
106 size_t cchRet = cchNumber;
107
108 /* Accunt for sign char. */
109 cchRet += !!(pState->fFlags & (STR_F_NEGATIVE | STR_F_PLUS | STR_F_BLANK));
110
111 /* Account for the hex prefix: '0x' or '0X' */
112 if (pState->fFlags & STR_F_SPECIAL)
113 {
114 cchRet += 2;
115 BS3_ASSERT(pState->uBase == 16);
116 }
117
118 /* Account for thousand separators (applied while printing). */
119 if (pState->fFlags & STR_F_THOUSAND_SEP)
120 cchRet += (cchNumber - 1) / (pState->uBase == 10 ? 3 : 8);
121
122 /*
123 * Do left blank padding.
124 */
125 if ((pState->fFlags & (STR_F_ZEROPAD | STR_F_LEFT | STR_F_WIDTH)) == STR_F_WIDTH)
126 while (cchRet < pState->cchWidth)
127 {
128 cchActual += pState->pfnOutput(' ', pState->pvUser);
129 cchRet++;
130 }
131
132 /*
133 * Sign indicator / space.
134 */
135 if (pState->fFlags & (STR_F_NEGATIVE | STR_F_PLUS | STR_F_BLANK))
136 {
137 char ch;
138 if (pState->fFlags & STR_F_NEGATIVE)
139 ch = '-';
140 else if (pState->fFlags & STR_F_PLUS)
141 ch = '+';
142 else
143 ch = ' ';
144 cchActual += pState->pfnOutput(ch, pState->pvUser);
145 }
146
147 /*
148 * Hex prefix.
149 */
150 if (pState->fFlags & STR_F_SPECIAL)
151 {
152 cchActual += pState->pfnOutput('0', pState->pvUser);
153 cchActual += pState->pfnOutput(!(pState->fFlags & STR_F_CAPITAL) ? 'x' : 'X', pState->pvUser);
154 }
155
156 /*
157 * Zero padding.
158 */
159 if (pState->fFlags & STR_F_ZEROPAD)
160 while (cchRet < pState->cchWidth)
161 {
162 cchActual += pState->pfnOutput('0', pState->pvUser);
163 cchRet++;
164 }
165
166 /*
167 * Output the number.
168 */
169 if ( !(pState->fFlags & STR_F_THOUSAND_SEP)
170 || cchNumber < 4)
171 while (cchNumber-- > 0)
172 cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
173 else
174 {
175 char const chSep = pState->uBase == 10 ? ' ' : '\'';
176 unsigned const cchEvery = pState->uBase == 10 ? 3 : 8;
177 unsigned cchLeft = --cchNumber % cchEvery;
178
179 cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
180 while (cchNumber-- > 0)
181 {
182 if (cchLeft == 0)
183 {
184 cchActual += pState->pfnOutput(chSep, pState->pvUser);
185 cchLeft = cchEvery;
186 }
187 cchLeft--;
188 cchActual += pState->pfnOutput(*pszNumber++, pState->pvUser);
189 }
190 }
191
192 /*
193 * Do right blank padding.
194 */
195 if ((pState->fFlags & (STR_F_ZEROPAD | STR_F_LEFT | STR_F_WIDTH)) == (STR_F_WIDTH | STR_F_LEFT))
196 while (cchRet < pState->cchWidth)
197 {
198 cchActual += pState->pfnOutput(' ', pState->pvUser);
199 cchRet++;
200 }
201
202 return cchActual;
203}
204
205
206/**
207 * Format a 64-bit number.
208 *
209 * @returns Number of characters.
210 * @param pState The string formatter state.
211 * @param uValue The value.
212 */
213static size_t bs3StrFormatU64(PBS3FMTSTATE pState, uint64_t uValue)
214{
215#if ARCH_BITS != 64
216 /* Avoid 64-bit division by formatting 64-bit numbers as hex if they're higher than _4G. */
217 if ( pState->uBase == 10
218 && !(uValue >> 32)) /* uValue <= UINT32_MAX does not work, trouble with 64-bit compile time math! */
219 return bs3StrFormatU32(pState, uValue);
220 pState->fFlags |= STR_F_SPECIAL;
221 pState->uBase = 16;
222#endif
223
224 {
225 const char *pachDigits = !(pState->fFlags & STR_F_CAPITAL) ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
226 char *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
227
228 *--psz = '\0';
229#if ARCH_BITS == 64
230 if (pState->uBase == 10)
231 {
232 do
233 {
234 *--psz = pachDigits[uValue % 10];
235 uValue /= 10;
236 } while (uValue > 0);
237 }
238 else
239#endif
240 {
241 BS3_ASSERT(pState->uBase == 16);
242 do
243 {
244 *--psz = pachDigits[uValue & 0xf];
245 uValue >>= 4;
246 } while (uValue > 0);
247 }
248 return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
249 }
250}
251
252
253/**
254 * Format a 32-bit number.
255 *
256 * @returns Number of characters.
257 * @param pState The string formatter state.
258 * @param uValue The value.
259 */
260static size_t bs3StrFormatU32(PBS3FMTSTATE pState, uint32_t uValue)
261{
262#if ARCH_BITS < 64
263 const char *pachDigits = !(pState->fFlags & STR_F_CAPITAL) ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
264 char *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
265
266 *--psz = '\0';
267 if (pState->uBase == 10)
268 {
269 do
270 {
271 *--psz = pachDigits[uValue % 10];
272 uValue /= 10;
273 } while (uValue > 0);
274 }
275 else
276 {
277 BS3_ASSERT(pState->uBase == 16);
278 do
279 {
280 *--psz = pachDigits[uValue & 0xf];
281 uValue >>= 4;
282 } while (uValue > 0);
283 }
284 return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
285
286#else
287 /* We've got native 64-bit division, save space. */
288 return bs3StrFormatU64(pState, uValue);
289#endif
290}
291
292
293#if ARCH_BITS == 16
294/**
295 * Format a 16-bit number.
296 *
297 * @returns Number of characters.
298 * @param pState The string formatter state.
299 * @param uValue The value.
300 */
301static size_t bs3StrFormatU16(PBS3FMTSTATE pState, uint16_t uValue)
302{
303 if (pState->uBase == 10)
304 {
305 const char *pachDigits = !(pState->fFlags & STR_F_CAPITAL)
306 ? g_achBs3HexDigits : g_achBs3HexDigitsUpper;
307 char *psz = &pState->szTmp[BS3FMT_TMP_SIZE];
308
309 *--psz = '\0';
310 do
311 {
312 *--psz = pachDigits[uValue % 10];
313 uValue /= 10;
314 } while (uValue > 0);
315 return bs3StrFormatNumberString(pState, psz, &pState->szTmp[BS3FMT_TMP_SIZE - 1] - psz);
316 }
317
318 /*
319 * 32-bit shifting is reasonably cheap and inlined, so combine with 32-bit.
320 */
321 return bs3StrFormatU32(pState, uValue);
322}
323#endif
324
325
326static size_t bs3StrFormatS64(PBS3FMTSTATE pState, int32_t iValue)
327{
328 if (iValue < 0)
329 {
330 iValue = -iValue;
331 pState->fFlags |= STR_F_NEGATIVE;
332 }
333 return bs3StrFormatU64(pState, iValue);
334}
335
336
337static size_t bs3StrFormatS32(PBS3FMTSTATE pState, int32_t iValue)
338{
339 if (iValue < 0)
340 {
341 iValue = -iValue;
342 pState->fFlags |= STR_F_NEGATIVE;
343 }
344 return bs3StrFormatU32(pState, iValue);
345}
346
347
348#if ARCH_BITS == 16
349static size_t bs3StrFormatS16(PBS3FMTSTATE pState, int16_t iValue)
350{
351 if (iValue < 0)
352 {
353 iValue = -iValue;
354 pState->fFlags |= STR_F_NEGATIVE;
355 }
356 return bs3StrFormatU16(pState, iValue);
357}
358#endif
359
360
361#undef Bs3StrFormatV
362BS3_CMN_DEF(size_t, Bs3StrFormatV,(const char BS3_FAR *pszFormat, va_list va,
363 PFNBS3STRFORMATOUTPUT pfnOutput, void BS3_FAR *pvUser))
364{
365 BS3FMTSTATE State;
366 size_t cchRet = 0;
367 char ch;
368
369 State.pfnOutput = pfnOutput;
370 State.pvUser = pvUser;
371
372 while ((ch = *pszFormat++) != '\0')
373 {
374 char chArgSize;
375
376 /*
377 * Deal with plain chars.
378 */
379 if (ch != '%')
380 {
381 cchRet += State.pfnOutput(ch, State.pvUser);
382 continue;
383 }
384
385 ch = *pszFormat++;
386 if (ch == '%')
387 {
388 cchRet += State.pfnOutput(ch, State.pvUser);
389 continue;
390 }
391
392 /*
393 * Flags.
394 */
395 State.fFlags = 0;
396 for (;;)
397 {
398 unsigned int fThis;
399 switch (ch)
400 {
401 default: fThis = 0; break;
402 case '#': fThis = STR_F_SPECIAL; break;
403 case '-': fThis = STR_F_LEFT; break;
404 case '+': fThis = STR_F_PLUS; break;
405 case ' ': fThis = STR_F_BLANK; break;
406 case '0': fThis = STR_F_ZEROPAD; break;
407 case '\'': fThis = STR_F_THOUSAND_SEP; break;
408 }
409 if (!fThis)
410 break;
411 State.fFlags |= fThis;
412 ch = *pszFormat++;
413 }
414
415 /*
416 * Width.
417 */
418 State.cchWidth = 0;
419 if (RT_C_IS_DIGIT(ch))
420 {
421 do
422 {
423 State.cchWidth *= 10;
424 State.cchWidth += ch - '0';
425 ch = *pszFormat++;
426 } while (RT_C_IS_DIGIT(ch));
427 State.fFlags |= STR_F_WIDTH;
428 }
429 else if (ch == '*')
430 {
431 State.cchWidth = va_arg(va, int);
432 if (State.cchWidth < 0)
433 {
434 State.cchWidth = -State.cchWidth;
435 State.fFlags |= STR_F_LEFT;
436 }
437 State.fFlags |= STR_F_WIDTH;
438 ch = *pszFormat++;
439 }
440
441 /*
442 * Precision
443 */
444 State.cchPrecision = 0;
445 if (ch == '.')
446 {
447 ch = *pszFormat++;
448 if (RT_C_IS_DIGIT(ch))
449 {
450 do
451 {
452 State.cchPrecision *= 10;
453 State.cchPrecision += ch - '0';
454 ch = *pszFormat++;
455 } while (RT_C_IS_DIGIT(ch));
456 State.fFlags |= STR_F_PRECISION;
457 }
458 else if (ch == '*')
459 {
460 State.cchPrecision = va_arg(va, int);
461 if (State.cchPrecision < 0)
462 State.cchPrecision = 0;
463 State.fFlags |= STR_F_PRECISION;
464 ch = *pszFormat++;
465 }
466 }
467
468 /*
469 * Argument size.
470 */
471 chArgSize = ch;
472 switch (ch)
473 {
474 default:
475 chArgSize = 0;
476 break;
477
478 case 'z':
479 case 'L':
480 case 'j':
481 case 't':
482 ch = *pszFormat++;
483 break;
484
485 case 'l':
486 ch = *pszFormat++;
487 if (ch == 'l')
488 {
489 chArgSize = 'L';
490 ch = *pszFormat++;
491 }
492 break;
493
494 case 'h':
495 ch = *pszFormat++;
496 if (ch == 'h')
497 {
498 chArgSize = 'H';
499 ch = *pszFormat++;
500 }
501 break;
502 }
503
504 /*
505 * The type.
506 */
507 switch (ch)
508 {
509 /*
510 * Char
511 */
512 case 'c':
513 {
514 char ch = va_arg(va, int /*char*/);
515 cchRet += State.pfnOutput(ch, State.pvUser);
516 break;
517 }
518
519 /*
520 * String.
521 */
522 case 's':
523 {
524 const char BS3_FAR *psz = va_arg(va, const char BS3_FAR *);
525 size_t cch;
526 if (psz != NULL)
527 cch = Bs3StrNLen(psz, State.fFlags & STR_F_PRECISION ? RT_ABS(State.cchPrecision) : ~(size_t)0);
528 else
529 {
530 psz = "<NULL>";
531 cch = 6;
532 }
533
534 if ((State.fFlags & (STR_F_LEFT | STR_F_WIDTH)) == STR_F_WIDTH)
535 while (--State.cchWidth >= cch)
536 cchRet += State.pfnOutput(' ', State.pvUser);
537
538 cchRet += cch;
539 while (cch-- > 0)
540 cchRet += State.pfnOutput(*psz++, State.pvUser);
541
542 if ((State.fFlags & (STR_F_LEFT | STR_F_WIDTH)) == (STR_F_LEFT | STR_F_WIDTH))
543 while (--State.cchWidth >= cch)
544 cchRet += State.pfnOutput(' ', State.pvUser);
545 break;
546 }
547
548 /*
549 * Signed integers.
550 */
551 case 'i':
552 case 'd':
553 State.fFlags &= ~STR_F_SPECIAL;
554 State.fFlags |= STR_F_VALSIGNED;
555 State.uBase = 10;
556 switch (chArgSize)
557 {
558 case 0:
559 case 'h': /* signed short should be promoted to int or be the same as int */
560 case 'H': /* signed char should be promoted to int. */
561 {
562 signed int iValue = va_arg(va, signed int);
563#if ARCH_BITS == 16
564 cchRet += bs3StrFormatS16(&State, iValue);
565#else
566 cchRet += bs3StrFormatS32(&State, iValue);
567#endif
568 break;
569 }
570 case 'l':
571 {
572 signed long lValue = va_arg(va, signed long);
573 if (sizeof(lValue) == 4)
574 cchRet += bs3StrFormatS32(&State, lValue);
575 else
576 cchRet += bs3StrFormatS64(&State, lValue);
577 break;
578 }
579 case 'L':
580 {
581 unsigned long long ullValue = va_arg(va, unsigned long long);
582 cchRet += bs3StrFormatS64(&State, ullValue);
583 break;
584 }
585 }
586 break;
587
588 /*
589 * Unsigned integers.
590 */
591 case 'X':
592 State.fFlags |= STR_F_CAPITAL;
593 case 'x':
594 case 'u':
595 {
596 if (ch == 'u')
597 {
598 State.uBase = 10;
599 State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK | STR_F_SPECIAL);
600 }
601 else
602 {
603 State.uBase = 16;
604 State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
605 }
606 switch (chArgSize)
607 {
608 case 0:
609 case 'h': /* unsigned short should be promoted to int or be the same as int */
610 case 'H': /* unsigned char should be promoted to int. */
611 {
612 unsigned int uValue = va_arg(va, unsigned int);
613#if ARCH_BITS == 16
614 cchRet += bs3StrFormatU16(&State, uValue);
615#else
616 cchRet += bs3StrFormatU32(&State, uValue);
617#endif
618 break;
619 }
620 case 'l':
621 {
622 unsigned long ulValue = va_arg(va, unsigned long);
623 if (sizeof(ulValue) == 4)
624 cchRet += bs3StrFormatU32(&State, ulValue);
625 else
626 cchRet += bs3StrFormatU64(&State, ulValue);
627 break;
628 }
629 case 'L':
630 {
631 unsigned long long ullValue = va_arg(va, unsigned long long);
632 cchRet += bs3StrFormatU64(&State, ullValue);
633 break;
634 }
635 }
636 break;
637 }
638
639 /*
640 * Our stuff.
641 */
642 case 'R':
643 {
644 ch = *pszFormat++;
645 switch (ch)
646 {
647 case 'I':
648 State.fFlags |= STR_F_VALSIGNED;
649 State.uBase &= ~STR_F_SPECIAL;
650 State.uBase = 10;
651 break;
652 case 'U':
653 State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK | STR_F_SPECIAL);
654 State.uBase = 10;
655 break;
656 case 'X':
657 State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
658 State.uBase = 16;
659 break;
660 case 'h':
661 ch = *pszFormat++;
662 if (ch == 'x')
663 {
664 /* Hex dumping. */
665 uint8_t const BS3_FAR *pbHex = va_arg(va, uint8_t const BS3_FAR *);
666 if (State.cchPrecision < 0)
667 State.cchPrecision = 16;
668 ch = *pszFormat++;
669 if (ch == 's' || ch == 'd')
670 {
671 /* %Rhxd is currently implemented as %Rhxs. */
672 while (State.cchPrecision-- > 0)
673 {
674 uint8_t b = *pbHex++;
675 State.pfnOutput(g_achBs3HexDigits[b >> 4], State.pvUser);
676 State.pfnOutput(g_achBs3HexDigits[b & 0x0f], State.pvUser);
677 if (State.cchPrecision)
678 State.pfnOutput(' ', State.pvUser);
679 }
680 }
681 }
682 State.uBase = 0;
683 break;
684 default:
685 State.uBase = 0;
686 break;
687 }
688 if (State.uBase)
689 {
690 ch = *pszFormat++;
691 switch (ch)
692 {
693#if ARCH_BITS != 16
694 case '3':
695 case '1': /* Will an unsigned 16-bit value always be promoted
696 to a 16-bit unsigned int. It certainly will be promoted to a 32-bit int. */
697 pszFormat++; /* Assumes (1)'6' or (3)'2' */
698#else
699 case '1':
700 pszFormat++; /* Assumes (1)'6' */
701#endif
702 case '8': /* An unsigned 8-bit value should be promoted to int, which is at least 16-bit. */
703 {
704 unsigned int uValue = va_arg(va, unsigned int);
705#if ARCH_BITS == 16
706 cchRet += bs3StrFormatU16(&State, uValue);
707#else
708 cchRet += bs3StrFormatU32(&State, uValue);
709#endif
710 break;
711 }
712#if ARCH_BITS == 16
713 case '3':
714 {
715 uint32_t uValue = va_arg(va, uint32_t);
716 pszFormat++;
717 cchRet += bs3StrFormatU32(&State, uValue);
718 break;
719 }
720#endif
721 case '6':
722 {
723 uint64_t uValue = va_arg(va, uint64_t);
724 pszFormat++;
725 cchRet += bs3StrFormatU64(&State, uValue);
726 break;
727 }
728 }
729 }
730 break;
731 }
732
733 /*
734 * Pointers.
735 */
736 case 'P':
737 State.fFlags |= STR_F_CAPITAL;
738 /* fall thru */
739 case 'p':
740 {
741 void BS3_FAR *pv = va_arg(va, void BS3_FAR *);
742 State.uBase = 16;
743 State.fFlags &= ~(STR_F_PLUS | STR_F_BLANK);
744#if ARCH_BITS == 16
745 State.fFlags |= STR_F_ZEROPAD;
746 State.cchWidth = State.fFlags & STR_F_SPECIAL ? 6: 4;
747 cchRet += bs3StrFormatU16(&State, BS3_FP_SEG(pv));
748 cchRet += State.pfnOutput(':', State.pvUser);
749 cchRet += bs3StrFormatU16(&State, BS3_FP_OFF(pv));
750#elif ARCH_BITS == 32
751 State.fFlags |= STR_F_SPECIAL | STR_F_ZEROPAD;
752 State.cchWidth = 10;
753 cchRet += bs3StrFormatU32(&State, (uintptr_t)pv);
754#elif ARCH_BITS == 64
755 State.fFlags |= STR_F_SPECIAL | STR_F_ZEROPAD | STR_F_THOUSAND_SEP;
756 State.cchWidth = 19;
757 cchRet += bs3StrFormatU64(&State, (uintptr_t)pv);
758#else
759# error "Undefined or invalid ARCH_BITS."
760#endif
761 break;
762 }
763
764 }
765 }
766
767 /*
768 * Termination call.
769 */
770 cchRet += State.pfnOutput(0, State.pvUser);
771
772 return cchRet;
773}
774
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