source: mainline/boot/generic/src/printf_core.c@ 6ff23ff

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6ff23ff was 6ff23ff, checked in by Jiri Svoboda <jiri@…>, 7 years ago

More comment fixing (ccheck).

  • Property mode set to 100644
File size: 19.3 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * Copyright (c) 2009 Martin Decky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @file
33 * @brief Printing functions.
34 */
35
36#include <printf_core.h>
37#include <printf.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <stddef.h>
41#include <macros.h>
42#include <str.h>
43
44/** show prefixes 0x or 0 */
45#define __PRINTF_FLAG_PREFIX 0x00000001
46
47/** signed / unsigned number */
48#define __PRINTF_FLAG_SIGNED 0x00000002
49
50/** print leading zeroes */
51#define __PRINTF_FLAG_ZEROPADDED 0x00000004
52
53/** align to left */
54#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
55
56/** always show + sign */
57#define __PRINTF_FLAG_SHOWPLUS 0x00000020
58
59/** print space instead of plus */
60#define __PRINTF_FLAG_SPACESIGN 0x00000040
61
62/** show big characters */
63#define __PRINTF_FLAG_BIGCHARS 0x00000080
64
65/** number has - sign */
66#define __PRINTF_FLAG_NEGATIVE 0x00000100
67
68/**
69 * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
70 * to terminate string... (last one is only for better testing end of buffer by
71 * zero-filling subroutine)
72 */
73#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
74
75/** Get signed or unsigned integer argument */
76#define PRINTF_GET_INT_ARGUMENT(type, ap, flags) \
77 ({ \
78 unsigned type res; \
79 \
80 if ((flags) & __PRINTF_FLAG_SIGNED) { \
81 signed type arg = va_arg((ap), signed type); \
82 \
83 if (arg < 0) { \
84 res = -arg; \
85 (flags) |= __PRINTF_FLAG_NEGATIVE; \
86 } else \
87 res = arg; \
88 } else \
89 res = va_arg((ap), unsigned type); \
90 \
91 res; \
92 })
93
94/** Enumeration of possible arguments types.
95 */
96typedef enum {
97 PrintfQualifierByte = 0,
98 PrintfQualifierShort,
99 PrintfQualifierInt,
100 PrintfQualifierLong,
101 PrintfQualifierLongLong,
102 PrintfQualifierPointer,
103 PrintfQualifierSize,
104 PrintfQualifierMax
105} qualifier_t;
106
107static const char *nullstr = "(NULL)";
108static const char *digits_small = "0123456789abcdef";
109static const char *digits_big = "0123456789ABCDEF";
110static const char invalch = U_SPECIAL;
111
112/** Print one or more characters without adding newline.
113 *
114 * @param buf Buffer holding characters with size of
115 * at least size bytes. NULL is not allowed!
116 * @param size Size of the buffer in bytes.
117 * @param ps Output method and its data.
118 *
119 * @return Number of characters printed.
120 *
121 */
122static int printf_putnchars(const char *buf, size_t size,
123 printf_spec_t *ps)
124{
125 return ps->str_write((void *) buf, size, ps->data);
126}
127
128/** Print string without adding a newline.
129 *
130 * @param str String to print.
131 * @param ps Write function specification and support data.
132 *
133 * @return Number of characters printed.
134 *
135 */
136static int printf_putstr(const char *str, printf_spec_t *ps)
137{
138 if (str == NULL)
139 return printf_putnchars(nullstr, str_size(nullstr), ps);
140
141 return ps->str_write((void *) str, str_size(str), ps->data);
142}
143
144/** Print one ASCII character.
145 *
146 * @param c ASCII character to be printed.
147 * @param ps Output method.
148 *
149 * @return Number of characters printed.
150 *
151 */
152static int printf_putchar(const char ch, printf_spec_t *ps)
153{
154 if (!ascii_check(ch))
155 return ps->str_write((void *) &invalch, 1, ps->data);
156
157 return ps->str_write(&ch, 1, ps->data);
158}
159
160/** Print one formatted ASCII character.
161 *
162 * @param ch Character to print.
163 * @param width Width modifier.
164 * @param flags Flags that change the way the character is printed.
165 *
166 * @return Number of characters printed, negative value on failure.
167 *
168 */
169static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
170{
171 size_t counter = 0;
172 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
173 while (--width > 0) {
174 /*
175 * One space is consumed by the character itself, hence
176 * the predecrement.
177 */
178 if (printf_putchar(' ', ps) > 0)
179 counter++;
180 }
181 }
182
183 if (printf_putchar(ch, ps) > 0)
184 counter++;
185
186 while (--width > 0) {
187 /*
188 * One space is consumed by the character itself, hence
189 * the predecrement.
190 */
191 if (printf_putchar(' ', ps) > 0)
192 counter++;
193 }
194
195 return (int) (counter);
196}
197
198/** Print string.
199 *
200 * @param str String to be printed.
201 * @param width Width modifier.
202 * @param precision Precision modifier.
203 * @param flags Flags that modify the way the string is printed.
204 *
205 * @return Number of characters printed, negative value on failure.
206 */
207static int print_str(char *str, int width, unsigned int precision,
208 uint32_t flags, printf_spec_t *ps)
209{
210 if (str == NULL)
211 return printf_putstr(nullstr, ps);
212
213 /* Print leading spaces. */
214 size_t strw = str_length(str);
215 if ((precision == 0) || (precision > strw))
216 precision = strw;
217
218 /* Left padding */
219 size_t counter = 0;
220 width -= precision;
221 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
222 while (width-- > 0) {
223 if (printf_putchar(' ', ps) == 1)
224 counter++;
225 }
226 }
227
228 /* Part of @a str fitting into the alloted space. */
229 int retval;
230 size_t size = str_lsize(str, precision);
231 if ((retval = printf_putnchars(str, size, ps)) < 0)
232 return -counter;
233
234 counter += retval;
235
236 /* Right padding */
237 while (width-- > 0) {
238 if (printf_putchar(' ', ps) == 1)
239 counter++;
240 }
241
242 return ((int) counter);
243}
244
245/** Print a number in a given base.
246 *
247 * Print significant digits of a number in given base.
248 *
249 * @param num Number to print.
250 * @param width Width modifier.
251 * @param precision Precision modifier.
252 * @param base Base to print the number in (must be between 2 and 16).
253 * @param flags Flags that modify the way the number is printed.
254 *
255 * @return Number of characters printed.
256 *
257 */
258static int print_number(uint64_t num, int width, int precision, int base,
259 uint32_t flags, printf_spec_t *ps)
260{
261 const char *digits;
262 if (flags & __PRINTF_FLAG_BIGCHARS)
263 digits = digits_big;
264 else
265 digits = digits_small;
266
267 char data[PRINT_NUMBER_BUFFER_SIZE];
268 char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
269
270 /* Size of number with all prefixes and signs */
271 int size = 0;
272
273 /* Put zero at end of string */
274 *ptr-- = 0;
275
276 if (num == 0) {
277 *ptr-- = '0';
278 size++;
279 } else {
280 do {
281 *ptr-- = digits[num % base];
282 size++;
283 } while (num /= base);
284 }
285
286 /* Size of plain number */
287 int number_size = size;
288
289 /*
290 * Collect the sum of all prefixes/signs/etc. to calculate padding and
291 * leading zeroes.
292 */
293 if (flags & __PRINTF_FLAG_PREFIX) {
294 switch (base) {
295 case 2:
296 /* Binary formating is not standard, but usefull */
297 size += 2;
298 break;
299 case 8:
300 size++;
301 break;
302 case 16:
303 size += 2;
304 break;
305 }
306 }
307
308 char sgn = 0;
309 if (flags & __PRINTF_FLAG_SIGNED) {
310 if (flags & __PRINTF_FLAG_NEGATIVE) {
311 sgn = '-';
312 size++;
313 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
314 sgn = '+';
315 size++;
316 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
317 sgn = ' ';
318 size++;
319 }
320 }
321
322 if (flags & __PRINTF_FLAG_LEFTALIGNED)
323 flags &= ~__PRINTF_FLAG_ZEROPADDED;
324
325 /*
326 * If the number is left-aligned or precision is specified then
327 * padding with zeros is ignored.
328 */
329 if (flags & __PRINTF_FLAG_ZEROPADDED) {
330 if ((precision == 0) && (width > size))
331 precision = width - size + number_size;
332 }
333
334 /* Print leading spaces */
335 if (number_size > precision) {
336 /* Print the whole number, not only a part */
337 precision = number_size;
338 }
339
340 width -= precision + size - number_size;
341 size_t counter = 0;
342
343 if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
344 while (width-- > 0) {
345 if (printf_putchar(' ', ps) == 1)
346 counter++;
347 }
348 }
349
350 /* Print sign */
351 if (sgn) {
352 if (printf_putchar(sgn, ps) == 1)
353 counter++;
354 }
355
356 /* Print prefix */
357 if (flags & __PRINTF_FLAG_PREFIX) {
358 switch (base) {
359 case 2:
360 /* Binary formating is not standard, but usefull */
361 if (printf_putchar('0', ps) == 1)
362 counter++;
363 if (flags & __PRINTF_FLAG_BIGCHARS) {
364 if (printf_putchar('B', ps) == 1)
365 counter++;
366 } else {
367 if (printf_putchar('b', ps) == 1)
368 counter++;
369 }
370 break;
371 case 8:
372 if (printf_putchar('o', ps) == 1)
373 counter++;
374 break;
375 case 16:
376 if (printf_putchar('0', ps) == 1)
377 counter++;
378 if (flags & __PRINTF_FLAG_BIGCHARS) {
379 if (printf_putchar('X', ps) == 1)
380 counter++;
381 } else {
382 if (printf_putchar('x', ps) == 1)
383 counter++;
384 }
385 break;
386 }
387 }
388
389 /* Print leading zeroes */
390 precision -= number_size;
391 while (precision-- > 0) {
392 if (printf_putchar('0', ps) == 1)
393 counter++;
394 }
395
396 /* Print the number itself */
397 int retval;
398 if ((retval = printf_putstr(++ptr, ps)) > 0)
399 counter += retval;
400
401 /* Print trailing spaces */
402
403 while (width-- > 0) {
404 if (printf_putchar(' ', ps) == 1)
405 counter++;
406 }
407
408 return ((int) counter);
409}
410
411/** Print formatted string.
412 *
413 * Print string formatted according to the fmt parameter and variadic arguments.
414 * Each formatting directive must have the following form:
415 *
416 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
417 *
418 * FLAGS:@n
419 * - "#" Force to print prefix. For \%o conversion, the prefix is 0, for
420 * \%x and \%X prefixes are 0x and 0X and for conversion \%b the
421 * prefix is 0b.
422 *
423 * - "-" Align to left.
424 *
425 * - "+" Print positive sign just as negative.
426 *
427 * - " " If the printed number is positive and "+" flag is not set,
428 * print space in place of sign.
429 *
430 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between
431 * sign and the rest of the number. This flag is ignored if "-"
432 * flag is specified.
433 *
434 * WIDTH:@n
435 * - Specify the minimal width of a printed argument. If it is bigger,
436 * width is ignored. If width is specified with a "*" character instead of
437 * number, width is taken from parameter list. And integer parameter is
438 * expected before parameter for processed conversion specification. If
439 * this value is negative its absolute value is taken and the "-" flag is
440 * set.
441 *
442 * PRECISION:@n
443 * - Value precision. For numbers it specifies minimum valid numbers.
444 * Smaller numbers are printed with leading zeroes. Bigger numbers are not
445 * affected. Strings with more than precision characters are cut off. Just
446 * as with width, an "*" can be used used instead of a number. An integer
447 * value is then expected in parameters. When both width and precision are
448 * specified using "*", the first parameter is used for width and the
449 * second one for precision.
450 *
451 * TYPE:@n
452 * - "hh" Signed or unsigned char.@n
453 * - "h" Signed or unsigned short.@n
454 * - "" Signed or unsigned int (default value).@n
455 * - "l" Signed or unsigned long int.@n
456 * - "ll" Signed or unsigned long long int.@n
457 *
458 * CONVERSION:@n
459 * - % Print percentile character itself.
460 *
461 * - c Print single character. The character is expected to be plain
462 * ASCII (e.g. only values 0 .. 127 are valid).@n
463 *
464 * - s Print zero terminated string. If a NULL value is passed as
465 * value, "(NULL)" is printed instead.@n
466 *
467 * - P, p Print value of a pointer. Void * value is expected and it is
468 * printed in hexadecimal notation with prefix (as with
469 * \%#0.8X / \%#0.8x for 32-bit or \%#0.16lX / \%#0.16lx for 64-bit
470 * long pointers).
471 *
472 * - b Print value as unsigned binary number. Prefix is not printed by
473 * default. (Nonstandard extension.)
474 *
475 * - o Print value as unsigned octal number. Prefix is not printed by
476 * default.
477 *
478 * - d, i Print signed decimal number. There is no difference between d
479 * and i conversion.
480 *
481 * - u Print unsigned decimal number.
482 *
483 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
484 * not printed by default.
485 *
486 * All other characters from fmt except the formatting directives are printed
487 * verbatim.
488 *
489 * @param fmt Format NULL-terminated string.
490 *
491 * @return Number of characters printed, negative value on failure.
492 *
493 */
494int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
495{
496 size_t i; /* Index of the currently processed character from fmt */
497 size_t nxt = 0; /* Index of the next character from fmt */
498 size_t j = 0; /* Index to the first not printed nonformating character */
499
500 size_t counter = 0; /* Number of characters printed */
501 int retval; /* Return values from nested functions */
502
503 while (true) {
504 i = nxt;
505 wchar_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
506
507 if (uc == 0)
508 break;
509
510 /* Control character */
511 if (uc == '%') {
512 /* Print common characters if any processed */
513 if (i > j) {
514 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
515 /* Error */
516 counter = -counter;
517 goto out;
518 }
519 counter += retval;
520 }
521
522 j = i;
523
524 /* Parse modifiers */
525 uint32_t flags = 0;
526 bool end = false;
527
528 do {
529 i = nxt;
530 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
531 switch (uc) {
532 case '#':
533 flags |= __PRINTF_FLAG_PREFIX;
534 break;
535 case '-':
536 flags |= __PRINTF_FLAG_LEFTALIGNED;
537 break;
538 case '+':
539 flags |= __PRINTF_FLAG_SHOWPLUS;
540 break;
541 case ' ':
542 flags |= __PRINTF_FLAG_SPACESIGN;
543 break;
544 case '0':
545 flags |= __PRINTF_FLAG_ZEROPADDED;
546 break;
547 default:
548 end = true;
549 }
550 } while (!end);
551
552 /* Width & '*' operator */
553 int width = 0;
554 if (isdigit(uc)) {
555 while (true) {
556 width *= 10;
557 width += uc - '0';
558
559 i = nxt;
560 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
561 if (uc == 0)
562 break;
563 if (!isdigit(uc))
564 break;
565 }
566 } else if (uc == '*') {
567 /* Get width value from argument list */
568 i = nxt;
569 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
570 width = (int) va_arg(ap, int);
571 if (width < 0) {
572 /* Negative width sets '-' flag */
573 width *= -1;
574 flags |= __PRINTF_FLAG_LEFTALIGNED;
575 }
576 }
577
578 /* Precision and '*' operator */
579 int precision = 0;
580 if (uc == '.') {
581 i = nxt;
582 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
583 if (isdigit(uc)) {
584 while (true) {
585 precision *= 10;
586 precision += uc - '0';
587
588 i = nxt;
589 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
590 if (uc == 0)
591 break;
592 if (!isdigit(uc))
593 break;
594 }
595 } else if (uc == '*') {
596 /* Get precision value from the argument list */
597 i = nxt;
598 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
599 precision = (int) va_arg(ap, int);
600 if (precision < 0) {
601 /* Ignore negative precision */
602 precision = 0;
603 }
604 }
605 }
606
607 qualifier_t qualifier;
608
609 switch (uc) {
610 case 't':
611 /* ptrdiff_t */
612 if (sizeof(ptrdiff_t) == sizeof(int32_t))
613 qualifier = PrintfQualifierInt;
614 else
615 qualifier = PrintfQualifierLongLong;
616 i = nxt;
617 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
618 break;
619 case 'h':
620 /* Char or short */
621 qualifier = PrintfQualifierShort;
622 i = nxt;
623 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
624 if (uc == 'h') {
625 i = nxt;
626 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
627 qualifier = PrintfQualifierByte;
628 }
629 break;
630 case 'l':
631 /* Long or long long */
632 qualifier = PrintfQualifierLong;
633 i = nxt;
634 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
635 if (uc == 'l') {
636 i = nxt;
637 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
638 qualifier = PrintfQualifierLongLong;
639 }
640 break;
641 case 'z':
642 qualifier = PrintfQualifierSize;
643 i = nxt;
644 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
645 break;
646 case 'j':
647 qualifier = PrintfQualifierMax;
648 i = nxt;
649 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
650 break;
651 default:
652 /* Default type */
653 qualifier = PrintfQualifierInt;
654 }
655
656 unsigned int base = 10;
657
658 switch (uc) {
659 /*
660 * String and character conversions.
661 */
662 case 's':
663 retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
664
665 if (retval < 0) {
666 counter = -counter;
667 goto out;
668 }
669
670 counter += retval;
671 j = nxt;
672 continue;
673 case 'c':
674 retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
675
676 if (retval < 0) {
677 counter = -counter;
678 goto out;
679 }
680
681 counter += retval;
682 j = nxt;
683 continue;
684
685 /*
686 * Integer values
687 */
688 case 'P':
689 /* Pointer */
690 flags |= __PRINTF_FLAG_BIGCHARS;
691 /* Fallthrough */
692 case 'p':
693 flags |= __PRINTF_FLAG_PREFIX;
694 flags |= __PRINTF_FLAG_ZEROPADDED;
695 base = 16;
696 qualifier = PrintfQualifierPointer;
697 break;
698 case 'b':
699 base = 2;
700 break;
701 case 'o':
702 base = 8;
703 break;
704 case 'd':
705 case 'i':
706 flags |= __PRINTF_FLAG_SIGNED;
707 /* Fallthrough */
708 case 'u':
709 break;
710 case 'X':
711 flags |= __PRINTF_FLAG_BIGCHARS;
712 /* Fallthrough */
713 case 'x':
714 base = 16;
715 break;
716
717 /* Percentile itself */
718 case '%':
719 j = i;
720 continue;
721
722 /*
723 * Bad formatting.
724 */
725 default:
726 /*
727 * Unknown format. Now, j is the index of '%'
728 * so we will print whole bad format sequence.
729 */
730 continue;
731 }
732
733 /* Print integers */
734 size_t size;
735 uint64_t number;
736
737 switch (qualifier) {
738 case PrintfQualifierByte:
739 size = sizeof(unsigned char);
740 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
741 break;
742 case PrintfQualifierShort:
743 size = sizeof(unsigned short);
744 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
745 break;
746 case PrintfQualifierInt:
747 size = sizeof(unsigned int);
748 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
749 break;
750 case PrintfQualifierLong:
751 size = sizeof(unsigned long);
752 number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
753 break;
754 case PrintfQualifierLongLong:
755 size = sizeof(unsigned long long);
756 number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
757 break;
758 case PrintfQualifierPointer:
759 size = sizeof(void *);
760 precision = size << 1;
761 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
762 break;
763 case PrintfQualifierSize:
764 size = sizeof(size_t);
765 number = (uint64_t) va_arg(ap, size_t);
766 break;
767 case PrintfQualifierMax:
768 size = sizeof(uintmax_t);
769 number = (uint64_t) va_arg(ap, uintmax_t);
770 break;
771 default:
772 /* Unknown qualifier */
773 counter = -counter;
774 goto out;
775 }
776
777 if ((retval = print_number(number, width, precision,
778 base, flags, ps)) < 0) {
779 counter = -counter;
780 goto out;
781 }
782
783 counter += retval;
784 j = nxt;
785 }
786 }
787
788 if (i > j) {
789 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
790 /* Error */
791 counter = -counter;
792 goto out;
793 }
794 counter += retval;
795 }
796
797out:
798 return ((int) counter);
799}
800
801/** @}
802 */
Note: See TracBrowser for help on using the repository browser.