688 lines
17 KiB
C
688 lines
17 KiB
C
/* psprintf.c
|
|
*
|
|
* Description: Implementation of printf: for platforms without C99 printf.
|
|
*/
|
|
|
|
/*****************************************************************************
|
|
* Copyright (c) 2007-2018 Rambus Inc. All Rights Reserved.
|
|
*
|
|
* The latest version of this code is available at http://www.matrixssl.org
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This General Public License does NOT permit incorporating this software
|
|
* into proprietary programs. If you are unable to comply with the GPL, a
|
|
* commercial license for this software may be purchased from Rambus Inc at
|
|
* http://www.rambus.com/
|
|
*
|
|
* This program is distributed in WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*****************************************************************************/
|
|
|
|
#include "implementation_defs.h"
|
|
|
|
#include "osdep_stdarg.h"
|
|
#include "osdep_stddef.h"
|
|
#include "osdep_stdio.h"
|
|
#include "osdep_stdlib.h" /* For strtol. */
|
|
#include "osdep_limits.h" /* For strtol. */
|
|
#include "coreApi.h"
|
|
#include "psprintf.h"
|
|
|
|
static const char SZ_nulls[] = "(null)";
|
|
|
|
static
|
|
void SZ_outc(struct arg *arg, int ch)
|
|
{
|
|
arg->count++;
|
|
if (arg->upper && (ch >= 'a' && ch <= 'z'))
|
|
{
|
|
arg->ch = ch - 32;
|
|
}
|
|
else
|
|
{
|
|
arg->ch = ch;
|
|
}
|
|
(*arg->do_putc)(arg);
|
|
}
|
|
|
|
static
|
|
void SZ_outs(struct arg *arg, const char *ptr)
|
|
{
|
|
long slen = 0, min, max;
|
|
unsigned int plen;
|
|
unsigned char left = 0;
|
|
|
|
if (!ptr)
|
|
{
|
|
ptr = SZ_nulls;
|
|
}
|
|
|
|
/* Check if min is negative (left flag). */
|
|
min = arg->min;
|
|
max = arg->max;
|
|
if (min < 0)
|
|
{
|
|
min = -min; left = 1;
|
|
}
|
|
|
|
/* Find length. (equivalent to strnlen). */
|
|
while (*ptr && max)
|
|
{
|
|
max--; ptr++; slen++;
|
|
}
|
|
ptr -= slen;
|
|
if (arg->altform)
|
|
{
|
|
slen++;
|
|
}
|
|
|
|
plen = min > slen ? min - slen : 0;
|
|
while (plen && !left && !arg->padz)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
SZ_outc(arg, '0'); slen--;
|
|
}
|
|
if ((*ptr < '0' || *ptr > '9') && slen)
|
|
{
|
|
SZ_outc(arg, *(ptr++)); slen--;
|
|
}
|
|
while (plen && !left && arg->padz)
|
|
{
|
|
SZ_outc(arg, '0'); plen--;
|
|
}
|
|
while (*ptr && slen)
|
|
{
|
|
SZ_outc(arg, *(ptr++)); slen--;
|
|
}
|
|
while (plen)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
}
|
|
|
|
static
|
|
void SZ_outs_w_ext(struct arg *arg, const char *ptr_c, int m)
|
|
{
|
|
const __WCHAR_TYPE__ *ptr_w = (const __WCHAR_TYPE__ *) ptr_c;
|
|
const unsigned int *ptr_i = (const unsigned int *) ptr_c;
|
|
static char out[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
long slen = 0, min, max;
|
|
unsigned int plen;
|
|
unsigned char left = 0;
|
|
int dir = 0;
|
|
|
|
if (!ptr_w)
|
|
{
|
|
SZ_outs(arg, SZ_nulls); return;
|
|
}
|
|
|
|
/* Check if min is negative (left flag). */
|
|
min = arg->min;
|
|
max = arg->max;
|
|
if (min < 0)
|
|
{
|
|
min = -min; left = 1;
|
|
}
|
|
|
|
/* Get length. */
|
|
slen = (long) arg->max;
|
|
max -= arg->max;
|
|
if (m == 4 || m == 5)
|
|
{
|
|
dir = -2; /* backwards. */
|
|
ptr_w += slen - 1;
|
|
ptr_i += slen - 1;
|
|
m -= 2;
|
|
}
|
|
if (m == 2)
|
|
{
|
|
slen *= 8;
|
|
}
|
|
if (m == 3)
|
|
{
|
|
slen *= 9;
|
|
if (slen > 0)
|
|
{
|
|
slen--;
|
|
}
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
slen++;
|
|
}
|
|
|
|
plen = min > slen ? min - slen : 0;
|
|
while (plen && !left && !arg->padz)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
SZ_outc(arg, '0'); slen--;
|
|
}
|
|
while (slen)
|
|
{
|
|
if (m == 1)
|
|
{
|
|
__WCHAR_TYPE__ ch = *(ptr_w++);
|
|
ch = ((ch < 32) || (ch > 126)) ? '.' : ch;
|
|
SZ_outc(arg, (char) ch);
|
|
slen--;
|
|
}
|
|
else
|
|
{
|
|
unsigned int uwch;
|
|
uwch = (unsigned int) *(ptr_i++);
|
|
ptr_i += dir;
|
|
SZ_outc(arg, out[(uwch / 0x10000000) & 15]);
|
|
SZ_outc(arg, out[(uwch / 0x1000000) & 15]);
|
|
SZ_outc(arg, out[(uwch / 0x100000) & 15]);
|
|
SZ_outc(arg, out[(uwch / 0x10000) & 15]);
|
|
SZ_outc(arg, out[(uwch / 0x1000) & 15]);
|
|
SZ_outc(arg, out[(uwch / 0x100) & 15]);
|
|
SZ_outc(arg, out[(uwch / 16) & 15]);
|
|
SZ_outc(arg, out[uwch & 15]);
|
|
slen -= 8;
|
|
if (m == 3 && slen > 0)
|
|
{
|
|
SZ_outc(arg, ' ');
|
|
slen--;
|
|
}
|
|
}
|
|
}
|
|
while (plen)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
}
|
|
|
|
static
|
|
void SZ_outs_ext(struct arg *arg, const char *ptr, int m)
|
|
{
|
|
static char out[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
long slen = 0, min, max;
|
|
unsigned int plen;
|
|
unsigned char left = 0;
|
|
int dir = 0;
|
|
|
|
if (!ptr)
|
|
{
|
|
SZ_outs(arg, SZ_nulls); return;
|
|
}
|
|
|
|
/* Check if min is negative (left flag). */
|
|
min = arg->min;
|
|
max = arg->max;
|
|
if (min < 0)
|
|
{
|
|
min = -min; left = 1;
|
|
}
|
|
|
|
/* Get length. */
|
|
slen = (long) arg->max;
|
|
max -= arg->max;
|
|
if (m == 4 || m == 5)
|
|
{
|
|
dir = -2; /* backwards. */
|
|
ptr += slen - 1;
|
|
m -= 2;
|
|
}
|
|
if (m == 2)
|
|
{
|
|
slen *= 2;
|
|
}
|
|
if (m == 3)
|
|
{
|
|
slen *= 3;
|
|
if (slen > 0)
|
|
{
|
|
slen--;
|
|
}
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
slen++;
|
|
}
|
|
|
|
plen = min > slen ? min - slen : 0;
|
|
while (plen && !left && !arg->padz)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
SZ_outc(arg, '0'); slen--;
|
|
}
|
|
while (slen)
|
|
{
|
|
if (m == 1)
|
|
{
|
|
char ch = *(ptr++);
|
|
ch = ((ch < 32) || (ch > 126)) ? '.' : ch;
|
|
SZ_outc(arg, ch);
|
|
slen--;
|
|
}
|
|
else
|
|
{
|
|
unsigned char uch;
|
|
uch = (unsigned char) *(ptr++);
|
|
SZ_outc(arg, out[uch / 16]);
|
|
SZ_outc(arg, out[uch & 15]);
|
|
slen -= 2;
|
|
if (m == 3 && slen > 0)
|
|
{
|
|
SZ_outc(arg, ' ');
|
|
slen--;
|
|
}
|
|
ptr += dir;
|
|
}
|
|
}
|
|
while (plen)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
}
|
|
|
|
static
|
|
void SZ_outs_w(struct arg *arg, const char *ptr_c)
|
|
{
|
|
wchar_t *ptr = (wchar_t *) ptr_c;
|
|
long slen = 0, min, max;
|
|
unsigned int plen;
|
|
unsigned char left = 0;
|
|
|
|
if (!ptr)
|
|
{
|
|
SZ_outs(arg, SZ_nulls); return;
|
|
}
|
|
|
|
/* Check if min is negative (left flag). */
|
|
min = arg->min;
|
|
max = arg->max;
|
|
if (min < 0)
|
|
{
|
|
min = -min; left = 1;
|
|
}
|
|
|
|
/* Find length. (equivalent to strnlen). */
|
|
while (*ptr && max)
|
|
{
|
|
max--; ptr++; slen++;
|
|
}
|
|
ptr -= slen;
|
|
if (arg->altform)
|
|
{
|
|
slen++;
|
|
}
|
|
|
|
plen = min > slen ? min - slen : 0;
|
|
while (plen && !left && !arg->padz)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
if (arg->altform)
|
|
{
|
|
SZ_outc(arg, '0'); slen--;
|
|
}
|
|
if ((*ptr < '0' || *ptr > '9') && slen)
|
|
{
|
|
SZ_outc(arg, (char) *(ptr++)); slen--;
|
|
}
|
|
while (plen && !left && arg->padz)
|
|
{
|
|
SZ_outc(arg, '0'); plen--;
|
|
}
|
|
while (*ptr && slen)
|
|
{
|
|
SZ_outc(arg, *(ptr++)); slen--;
|
|
}
|
|
while (plen)
|
|
{
|
|
SZ_outc(arg, ' '); plen--;
|
|
}
|
|
}
|
|
|
|
void SZ_ulltoa(char *target, unsigned long long val, unsigned int base)
|
|
{
|
|
unsigned int idx;
|
|
unsigned long long val_mag = val;
|
|
static char out[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
|
|
idx = 0;
|
|
do
|
|
{
|
|
idx++;
|
|
val_mag /= base;
|
|
}
|
|
while (val_mag > 0);
|
|
|
|
target[idx] = 0;
|
|
do
|
|
{
|
|
target[--idx] = out[val % base];
|
|
val /= base;
|
|
}
|
|
while (val > 0);
|
|
ASSERT(idx == 0);
|
|
}
|
|
|
|
void psVprintf(struct arg *arg, const char *fmt, va_list va)
|
|
{
|
|
unsigned long long val;
|
|
char buf[(sizeof(val) * 8 + 2) / 3 + 2];
|
|
char ch;
|
|
signed char left_right;
|
|
char pad;
|
|
char l;
|
|
signed char arg_size;
|
|
int mode = 0;
|
|
|
|
arg->upper = 0;
|
|
|
|
if (!fmt)
|
|
{
|
|
fmt = SZ_nulls;
|
|
}
|
|
while (*fmt)
|
|
{
|
|
ch = *fmt;
|
|
fmt++;
|
|
if (ch != '%')
|
|
{
|
|
SZ_outc(arg, ch);
|
|
}
|
|
else
|
|
{
|
|
char *ptr = buf;
|
|
long *fill = (long *) &(arg->min);
|
|
|
|
arg->min = 0;
|
|
arg->max = ~0;
|
|
arg->padz = 0;
|
|
arg->altform = 0;
|
|
arg->upper = 0;
|
|
left_right = 1;
|
|
pad = 0;
|
|
l = 0;
|
|
arg_size = 0;
|
|
|
|
do
|
|
{
|
|
next_ch:
|
|
ch = *(fmt++);
|
|
switch (ch)
|
|
{
|
|
case '-':
|
|
left_right = -1;
|
|
goto next_ch;
|
|
case '.': fill = &(arg->max); left_right = 1;
|
|
goto next_ch;
|
|
case '*': *fill = left_right * va_arg(va, int); goto next_ch;
|
|
case '#':
|
|
arg->altform = 1;
|
|
goto next_ch;
|
|
case '0':
|
|
arg->padz = 1;
|
|
goto next_ch;
|
|
case '1': case '2': case '3': case '4': case '5':
|
|
case '6': case '7': case '8': case '9':
|
|
{
|
|
char *newfmt;
|
|
*fill = left_right * (long) Strtol(fmt - 1, &newfmt, 10);
|
|
fmt = newfmt;
|
|
goto next_ch;
|
|
}
|
|
case ' ':
|
|
case '+': pad = ch; goto next_ch;
|
|
case 'h': arg_size--; goto next_ch;
|
|
case 'j': arg_size++; /* assume: long long is intmax_t */
|
|
/* fall-through. */
|
|
case 't': /* assume: ptrdiff_t same size than size_t. */
|
|
case 'z': /* assume: size_t same size than long. */
|
|
case 'l': arg_size++; goto next_ch;
|
|
case 'p': arg->altform = 1; l = 16;
|
|
val = (uintptr_t) va_arg(va, void *);
|
|
goto print_num;
|
|
case 'X': arg->upper = 1;
|
|
/* fall-through. */
|
|
case 'x': l = 6;
|
|
/* fall-through. */
|
|
case 'u':
|
|
case 'i':
|
|
case 'd': l += 2;
|
|
/* fall-through. */
|
|
case 'o': l += 8;
|
|
/* fall-through. */
|
|
case 'c':
|
|
/* Promote "long" to "long long" on LP64 architectures. */
|
|
if (arg_size > 0 && sizeof(long) > sizeof(int))
|
|
{
|
|
arg_size++;
|
|
}
|
|
if (arg_size >= 2)
|
|
{
|
|
val = va_arg(va, unsigned long long);
|
|
}
|
|
else
|
|
{
|
|
val = va_arg(va, unsigned int);
|
|
}
|
|
if (arg_size == -1)
|
|
{
|
|
val &= 0xffff;
|
|
}
|
|
else if (arg_size == -2)
|
|
{
|
|
val &= 0xff;
|
|
}
|
|
if (ch == 'c')
|
|
{
|
|
goto out_char;
|
|
}
|
|
print_num:
|
|
if (arg->altform)
|
|
{
|
|
if (l == 8)
|
|
{
|
|
}
|
|
else if (l == 16)
|
|
{
|
|
buf[0] = 'x'; ptr++;
|
|
}
|
|
else
|
|
{
|
|
arg->altform = 0;
|
|
}
|
|
}
|
|
if (l == 10 && ch != 'u')
|
|
{
|
|
if (arg_size == -1 && val > 0x7fff)
|
|
{
|
|
short vals = (short) val; val = (long long) vals;
|
|
}
|
|
else if (arg_size == -2 && val > 0x7f)
|
|
{
|
|
char valb = (char) val; val = (long long) valb;
|
|
}
|
|
else if (((int) val) < 0 && arg_size < 2)
|
|
{
|
|
val = (int) val;
|
|
}
|
|
if (((int) val) < 0)
|
|
{
|
|
buf[0] = '-'; ptr++; val = -val;
|
|
}
|
|
else if (pad != 0)
|
|
{
|
|
buf[0] = pad; ptr++;
|
|
}
|
|
}
|
|
arg->max = sizeof(buf);
|
|
SZ_ulltoa(ptr, val, l);
|
|
ptr = buf;
|
|
goto out_string;
|
|
out_char:
|
|
/* Support for printing explicit \000 to string. */
|
|
if ((char) val == 0)
|
|
{
|
|
SZ_outc(arg, 0);
|
|
goto format_done;
|
|
}
|
|
buf[0] = (char) val;
|
|
buf[1] = 0;
|
|
goto out_string_nullpad;
|
|
case 'S':
|
|
ptr = va_arg(va, char *);
|
|
if (mode == 1 || mode == 2 || mode == 3 ||
|
|
mode == 4 || mode == 5)
|
|
{
|
|
if (arg->max == ~0)
|
|
{
|
|
goto error_in_args;
|
|
}
|
|
|
|
SZ_outs_w_ext(arg, ptr, mode);
|
|
mode = 0;
|
|
goto format_done;
|
|
}
|
|
/* out_wstring_nullpad: */
|
|
arg->padz = 0;
|
|
/* out_wstring: */
|
|
SZ_outs_w(arg, ptr);
|
|
goto format_done;
|
|
case 's':
|
|
ptr = va_arg(va, char *);
|
|
if (mode == 1 || mode == 2 || mode == 3 ||
|
|
mode == 4 || mode == 5)
|
|
{
|
|
if (arg->max == ~0)
|
|
{
|
|
goto error_in_args;
|
|
}
|
|
|
|
SZ_outs_ext(arg, ptr, mode);
|
|
mode = 0;
|
|
goto format_done;
|
|
}
|
|
out_string_nullpad:
|
|
arg->padz = 0;
|
|
out_string:
|
|
SZ_outs(arg, ptr);
|
|
goto format_done;
|
|
case 'm':
|
|
/* Mode switch. */
|
|
mode = arg->min;
|
|
buf[0] = 0;
|
|
arg->min = 0;
|
|
goto format_done;
|
|
case '%':
|
|
SZ_outc(arg, '%');
|
|
goto format_done;
|
|
default:
|
|
/* Default error handling: print formatting code
|
|
with problems and stop handling. */
|
|
error_in_args:
|
|
arg->upper = 0;
|
|
SZ_outc(arg, '%');
|
|
SZ_outc(arg, ch);
|
|
return; /* Unexpected character, do not continue printing. */
|
|
/* Note: the behavior is different than usual print */
|
|
/* The rationale for behavior is: after unexpected */
|
|
/* character, the amount of arguments read might */
|
|
/* differ from expectations of the caller and thus */
|
|
/* it could cause crash. */
|
|
}
|
|
}
|
|
while (1);
|
|
format_done:
|
|
arg->upper = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void SZ_putbuf(struct arg *arg)
|
|
{
|
|
(void) psBufAppendChar(arg->context, (char) arg->ch);
|
|
}
|
|
|
|
int psSbufprintf(psBuf_t *buf, const char *format, ...)
|
|
{
|
|
struct arg arg;
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
arg.do_putc = &SZ_putbuf;
|
|
arg.count = 0;
|
|
arg.context = buf;
|
|
(void) psVprintf(&arg, format, ap);
|
|
va_end(ap);
|
|
|
|
return arg.count < (size_t) INT_MAX ? arg.count : INT_MAX;
|
|
}
|
|
|
|
int psVsnprintf(char *str, size_t size, const char *format, va_list ap)
|
|
{
|
|
struct arg arg;
|
|
psBuf_t buf;
|
|
|
|
buf.buf = (unsigned char *) str;
|
|
buf.start = buf.buf;
|
|
buf.end = buf.start;
|
|
buf.size = (int32) size;
|
|
if (size != (size_t) buf.size)
|
|
{
|
|
/* Size has been truncated. */
|
|
buf.size = 0x7FFFFFFFL;
|
|
}
|
|
|
|
arg.do_putc = &SZ_putbuf;
|
|
arg.count = 0;
|
|
arg.context = &buf;
|
|
(void) psVprintf(&arg, format, ap);
|
|
|
|
/* Zero terminate. */
|
|
arg.ch = 0;
|
|
if (arg.count >= size && size >= 1)
|
|
{
|
|
str[size - 1] = 0;
|
|
}
|
|
else if (size >= 1)
|
|
{
|
|
str[arg.count] = 0;
|
|
}
|
|
|
|
return arg.count < (size_t) INT_MAX ? arg.count : INT_MAX;
|
|
}
|
|
|
|
int psSnprintf(char *str, size_t size, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
int res;
|
|
|
|
va_start(ap, format);
|
|
res = psVsnprintf(str, size, format, ap);
|
|
va_end(ap);
|
|
|
|
return res;
|
|
}
|
|
|
|
/* end of file debug_printf.c */
|