Blame printf.c

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
			&param_printf_init, &param_printf_next,
Packit 0021fb
			&param_printf_stop, &param_printf_done);
Packit 0021fb
}