mirror of https://git.code.sf.net/p/isync/isync
Browse Source
do that by wrapping the actual stores into proxies. the proxy driver's code is auto-generated from function templates, some parameters, and the declarations of the driver functions themselves. attempts to do it with CPP macros turned out to be a nightmare.wip/movedetect
Oswald Buddenhagen
8 years ago
11 changed files with 553 additions and 33 deletions
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* mbsync - mailbox synchronizer |
||||
* Copyright (C) 2017 Oswald Buddenhagen <ossi@users.sf.net> |
||||
* |
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* As a special exception, mbsync may be linked with the OpenSSL library, |
||||
* despite that library's more restrictive license. |
||||
*/ |
||||
|
||||
#include "driver.h" |
||||
|
||||
#include <limits.h> |
||||
#include <stdlib.h> |
||||
|
||||
typedef struct { |
||||
store_t gen; |
||||
const char *label; // foreign
|
||||
int ref_count; |
||||
driver_t *real_driver; |
||||
store_t *real_store; |
||||
|
||||
void (*bad_callback)( void *aux ); |
||||
void *bad_callback_aux; |
||||
} proxy_store_t; |
||||
|
||||
static void ATTR_PRINTFLIKE(1, 2) |
||||
debug( const char *msg, ... ) |
||||
{ |
||||
va_list va; |
||||
|
||||
va_start( va, msg ); |
||||
vdebug( DEBUG_DRV, msg, va ); |
||||
va_end( va ); |
||||
} |
||||
|
||||
static void ATTR_PRINTFLIKE(1, 2) |
||||
debugn( const char *msg, ... ) |
||||
{ |
||||
va_list va; |
||||
|
||||
va_start( va, msg ); |
||||
vdebugn( DEBUG_DRV, msg, va ); |
||||
va_end( va ); |
||||
} |
||||
|
||||
static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' }; |
||||
|
||||
static char * |
||||
proxy_make_flags( int flags, char *buf ) |
||||
{ |
||||
uint i, d; |
||||
|
||||
for (d = 0, i = 0; i < as(Flags); i++) |
||||
if (flags & (1 << i)) |
||||
buf[d++] = Flags[i]; |
||||
buf[d] = 0; |
||||
return buf; |
||||
} |
||||
|
||||
static void |
||||
proxy_store_deref( proxy_store_t *ctx ) |
||||
{ |
||||
if (!--ctx->ref_count) |
||||
free( ctx ); |
||||
} |
||||
|
||||
static int curr_tag; |
||||
|
||||
typedef struct { |
||||
int ref_count; |
||||
int tag; |
||||
proxy_store_t *ctx; |
||||
} gen_cmd_t; |
||||
|
||||
static gen_cmd_t * |
||||
proxy_cmd_new( proxy_store_t *ctx, int sz ) |
||||
{ |
||||
gen_cmd_t *cmd = nfmalloc( sz ); |
||||
cmd->ref_count = 2; |
||||
cmd->tag = ++curr_tag; |
||||
cmd->ctx = ctx; |
||||
ctx->ref_count++; |
||||
return cmd; |
||||
} |
||||
|
||||
static void |
||||
proxy_cmd_done( gen_cmd_t *cmd ) |
||||
{ |
||||
if (!--cmd->ref_count) { |
||||
proxy_store_deref( cmd->ctx ); |
||||
free( cmd ); |
||||
} |
||||
} |
||||
|
||||
#if 0 |
||||
//# TEMPLATE GETTER
|
||||
static @type@proxy_@name@( store_t *gctx ) |
||||
{ |
||||
proxy_store_t *ctx = (proxy_store_t *)gctx; |
||||
|
||||
@type@rv = ctx->real_driver->@name@( ctx->real_store ); |
||||
debug( "%sCalled @name@, ret=@fmt@\n", ctx->label, rv ); |
||||
return rv; |
||||
} |
||||
//# END
|
||||
|
||||
//# TEMPLATE REGULAR
|
||||
static @type@proxy_@name@( store_t *gctx@decl_args@ ) |
||||
{ |
||||
proxy_store_t *ctx = (proxy_store_t *)gctx; |
||||
|
||||
@pre_print_args@ |
||||
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ ); |
||||
@print_args@ |
||||
@type@rv = ctx->real_driver->@name@( ctx->real_store@pass_args@ ); |
||||
debug( "%sLeave @name@, ret=@fmt@\n", ctx->label, rv ); |
||||
return rv; |
||||
} |
||||
//# END
|
||||
|
||||
//# TEMPLATE REGULAR_VOID
|
||||
static void proxy_@name@( store_t *gctx@decl_args@ ) |
||||
{ |
||||
proxy_store_t *ctx = (proxy_store_t *)gctx; |
||||
|
||||
@pre_print_args@ |
||||
debug( "%sEnter @name@@print_fmt_args@\n", ctx->label@print_pass_args@ ); |
||||
@print_args@ |
||||
ctx->real_driver->@name@( ctx->real_store@pass_args@ ); |
||||
debug( "%sLeave @name@\n", ctx->label ); |
||||
@action@ |
||||
} |
||||
//# END
|
||||
|
||||
//# TEMPLATE CALLBACK
|
||||
typedef struct { |
||||
gen_cmd_t gen; |
||||
void (*callback)( @decl_cb_args@void *aux ); |
||||
void *callback_aux; |
||||
@decl_state@ |
||||
} @name@_cmd_t; |
||||
|
||||
static void |
||||
proxy_@name@_cb( @decl_cb_args@void *aux ) |
||||
{ |
||||
@name@_cmd_t *cmd = (@name@_cmd_t *)aux; |
||||
|
||||
@pre_print_cb_args@ |
||||
debug( "%s[% 2d] Callback enter @name@@print_fmt_cb_args@\n", cmd->gen.ctx->label, cmd->gen.tag@print_pass_cb_args@ ); |
||||
@print_cb_args@ |
||||
cmd->callback( @pass_cb_args@cmd->callback_aux ); |
||||
debug( "%s[% 2d] Callback leave @name@\n", cmd->gen.ctx->label, cmd->gen.tag ); |
||||
proxy_cmd_done( &cmd->gen ); |
||||
} |
||||
|
||||
static void |
||||
proxy_@name@( store_t *gctx@decl_args@, void (*cb)( @decl_cb_args@void *aux ), void *aux ) |
||||
{ |
||||
proxy_store_t *ctx = (proxy_store_t *)gctx; |
||||
|
||||
@name@_cmd_t *cmd = (@name@_cmd_t *)proxy_cmd_new( ctx, sizeof(@name@_cmd_t) ); |
||||
cmd->callback = cb; |
||||
cmd->callback_aux = aux; |
||||
@assign_state@ |
||||
@pre_print_args@ |
||||
debug( "%s[% 2d] Enter @name@@print_fmt_args@\n", ctx->label, cmd->gen.tag@print_pass_args@ ); |
||||
@print_args@ |
||||
ctx->real_driver->@name@( ctx->real_store@pass_args@, proxy_@name@_cb, cmd ); |
||||
debug( "%s[% 2d] Leave @name@\n", ctx->label, cmd->gen.tag ); |
||||
proxy_cmd_done( &cmd->gen ); |
||||
} |
||||
//# END
|
||||
|
||||
//# UNDEFINE list_store_print_fmt_cb_args
|
||||
//# UNDEFINE list_store_print_pass_cb_args
|
||||
//# DEFINE list_store_print_cb_args
|
||||
if (sts == DRV_OK) { |
||||
for (string_list_t *box = boxes; box; box = box->next) |
||||
debug( " %s\n", box->string ); |
||||
} |
||||
//# END
|
||||
|
||||
//# DEFINE load_box_pre_print_args
|
||||
static char ubuf[12]; |
||||
//# END
|
||||
//# DEFINE load_box_print_fmt_args , [%d,%s] (new >= %d, seen <= %d)
|
||||
//# DEFINE load_box_print_pass_args , minuid, (maxuid == INT_MAX) ? "inf" : (nfsnprintf( ubuf, sizeof(ubuf), "%d", maxuid ), ubuf), newuid, seenuid
|
||||
//# DEFINE load_box_print_args
|
||||
if (excs.size) { |
||||
debugn( " excs:" ); |
||||
for (int t = 0; t < excs.size; t++) |
||||
debugn( " %d", excs.data[t] ); |
||||
debug( "\n" ); |
||||
} |
||||
//# END
|
||||
//# DEFINE load_box_pre_print_cb_args
|
||||
static char fbuf[as(Flags) + 1]; |
||||
//# END
|
||||
//# DEFINE load_box_print_fmt_cb_args , sts=%d, total=%d, recent=%d
|
||||
//# DEFINE load_box_print_pass_cb_args , sts, total_msgs, recent_msgs
|
||||
//# DEFINE load_box_print_cb_args
|
||||
if (sts == DRV_OK) { |
||||
for (message_t *msg = msgs; msg; msg = msg->next) |
||||
debug( " uid=%5d, flags=%4s, size=%6d, tuid=%." stringify(TUIDL) "s\n", |
||||
msg->uid, (msg->status & M_FLAGS) ? (proxy_make_flags( msg->flags, fbuf ), fbuf) : "?", msg->size, *msg->tuid ? msg->tuid : "?" ); |
||||
} |
||||
//# END
|
||||
|
||||
//# DEFINE find_new_msgs_print_fmt_cb_args , sts=%d
|
||||
//# DEFINE find_new_msgs_print_pass_cb_args , sts
|
||||
//# DEFINE find_new_msgs_print_cb_args
|
||||
if (sts == DRV_OK) { |
||||
for (message_t *msg = msgs; msg; msg = msg->next) |
||||
debug( " uid=%5d, tuid=%." stringify(TUIDL) "s\n", msg->uid, msg->tuid ); |
||||
} |
||||
//# END
|
||||
|
||||
//# DEFINE fetch_msg_decl_state
|
||||
msg_data_t *data; |
||||
//# END
|
||||
//# DEFINE fetch_msg_assign_state
|
||||
cmd->data = data; |
||||
//# END
|
||||
//# DEFINE fetch_msg_print_fmt_args , uid=%d, want_flags=%s, want_date=%s
|
||||
//# DEFINE fetch_msg_print_pass_args , msg->uid, !(msg->status & M_FLAGS) ? "yes" : "no", data->date ? "yes" : "no"
|
||||
//# DEFINE fetch_msg_pre_print_cb_args
|
||||
static char fbuf[as(Flags) + 1]; |
||||
proxy_make_flags( cmd->data->flags, fbuf ); |
||||
//# END
|
||||
//# DEFINE fetch_msg_print_fmt_cb_args , flags=%s, date=%ld, size=%d
|
||||
//# DEFINE fetch_msg_print_pass_cb_args , fbuf, cmd->data->date, cmd->data->len
|
||||
//# DEFINE fetch_msg_print_cb_args
|
||||
if (sts == DRV_OK && (DFlags & DEBUG_DRV_ALL)) { |
||||
printf( "%s=========\n", cmd->gen.ctx->label ); |
||||
fwrite( cmd->data->data, cmd->data->len, 1, stdout ); |
||||
printf( "%s=========\n", cmd->gen.ctx->label ); |
||||
fflush( stdout ); |
||||
} |
||||
//# END
|
||||
|
||||
//# DEFINE store_msg_pre_print_args
|
||||
static char fbuf[as(Flags) + 1]; |
||||
proxy_make_flags( data->flags, fbuf ); |
||||
//# END
|
||||
//# DEFINE store_msg_print_fmt_args , flags=%s, date=%ld, size=%d, to_trash=%s
|
||||
//# DEFINE store_msg_print_pass_args , fbuf, data->date, data->len, to_trash ? "yes" : "no"
|
||||
//# DEFINE store_msg_print_args
|
||||
if (DFlags & DEBUG_DRV_ALL) { |
||||
printf( "%s>>>>>>>>>\n", ctx->label ); |
||||
fwrite( data->data, data->len, 1, stdout ); |
||||
printf( "%s>>>>>>>>>\n", ctx->label ); |
||||
fflush( stdout ); |
||||
} |
||||
//# END
|
||||
|
||||
//# DEFINE set_msg_flags_pre_print_args
|
||||
static char fbuf1[as(Flags) + 1], fbuf2[as(Flags) + 1]; |
||||
proxy_make_flags( add, fbuf1 ); |
||||
proxy_make_flags( del, fbuf2 ); |
||||
//# END
|
||||
//# DEFINE set_msg_flags_print_fmt_args , uid=%d, add=%s, del=%s
|
||||
//# DEFINE set_msg_flags_print_pass_args , uid, fbuf1, fbuf2
|
||||
|
||||
//# DEFINE trash_msg_print_fmt_args , uid=%d
|
||||
//# DEFINE trash_msg_print_pass_args , msg->uid
|
||||
|
||||
//# DEFINE free_store_action
|
||||
proxy_store_deref( ctx ); |
||||
//# END
|
||||
|
||||
//# DEFINE cancel_store_action
|
||||
proxy_store_deref( ctx ); |
||||
//# END
|
||||
#endif |
||||
|
||||
//# SPECIAL commit_cmds
|
||||
static void |
||||
proxy_commit_cmds( store_t *gctx ) |
||||
{ |
||||
// Currently a dummy in all real drivers.
|
||||
(void) gctx; |
||||
} |
||||
|
||||
//# SPECIAL set_bad_callback
|
||||
static void |
||||
proxy_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux ) |
||||
{ |
||||
proxy_store_t *ctx = (proxy_store_t *)gctx; |
||||
|
||||
ctx->bad_callback = cb; |
||||
ctx->bad_callback_aux = aux; |
||||
} |
||||
|
||||
static void |
||||
proxy_invoke_bad_callback( proxy_store_t *ctx ) |
||||
{ |
||||
debug( "%sCallback enter bad store\n", ctx->label ); |
||||
ctx->bad_callback( ctx->bad_callback_aux ); |
||||
debug( "%sCallback leave bad store\n", ctx->label ); \
|
||||
} |
||||
|
||||
//# EXCLUDE alloc_store
|
||||
store_t * |
||||
proxy_alloc_store( store_t *real_ctx, const char *label ) |
||||
{ |
||||
proxy_store_t *ctx; |
||||
|
||||
ctx = nfcalloc( sizeof(*ctx) ); |
||||
ctx->gen.driver = &proxy_driver; |
||||
ctx->gen.conf = real_ctx->conf; |
||||
ctx->ref_count = 1; |
||||
ctx->label = label; |
||||
ctx->real_driver = real_ctx->driver; |
||||
ctx->real_store = real_ctx; |
||||
ctx->real_driver->set_bad_callback( ctx->real_store, (void (*)(void *))proxy_invoke_bad_callback, ctx ); |
||||
return &ctx->gen; |
||||
} |
||||
|
||||
//# EXCLUDE parse_store
|
||||
//# EXCLUDE cleanup
|
||||
//# EXCLUDE get_fail_state
|
||||
|
||||
#include "drv_proxy.inc" |
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/perl |
||||
# |
||||
# mbsync - mailbox synchronizer |
||||
# Copyright (C) 2017 Oswald Buddenhagen <ossi@users.sf.net> |
||||
# |
||||
# 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, see <http://www.gnu.org/licenses/>. |
||||
# |
||||
# As a special exception, mbsync may be linked with the OpenSSL library, |
||||
# despite that library's more restrictive license. |
||||
# |
||||
|
||||
use strict; |
||||
use warnings; |
||||
|
||||
die("Usage: $0 driver.h drv_proxy.c drv_proxy.inc\n") |
||||
if ($#ARGV != 2); |
||||
|
||||
my ($in_header, $in_source, $out_source) = @ARGV; |
||||
|
||||
my %templates; |
||||
my %defines; |
||||
my %excluded; |
||||
my %special; |
||||
|
||||
open(my $ins, $in_source) or die("Cannot open $in_source: $!\n"); |
||||
my $template; |
||||
my $define; |
||||
my $conts; |
||||
while (<$ins>) { |
||||
if ($template) { |
||||
if (/^\/\/\# END$/) { |
||||
$templates{$template} = $conts; |
||||
$template = undef; |
||||
} else { |
||||
$conts .= $_; |
||||
} |
||||
} elsif ($define) { |
||||
if (/^\/\/\# END$/) { |
||||
$defines{$define} = $conts; |
||||
$define = undef; |
||||
} else { |
||||
$conts .= $_; |
||||
} |
||||
} else { |
||||
if (/^\/\/\# TEMPLATE (\w+)$/) { |
||||
$template = $1; |
||||
$conts = ""; |
||||
} elsif (/^\/\/\# DEFINE (\w+)$/) { |
||||
$define = $1; |
||||
$conts = ""; |
||||
} elsif (/^\/\/\# DEFINE (\w+) (.*)$/) { |
||||
$defines{$1} = $2; |
||||
} elsif (/^\/\/\# UNDEFINE (\w+)$/) { |
||||
$defines{$1} = ""; |
||||
} elsif (/^\/\/\# EXCLUDE (\w+)$/) { |
||||
$excluded{$1} = 1; |
||||
} elsif (/^\/\/\# SPECIAL (\w+)$/) { |
||||
$special{$1} = 1; |
||||
} |
||||
} |
||||
} |
||||
close($ins); |
||||
|
||||
open(my $inh, $in_header) or die("Cannot open $in_header: $!\n"); |
||||
my $sts = 0; |
||||
my $cont = ""; |
||||
while (<$inh>) { |
||||
if ($sts == 0) { |
||||
if (/^struct driver \{$/) { |
||||
$sts = 1; |
||||
} |
||||
} elsif ($sts == 1) { |
||||
if (/^\};$/) { |
||||
$sts = 0; |
||||
} else { |
||||
$cont .= $_; |
||||
} |
||||
} |
||||
} |
||||
close($inh); |
||||
|
||||
$cont =~ s,\n, ,g; |
||||
$cont =~ s,/\*.*?\*/, ,g; |
||||
$cont =~ s,\h+, ,g; |
||||
my @ptypes = map { s,^ ,,r } split(/;/, $cont); |
||||
pop @ptypes; # last one is empty |
||||
|
||||
my @cmd_table; |
||||
|
||||
sub make_args($) |
||||
{ |
||||
$_ = shift; |
||||
s/(?:^|(?<=, ))(?:const )?\w+ \*?//g; |
||||
return $_; |
||||
} |
||||
|
||||
sub type_to_format($) |
||||
{ |
||||
$_ = shift; |
||||
s/xint /\%\#x/g; |
||||
s/int /\%d/g; |
||||
s/const char \*/\%s/g; |
||||
return $_; |
||||
} |
||||
|
||||
sub make_format($) |
||||
{ |
||||
$_ = type_to_format(shift); |
||||
s/, (\%\#?.)(\w+)/, $2=$1/g; |
||||
return $_; |
||||
} |
||||
|
||||
open(my $outh, ">".$out_source) or die("Cannot create $out_source: $!\n"); |
||||
|
||||
for (@ptypes) { |
||||
/^([\w* ]+)\(\*(\w+)\)\( (.*) \)$/ or die("Cannot parse prototype '$_'\n"); |
||||
my ($cmd_type, $cmd_name, $cmd_args) = ($1, $2, $3); |
||||
if (defined($excluded{$cmd_name})) { |
||||
push @cmd_table, "0"; |
||||
next; |
||||
} |
||||
push @cmd_table, "proxy_$cmd_name"; |
||||
next if (defined($special{$cmd_name})); |
||||
my %replace; |
||||
$replace{'name'} = $cmd_name; |
||||
$replace{'type'} = $cmd_type; |
||||
$cmd_args =~ s/^store_t \*ctx// or die("Arguments '$cmd_args' don't start with 'store_t *ctx'\n"); |
||||
if ($cmd_type eq "void " && $cmd_args =~ s/, void \(\*cb\)\( (.*)void \*aux \), void \*aux$//) { |
||||
my $cmd_cb_args = $1; |
||||
$replace{'decl_cb_args'} = $cmd_cb_args; |
||||
$replace{'pass_cb_args'} = make_args($cmd_cb_args); |
||||
my $cmd_print_cb_args = $cmd_cb_args =~ s/(.*), $/, $1/r; |
||||
$replace{'print_pass_cb_args'} = make_args($cmd_print_cb_args); |
||||
$replace{'print_fmt_cb_args'} = make_format($cmd_print_cb_args); |
||||
$template = "CALLBACK"; |
||||
} elsif ($cmd_name =~ /^get_/) { |
||||
$template = "GETTER"; |
||||
$replace{'fmt'} = type_to_format($cmd_type); |
||||
} elsif ($cmd_type eq "void ") { |
||||
$template = "REGULAR_VOID"; |
||||
} else { |
||||
$template = "REGULAR"; |
||||
$replace{'fmt'} = type_to_format($cmd_type); |
||||
} |
||||
$replace{'decl_args'} = $cmd_args; |
||||
$replace{'print_pass_args'} = $replace{'pass_args'} = make_args($cmd_args); |
||||
$replace{'print_fmt_args'} = make_format($cmd_args); |
||||
for (keys %defines) { |
||||
$replace{$1} = $defines{$_} if (/^${cmd_name}_(.*)$/); |
||||
} |
||||
my $text = $templates{$template}; |
||||
$text =~ s/^\h*\@(\w+)\@\n/$replace{$1} \/\/ ""/smeg; |
||||
$text =~ s/\@(\w+)\@/$replace{$1} \/\/ ""/eg; |
||||
print $outh $text."\n"; |
||||
} |
||||
|
||||
print $outh "struct driver proxy_driver = {\n".join("", map { "\t$_,\n" } @cmd_table)."};\n"; |
||||
close $outh; |
Loading…
Reference in new issue