our @h = ();
our @c = ();
+our @ah = ();
+our @ac = ();
+our @missing = ();
+
+# Mapping of return type sequences to eclient callbacks
+our @eclient_return = (
+ ["no_response" => []],
+ ["string_response" => ["string"]],
+ ["string_response" => ["string-raw"]],
+ ["integer_response" => ["integer"]],
+ ["integer_response" => ["boolean"]],
+ ["time_response" => ["time"]],
+ ["pair_integer_response" => ["integer", "integer"]],
+ ["queue_response" => ["queue"]],
+ ["playing_response" => ["queue-one"]],
+ ["list_response" => ["body"]],
+ );
+
+# eclient_response_matces(RETURNS, VARIANT)
+#
+# Return true if VARIANT matches RETURNS
+sub eclient_response_matches {
+ my $returns = shift;
+ my $variant = shift;
+ my $types = $variant->[1];
+ if(scalar @$returns != scalar @$types) { return 0; }
+ for my $n (0 .. $#$returns) {
+ my $return = $returns->[$n];
+ my $type = $return->[0];
+ if($type ne $types->[$n]) { return 0; }
+ }
+ return 1;
+}
+
+# find_eclient_type(RETURNS)
+#
+# Find the result type for an eclient call
+sub find_eclient_response {
+ my $returns = shift;
+ if(!defined $returns) {
+ $returns = [];
+ }
+ for my $variant (@eclient_return) {
+ if(eclient_response_matches($returns, $variant)) {
+ return $variant->[0];
+ }
+ }
+ return undef;
+}
# Write(PATH, LINES)
#
$cmdc =~ s/-/_/g;
}
print STDERR "Processing $cmd... ";
+ # C argument types and conversions
+ my @cargs = ();
+ my @conversions = ();
+ for my $arg (@$args) {
+ if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
+ push(@cargs, "disorder__$arg->[0]", $arg->[1], "n$arg->[1]");
+ } elsif($arg->[0] eq 'string') {
+ push(@cargs, $arg->[1]);
+ } elsif($arg->[0] eq 'integer') {
+ push(@cargs, "buf_$arg->[1]");
+ push(@conversions,
+ " char buf_$arg->[1]\[16];\n",
+ " byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
+ } elsif($arg->[0] eq 'time') {
+ push(@cargs, "buf_$arg->[1]");
+ push(@conversions,
+ " char buf_$arg->[1]\[16];\n",
+ " byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%lld\", (long long)$arg->[1]);\n");
+ } elsif($arg->[0] eq 'literal') {
+ push(@cargs, "\"$arg->[1]\"");
+ } else {
+ die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
+ }
+ }
# Synchronous C API
print STDERR "H ";
push(@h, "/** \@brief $summary\n",
join(", ", "disorder_client *c",
map(c_in_decl($_), @$args),
map(c_out_decl($_), @$returns)),
- ") {\n");
- my @cargs = ();
- for my $arg (@$args) {
- if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
- push(@cargs, "disorder_$arg->[0]", $arg->[1], "n$arg->[1]");
- } elsif($arg->[0] eq 'string') {
- push(@cargs, $arg->[1]);
- } elsif($arg->[0] eq 'integer') {
- push(@cargs, "buf_$arg->[1]");
- push(@c, " char buf_$arg->[1]\[16];\n",
- " byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
- } elsif($arg->[0] eq 'time') {
- push(@cargs, "buf_$arg->[1]");
- push(@c, " char buf_$arg->[1]\[16];\n",
- " byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%lld\", (long long)$arg->[1]);\n");
- } elsif($arg->[0] eq 'literal') {
- push(@cargs, "\"$arg->[1]\"");
- } else {
- die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
- }
- }
+ ") {\n",
+ @conversions);
if(!defined $returns or scalar @$returns == 0) {
# Simple case
push(@c, " return disorder_simple(",
my $name = $return->[1];
if($type eq 'string') {
push(@c,
- " *${name}p = v[$n];\n");
+ " *${name}p = v[$n];\n",
+ " v[$n] = NULL;\n");
} elsif($type eq 'boolean') {
push(@c,
" if(boolean(\"$cmd\", v[$n], ${name}p))\n",
" *${name}p = atoll(v[$n]);\n");
} elsif($type eq 'user') {
push(@c,
- " c->user = v[$n];\n");
+ " c->user = v[$n];\n",
+ " v[$n] = NULL;\n");
} elsif($type eq 'body') {
push(@c,
" if(readlist(c, ${name}p, n${name}p))\n",
die "$0: C API: unknown return type '$type' for '$name'\n";
}
}
+ if($expected) {
+ push(@c,
+ " free_strings(nv, v);\n");
+ }
push(@c, " return 0;\n");
- # TODO xfree unconsumed split output
}
push(@c, "}\n\n");
# Asynchronous C API
- # TODO
+ my $variant = find_eclient_response($returns);
+ if(defined $variant) {
+ print STDERR "AH ";
+ push(@ah,
+ "/** \@brief $summary\n",
+ " *\n",
+ " * $detail\n",
+ " *\n",
+ " * \@param c Client\n",
+ " * \@param completed Called upon completion\n",
+ c_param_docs($args),
+ " * \@param v Passed to \@p completed\n",
+ " * \@return 0 if the command was queued successfuly, non-0 on error\n",
+ " */\n",
+ "int disorder_eclient_$cmdc(",
+ join(", ", "disorder_eclient *c",
+ "disorder_eclient_$variant *completed",
+ map(c_in_decl($_), @$args),
+ "void *v"),
+ ");\n\n");
+
+ print STDERR "AC ";
+ push(@ac,
+ "int disorder_eclient_$cmdc(",
+ join(", ", "disorder_eclient *c",
+ "disorder_eclient_$variant *completed",
+ map(c_in_decl($_), @$args),
+ "void *v"),
+ ") {\n",
+ @conversions);
+ push(@ac, " return simple(",
+ join(", ",
+ "c",
+ "${variant}_opcallback",
+ "(void (*)())completed",
+ "v",
+ "\"$cmd\"",
+ @cargs,
+ "(char *)0"),
+ ");\n");
+ push(@ac, "}\n\n");
+ } else {
+ push(@missing, "disorder_eclient_$cmdc");
+ }
# Python API
# TODO
push(@c, @generated, @gpl,
"\n");
+push(@ah, @generated, @gpl,
+ "#ifndef ECLIENT_STUBS_H\n",
+ "#define ECLIENT_STUBS_H\n",
+ "\n");
+
+push(@ac, @generated, @gpl,
+ "\n");
+
# The protocol ----------------------------------------------------------------
simple("adopt",
push(@h, "#endif\n");
+push(@ah, "#endif\n");
+
# Write it all out ------------------------------------------------------------
Write("lib/client-stubs.h", \@h);
Write("lib/client-stubs.c", \@c);
+
+Write("lib/eclient-stubs.h", \@ah);
+Write("lib/eclient-stubs.c", \@ac);
+
+if(scalar @missing) {
+ print "Missing:\n";
+ print map(" $_\n", @missing);
+}