|
Packit |
0021fb |
/*
|
|
Packit |
0021fb |
* This file is part of ltrace.
|
|
Packit |
0021fb |
* Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
|
|
Packit |
0021fb |
* Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
|
|
Packit |
0021fb |
* Copyright (C) 2006 Steve Fink
|
|
Packit |
0021fb |
* Copyright (C) 2006 Ian Wienand
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is free software; you can redistribute it and/or
|
|
Packit |
0021fb |
* modify it under the terms of the GNU General Public License as
|
|
Packit |
0021fb |
* published by the Free Software Foundation; either version 2 of the
|
|
Packit |
0021fb |
* License, or (at your option) any later version.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
0021fb |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
0021fb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
0021fb |
* General Public License for more details.
|
|
Packit |
0021fb |
*
|
|
Packit |
0021fb |
* You should have received a copy of the GNU General Public License
|
|
Packit |
0021fb |
* along with this program; if not, write to the Free Software
|
|
Packit |
0021fb |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
Packit |
0021fb |
* 02110-1301 USA
|
|
Packit |
0021fb |
*/
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#include <assert.h>
|
|
Packit |
0021fb |
#include <stdlib.h>
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
#include "printf.h"
|
|
Packit |
0021fb |
#include "type.h"
|
|
Packit |
0021fb |
#include "value.h"
|
|
Packit |
0021fb |
#include "expr.h"
|
|
Packit |
0021fb |
#include "zero.h"
|
|
Packit |
0021fb |
#include "param.h"
|
|
Packit |
0021fb |
#include "lens_default.h"
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct param_enum {
|
|
Packit |
0021fb |
struct value array;
|
|
Packit |
0021fb |
int percent;
|
|
Packit |
0021fb |
size_t *future_length;
|
|
Packit |
0021fb |
char *format;
|
|
Packit |
0021fb |
char const *ptr;
|
|
Packit |
0021fb |
char const *end;
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static struct param_enum *
|
|
Packit |
0021fb |
param_printf_init(struct value *cb_args, size_t nargs,
|
|
Packit |
0021fb |
struct value_dict *arguments)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
assert(nargs == 1);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* We expect a char array pointer. */
|
|
Packit |
0021fb |
if (cb_args->type->type != ARGTYPE_POINTER
|
|
Packit |
0021fb |
|| cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY
|
|
Packit |
0021fb |
|| (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type
|
|
Packit |
0021fb |
!= ARGTYPE_CHAR))
|
|
Packit |
0021fb |
return NULL;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct param_enum *self = malloc(sizeof(*self));
|
|
Packit |
0021fb |
if (self == NULL) {
|
|
Packit |
0021fb |
fail:
|
|
Packit |
0021fb |
free(self);
|
|
Packit |
0021fb |
return NULL;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (value_init_deref(&self->array, cb_args) < 0)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
assert(self->array.type->type == ARGTYPE_ARRAY);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
self->format = (char *)value_get_data(&self->array, arguments);
|
|
Packit |
0021fb |
if (self->format == NULL) {
|
|
Packit |
0021fb |
value_destroy(&self->array);
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
size_t size = value_size(&self->array, arguments);
|
|
Packit |
0021fb |
if (size == (size_t)-1) {
|
|
Packit |
0021fb |
value_destroy(&self->array);
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
self->ptr = self->format;
|
|
Packit |
0021fb |
self->end = self->format + size;
|
|
Packit |
0021fb |
self->future_length = NULL;
|
|
Packit |
0021fb |
return self;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
drop_future_length(struct param_enum *self)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (self->future_length != NULL) {
|
|
Packit |
0021fb |
free(self->future_length);
|
|
Packit |
0021fb |
self->future_length = NULL;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static int
|
|
Packit |
0021fb |
form_next_param(struct param_enum *self,
|
|
Packit |
0021fb |
enum arg_type format_type, enum arg_type elt_type,
|
|
Packit |
0021fb |
unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len,
|
|
Packit |
0021fb |
struct arg_type_info *infop)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
/* XXX note: Some types are wrong because we lack
|
|
Packit |
0021fb |
ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */
|
|
Packit |
0021fb |
assert(lng <= 2);
|
|
Packit |
0021fb |
assert(hlf <= 2);
|
|
Packit |
0021fb |
static enum arg_type ints[] =
|
|
Packit |
0021fb |
{ ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT,
|
|
Packit |
0021fb |
ARGTYPE_LONG, ARGTYPE_ULONG };
|
|
Packit |
0021fb |
static enum arg_type uints[] =
|
|
Packit |
0021fb |
{ ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT,
|
|
Packit |
0021fb |
ARGTYPE_ULONG, ARGTYPE_ULONG };
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct arg_type_info *elt_info = NULL;
|
|
Packit |
0021fb |
if (format_type == ARGTYPE_ARRAY || format_type == ARGTYPE_POINTER)
|
|
Packit |
0021fb |
elt_info = type_get_simple(elt_type);
|
|
Packit |
0021fb |
else if (format_type == ARGTYPE_INT)
|
|
Packit |
0021fb |
format_type = ints[2 + lng - hlf];
|
|
Packit |
0021fb |
else if (format_type == ARGTYPE_UINT)
|
|
Packit |
0021fb |
format_type = uints[2 + lng - hlf];
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (format_type == ARGTYPE_ARRAY) {
|
|
Packit |
0021fb |
struct arg_type_info *array = malloc(sizeof(*array));
|
|
Packit |
0021fb |
if (array == NULL)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
struct expr_node *node = NULL;
|
|
Packit |
0021fb |
int own_node;
|
|
Packit |
0021fb |
if (len_buf_len != 0
|
|
Packit |
0021fb |
|| self->future_length != NULL) {
|
|
Packit |
0021fb |
struct tmp {
|
|
Packit |
0021fb |
struct expr_node node;
|
|
Packit |
0021fb |
struct arg_type_info type;
|
|
Packit |
0021fb |
};
|
|
Packit |
0021fb |
struct tmp *len = malloc(sizeof(*len));
|
|
Packit |
0021fb |
if (len == NULL) {
|
|
Packit |
0021fb |
fail:
|
|
Packit |
0021fb |
free(len);
|
|
Packit |
0021fb |
free(array);
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
len->type = *type_get_simple(ARGTYPE_LONG);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
long l;
|
|
Packit |
0021fb |
if (self->future_length != NULL) {
|
|
Packit |
0021fb |
l = *self->future_length;
|
|
Packit |
0021fb |
drop_future_length(self);
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
l = atol(len_buf);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
expr_init_const_word(&len->node, l, &len->type, 0);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
node = build_zero_w_arg(&len->node, 1);
|
|
Packit |
0021fb |
if (node == NULL)
|
|
Packit |
0021fb |
goto fail;
|
|
Packit |
0021fb |
own_node = 1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
node = expr_node_zero();
|
|
Packit |
0021fb |
own_node = 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
assert(node != NULL);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
type_init_array(array, elt_info, 0, node, own_node);
|
|
Packit |
0021fb |
type_init_pointer(infop, array, 1);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else if (format_type == ARGTYPE_POINTER) {
|
|
Packit |
0021fb |
type_init_pointer(infop, elt_info, 0);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
} else {
|
|
Packit |
0021fb |
*infop = *type_get_simple(format_type);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static int
|
|
Packit |
0021fb |
param_printf_next(struct param_enum *self, struct arg_type_info *infop,
|
|
Packit |
0021fb |
int *insert_stop)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
unsigned hlf = 0;
|
|
Packit |
0021fb |
unsigned lng = 0;
|
|
Packit |
0021fb |
enum arg_type format_type = ARGTYPE_VOID;
|
|
Packit |
0021fb |
enum arg_type elt_type = ARGTYPE_VOID;
|
|
Packit |
0021fb |
char len_buf[25] = {};
|
|
Packit |
0021fb |
size_t len_buf_len = 0;
|
|
Packit |
0021fb |
struct lens *lens = NULL;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
for (; self->ptr < self->end; ++self->ptr) {
|
|
Packit |
0021fb |
if (!self->percent) {
|
|
Packit |
0021fb |
if (*self->ptr == '%')
|
|
Packit |
0021fb |
self->percent = 1;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
switch (*self->ptr) {
|
|
Packit |
0021fb |
case '#': case ' ': case '-':
|
|
Packit |
0021fb |
case '+': case 'I': case '\'':
|
|
Packit |
0021fb |
/* These are only important for formatting,
|
|
Packit |
0021fb |
* not for interpreting the type. */
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case '*':
|
|
Packit |
0021fb |
/* Length parameter given in the next
|
|
Packit |
0021fb |
* argument. */
|
|
Packit |
0021fb |
if (self->future_length == NULL)
|
|
Packit |
0021fb |
/* This should really be an assert,
|
|
Packit |
0021fb |
* but we can't just fail on invalid
|
|
Packit |
0021fb |
* format string. */
|
|
Packit |
0021fb |
self->future_length
|
|
Packit |
0021fb |
= malloc(sizeof(*self->future_length));
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (self->future_length != NULL) {
|
|
Packit |
0021fb |
++self->ptr;
|
|
Packit |
0021fb |
format_type = ARGTYPE_INT;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case '0':
|
|
Packit |
0021fb |
case '1': case '2': case '3':
|
|
Packit |
0021fb |
case '4': case '5': case '6':
|
|
Packit |
0021fb |
case '7': case '8': case '9':
|
|
Packit |
0021fb |
/* Field length likewise, but we need to parse
|
|
Packit |
0021fb |
* this to attach the appropriate string
|
|
Packit |
0021fb |
* length expression. */
|
|
Packit |
0021fb |
if (len_buf_len < sizeof(len_buf) - 1)
|
|
Packit |
0021fb |
len_buf[len_buf_len++] = *self->ptr;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'h':
|
|
Packit |
0021fb |
if (hlf < 2)
|
|
Packit |
0021fb |
hlf++;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'l':
|
|
Packit |
0021fb |
if (lng < 2)
|
|
Packit |
0021fb |
lng++;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'q':
|
|
Packit |
0021fb |
lng = 2;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'L': /* long double */
|
|
Packit |
0021fb |
lng = 1;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'j': /* intmax_t */
|
|
Packit |
0021fb |
/* XXX ABI should know */
|
|
Packit |
0021fb |
lng = 2;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 't': /* ptrdiff_t */
|
|
Packit |
0021fb |
case 'Z': case 'z': /* size_t */
|
|
Packit |
0021fb |
lng = 1; /* XXX ABI should tell */
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'd':
|
|
Packit |
0021fb |
case 'i':
|
|
Packit |
0021fb |
format_type = ARGTYPE_INT;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'o':
|
|
Packit |
0021fb |
lens = &octal_lens;
|
|
Packit |
0021fb |
goto uint;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'x': case 'X':
|
|
Packit |
0021fb |
lens = &hex_lens;
|
|
Packit |
0021fb |
/* Fall through. */
|
|
Packit |
0021fb |
case 'u':
|
|
Packit |
0021fb |
uint:
|
|
Packit |
0021fb |
format_type = ARGTYPE_UINT;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'e': case 'E':
|
|
Packit |
0021fb |
case 'f': case 'F':
|
|
Packit |
0021fb |
case 'g': case 'G':
|
|
Packit |
0021fb |
case 'a': case 'A':
|
|
Packit |
0021fb |
format_type = ARGTYPE_DOUBLE;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'C': /* like "lc" */
|
|
Packit |
0021fb |
if (lng == 0)
|
|
Packit |
0021fb |
lng++;
|
|
Packit |
0021fb |
case 'c':
|
|
Packit |
0021fb |
/* XXX "lc" means wchar_t string. */
|
|
Packit |
0021fb |
format_type = ARGTYPE_CHAR;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'S': /* like "ls" */
|
|
Packit |
0021fb |
if (lng == 0)
|
|
Packit |
0021fb |
lng++;
|
|
Packit |
0021fb |
case 's':
|
|
Packit |
0021fb |
format_type = ARGTYPE_ARRAY;
|
|
Packit |
0021fb |
/* XXX "ls" means wchar_t string. */
|
|
Packit |
0021fb |
elt_type = ARGTYPE_CHAR;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
lens = &string_lens;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'p':
|
|
Packit |
0021fb |
case 'n': /* int* where to store no. of printed chars. */
|
|
Packit |
0021fb |
format_type = ARGTYPE_POINTER;
|
|
Packit |
0021fb |
elt_type = ARGTYPE_VOID;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
break;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
case 'm': /* (glibc) print argument of errno */
|
|
Packit |
0021fb |
case '%':
|
|
Packit |
0021fb |
lng = 0;
|
|
Packit |
0021fb |
hlf = 0;
|
|
Packit |
0021fb |
self->percent = 0;
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
default:
|
|
Packit |
0021fb |
continue;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
/* If we got here, the type must have been set. */
|
|
Packit |
0021fb |
assert(format_type != ARGTYPE_VOID);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
if (form_next_param(self, format_type, elt_type, hlf, lng,
|
|
Packit |
0021fb |
len_buf, len_buf_len, infop) < 0)
|
|
Packit |
0021fb |
return -1;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
infop->lens = lens;
|
|
Packit |
0021fb |
infop->own_lens = 0;
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
*infop = *type_get_simple(ARGTYPE_VOID);
|
|
Packit |
0021fb |
return 0;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static enum param_status
|
|
Packit |
0021fb |
param_printf_stop(struct param_enum *self, struct value *value)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
if (self->future_length != NULL
|
|
Packit |
0021fb |
&& value_extract_word(value, (long *)self->future_length, NULL) < 0)
|
|
Packit |
0021fb |
drop_future_length(self);
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
return PPCB_CONT;
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
static void
|
|
Packit |
0021fb |
param_printf_done(struct param_enum *context)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
value_destroy(&context->array);
|
|
Packit |
0021fb |
free(context);
|
|
Packit |
0021fb |
}
|
|
Packit |
0021fb |
|
|
Packit |
0021fb |
void
|
|
Packit |
0021fb |
param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg)
|
|
Packit |
0021fb |
{
|
|
Packit |
0021fb |
param_init_pack(param, PARAM_PACK_VARARGS, arg, 1, own_arg,
|
|
Packit |
0021fb |
¶m_printf_init, ¶m_printf_next,
|
|
Packit |
0021fb |
¶m_printf_stop, ¶m_printf_done);
|
|
Packit |
0021fb |
}
|