+
+ /*
+ ** Argument extraction and printing.
+ ** First we determine the argument type.
+ ** Then, we convert the argument to a string.
+ ** On exit from the switch, s points to the string that
+ ** must be printed, s_len has the length of the string
+ ** The precision requirements, if any, are reflected
+ ** in s_len.
+ **
+ ** NOTE: pad_char may be set to '0' because of the
+ ** 0 flag.
+ ** It is reset to ' ' by non-numeric formats
+ */
+ switch (*fmt) {
+ case 'u':
+ if (is_long)
+ i_num = va_arg(ap, u_wide_int);
+ else
+ i_num = (wide_int) va_arg(ap, unsigned int);
+ s = conv_10(i_num, 1, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ break;
+
+ case 'd':
+ case 'i':
+ if (is_long)
+ i_num = va_arg(ap, wide_int);
+ else
+ i_num = (wide_int) va_arg(ap, int);
+ s = conv_10(i_num, 0, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ break;
+
+
+ case 'o':
+ if (is_long)
+ ui_num = va_arg(ap, u_wide_int);
+ else
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ s = conv_p2(ui_num, 3, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && *s != '0') {
+ *--s = '0';
+ s_len++;
+ }
+ break;
+
+
+ case 'x':
+ case 'X':
+ if (is_long)
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ else
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ s = conv_p2(ui_num, 4, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && i_num != 0) {
+ *--s = *fmt; /* 'x' or 'X' */
+ *--s = '0';
+ s_len += 2;
+ }
+ break;
+
+
+ case 's':
+ s = va_arg(ap, char *);
+ if (s != NULL) {
+ s_len = strlen(s);
+ if (adjust_precision && precision < s_len)
+ s_len = precision;
+ }
+ else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case 'f':
+ case 'e':
+ case 'E':
+ fp_num = va_arg(ap, double);
+ /*
+ ** We use &num_buf[ 1 ], so that we have
+ ** room for the sign
+ */
+ s = conv_fp(*fmt, fp_num, alternate_form,
+ (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+ &is_negative, &num_buf[1], &s_len);
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ break;
+
+
+ case 'g':
+ case 'G':
+ if (adjust_precision == NO)
+ precision = FLOAT_DIGITS;
+ else if (precision == 0)
+ precision = 1;
+ /*
+ ** We use &num_buf[ 1 ], so that we have
+ ** room for the sign
+ */
+ s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
+ alternate_form);
+ if (*s == '-')
+ prefix_char = *s++;
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+
+ s_len = strlen(s);
+
+ if (alternate_form && (q = strchr(s, '.')) == NULL) {
+ s[s_len++] = '.';
+ s[s_len] = '\0'; /* delimit for following strchr() */
+ }
+ if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+ *q = 'E';
+ break;
+
+
+ case 'c':
+ char_buf[0] = (char) (va_arg(ap, int));
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case '%':
+ char_buf[0] = '%';
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case 'n':
+ *(va_arg(ap, int *)) = cc;
+ break;
+
+ /*
+ ** This is where we extend the printf format,
+ ** with a second type specifier
+ */
+ case 'p':
+ switch(*++fmt) {
+ /*
+ ** If the pointer size is equal to
+ ** the size of an unsigned integer
+ ** we convert the pointer to a hex
+ ** number, otherwise we print "%p"
+ ** to indicate that we don't handle
+ ** "%p".
+ */
+ case 'p':
+ ui_num = (u_wide_int) va_arg(ap, void *);
+
+ if (sizeof(char *) <= sizeof(u_wide_int))
+ s = conv_p2(ui_num, 4, 'x',
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ else {
+ s = "%p";
+ s_len = 2;
+ prefix_char = NUL;
+ }
+ pad_char = ' ';
+ break;
+
+ /*
+ ** print a struct sockaddr_in as
+ ** a.b.c.d:port
+ */
+ case 'I':
+ {
+ struct sockaddr_in *si;
+
+ si = va_arg(ap, struct sockaddr_in *);
+ if (si != NULL) {
+ s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
+ if (adjust_precision && precision < s_len)
+ s_len = precision;
+ } else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ }
+ break;
+
+ /* print a struct in_addr as a.b.c.d */
+ case 'A':
+ {
+ struct in_addr *ia;
+
+ ia = va_arg(ap, struct in_addr *);
+ if (ia != NULL) {
+ s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
+ if (adjust_precision && precision < s_len)
+ s_len = precision;
+ } else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ }
+ break;
+
+ case NUL:
+ /* if %p ends the string, oh well ignore it */
+ continue;
+
+ default:
+ s = "bogus %p";
+ s_len = 8;
+ prefix_char = NUL;
+ break;
+ }
+ break;
+
+ case NUL:
+ /*
+ ** The last character of the format string
+ ** was %. We ignore it.
+ */
+ continue;
+
+
+ /*
+ ** The default case is for unrecognized %'s.
+ ** We print %<char> to help the user identify
+ ** what option is not understood. This is
+ ** also useful in case the user wants to
+ ** pass the output of format_converter
+ ** to another function that understands
+ ** some other %<char> (like syslog).
+ ** Note that we can't point s inside fmt
+ ** because the unknown <char> could be
+ ** preceded by width etc.
+ */
+ default:
+ char_buf[0] = '%';
+ char_buf[1] = *fmt;
+ s = char_buf;
+ s_len = 2;
+ pad_char = ' ';
+ break;