Commit 940ec065 authored by Petr Machata's avatar Petr Machata

Introduce printf module, drop ARGTYPE_FORMAT

parent 6f9f03b3
2012-01-06 Petr Machata <[email protected]>
* printf.c, printf.h: New file with support for functions with
vararg parameter pack described by a printf-style format argument
* type.h (enum arg_type): Drop ARGTYPE_FORMAT
* type.c, display_args.c: Likewise
* read_config_file.c: Introduce backward-compatible support for
the "format" keyword
2012-01-06 Petr Machata <[email protected]>
* type.h (struct arg_type_info): Drop float_info, double_info
......
......@@ -37,6 +37,7 @@ libltrace_la_SOURCES = \
fetch.c \
vect.c \
param.c \
printf.c \
zero.c
libltrace_la_LIBADD = \
......@@ -82,6 +83,7 @@ noinst_HEADERS = \
fetch.h \
vect.h \
param.h \
printf.h \
zero.h
dist_man1_MANS = \
......
......@@ -310,8 +310,11 @@ format_array(FILE *stream, struct value *value, struct value_dict *arguments,
int
format_argument(FILE *stream, struct value *value, struct value_dict *arguments)
{
struct expr_node *length = expr_node_zero();
switch (value->type->type) {
struct value tmp;
struct arg_type_info info[2];
int ret;
case ARGTYPE_VOID:
return fprintf(stream, "<void>");
......@@ -359,28 +362,22 @@ format_argument(FILE *stream, struct value *value, struct value_dict *arguments)
options.strlen, 0, "\"", "\"", "");
case ARGTYPE_STRING_N:
length = value->type->u.string_n_info.length;
/* fall-through */
case ARGTYPE_FORMAT: {
/* Strings are in fact char pointers. Smuggle in the
* pointer here. */
struct arg_type_info info[2];
type_init_array(&info[1], type_get_simple(ARGTYPE_CHAR), 0,
length, 0);
value->type->u.string_n_info.length, 0);
type_init_pointer(&info[0], &info[1], 0);
struct value tmp;
value_clone(&tmp, value);
value_set_type(&tmp, info, 0);
int ret = format_argument(stream, &tmp, arguments);
ret = format_argument(stream, &tmp, arguments);
value_destroy(&tmp);
type_destroy(&info[0]);
type_destroy(&info[1]);
return ret;
}
case ARGTYPE_ENUM:
return format_enum(stream, value, arguments);
......
/*
* This file is part of ltrace.
* Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
* Copyright (C) 1998,2004,2007,2008,2009 Juan Cespedes
* Copyright (C) 2006 Steve Fink
* Copyright (C) 2006 Ian Wienand
*
* This program is free software; 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 program is distributed in the hope that it will be useful, but
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <assert.h>
#include <stdlib.h>
#include "printf.h"
#include "type.h"
#include "value.h"
#include "expr.h"
#include "zero.h"
#include "param.h"
struct param_enum {
struct value array;
int percent;
size_t *future_length;
char *format;
char const *ptr;
char const *end;
};
static struct param_enum *
param_printf_init(struct value *cb_args, size_t nargs,
struct value_dict *arguments)
{
assert(nargs == 1);
/* We expect a char array pointer. */
if (cb_args->type->type != ARGTYPE_STRING_N
&& (cb_args->type->type != ARGTYPE_POINTER
|| cb_args->type->u.ptr_info.info->type != ARGTYPE_ARRAY
|| (cb_args->type->u.ptr_info.info->u.array_info.elt_type->type
!= ARGTYPE_CHAR)))
return NULL;
struct param_enum *self = malloc(sizeof(*self));
if (self == NULL) {
fail:
free(self);
return NULL;
}
if (cb_args->type->type == ARGTYPE_STRING_N) {
struct value *tmp = value_string_to_charp(cb_args);
if (tmp == NULL)
goto fail;
if (value_init_deref(&self->array, tmp) < 0) {
value_destroy(tmp);
goto fail;
}
free(tmp);
} else if (value_init_deref(&self->array, cb_args) < 0)
goto fail;
assert(self->array.type->type == ARGTYPE_ARRAY);
self->format = (char *)value_get_data(&self->array, arguments);
if (self->format == NULL)
goto fail;
size_t size = value_size(&self->array, arguments);
if (size == (size_t)-1)
goto fail;
self->percent = 0;
self->ptr = self->format;
self->end = self->format + size;
self->future_length = NULL;
return self;
}
static void
drop_future_length(struct param_enum *self)
{
if (self->future_length != NULL) {
free(self->future_length);
self->future_length = NULL;
}
}
static int
form_next_param(struct param_enum *self,
enum arg_type format_type, enum arg_type elt_type,
unsigned hlf, unsigned lng, char *len_buf, size_t len_buf_len,
struct arg_type_info *infop)
{
/* XXX note: Some types are wrong because we lack
ARGTYPE_LONGLONG, ARGTYPE_UCHAR and ARGTYPE_SCHAR. */
assert(lng <= 2);
assert(hlf <= 2);
static enum arg_type ints[] =
{ ARGTYPE_CHAR, ARGTYPE_SHORT, ARGTYPE_INT,
ARGTYPE_LONG, ARGTYPE_ULONG };
static enum arg_type uints[] =
{ ARGTYPE_CHAR, ARGTYPE_USHORT, ARGTYPE_UINT,
ARGTYPE_ULONG, ARGTYPE_ULONG };
struct arg_type_info *elt_info = NULL;
if (elt_type != ARGTYPE_UNKNOWN)
elt_info = type_get_simple(elt_type);
if (format_type == ARGTYPE_INT)
format_type = ints[2 + lng - hlf];
else if (format_type == ARGTYPE_UINT)
format_type = uints[2 + lng - hlf];
if (format_type == ARGTYPE_ARRAY) {
struct expr_node *node = NULL;
if (len_buf_len != 0
|| self->future_length != NULL) {
struct tmp {
struct expr_node node;
struct arg_type_info type;
};
struct tmp *len = malloc(sizeof(*len));
if (len == NULL) {
fail:
free(len);
return -1;
}
len->type.type = ARGTYPE_LONG;
long l;
if (self->future_length != NULL) {
l = *self->future_length;
drop_future_length(self);
} else {
l = atol(len_buf);
}
expr_init_const_word(&len->node, l, &len->type, 0);
node = build_zero_w_arg(&len->node, 1);
if (node == NULL)
goto fail;
} else {
node = expr_node_zero();
}
assert(node != NULL);
type_init_array(infop, elt_info, 0, node, 1);
} else if (format_type == ARGTYPE_POINTER) {
type_init_pointer(infop, elt_info, 1);
} else {
*infop = *type_get_simple(format_type);
}
return 0;
}
static int
param_printf_next(struct param_enum *self, struct arg_type_info *infop,
int *insert_stop)
{
unsigned hlf = 0;
unsigned lng = 0;
enum arg_type format_type = ARGTYPE_UNKNOWN;
enum arg_type elt_type = ARGTYPE_UNKNOWN;
char len_buf[25] = {};
size_t len_buf_len = 0;
for (; self->ptr < self->end; ++self->ptr) {
if (!self->percent) {
if (*self->ptr == '%')
self->percent = 1;
continue;
}
switch (*self->ptr) {
case '#': case ' ': case '-':
case '+': case 'I': case '\'':
/* These are only important for formatting,
* not for interpreting the type. */
continue;
case '*':
/* Length parameter given in the next
* argument. */
if (self->future_length == NULL)
/* This should really be an assert,
* but we can't just fail on invalid
* format string. */
self->future_length
= malloc(sizeof(*self->future_length));
if (self->future_length != NULL) {
++self->ptr;
format_type = ARGTYPE_INT;
break;
}
case '0':
case '1': case '2': case '3':
case '4': case '5': case '6':
case '7': case '8': case '9':
/* Field length likewise, but we need to parse
* this to attach the appropriate string
* length expression. */
if (len_buf_len < sizeof(len_buf) - 1)
len_buf[len_buf_len++] = *self->ptr;
continue;
case 'h':
if (hlf < 2)
hlf++;
continue;
case 'l':
if (lng < 2)
lng++;
continue;
case 'q':
lng = 2;
continue;
case 'L': /* long double */
lng = 1;
continue;
case 'j': /* intmax_t */
/* XXX ABI should know */
lng = 2;
continue;
case 't': /* ptrdiff_t */
case 'Z': case 'z': /* size_t */
lng = 1; /* XXX ABI should tell */
continue;
case 'd':
case 'i':
format_type = ARGTYPE_INT;
self->percent = 0;
break;
case 'u':
case 'o':
case 'x': case 'X':
format_type = ARGTYPE_UINT;
self->percent = 0;
break;
case 'e': case 'E':
case 'f': case 'F':
case 'g': case 'G':
case 'a': case 'A':
format_type = ARGTYPE_DOUBLE;
self->percent = 0;
break;
case 'C': /* like "lc" */
if (lng == 0)
lng++;
case 'c':
/* XXX "lc" means wchar_t string. */
format_type = ARGTYPE_CHAR;
self->percent = 0;
break;
case 'S': /* like "ls" */
if (lng == 0)
lng++;
case 's':
format_type = ARGTYPE_ARRAY;
/* XXX "ls" means wchar_t string. */
elt_type = ARGTYPE_CHAR;
self->percent = 0;
break;
case 'p':
case 'n': /* int* where to store no. of printed chars. */
format_type = ARGTYPE_POINTER;
elt_type = ARGTYPE_VOID;
self->percent = 0;
break;
case 'm': /* (glibc) print argument of errno */
case '%':
lng = 0;
hlf = 0;
self->percent = 0;
continue;
default:
continue;
}
/* If we got here, the type must have been set. */
assert(format_type != ARGTYPE_UNKNOWN);
if (form_next_param(self, format_type, elt_type, hlf, lng,
len_buf, len_buf_len, infop) < 0)
return -1;
return 0;
}
infop->type = ARGTYPE_VOID;
return 0;
}
static enum param_status
param_printf_stop(struct param_enum *self, struct value *value)
{
if (self->future_length != NULL
&& value_extract_word(value, (long *)self->future_length, NULL) < 0)
drop_future_length(self);
return PPCB_CONT;
}
static void
param_printf_done(struct param_enum *context)
{
free(context);
}
void
param_pack_init_printf(struct param *param, struct expr_node *arg, int own_arg)
{
param_init_pack(param, arg, 1, own_arg,
&param_printf_init, &param_printf_next,
&param_printf_stop, &param_printf_done);
}
/*
* This file is part of ltrace.
* Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
*
* This program is free software; 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 program is distributed in the hope that it will be useful, but
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef PRINTF_H
#define PRINTF_H
#include <stddef.h>
#include "forward.h"
/* Wrapper around param_init_pack with callbacks to handle
* printf-style format strings. ARG should be an expression that
* evaluates to a pointer to a character array with the format string
* contents. */
void param_pack_init_printf(struct param *param,
struct expr_node *arg, int own_arg);
#endif /* PRINTF_H */
......@@ -33,14 +33,16 @@
#include "common.h"
#include "output.h"
#include "expr.h"
#include "zero.h"
#include "param.h"
#include "printf.h"
#include "zero.h"
#include "type.h"
static int line_no;
static char *filename;
static struct arg_type_info *parse_type(char **str, int *ownp);
static struct arg_type_info *parse_type(char **str, struct param **extra_param,
size_t param_num, int *ownp);
Function *list_of_functions = NULL;
......@@ -74,9 +76,6 @@ parse_arg_type(char **name, enum arg_type *ret)
KEYWORD("enum", ARGTYPE_ENUM);
KEYWORD("struct", ARGTYPE_STRUCT);
/* XXX temporary. */
KEYWORD("format", ARGTYPE_FORMAT);
assert(rest == NULL);
return -1;
......@@ -391,7 +390,7 @@ parse_typedef(char **str) {
// Parse the type
int own;
info = parse_type(str, &own);
info = parse_type(str, NULL, 0, &own);
insert_typedef(name, info, own);
}
......@@ -436,7 +435,7 @@ parse_struct(char **str, struct arg_type_info *info)
eat_spaces(str);
int own;
struct arg_type_info *field = parse_type(str, &own);
struct arg_type_info *field = parse_type(str, NULL, 0, &own);
if (field == NULL || type_struct_add(info, field, own)) {
type_destroy(info);
return -1;
......@@ -511,7 +510,43 @@ parse_string(char **str, struct arg_type_info **retp)
}
static int
parse_alias(char **str, struct arg_type_info **retp, int *ownp)
build_printf_pack(struct param **packp, size_t param_num)
{
if (packp == NULL) {
report_error(filename, line_no,
"'format' type in unexpected context");
return -1;
}
if (*packp != NULL) {
report_error(filename, line_no,
"only one 'format' type per function supported");
return -1;
}
*packp = malloc(sizeof(**packp));
if (*packp == NULL)
return -1;
struct expr_node *node = malloc(sizeof(*node));
if (node == NULL) {
free(*packp);
return -1;
}
expr_init_argno(node, param_num);
param_pack_init_printf(*packp, node, 1);
return 0;
}
/* XXX extra_param and param_num are a kludge to get in
* backward-compatible support for "format" parameter type. The
* latter is only valid if the former is non-NULL, which is only in
* top-level context. */
static int
parse_alias(char **str, struct arg_type_info **retp, int *ownp,
struct param **extra_param, size_t param_num)
{
/* For backward compatibility, we need to support things like
* stringN (which is like string[argN], string[N], and also
......@@ -522,6 +557,19 @@ parse_alias(char **str, struct arg_type_info **retp, int *ownp)
(*str) += 6;
return parse_string(str, retp);
} else if (strncmp(*str, "format", 6) == 0
&& !isalnum((*str)[6])
&& extra_param != NULL) {
/* For backward compatibility, format is parsed as
* "string", but it smuggles to the parameter list of
* a function a "printf" argument pack with this
* parameter as argument. */
(*str) += 6;
if (parse_string(str, retp) < 0)
return -1;
return build_printf_pack(extra_param, param_num);
} else {
*retp = NULL;
return 0;
......@@ -538,7 +586,7 @@ parse_array(char **str, struct arg_type_info *info)
eat_spaces(str);
int own;
struct arg_type_info *elt_info = parse_type(str, &own);
struct arg_type_info *elt_info = parse_type(str, NULL, 0, &own);
if (elt_info == NULL)
return -1;
......@@ -614,12 +662,13 @@ parse_enum(char **str, struct arg_type_info *info)
}
static struct arg_type_info *
parse_nonpointer_type(char **str, int *ownp)
parse_nonpointer_type(char **str, struct param **extra_param, size_t param_num,
int *ownp)
{
enum arg_type type;
if (parse_arg_type(str, &type) < 0) {
struct arg_type_info *simple;
if (parse_alias(str, &simple, ownp) < 0)
if (parse_alias(str, &simple, ownp, extra_param, param_num) < 0)
return NULL;
if (simple == NULL)
simple = lookup_typedef(str);
......@@ -627,12 +676,14 @@ parse_nonpointer_type(char **str, int *ownp)
*ownp = 0;
return simple;
}
report_error(filename, line_no,
"unknown type around '%s'", *str);
return NULL;
}
int (*parser) (char **, struct arg_type_info *) = NULL;
/* For some types that's all we need. */
switch (type) {
case ARGTYPE_UNKNOWN:
......@@ -647,7 +698,6 @@ parse_nonpointer_type(char **str, int *ownp)
case ARGTYPE_USHORT:
case ARGTYPE_FLOAT:
case ARGTYPE_DOUBLE:
case ARGTYPE_FORMAT:
*ownp = 0;
return type_get_simple(type);
......@@ -673,6 +723,7 @@ parse_nonpointer_type(char **str, int *ownp)
/* Pointer syntax is not based on keyword, so we
* should never get this type. */
assert(type != ARGTYPE_POINTER);
abort();
}
struct arg_type_info *info = malloc(sizeof(*info));
......@@ -692,9 +743,10 @@ parse_nonpointer_type(char **str, int *ownp)
}
static struct arg_type_info *
parse_type(char **str, int *ownp)
parse_type(char **str, struct param **extra_param, size_t param_num, int *ownp)
{
struct arg_type_info *info = parse_nonpointer_type(str, ownp);
struct arg_type_info *info
= parse_nonpointer_type(str, extra_param, param_num, ownp);
if (info == NULL)
return NULL;
......@@ -763,7 +815,7 @@ process_line(char *buf) {
return NULL;
}
fun->return_info = parse_type(&str, &fun->own_return_info);
fun->return_info = parse_type(&str, NULL, 0, &fun->own_return_info);
if (fun->return_info == NULL
|| fun->return_info->type == ARGTYPE_UNKNOWN) {
err:
......@@ -814,7 +866,8 @@ process_line(char *buf) {
}
int own;
struct arg_type_info *type = parse_type(&str, &own);
struct arg_type_info *type = parse_type(&str, &extra_param,
fun->num_params, &own);
if (type == NULL) {
report_error(filename, line_no,
"unknown argument type");
......
......@@ -49,8 +49,6 @@ type_get_simple(enum arg_type type)
HANDLE(ARGTYPE_FLOAT)
HANDLE(ARGTYPE_DOUBLE)
HANDLE(ARGTYPE_FORMAT)
#undef HANDLE
case ARGTYPE_STRING_N:
......@@ -229,21 +227,21 @@ layout_struct(struct Process *proc, struct arg_type_info *info,
void
type_init_array(struct arg_type_info *info,
struct arg_type_info *element_info, int own_info,
struct expr_node *length, int own_length)
struct expr_node *length_expr, int own_length)
{
info->type = ARGTYPE_ARRAY;
info->u.array_info.elt_type = element_info;
info->u.array_info.own_info = own_info;
info->u.array_info.length = length;
info->u.array_info.length = length_expr;
info->u.array_info.own_length = own_length;
}
void
type_init_string(struct arg_type_info *info,
struct expr_node *length, int own_length)
struct expr_node *length_expr, int own_length)
{
info->type = ARGTYPE_STRING_N;
info->u.string_n_info.length = length;
info->u.string_n_info.length = length_expr;
info->u.string_n_info.own_length = own_length;
}
......@@ -325,9 +323,6 @@ type_destroy(struct arg_type_info *info)
case ARGTYPE_FLOAT:
case ARGTYPE_DOUBLE:
break;
case ARGTYPE_FORMAT:
break;
}
}
......@@ -436,7 +431,6 @@ type_sizeof(struct Process *proc, struct arg_type_info *type)
case ARGTYPE_UNKNOWN:
return sizeof(long);
case ARGTYPE_FORMAT:
case ARGTYPE_STRING_N:
return -1;
}
......
......@@ -39,7 +39,6 @@ enum arg_type {
ARGTYPE_USHORT,
ARGTYPE_FLOAT,
ARGTYPE_DOUBLE,
ARGTYPE_FORMAT, /* printf-like format */
ARGTYPE_STRING_N, /* String of known maxlen */
ARGTYPE_ARRAY, /* Series of values in memory */
ARGTYPE_ENUM, /* Enumeration */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment