[test] Add binder-call. JB#55084
This commit is contained in:
@@ -11,6 +11,8 @@ Source: %{name}-%{version}.tar.bz2
|
||||
|
||||
BuildRequires: pkgconfig(glib-2.0)
|
||||
BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version}
|
||||
BuildRequires: bison
|
||||
BuildRequires: flex
|
||||
Requires: libglibutil >= %{libglibutil_version}
|
||||
Requires(post): /sbin/ldconfig
|
||||
Requires(postun): /sbin/ldconfig
|
||||
@@ -34,6 +36,7 @@ make %{_smp_mflags} LIBDIR=%{_libdir} KEEP_SYMBOLS=1 release pkgconfig
|
||||
make -C test/binder-bridge KEEP_SYMBOLS=1 release
|
||||
make -C test/binder-list KEEP_SYMBOLS=1 release
|
||||
make -C test/binder-ping KEEP_SYMBOLS=1 release
|
||||
make -C test/binder-call KEEP_SYMBOLS=1 release
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
@@ -41,6 +44,7 @@ make LIBDIR=%{_libdir} DESTDIR=%{buildroot} install-dev
|
||||
make -C test/binder-bridge DESTDIR=%{buildroot} install
|
||||
make -C test/binder-list DESTDIR=%{buildroot} install
|
||||
make -C test/binder-ping DESTDIR=%{buildroot} install
|
||||
make -C test/binder-call DESTDIR=%{buildroot} install
|
||||
|
||||
%check
|
||||
make -C unit test
|
||||
@@ -73,3 +77,4 @@ Binder command line utilities
|
||||
%{_bindir}/binder-bridge
|
||||
%{_bindir}/binder-list
|
||||
%{_bindir}/binder-ping
|
||||
%{_bindir}/binder-call
|
||||
|
||||
@@ -8,4 +8,5 @@ all:
|
||||
@$(MAKE) -C binder-list $*
|
||||
@$(MAKE) -C binder-ping $*
|
||||
@$(MAKE) -C binder-service $*
|
||||
@$(MAKE) -C binder-call $*
|
||||
@$(MAKE) -C rild-card-status $*
|
||||
|
||||
178
test/binder-call/Makefile
Normal file
178
test/binder-call/Makefile
Normal file
@@ -0,0 +1,178 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
.PHONY: all debug release clean cleaner
|
||||
.PHONY: libgbinder-release libgbinder-debug
|
||||
|
||||
#
|
||||
# Required packages
|
||||
#
|
||||
|
||||
PKGS = glib-2.0 gio-2.0 gio-unix-2.0 libglibutil
|
||||
|
||||
#
|
||||
# Default target
|
||||
#
|
||||
|
||||
all: debug release
|
||||
|
||||
#
|
||||
# Executable
|
||||
#
|
||||
|
||||
EXE = binder-call
|
||||
|
||||
#
|
||||
# Sources
|
||||
#
|
||||
|
||||
SRC = $(EXE).c
|
||||
GEN_SRC = \
|
||||
cmdline.tab.c \
|
||||
lex.cmdline.c
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
SRC_DIR = .
|
||||
BUILD_DIR = build
|
||||
GEN_DIR = $(BUILD_DIR)
|
||||
LIB_DIR = ../..
|
||||
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
|
||||
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
|
||||
|
||||
#
|
||||
# Tools and flags
|
||||
#
|
||||
|
||||
CC ?= $(CROSS_COMPILE)gcc
|
||||
LD = $(CC)
|
||||
WARNINGS = -Wall
|
||||
INCLUDES = -I$(LIB_DIR)/include -I$(GEN_DIR) -I$(SRC_DIR)
|
||||
BASE_FLAGS = -fPIC
|
||||
CFLAGS = $(BASE_FLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) -MMD -MP \
|
||||
$(shell pkg-config --cflags $(PKGS))
|
||||
LDFLAGS = $(BASE_FLAGS) $(shell pkg-config --libs $(PKGS))
|
||||
QUIET_MAKE = make --no-print-directory
|
||||
DEBUG_FLAGS = -g
|
||||
RELEASE_FLAGS =
|
||||
|
||||
ifndef KEEP_SYMBOLS
|
||||
KEEP_SYMBOLS = 0
|
||||
endif
|
||||
|
||||
ifneq ($(KEEP_SYMBOLS),0)
|
||||
RELEASE_FLAGS += -g
|
||||
SUBMAKE_OPTS += KEEP_SYMBOLS=1
|
||||
endif
|
||||
|
||||
DEBUG_LDFLAGS = $(LDFLAGS) $(DEBUG_FLAGS)
|
||||
RELEASE_LDFLAGS = $(LDFLAGS) $(RELEASE_FLAGS)
|
||||
DEBUG_CFLAGS = $(CFLAGS) $(DEBUG_FLAGS) -DDEBUG
|
||||
RELEASE_CFLAGS = $(CFLAGS) $(RELEASE_FLAGS) -O2
|
||||
|
||||
#
|
||||
# Files
|
||||
#
|
||||
|
||||
DEBUG_OBJS = $(GEN_SRC:%.c=$(DEBUG_BUILD_DIR)/%.o) \
|
||||
$(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
|
||||
RELEASE_OBJS = $(GEN_SRC:%.c=$(RELEASE_BUILD_DIR)/%.o) \
|
||||
$(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
|
||||
DEBUG_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_so)
|
||||
RELEASE_SO_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_so)
|
||||
DEBUG_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_link)
|
||||
RELEASE_LINK_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_link)
|
||||
DEBUG_SO = $(LIB_DIR)/$(DEBUG_SO_FILE)
|
||||
RELEASE_SO = $(LIB_DIR)/$(RELEASE_SO_FILE)
|
||||
GEN_FILES = $(GEN_SRC:%=$(GEN_DIR)/%)
|
||||
.PRECIOUS: $(GEN_FILES)
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d)
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(strip $(DEPS)),)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(GEN_FILES): | $(GEN_DIR)
|
||||
$(DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
|
||||
$(RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
|
||||
|
||||
#
|
||||
# Rules
|
||||
#
|
||||
|
||||
DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE)
|
||||
RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE)
|
||||
|
||||
debug: libgbinder-debug $(DEBUG_EXE)
|
||||
|
||||
release: libgbinder-release $(RELEASE_EXE)
|
||||
|
||||
clean:
|
||||
rm -f *~
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
cleaner: clean
|
||||
@make -C $(LIB_DIR) clean
|
||||
|
||||
$(DEBUG_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(RELEASE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(GEN_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(GEN_DIR)/%.tab.c : $(SRC_DIR)/%.y
|
||||
bison -pcmdline -bcmdline -d -o $@ $<
|
||||
|
||||
$(GEN_DIR)/lex.%.c : $(SRC_DIR)/%.l
|
||||
flex -o $@ $<
|
||||
|
||||
$(DEBUG_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
|
||||
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(RELEASE_BUILD_DIR)/%.o : $(GEN_DIR)/%.c
|
||||
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(DEBUG_EXE): $(DEBUG_SO) $(DEBUG_BUILD_DIR) $(DEBUG_OBJS)
|
||||
$(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) $< -o $@
|
||||
|
||||
$(RELEASE_EXE): $(RELEASE_SO) $(RELEASE_BUILD_DIR) $(RELEASE_OBJS)
|
||||
$(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) $< -o $@
|
||||
ifeq ($(KEEP_SYMBOLS),0)
|
||||
strip $@
|
||||
endif
|
||||
|
||||
libgbinder-debug:
|
||||
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(DEBUG_SO_FILE) $(DEBUG_LINK_FILE)
|
||||
|
||||
libgbinder-release:
|
||||
@make $(SUBMAKE_OPTS) -C $(LIB_DIR) $(RELEASE_SO_FILE) $(RELEASE_LINK_FILE)
|
||||
|
||||
#
|
||||
# Install
|
||||
#
|
||||
|
||||
INSTALL = install
|
||||
|
||||
INSTALL_BIN_DIR = $(DESTDIR)/usr/bin
|
||||
|
||||
install: release $(INSTALL_BIN_DIR)
|
||||
$(INSTALL) -m 755 $(RELEASE_EXE) $(INSTALL_BIN_DIR)
|
||||
|
||||
$(INSTALL_BIN_DIR):
|
||||
$(INSTALL) -d $@
|
||||
768
test/binder-call/binder-call.c
Normal file
768
test/binder-call/binder-call.c
Normal file
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jolla Ltd.
|
||||
* Copyright (C) 2018 Slava Monich <slava.monich@jolla.com>
|
||||
* Copyright (C) 2021 Franz-Josef Haider <franz.haider@jolla.com>
|
||||
*
|
||||
* You may use this file under the terms of BSD license as follows:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <gbinder.h>
|
||||
#include <gutil_log.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <binder-call.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct transaction_and_reply* ast;
|
||||
|
||||
#define RET_OK (0)
|
||||
#define RET_NOTFOUND (1)
|
||||
#define RET_INVARG (2)
|
||||
#define RET_ERR (3)
|
||||
|
||||
#define DEFAULT_DEVICE GBINDER_DEFAULT_BINDER
|
||||
|
||||
#define GBINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4)
|
||||
#define GBINDER_INTERFACE_TRANSACTION GBINDER_TRANSACTION('N','T','F')
|
||||
|
||||
static const char pname[] = "binder-call";
|
||||
|
||||
enum transaction_pass {
|
||||
COMPUTE_SIZES = 0,
|
||||
FILL_BUFFERS,
|
||||
BUILD_TRANSACTION
|
||||
};
|
||||
|
||||
enum reply_pass {
|
||||
PRINT_REPLY = 0,
|
||||
COMPUTE_SIZES_REPLY
|
||||
};
|
||||
|
||||
static
|
||||
int
|
||||
go_through_transaction_ast(
|
||||
App* app,
|
||||
GList* node_list,
|
||||
int parent_idx,
|
||||
void* buf,
|
||||
enum transaction_pass cur_pass,
|
||||
int cont_offset)
|
||||
{
|
||||
GList* l;
|
||||
int offset = cont_offset;
|
||||
|
||||
for (l = node_list; l; l = l->next) {
|
||||
struct value_info* v = l->data;
|
||||
|
||||
switch(v->type) {
|
||||
case INT8_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int8");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
|
||||
} else if (cur_pass == FILL_BUFFERS) {
|
||||
*((unsigned char*)(((char*)buf)+offset)) = *((unsigned char*)v->value);
|
||||
}
|
||||
offset++;
|
||||
break;
|
||||
case INT32_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int32");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_int32(&app->writer, *((int*)v->value));
|
||||
} else if (cur_pass == FILL_BUFFERS) {
|
||||
*((int*)(((char*)buf)+offset)) = *((int*)v->value);
|
||||
}
|
||||
offset += sizeof(gint32);
|
||||
break;
|
||||
case INT64_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("int64");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_int64(&app->writer, *((gint64*)v->value));
|
||||
} else if (cur_pass == FILL_BUFFERS) {
|
||||
*((gint64*)(((char*)buf)+offset)) = *((gint64*)v->value);
|
||||
}
|
||||
offset += sizeof(gint64);
|
||||
break;
|
||||
case FLOAT_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("float");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_float(&app->writer, *((float*)v->value));
|
||||
} else if (cur_pass == FILL_BUFFERS) {
|
||||
*((float*)(((char*)buf)+offset)) = *((float*)v->value);
|
||||
}
|
||||
offset += sizeof(float);
|
||||
break;
|
||||
case DOUBLE_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("double");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_double(&app->writer, *((double*)v->value));
|
||||
} else if (cur_pass == FILL_BUFFERS) {
|
||||
*((double*)(((char*)buf)+offset)) = *((double*)v->value);
|
||||
}
|
||||
offset += sizeof(double);
|
||||
break;
|
||||
case STRING8_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string8");
|
||||
gbinder_writer_append_string8(&app->writer, v->value);
|
||||
// offset not incremented since it only makes sense for hidl
|
||||
break;
|
||||
case STRING16_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("string16");
|
||||
gbinder_writer_append_string16(&app->writer, v->value);
|
||||
// offset not incremented since it only makes sense for hidl
|
||||
break;
|
||||
case HSTRING_TYPE:
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("hstring");
|
||||
if (parent_idx == -1) {
|
||||
gbinder_writer_append_hidl_string(&app->writer, v->value);
|
||||
} else {
|
||||
GBinderHidlString* hidl_str = (GBinderHidlString*)(((char*)buf)+offset);
|
||||
if (cur_pass == FILL_BUFFERS) {
|
||||
hidl_str->data.str = v->value;
|
||||
hidl_str->len = strlen(v->value);
|
||||
hidl_str->owns_buffer = TRUE;
|
||||
}
|
||||
if (cur_pass == BUILD_TRANSACTION) {
|
||||
GBinderParent p;
|
||||
p.index = parent_idx;
|
||||
p.offset = offset;
|
||||
gbinder_writer_append_buffer_object_with_parent(&app->writer, hidl_str->data.str, hidl_str->len+1, &p);
|
||||
}
|
||||
}
|
||||
offset += sizeof(GBinderHidlString);
|
||||
break;
|
||||
case STRUCT_TYPE: {
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("struct");
|
||||
if (!app->opt->aidl) {
|
||||
if (parent_idx == -1) {
|
||||
int s = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
|
||||
void *new_buf = gbinder_writer_malloc(&app->writer, s);
|
||||
int new_parent_idx;
|
||||
|
||||
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
|
||||
new_parent_idx = gbinder_writer_append_buffer_object(&app->writer, new_buf, s);
|
||||
// if parent_idx == -1 there is no need to update the offset,
|
||||
// since we are processing the argument list and are not
|
||||
// inside an argument.
|
||||
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
|
||||
} else {
|
||||
if (cur_pass == FILL_BUFFERS) {
|
||||
// fill struct mode.
|
||||
offset += go_through_transaction_ast(app, v->value, 0, ((char*)buf)+offset, cur_pass, 0);
|
||||
} else if (cur_pass == BUILD_TRANSACTION) {
|
||||
int s = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
|
||||
go_through_transaction_ast(app, v->value, 0, buf, FILL_BUFFERS, offset);
|
||||
go_through_transaction_ast(app, v->value, /*new_*/parent_idx, buf, BUILD_TRANSACTION, offset);
|
||||
offset += s;
|
||||
} else if (cur_pass == COMPUTE_SIZES) {
|
||||
offset += go_through_transaction_ast(app, v->value, 0, NULL, cur_pass, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
go_through_transaction_ast(app, v->value, -1, NULL, cur_pass, 0);
|
||||
}
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("structend");
|
||||
break;
|
||||
}
|
||||
case VECTOR_TYPE: {
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vector");
|
||||
if (!app->opt->aidl) {
|
||||
if (parent_idx == -1) {
|
||||
GBinderHidlVec* vec;
|
||||
int vs = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
|
||||
int es = go_through_transaction_ast(app, g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
|
||||
void* new_buf = gbinder_writer_malloc(&app->writer, vs);
|
||||
int new_parent_idx;
|
||||
GBinderParent vec_parent;
|
||||
|
||||
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
|
||||
vec = gbinder_writer_new0(&app->writer, GBinderHidlVec);
|
||||
vec->data.ptr = new_buf;
|
||||
vec->count = vs / es;
|
||||
if (vec->count != g_list_length(v->value)) {
|
||||
GERR("SEMANTIC ERROR VECTOR");
|
||||
abort();
|
||||
}
|
||||
vec_parent.index = gbinder_writer_append_buffer_object(&app->writer, vec, sizeof(*vec));
|
||||
vec_parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
|
||||
new_parent_idx = gbinder_writer_append_buffer_object_with_parent(&app->writer, new_buf, vs, &vec_parent);
|
||||
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
|
||||
} else {
|
||||
if (cur_pass == FILL_BUFFERS) {
|
||||
// fill struct mode.
|
||||
int sl = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
|
||||
int es = go_through_transaction_ast(app, g_list_last(v->value), 0, NULL, COMPUTE_SIZES, 0);
|
||||
void* new_buf = gbinder_writer_malloc(&app->writer, sl);
|
||||
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
|
||||
|
||||
vec->data.ptr = new_buf;
|
||||
vec->count = sl / es;
|
||||
} else if (cur_pass == BUILD_TRANSACTION) {
|
||||
void* new_buf;
|
||||
int new_parent_idx;
|
||||
int sl = go_through_transaction_ast(app, v->value, 0, NULL, COMPUTE_SIZES, 0);
|
||||
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
|
||||
GBinderParent p;
|
||||
|
||||
new_buf = (void*)vec->data.ptr;
|
||||
go_through_transaction_ast(app, v->value, 0, new_buf, FILL_BUFFERS, 0);
|
||||
if (vec->count != g_list_length(v->value)) {
|
||||
GERR("SEMANTIC ERROR VECTOR");
|
||||
abort();
|
||||
}
|
||||
p.index = parent_idx;
|
||||
p.offset = offset;
|
||||
new_parent_idx = gbinder_writer_append_buffer_object_with_parent(&app->writer, new_buf, sl, &p);
|
||||
|
||||
go_through_transaction_ast(app, v->value, new_parent_idx, new_buf, BUILD_TRANSACTION, 0);
|
||||
}
|
||||
offset += sizeof(GBinderHidlVec);
|
||||
}
|
||||
} else {
|
||||
int vl = g_list_length(v->value);
|
||||
|
||||
gbinder_writer_append_int32(&app->writer, vl);
|
||||
go_through_transaction_ast(app, v->value, -1, NULL, cur_pass, 0);
|
||||
}
|
||||
if (cur_pass == BUILD_TRANSACTION) GDEBUG("vectorend");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GERR("unknown type: %d\n", v->type);
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int
|
||||
go_through_reply_ast(
|
||||
App* app,
|
||||
GList* node_list,
|
||||
struct type_info* tt,
|
||||
const void* buf,
|
||||
enum reply_pass cur_pass)
|
||||
{
|
||||
GList* l;
|
||||
int offset = 0;
|
||||
|
||||
for (l = node_list; l || tt; l = l->next) {
|
||||
struct type_info* t = node_list ? l->data : tt;
|
||||
|
||||
switch(t->type) {
|
||||
case INT8_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("int8");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
int val;
|
||||
|
||||
if (!buf) {
|
||||
gbinder_reader_read_int32(&app->reader, &val);
|
||||
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
val = *((unsigned char*)(((char*)buf)+offset));
|
||||
}
|
||||
printf("%d:8 ", val);
|
||||
}
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
case INT32_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("int32");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
gint32 val;
|
||||
|
||||
if (!buf) {
|
||||
gbinder_reader_read_int32(&app->reader, &val);
|
||||
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
val = *((gint32*)(((char*)buf)+offset));
|
||||
}
|
||||
printf("%d ", val);
|
||||
}
|
||||
offset += sizeof(gint32);
|
||||
break;
|
||||
}
|
||||
case INT64_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("int64");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
gint64 val;
|
||||
|
||||
if (!buf) {
|
||||
gbinder_reader_read_int64(&app->reader, &val);
|
||||
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
val = *((gint64*)(((char*)buf)+offset));
|
||||
}
|
||||
printf("%ldL ", val);
|
||||
}
|
||||
offset += sizeof(gint64);
|
||||
break;
|
||||
}
|
||||
case FLOAT_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("float");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
float val;
|
||||
|
||||
if (!buf) {
|
||||
gbinder_reader_read_float(&app->reader, &val);
|
||||
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
val = *((float*)(((char*)buf)+offset));
|
||||
}
|
||||
printf("%f ", val);
|
||||
}
|
||||
offset += sizeof(float);
|
||||
break;
|
||||
}
|
||||
case DOUBLE_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("double");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
double val;
|
||||
|
||||
if (!buf) {
|
||||
gbinder_reader_read_double(&app->reader, &val);
|
||||
} else if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
val = *((double*)(((char*)buf)+offset));
|
||||
}
|
||||
printf("%lfL ", val);
|
||||
}
|
||||
offset += sizeof(double);
|
||||
break;
|
||||
}
|
||||
case STRING8_TYPE: {
|
||||
const char* val = gbinder_reader_read_string8(&app->reader);
|
||||
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("string8");
|
||||
printf("\"%s\" ", val);
|
||||
// offset not incremented since it only makes sense for hidl
|
||||
break;
|
||||
}
|
||||
case STRING16_TYPE: {
|
||||
char* val = gbinder_reader_read_string16(&app->reader);
|
||||
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("string16");
|
||||
printf("\"%s\"U ", val);
|
||||
g_free(val);
|
||||
// offset not incremented since it only makes sense for hidl
|
||||
break;
|
||||
}
|
||||
case HSTRING_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("hstring");
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
char* val = NULL;
|
||||
|
||||
if (!buf) {
|
||||
val = gbinder_reader_read_hidl_string(&app->reader);
|
||||
} else {
|
||||
GBinderHidlString* hidl_str = (GBinderHidlString*)(((char*)buf)+offset);
|
||||
val = strdup(hidl_str->data.str);
|
||||
}
|
||||
printf("\"%s\"H ", val);
|
||||
g_free(val);
|
||||
}
|
||||
offset += sizeof(GBinderHidlString);
|
||||
break;
|
||||
}
|
||||
case STRUCT_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("struct");
|
||||
if (!app->opt->aidl) {
|
||||
if (cur_pass == COMPUTE_SIZES_REPLY) {
|
||||
offset += go_through_reply_ast(app, t->data, NULL, NULL, COMPUTE_SIZES_REPLY);
|
||||
} else {
|
||||
printf("{ ");
|
||||
if (!buf) {
|
||||
int sl = go_through_reply_ast(app, t->data, NULL, NULL, COMPUTE_SIZES_REPLY);
|
||||
const void* new_buf = gbinder_reader_read_hidl_struct1(&app->reader, sl);
|
||||
|
||||
offset += go_through_reply_ast(app, t->data, NULL, new_buf, PRINT_REPLY);
|
||||
} else {
|
||||
const void* new_buf = ((char*)buf)+offset;
|
||||
|
||||
offset += go_through_reply_ast(app, t->data, NULL, new_buf, PRINT_REPLY);
|
||||
}
|
||||
printf("} ");
|
||||
}
|
||||
} else {
|
||||
go_through_reply_ast(app, t->data, NULL, NULL, cur_pass);
|
||||
}
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("structend");
|
||||
break;
|
||||
}
|
||||
case VECTOR_TYPE: {
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("vector");
|
||||
if (!app->opt->aidl) {
|
||||
if (cur_pass != COMPUTE_SIZES_REPLY) {
|
||||
if (!buf) {
|
||||
guint i;
|
||||
gsize count, elemsize;
|
||||
const void* new_buf = gbinder_reader_read_hidl_vec(&app->reader, &count, &elemsize);
|
||||
|
||||
printf("[ ");
|
||||
for (i = 0; i < count; i++) {
|
||||
// TODO: validate elemsize somehow?
|
||||
go_through_reply_ast(app, NULL, t->data, new_buf + elemsize*i, cur_pass);
|
||||
}
|
||||
printf("] ");
|
||||
} else {
|
||||
guint i;
|
||||
gsize count;
|
||||
GBinderHidlVec* vec = (GBinderHidlVec*)(((char*)buf)+offset);
|
||||
int off;
|
||||
|
||||
count = vec->count;
|
||||
printf("[ ");
|
||||
off = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
off += go_through_reply_ast(app, NULL, t->data, vec->data.ptr + off, cur_pass);
|
||||
}
|
||||
printf("] ");
|
||||
}
|
||||
}
|
||||
offset += sizeof(GBinderHidlVec);
|
||||
} else {
|
||||
guint i;
|
||||
gint32 vl;
|
||||
|
||||
gbinder_reader_read_int32(&app->reader, &vl);
|
||||
printf("[ ");
|
||||
for (i = 0; i < vl; i++) {
|
||||
go_through_reply_ast(app, NULL, t->data, NULL, cur_pass);
|
||||
}
|
||||
printf("] ");
|
||||
}
|
||||
if (cur_pass == PRINT_REPLY) GDEBUG("vectorend");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GERR("unknown type: %d\n", t->type);
|
||||
break;
|
||||
};
|
||||
|
||||
if (tt) break;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
go_through_ast(
|
||||
App* app,
|
||||
struct transaction_and_reply* ast,
|
||||
gboolean transaction)
|
||||
{
|
||||
if (ast) {
|
||||
if (transaction && ast->tree_transaction) {
|
||||
go_through_transaction_ast(app, ast->tree_transaction, -1, NULL, BUILD_TRANSACTION, 0);
|
||||
} else if (!transaction && ast->tree_reply) {
|
||||
GDEBUG("REPLY:");
|
||||
go_through_reply_ast(app, ast->tree_reply, NULL, NULL, PRINT_REPLY);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
free_ast_transaction_tree(
|
||||
gpointer data)
|
||||
{
|
||||
struct value_info* v = data;
|
||||
|
||||
if (v->type == STRUCT_TYPE || v->type == VECTOR_TYPE) {
|
||||
g_list_free_full(v->value, free_ast_transaction_tree);
|
||||
} else {
|
||||
g_free(v->value);
|
||||
}
|
||||
|
||||
g_free(v);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
free_ast_reply_tree(
|
||||
gpointer data)
|
||||
{
|
||||
struct type_info* t = data;
|
||||
|
||||
if (t->type == VECTOR_TYPE) {
|
||||
free_ast_reply_tree(t->data);
|
||||
} else if (t->type == STRUCT_TYPE) {
|
||||
g_list_free_full(t->data, free_ast_reply_tree);
|
||||
}
|
||||
|
||||
g_free(t);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
free_ast(
|
||||
struct transaction_and_reply* ast)
|
||||
{
|
||||
if (ast) {
|
||||
g_list_free_full(ast->tree_transaction, free_ast_transaction_tree);
|
||||
g_list_free_full(ast->tree_reply, free_ast_reply_tree);
|
||||
g_free(ast);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
app_run(
|
||||
App* app)
|
||||
{
|
||||
const AppOptions* opt = app->opt;
|
||||
char* iface = opt->iface ? g_strdup(opt->iface) : NULL;
|
||||
int status = 0;
|
||||
int rargc = 1;
|
||||
char* service = opt->argv[rargc++];
|
||||
int code = atoi(opt->argv[rargc++]);
|
||||
GBinderClient* client;
|
||||
GBinderLocalRequest* req;
|
||||
GBinderRemoteReply* reply;
|
||||
GBinderRemoteObject* obj;
|
||||
|
||||
if (!code) {
|
||||
GERR("Transaction code must be > GBINDER_FIRST_CALL_TRANSACTION(=1).");
|
||||
return;
|
||||
}
|
||||
|
||||
obj = gbinder_servicemanager_get_service_sync(app->sm,
|
||||
service, &status);
|
||||
if (!obj) {
|
||||
GERR("No such service: %s", service);
|
||||
g_free(iface);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strstr(service, "/") != NULL) {
|
||||
iface = g_strndup(service, strchr(service, '/') - service);
|
||||
} else {
|
||||
GBinderReader reader;
|
||||
|
||||
client = gbinder_client_new(obj, NULL);
|
||||
req = gbinder_client_new_request(client);
|
||||
reply = gbinder_client_transact_sync_reply(client, GBINDER_INTERFACE_TRANSACTION, req, &status);
|
||||
gbinder_remote_reply_init_reader(reply, &reader);
|
||||
iface = gbinder_reader_read_string16(&reader);
|
||||
gbinder_local_request_unref(req);
|
||||
gbinder_remote_reply_unref(reply);
|
||||
gbinder_client_unref(client);
|
||||
}
|
||||
|
||||
if (!iface) {
|
||||
GERR("Failed to get interface");
|
||||
return;
|
||||
}
|
||||
|
||||
GDEBUG("Got iface: %s", iface);
|
||||
|
||||
client = gbinder_client_new(obj, iface);
|
||||
g_free(iface);
|
||||
|
||||
req = gbinder_client_new_request(client);
|
||||
|
||||
app->rargc = rargc;
|
||||
app->code = code;
|
||||
|
||||
cmdline_parse(app);
|
||||
|
||||
gbinder_local_request_init_writer(req, &app->writer);
|
||||
go_through_ast(app, ast, TRUE);
|
||||
|
||||
if (opt->oneway) {
|
||||
gbinder_client_transact_sync_oneway(client, code, req);
|
||||
reply = NULL;
|
||||
} else {
|
||||
reply = gbinder_client_transact_sync_reply(client, code, req, &status);
|
||||
}
|
||||
|
||||
gbinder_local_request_unref(req);
|
||||
|
||||
if (!reply) {
|
||||
printf("NO REPLY\n");
|
||||
} else {
|
||||
if (ast && !ast->tree_reply) {
|
||||
guchar b;
|
||||
gbinder_remote_reply_init_reader(reply, &app->reader);
|
||||
|
||||
printf("TRANSACTION BUFFER: 0x");
|
||||
while (gbinder_reader_read_byte(&app->reader, &b)) {
|
||||
printf("%02X", b);
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
gbinder_remote_reply_init_reader(reply, &app->reader);
|
||||
go_through_ast(app, ast, FALSE);
|
||||
}
|
||||
gbinder_remote_reply_unref(reply);
|
||||
}
|
||||
gbinder_client_unref(client);
|
||||
free_ast(ast);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
app_log_verbose(
|
||||
const gchar* name,
|
||||
const gchar* value,
|
||||
gpointer data,
|
||||
GError** error)
|
||||
{
|
||||
gutil_log_default.level = (gutil_log_default.level < GLOG_LEVEL_DEBUG) ?
|
||||
GLOG_LEVEL_DEBUG : GLOG_LEVEL_VERBOSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
app_log_quiet(
|
||||
const gchar* name,
|
||||
const gchar* value,
|
||||
gpointer data,
|
||||
GError** error)
|
||||
{
|
||||
gutil_log_default.level = GLOG_LEVEL_ERR;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
app_init(
|
||||
AppOptions* opt,
|
||||
int argc,
|
||||
char* argv[])
|
||||
{
|
||||
gboolean ok = FALSE;
|
||||
GOptionEntry entries[] = {
|
||||
{ "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
|
||||
app_log_verbose, "Enable verbose output", NULL },
|
||||
{ "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
|
||||
app_log_quiet, "Be quiet", NULL },
|
||||
{ "device", 'd', 0, G_OPTION_ARG_STRING, &opt->dev,
|
||||
"Binder device [" DEFAULT_DEVICE "]", "DEVICE" },
|
||||
{ "oneway", 'o', 0, G_OPTION_ARG_NONE, &opt->oneway,
|
||||
"Use a oneway transaction", NULL },
|
||||
{ "aidl", 'a', 0, G_OPTION_ARG_NONE, &opt->aidl,
|
||||
"Treat types as aidl types (default: hidl)", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
GError* error = NULL;
|
||||
GOptionContext* options = g_option_context_new("NAME CODE [[VALUE1] [VALUE2] ...] [reply [TYPE1] [TYPE2] ...]");
|
||||
g_option_context_set_description(options, "This tool can be used to call binder transactions from the commandline.\n"
|
||||
"After the regular options the NAME specifies the service name to call.\n"
|
||||
"For example \"android.hardware.sensors@1.0::ISensors/default\".\n"
|
||||
"CODE is used to specify the transaction id (>=1).\n"
|
||||
"After the code arguments to the transaction can be specified.\n"
|
||||
"Possible arguments are:\n"
|
||||
"\t[0-9]*:8 for an 8-bit integer\n"
|
||||
"\t[0-9]* for a 32-bit integer\n"
|
||||
"\t[0-9]*L for an 64-bit integer\n"
|
||||
"\t[0-9]*.[0-9]* for a 32-bit float\n"
|
||||
"\t[0-9]*.[0-9]*L for a 64-bit double\n"
|
||||
"\t\"[.*]\" for an 8-bit aidl string\n"
|
||||
"\t\"[.*]\"L for an utf16 aidl string\n"
|
||||
"\t\"[.*]\"h for an 8-bit hidl string\n"
|
||||
"\t{ VALUE1 VALUE2 ... VALUEN } for a struct containing VALUE1, VALUE2, etc., where\n"
|
||||
"\t all of these values can be any of the possible values described here.\n"
|
||||
"\t[ VALUE1 VALUE2 ... VALUEN ] for a vector of length N containing VALUE1, VALUE2, etc., where\n"
|
||||
"\t all of these values can be one of the possible VALUES described here.\n"
|
||||
"\t They must be of the same type.\n"
|
||||
"Afterwards the structure of the reply can be specified.\n"
|
||||
"After writing \"reply\" on the command line the following types are accepted:\n"
|
||||
"\ti8 for an 8-bit integer\n"
|
||||
"\ti32 for a 32-bit integer\n"
|
||||
"\ti64 for a 64-bit integer\n"
|
||||
"\ts8 for an 8-bit aidl string\n"
|
||||
"\ts16 for an utf16 aidl string\n"
|
||||
"\thstr for an 8-bit hidl string\n"
|
||||
"\tf|float for a 32-bit float\n"
|
||||
"\td|double for a 64-bit double\n"
|
||||
"\t[ TYPE ] for a vector<TYPE> where TYPE can be any of the possible types decribed here\n"
|
||||
"\t{ TYPE1 TYPE2 ... TYPEN } for a struct containing TYPE1, TYPE2, etc. where\n"
|
||||
"\t all of the types can be any of the possible types decribed here.\n"
|
||||
"An example to call the getSensorsList method on the \"android.hardware.sensors@1.0::ISensors/default\"\n"
|
||||
"service, looks like this:\n"
|
||||
"\tbinder-call -d /dev/hwbinder android.hardware.sensors@1.0::ISensors/default 1 reply i32 \"[ { i32 i32 hstr hstr i32 i32 hstr f f f i32 i32 i32 hstr i32 i32 } ]\"\n");
|
||||
|
||||
g_option_context_add_main_entries(options, entries, NULL);
|
||||
|
||||
memset(opt, 0, sizeof(*opt));
|
||||
|
||||
gutil_log_timestamp = FALSE;
|
||||
gutil_log_set_type(GLOG_TYPE_STDERR, pname);
|
||||
gutil_log_default.level = GLOG_LEVEL_DEFAULT;
|
||||
|
||||
if (g_option_context_parse(options, &argc, &argv, &error)) {
|
||||
char* help;
|
||||
|
||||
if (argc > 2) {
|
||||
opt->argc = argc;
|
||||
opt->argv = argv;
|
||||
ok = TRUE;
|
||||
} else {
|
||||
help = g_option_context_get_help(options, TRUE, NULL);
|
||||
fprintf(stderr, "%s", help);
|
||||
g_free(help);
|
||||
}
|
||||
} else {
|
||||
GERR("%s", error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
g_option_context_free(options);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
App app;
|
||||
AppOptions opt;
|
||||
|
||||
memset(&app, 0, sizeof(app));
|
||||
app.ret = RET_INVARG;
|
||||
app.opt = &opt;
|
||||
if (app_init(&opt, argc, argv)) {
|
||||
app.sm = gbinder_servicemanager_new(opt.dev);
|
||||
if (app.sm) {
|
||||
app_run(&app);
|
||||
gbinder_servicemanager_unref(app.sm);
|
||||
} else {
|
||||
GERR("servicemanager seems to be missing");
|
||||
}
|
||||
}
|
||||
g_free(opt.iface);
|
||||
g_free(opt.dev);
|
||||
return app.ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
63
test/binder-call/binder-call.h
Normal file
63
test/binder-call/binder-call.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef BINDER_CALL_H__
|
||||
#define BINDER_CALL_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <gbinder.h>
|
||||
|
||||
typedef struct app_options {
|
||||
char* dev;
|
||||
char *iface;
|
||||
gboolean oneway;
|
||||
gboolean aidl;
|
||||
gint transaction;
|
||||
int argc;
|
||||
char** argv;
|
||||
} AppOptions;
|
||||
|
||||
typedef struct app {
|
||||
const AppOptions* opt;
|
||||
GMainLoop* loop;
|
||||
GBinderServiceManager* sm;
|
||||
GBinderWriter writer;
|
||||
GBinderReader reader;
|
||||
int code;
|
||||
int rargc;
|
||||
int ret;
|
||||
} App;
|
||||
|
||||
int cmdlinelex(void *args);
|
||||
|
||||
enum TYPE_INFO {
|
||||
INT8_TYPE = 0,
|
||||
INT32_TYPE,
|
||||
INT64_TYPE,
|
||||
FLOAT_TYPE,
|
||||
DOUBLE_TYPE,
|
||||
STRING8_TYPE,
|
||||
STRING16_TYPE,
|
||||
HSTRING_TYPE,
|
||||
STRUCT_TYPE,
|
||||
VECTOR_TYPE
|
||||
};
|
||||
|
||||
struct type_info {
|
||||
enum TYPE_INFO type;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct value_info {
|
||||
enum TYPE_INFO type;
|
||||
void *value;
|
||||
};
|
||||
|
||||
struct transaction_and_reply {
|
||||
GList *tree_transaction;
|
||||
GList *tree_reply;
|
||||
};
|
||||
|
||||
int cmdline_parse(App *app);
|
||||
|
||||
extern struct transaction_and_reply *ast;
|
||||
|
||||
#endif
|
||||
|
||||
99
test/binder-call/cmdline.l
Normal file
99
test/binder-call/cmdline.l
Normal file
@@ -0,0 +1,99 @@
|
||||
D [0-9]
|
||||
l "l"
|
||||
L "L"
|
||||
u "u"
|
||||
U "U"
|
||||
h "h"
|
||||
H "H"
|
||||
COLON ":"
|
||||
EIGHT "8"
|
||||
|
||||
INT8_SUFFIX {COLON}{EIGHT}
|
||||
INT64_SUFFIX [lL]
|
||||
UTF16_SUFFIX [uU]
|
||||
HSTRING_SUFFIX [hH]
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include "cmdline.tab.h"
|
||||
|
||||
#include "binder-call.h"
|
||||
#define YY_NO_INPUT
|
||||
#define YY_SKIP_YYWRAP
|
||||
#define YY_NO_UNPUT
|
||||
int cmdlinewrap(App* app);
|
||||
#undef yywrap
|
||||
#define yywrap() cmdlinewrap( (App*) args )
|
||||
#define YY_DECL int cmdlinelex( void* args )
|
||||
|
||||
char* handle_str(char* text) {
|
||||
// extract str from "str"X
|
||||
char* str = g_strndup(text + 1, strlen(text) - 3);
|
||||
return str;
|
||||
}
|
||||
|
||||
char* handle_str8(char* text) {
|
||||
// extract str from "str"
|
||||
char* str = g_strndup(text + 1, strlen(text) - 2);
|
||||
return str;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
"i8" { return(INT8); }
|
||||
"i32" { return(INT32); }
|
||||
"i64" { return(INT64); }
|
||||
"s8" { return(STRING8); }
|
||||
"s16" { return(STRING16); }
|
||||
"float" { return(FLOAT); }
|
||||
"double" { return(DOUBLE); }
|
||||
"f" { return(FLOAT); }
|
||||
"d" { return(DOUBLE); }
|
||||
"hstr" { return(HSTRING); }
|
||||
"{" { return('{'); }
|
||||
"}" { return('}'); }
|
||||
"[" { return('['); }
|
||||
"]" { return(']'); }
|
||||
{D}*{INT8_SUFFIX} { cmdlinelval.int8_value = atoi(yytext); return(INT8_VALUE); }
|
||||
{D}*{INT64_SUFFIX} { cmdlinelval.int64_value = atol(yytext); return(INT64_VALUE); }
|
||||
{D}* { cmdlinelval.int32_value = atoi(yytext); return(INT32_VALUE); }
|
||||
{D}+"."{D}*{INT64_SUFFIX} { cmdlinelval.double_value = atof(yytext); return(DOUBLE_VALUE); }
|
||||
{D}+"."{D}* { cmdlinelval.float_value = atof(yytext); return(FLOAT_VALUE); }
|
||||
"reply" { return(REPLY); }
|
||||
\".*\"{HSTRING_SUFFIX} { cmdlinelval.hstring_value = handle_str(yytext); return(HSTRING_VALUE); }
|
||||
\".*\"{UTF16_SUFFIX} { cmdlinelval.string16_value = handle_str(yytext); return(STRING16_VALUE); }
|
||||
\".*\" { cmdlinelval.string8_value = handle_str8(yytext); return(STRING8_VALUE); }
|
||||
" " { /* eat */ }
|
||||
. { fprintf(stderr, "Unrecognized character: '%c'\n", yytext[0]); }
|
||||
|
||||
%%
|
||||
|
||||
#include "binder-call.h"
|
||||
|
||||
int cmdlinewrap(App* app)
|
||||
{
|
||||
if (YY_CURRENT_BUFFER) {
|
||||
yy_delete_buffer( YY_CURRENT_BUFFER );
|
||||
}
|
||||
|
||||
if (app->rargc == app->opt->argc) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
yy_scan_string(app->opt->argv[app->rargc++]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmdline_parse(App* app) {
|
||||
if (app->opt->argc > app->rargc) {
|
||||
cmdlinewrap(app);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return cmdlineparse (app);
|
||||
}
|
||||
|
||||
402
test/binder-call/cmdline.y
Normal file
402
test/binder-call/cmdline.y
Normal file
@@ -0,0 +1,402 @@
|
||||
%{
|
||||
#include <glib.h>
|
||||
#include "binder-call.h"
|
||||
|
||||
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply);
|
||||
struct value_info* handle_int8(App* app, int value);
|
||||
struct value_info* handle_int32(App* app, int value);
|
||||
struct value_info* handle_int64(App* app, long value);
|
||||
struct value_info* handle_float(App* app, float value);
|
||||
struct value_info* handle_double(App* app, double value);
|
||||
struct value_info* handle_string8(App* app, char* value);
|
||||
struct value_info* handle_string16(App* app, char* value);
|
||||
struct value_info* handle_hstring(App* app, char* value);
|
||||
struct value_info* handle_vector(App* app, GList* values);
|
||||
struct value_info* handle_struct(App* app, GList* values);
|
||||
|
||||
void cmdlineerror(App* app, char const* s);
|
||||
|
||||
struct type_info* handle_type_int8(App* app);
|
||||
struct type_info* handle_type_int32(App* app);
|
||||
struct type_info* handle_type_int64(App* app);
|
||||
struct type_info* handle_type_float(App* app);
|
||||
struct type_info* handle_type_double(App* app);
|
||||
struct type_info* handle_type_string8(App* app);
|
||||
struct type_info* handle_type_string16(App* app);
|
||||
struct type_info* handle_type_hstring(App* app);
|
||||
|
||||
|
||||
struct type_info* handle_type_vector(App* app, struct type_info* t);
|
||||
struct type_info* handle_type_struct(App* app, GList* l);
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
union {
|
||||
int int8_value;
|
||||
int int32_value;
|
||||
long int64_value;
|
||||
float float_value;
|
||||
double double_value;
|
||||
char* string8_value;
|
||||
char* string16_value;
|
||||
char* hstring_value;
|
||||
};
|
||||
struct value_info* value;
|
||||
struct type_info* type;
|
||||
GList* value_list;
|
||||
GList* type_list;
|
||||
GList* struct_type_list;
|
||||
struct transaction_and_reply* trans_and_reply;
|
||||
}
|
||||
|
||||
%parse-param { void* args }
|
||||
%lex-param { void* args }
|
||||
|
||||
%token INT8 INT32 INT64 FLOAT DOUBLE STRING8 STRING16 HSTRING
|
||||
|
||||
%token INT8_VALUE INT32_VALUE INT64_VALUE FLOAT_VALUE DOUBLE_VALUE STRING8_VALUE STRING16_VALUE HSTRING_VALUE
|
||||
%type <value> values
|
||||
|
||||
%type <value> struct_values
|
||||
%type <value> vec_values
|
||||
%type <value> value_specifiers
|
||||
%type <trans_and_reply> translation_unit
|
||||
|
||||
|
||||
%type <value_list> values_list
|
||||
%type <type_list> specifiers_list
|
||||
|
||||
%type <type> specifiers
|
||||
%type <type> type_specifier
|
||||
%type <type> vec_specifier
|
||||
%type <type> struct_specifier
|
||||
%type <struct_type_list> struct_declaration_list
|
||||
|
||||
//%type <value> values
|
||||
|
||||
%token REPLY
|
||||
|
||||
%start translation_unit
|
||||
%%
|
||||
|
||||
type_specifier
|
||||
: INT8 { $$ = handle_type_int8(args); }
|
||||
| INT32 { $$ = handle_type_int32(args); }
|
||||
| INT64 { $$ = handle_type_int64(args); }
|
||||
| STRING8 { $$ = handle_type_string8(args); }
|
||||
| STRING16 { $$ = handle_type_string16(args); }
|
||||
| FLOAT { $$ = handle_type_float(args); }
|
||||
| DOUBLE { $$ = handle_type_double(args); }
|
||||
| HSTRING { $$ = handle_type_hstring(args); }
|
||||
;
|
||||
|
||||
values
|
||||
: INT8_VALUE { $$ = handle_int8(args, cmdlinelval.int8_value); }
|
||||
| INT32_VALUE { $$ = handle_int32(args, cmdlinelval.int32_value); }
|
||||
| INT64_VALUE { $$ = handle_int64(args, cmdlinelval.int64_value); }
|
||||
| STRING8_VALUE { $$ = handle_string8(args, cmdlinelval.string8_value); }
|
||||
| STRING16_VALUE { $$ = handle_string16(args, cmdlinelval.string16_value); }
|
||||
| HSTRING_VALUE { $$ = handle_hstring(args, cmdlinelval.hstring_value); }
|
||||
| FLOAT_VALUE { $$ = handle_float(args, cmdlinelval.float_value); }
|
||||
| DOUBLE_VALUE { $$ = handle_double(args, cmdlinelval.double_value); }
|
||||
;
|
||||
|
||||
struct
|
||||
: '{'
|
||||
;
|
||||
|
||||
struct_end
|
||||
: '}'
|
||||
;
|
||||
|
||||
vec
|
||||
: '['
|
||||
;
|
||||
|
||||
vec_end
|
||||
: ']'
|
||||
;
|
||||
|
||||
struct_specifier
|
||||
: struct struct_declaration_list struct_end { $$ = handle_type_struct(args, $2); }
|
||||
;
|
||||
|
||||
vec_specifier
|
||||
: vec specifiers vec_end { $$ = handle_type_vector(args, $2); }
|
||||
;
|
||||
|
||||
struct_declaration_list
|
||||
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
|
||||
| struct_declaration_list specifiers { $$ = g_list_append($$, $2); }
|
||||
;
|
||||
|
||||
specifiers
|
||||
: type_specifier
|
||||
| struct_specifier
|
||||
| vec_specifier
|
||||
;
|
||||
|
||||
specifiers_list
|
||||
: specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
|
||||
| specifiers_list specifiers { $$ = g_list_append($$, $2); }
|
||||
;
|
||||
|
||||
struct_values
|
||||
: struct values_list struct_end { $$ = handle_struct(args, $2); }
|
||||
;
|
||||
|
||||
vec_values
|
||||
: vec values_list vec_end { $$ = handle_vector(args, $2); }
|
||||
;
|
||||
|
||||
value_specifiers
|
||||
: values
|
||||
| struct_values
|
||||
| vec_values
|
||||
;
|
||||
|
||||
values_list
|
||||
: value_specifiers { $$ = NULL; $$ = g_list_append($$, $1); }
|
||||
| values_list value_specifiers { $$ = g_list_append($$, $2); }
|
||||
;
|
||||
|
||||
reply
|
||||
: REPLY
|
||||
;
|
||||
|
||||
translation_unit
|
||||
: values_list reply specifiers_list { $$ = make_transaction_and_reply($1, $3); ast = $$; }
|
||||
| values_list { $$ = make_transaction_and_reply($1, 0); ast = $$; }
|
||||
| reply specifiers_list { $$ = make_transaction_and_reply(0, $2); ast = $$; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <gutil_log.h>
|
||||
#include <binder-call.h>
|
||||
|
||||
extern char yytext[];
|
||||
|
||||
struct value_info* handle_int8(App* app, int value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = INT8_TYPE;
|
||||
v->value = g_new0(unsigned char, 1);
|
||||
* ((unsigned char*)v->value) = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_int32(App* app, int value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = INT32_TYPE;
|
||||
v->value = g_new0(int, 1);
|
||||
* ((int*)v->value) = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_int64(App* app, long value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = INT64_TYPE;
|
||||
v->value = g_new0(long, 1);
|
||||
* ((long*)v->value) = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_float(App* app, float value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = FLOAT_TYPE;
|
||||
v->value = g_new0(float, 1);
|
||||
* ((float*)v->value) = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_double(App* app, double value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = DOUBLE_TYPE;
|
||||
v->value = g_new0(double, 1);
|
||||
* ((double*)v->value) = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_string8(App* app, char* value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = STRING8_TYPE;
|
||||
v->value = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_string16(App* app, char* value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = STRING16_TYPE;
|
||||
v->value = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_hstring(App* app, char* value)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = HSTRING_TYPE;
|
||||
v->value = value;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_vector(App* app, GList* values)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = VECTOR_TYPE;
|
||||
v->value = values;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct value_info* handle_struct(App* app, GList* values)
|
||||
{
|
||||
struct value_info* v = g_new0(struct value_info, 1);
|
||||
|
||||
v->type = STRUCT_TYPE;
|
||||
v->value = values;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_int8(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = INT8_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_int32(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = INT32_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_int64(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = INT64_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_float(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = FLOAT_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_double(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = DOUBLE_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_string8(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = STRING8_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_string16(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = STRING16_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_hstring(App* app)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = HSTRING_TYPE;
|
||||
info->data = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_vector(App* app, struct type_info* t)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = VECTOR_TYPE;
|
||||
info->data = t;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct type_info* handle_type_struct(App* app, GList* l)
|
||||
{
|
||||
struct type_info* info = g_new0(struct type_info, 1);
|
||||
|
||||
info->type = STRUCT_TYPE;
|
||||
info->data = l;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
struct transaction_and_reply* make_transaction_and_reply(GList* transaction, GList* reply)
|
||||
{
|
||||
struct transaction_and_reply* tar = g_new0(struct transaction_and_reply, 1);
|
||||
|
||||
tar->tree_transaction = transaction;
|
||||
tar->tree_reply = reply;
|
||||
|
||||
return tar;
|
||||
}
|
||||
|
||||
void cmdlineerror(App* app, char const* s)
|
||||
{
|
||||
fprintf(stderr, "@%d %s: %s\n", app->rargc - 1, s, app->opt->argv[app->rargc - 1]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user