Files
mars-matrixssl/core/src/psprintf.c
Janne Johansson 69b5f2c6c3 MatrixSSL 4.5.1
2022-07-29 12:30:12 +03:00

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 */