From 993d1bce74f4cc5be2bfa69a467aae106e2194ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Jun 2004 00:22:37 +0000 Subject: basic cli interface git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@22 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 35 ++++++++-- src/cli.c | 83 ++++++++++++++++++++++ src/cli.h | 14 ++++ src/client.c | 18 +++++ src/client.h | 3 +- src/core.c | 27 ------- src/core.h | 3 - src/ioline.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ioline.h | 14 ++++ src/main.c | 4 +- src/main.h | 6 ++ src/module-cli.c | 34 +++++++++ src/module.c | 17 +++++ src/module.h | 7 +- src/protocol-simple.c | 4 +- src/sink.c | 36 ++++++++++ src/sink.h | 6 ++ src/source.c | 35 ++++++++++ src/source.h | 4 ++ src/strbuf.c | 13 ++-- src/strbuf.h | 1 + 21 files changed, 505 insertions(+), 49 deletions(-) create mode 100644 src/cli.c create mode 100644 src/cli.h create mode 100644 src/ioline.c create mode 100644 src/ioline.h create mode 100644 src/main.h create mode 100644 src/module-cli.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 8ce5202d..5afc5bc0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,12 +22,25 @@ bin_PROGRAMS = polypaudio pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \ libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \ - libpacket.la module-oss.la module-oss-mmap.la liboss.la + libpacket.la module-oss.la module-oss-mmap.la liboss.la libioline.la \ + libcli.la module-cli.la + +polypaudio_SOURCES = idxset.c idxset.h \ + queue.c queue.h \ + strbuf.c strbuf.h \ + mainloop.c mainloop.h \ + memblock.c memblock.h \ + sample.c sample.h \ + memblockq.c memblockq.h \ + client.c client.h \ + core.c core.h \ + main.c main.h \ + sourceoutput.c sourceoutput.h \ + sinkinput.c sinkinput.h \ + source.c source.h \ + sink.c sink.h \ + module.c module.h -polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c \ - memblock.c sample.c memblockq.c client.c \ - core.c main.c sourceoutput.c sinkinput.c source.c sink.c \ - module.c polypaudio_INCLUDES = $(INCLTDL) polypaudio_LDADD = $(LIBLTDL) polypaudio_LDFLAGS=-export-dynamic @@ -53,6 +66,14 @@ libpacket_la_LDFLAGS = -avoid-version liboss_la_SOURCES = oss.c liboss_la_LDFLAGS = -avoid-version +libioline_la_SOURCES = ioline.c +libioline_la_LDFLAGS = -avoid-version +libioline_la_LIBADD = libiochannel.la + +libcli_la_SOURCES = cli.c +libcli_la_LDFLAGS = -avoid-version +libcli_la_LIBADD = libiochannel.la libioline.la + module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la @@ -68,3 +89,7 @@ module_oss_la_LIBADD = libiochannel.la liboss.la module_oss_mmap_la_SOURCES = module-oss-mmap.c module_oss_mmap_la_LDFLAGS = -module -avoid-version module_oss_mmap_la_LIBADD = libiochannel.la liboss.la + +module_cli_la_SOURCES = module-cli.c +module_cli_la_LDFLAGS = -module -avoid-version +module_cli_la_LIBADD = libcli.la libiochannel.la diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 00000000..9aad7f56 --- /dev/null +++ b/src/cli.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +#include "ioline.h" +#include "cli.h" +#include "module.h" +#include "sink.h" +#include "source.h" +#include "client.h" + +struct cli { + struct core *core; + struct ioline *line; + + void (*eof_callback)(struct cli *c, void *userdata); + void *userdata; +}; + +static void line_callback(struct ioline *line, const char *s, void *userdata); + +struct cli* cli_new(struct core *core, struct iochannel *io) { + struct cli *c; + assert(io); + + c = malloc(sizeof(struct cli)); + assert(c); + c->core = core; + c->line = ioline_new(io); + assert(c->line); + + c->userdata = NULL; + c->eof_callback = NULL; + + ioline_set_callback(c->line, line_callback, c); + ioline_puts(c->line, "Welcome to polypaudio!\n> "); + + return c; +} + +void cli_free(struct cli *c) { + assert(c); + ioline_free(c->line); + free(c); +} + +static void line_callback(struct ioline *line, const char *s, void *userdata) { + struct cli *c = userdata; + char *t = NULL; + assert(line && c); + + if (!s) { + fprintf(stderr, "CLI client exited\n"); + if (c->eof_callback) + c->eof_callback(c, c->userdata); + + return; + } + + if (!strcmp(s, "modules")) + ioline_puts(line, (t = module_list_to_string(c->core))); + else if (!strcmp(s, "sources")) + ioline_puts(line, (t = source_list_to_string(c->core))); + else if (!strcmp(s, "sinks")) + ioline_puts(line, (t = sink_list_to_string(c->core))); + else if (!strcmp(s, "clients")) + ioline_puts(line, (t = client_list_to_string(c->core))); + else if (!strcmp(s, "exit")) { + assert(c->core && c->core->mainloop); + mainloop_quit(c->core->mainloop, -1); + } else if (*s) + ioline_puts(line, "Unknown command\n"); + + free(t); + ioline_puts(line, "> "); +} + +void cli_set_eof_callback(struct cli *c, void (*cb)(struct cli*c, void *userdata), void *userdata) { + assert(c && cb); + c->eof_callback = cb; + c->userdata = userdata; +} diff --git a/src/cli.h b/src/cli.h new file mode 100644 index 00000000..f1b74397 --- /dev/null +++ b/src/cli.h @@ -0,0 +1,14 @@ +#ifndef fooclihfoo +#define fooclihfoo + +#include "iochannel.h" +#include "core.h" + +struct cli; + +struct cli* cli_new(struct core *core, struct iochannel *io); +void cli_free(struct cli *cli); + +void cli_set_eof_callback(struct cli *cli, void (*cb)(struct cli*c, void *userdata), void *userdata); + +#endif diff --git a/src/client.c b/src/client.c index 1b84c6bf..4c8a0491 100644 --- a/src/client.c +++ b/src/client.c @@ -4,6 +4,7 @@ #include #include "client.h" +#include "strbuf.h" struct client *client_new(struct core *core, const char *protocol_name, char *name) { struct client *c; @@ -42,3 +43,20 @@ void client_kill(struct client *c) { c->kill(c); } +char *client_list_to_string(struct core *c) { + struct strbuf *s; + struct client *client; + uint32_t index = IDXSET_INVALID; + assert(c); + + s = strbuf_new(); + assert(s); + + strbuf_printf(s, "%u client(s).\n", idxset_ncontents(c->clients)); + + for (client = idxset_first(c->clients, &index); client; client = idxset_next(c->clients, &index)) + strbuf_printf(s, " index: %u, name: <%s>, protocol_name: <%s>\n", client->index, client->name, client->protocol_name); + + return strbuf_tostring_free(s); +} + diff --git a/src/client.h b/src/client.h index 556b5fb3..48172051 100644 --- a/src/client.h +++ b/src/client.h @@ -11,7 +11,6 @@ struct client { const char *protocol_name; void (*kill)(struct client *c); - void *userdata; }; @@ -24,4 +23,6 @@ void client_free(struct client *c); * request destruction of the client */ void client_kill(struct client *c); +char *client_list_to_string(struct core *c); + #endif diff --git a/src/core.c b/src/core.c index 0bc13a5b..50248501 100644 --- a/src/core.c +++ b/src/core.c @@ -50,30 +50,3 @@ void core_free(struct core *c) { free(c); }; -struct sink* core_get_default_sink(struct core *c) { - struct sink *sink; - assert(c); - - if ((sink = idxset_get_by_index(c->sinks, c->default_sink_index))) - return sink; - - if (!(sink = idxset_first(c->sinks, &c->default_sink_index))) - return NULL; - - fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index); - return sink; -} - -struct source* core_get_default_source(struct core *c) { - struct source *source; - assert(c); - - if ((source = idxset_get_by_index(c->sources, c->default_source_index))) - return source; - - if (!(source = idxset_first(c->sources, &c->default_source_index))) - return NULL; - - fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index); - return source; -} diff --git a/src/core.h b/src/core.h index a8a140b8..f6f794b9 100644 --- a/src/core.h +++ b/src/core.h @@ -15,7 +15,4 @@ struct core { struct core* core_new(struct mainloop *m); void core_free(struct core*c); -struct sink* core_get_default_sink(struct core *c); -struct source* core_get_default_source(struct core *c); - #endif diff --git a/src/ioline.c b/src/ioline.c new file mode 100644 index 00000000..c37737a6 --- /dev/null +++ b/src/ioline.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include + +#include "ioline.h" + +#define BUFFER_LIMIT (64*1024) +#define READ_SIZE (1024) + +struct ioline { + struct iochannel *io; + int dead; + + char *wbuf; + size_t wbuf_length, wbuf_index, wbuf_valid_length; + + char *rbuf; + size_t rbuf_length, rbuf_index, rbuf_valid_length; + + void (*callback)(struct ioline*io, const char *s, void *userdata); + void *userdata; +}; + +static void io_callback(struct iochannel*io, void *userdata); +static int do_write(struct ioline *l); + +struct ioline* ioline_new(struct iochannel *io) { + struct ioline *l; + assert(io); + + l = malloc(sizeof(struct ioline)); + assert(l); + l->io = io; + l->dead = 0; + + l->wbuf = NULL; + l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; + + l->rbuf = NULL; + l->rbuf_length = l->rbuf_index = l->rbuf_valid_length = 0; + + l->callback = NULL; + l->userdata = NULL; + + iochannel_set_callback(io, io_callback, l); + + return l; +} + +void ioline_free(struct ioline *l) { + assert(l); + iochannel_free(l->io); + free(l->wbuf); + free(l->rbuf); + free(l); +} + +void ioline_puts(struct ioline *l, const char *c) { + size_t len; + assert(l && c); + + len = strlen(c); + if (len > BUFFER_LIMIT - l->wbuf_valid_length) + len = BUFFER_LIMIT - l->wbuf_valid_length; + + if (!len) + return; + + if (len > l->wbuf_length - l->wbuf_valid_length) { + size_t n = l->wbuf_valid_length+len; + char *new = malloc(n); + if (l->wbuf) { + memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + free(l->wbuf); + } + l->wbuf = new; + l->wbuf_length = n; + l->wbuf_index = 0; + } else if (len > l->wbuf_length - l->wbuf_valid_length - l->wbuf_index) { + memmove(l->wbuf, l->wbuf+l->wbuf_index, l->wbuf_valid_length); + l->wbuf_index = 0; + } + + memcpy(l->wbuf+l->wbuf_index+l->wbuf_valid_length, c, len); + l->wbuf_valid_length += len; + + do_write(l); +} + +void ioline_set_callback(struct ioline*l, void (*callback)(struct ioline*io, const char *s, void *userdata), void *userdata) { + assert(l && callback); + l->callback = callback; + l->userdata = userdata; +} + +static int do_read(struct ioline *l) { + ssize_t r; + size_t m, len; + char *p, *e; + assert(l); + + if (!iochannel_is_readable(l->io)) + return 0; + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + if (len < READ_SIZE) { + size_t n = l->rbuf_valid_length+READ_SIZE; + + if (n >= BUFFER_LIMIT) + n = BUFFER_LIMIT; + + if (l->rbuf_length >= n) { + if (l->rbuf_valid_length) + memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + } else { + char *new = malloc(n); + if (l->rbuf_valid_length) + memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); + free(l->rbuf); + l->rbuf = new; + l->rbuf_length = n; + } + + l->rbuf_index = 0; + } + + len = l->rbuf_length - l->rbuf_index - l->rbuf_valid_length; + + if ((r = iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) + return -1; + + e = memchr(l->rbuf+l->rbuf_index+l->rbuf_valid_length, '\n', r); + l->rbuf_valid_length += r; + + if (!e && l->rbuf_valid_length >= BUFFER_LIMIT) + e = l->rbuf+BUFFER_LIMIT-1; + + *e = 0; + p = l->rbuf+l->rbuf_index; + m = strlen(p); + + if (l->callback) + l->callback(l, p, l->userdata); + + l->rbuf_index += m+1; + l->rbuf_valid_length -= m+1; + + if (l->rbuf_valid_length == 0) + l->rbuf_index = 0; + + return 0; +} + +static int do_write(struct ioline *l) { + ssize_t r; + assert(l); + + if (!l->wbuf_valid_length || !iochannel_is_writable(l->io)) + return 0; + + if ((r = iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) < 0) + return -1; + + l->wbuf_valid_length -= r; + if (l->wbuf_valid_length == 0) + l->wbuf_index = 0; + + return 0; +} + +static void io_callback(struct iochannel*io, void *userdata) { + struct ioline *l = userdata; + assert(io && l); + + if (!l->dead && do_read(l) < 0) + goto fail; + + if (!l->dead && do_write(l) < 0) + goto fail; + + return; + +fail: + if (l->callback) + l->callback(l, NULL, l->userdata); + l->dead = 1; +} diff --git a/src/ioline.h b/src/ioline.h new file mode 100644 index 00000000..55d7d4a3 --- /dev/null +++ b/src/ioline.h @@ -0,0 +1,14 @@ +#ifndef fooiolinehfoo +#define fooiolinehfoo + +#include "iochannel.h" + +struct ioline; + +struct ioline* ioline_new(struct iochannel *io); +void ioline_free(struct ioline *l); + +void ioline_puts(struct ioline *s, const char *c); +void ioline_set_callback(struct ioline*io, void (*callback)(struct ioline*io, const char *s, void *userdata), void *userdata); + +#endif diff --git a/src/main.c b/src/main.c index bce07823..f35505ec 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,8 @@ #include "mainloop.h" #include "module.h" +int stdin_inuse = 0, stdout_inuse = 0; + static void signal_callback(struct mainloop_source *m, int sig, void *userdata) { mainloop_quit(mainloop_source_get_mainloop(m), -1); fprintf(stderr, "main: got signal.\n"); @@ -33,12 +35,12 @@ int main(int argc, char *argv[]) { module_load(c, "module-oss-mmap", "/dev/dsp1"); module_load(c, "module-pipe-sink", NULL); module_load(c, "module-simple-protocol-tcp", NULL); + module_load(c, "module-cli", NULL); fprintf(stderr, "main: mainloop entry.\n"); while (mainloop_iterate(m, 1) == 0); /* fprintf(stderr, "main: %u blocks\n", n_blocks);*/ fprintf(stderr, "main: mainloop exit.\n"); - mainloop_run(m); diff --git a/src/main.h b/src/main.h new file mode 100644 index 00000000..c4bea049 --- /dev/null +++ b/src/main.h @@ -0,0 +1,6 @@ +#ifndef foomainhfoo +#define foomainhfoo + +extern int stdin_inuse, stdout_inuse; + +#endif diff --git a/src/module-cli.c b/src/module-cli.c new file mode 100644 index 00000000..4af37f67 --- /dev/null +++ b/src/module-cli.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "main.h" +#include "module.h" +#include "iochannel.h" +#include "cli.h" + +int module_init(struct core *c, struct module*m) { + struct iochannel *io; + assert(c && m); + + if (stdin_inuse || stdout_inuse) { + fprintf(stderr, "STDIN/STDUSE already used\n"); + return -1; + } + + stdin_inuse = stdout_inuse = 1; + io = iochannel_new(c->mainloop, STDIN_FILENO, STDOUT_FILENO); + assert(io); + + m->userdata = cli_new(c, io); + assert(m->userdata); + return 0; +} + +void module_done(struct core *c, struct module*m) { + assert(c && m); + + cli_free(m->userdata); + assert(stdin_inuse && stdout_inuse); + stdin_inuse = stdout_inuse = 0; +} diff --git a/src/module.c b/src/module.c index 7f2bc218..0be7f5ed 100644 --- a/src/module.c +++ b/src/module.c @@ -6,6 +6,7 @@ #include #include "module.h" +#include "strbuf.h" struct module* module_load(struct core *c, const char *name, const char *argument) { struct module *m = NULL; @@ -110,3 +111,19 @@ void module_unload_all(struct core *c) { c->modules = NULL; } +char *module_list_to_string(struct core *c) { + struct strbuf *s; + struct module *m; + uint32_t index = IDXSET_INVALID; + assert(c); + + s = strbuf_new(); + assert(s); + + strbuf_printf(s, "%u module(s) loaded.\n", idxset_ncontents(c->modules)); + + for (m = idxset_first(c->modules, &index); m; m = idxset_next(c->modules, &index)) + strbuf_printf(s, " index: %u, name: <%s>, argument: <%s>\n", m->index, m->name, m->argument); + + return strbuf_tostring_free(s); +} diff --git a/src/module.h b/src/module.h index 98082194..709c7f55 100644 --- a/src/module.h +++ b/src/module.h @@ -6,11 +6,6 @@ #include "core.h" -struct dependency_module { - lt_dlhandle dl; - struct dependency_module *next; -}; - struct module { struct core *core; char *name, *argument; @@ -30,4 +25,6 @@ void module_unload_by_index(struct core *c, uint32_t index); void module_unload_all(struct core *c); +char *module_list_to_string(struct core *c); + #endif diff --git a/src/protocol-simple.c b/src/protocol-simple.c index f779a56a..3563419e 100644 --- a/src/protocol-simple.c +++ b/src/protocol-simple.c @@ -205,7 +205,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us struct source *source; size_t l; - if (!(source = core_get_default_source(p->core))) { + if (!(source = source_get_default(p->core))) { fprintf(stderr, "Failed to get default source.\n"); goto fail; } @@ -224,7 +224,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us struct sink *sink; size_t l; - if (!(sink = core_get_default_sink(p->core))) { + if (!(sink = sink_get_default(p->core))) { fprintf(stderr, "Failed to get default sink.\n"); goto fail; } diff --git a/src/sink.c b/src/sink.c index 4d3206d8..89f5e088 100644 --- a/src/sink.c +++ b/src/sink.c @@ -5,6 +5,7 @@ #include "sink.h" #include "sinkinput.h" +#include "strbuf.h" #define MAX_MIX_CHANNELS 32 @@ -214,3 +215,38 @@ uint32_t sink_get_latency(struct sink *s) { return s->get_latency(s); } + +struct sink* sink_get_default(struct core *c) { + struct sink *sink; + assert(c); + + if ((sink = idxset_get_by_index(c->sinks, c->default_sink_index))) + return sink; + + if (!(sink = idxset_first(c->sinks, &c->default_sink_index))) + return NULL; + + fprintf(stderr, "core: default sink vanished, setting to %u.\n", sink->index); + return sink; +} + +char *sink_list_to_string(struct core *c) { + struct strbuf *s; + struct sink *sink, *default_sink; + uint32_t index = IDXSET_INVALID; + assert(c); + + s = strbuf_new(); + assert(s); + + strbuf_printf(s, "%u sink(s) available.\n", idxset_ncontents(c->sinks)); + + default_sink = sink_get_default(c); + + for (sink = idxset_first(c->sinks, &index); sink; sink = idxset_next(c->sinks, &index)) { + assert(sink->monitor_source); + strbuf_printf(s, " %c index: %u, name: <%s>, volume: <0x%02x>, latency: <%u usec>, monitor_source: <%u>\n", sink == default_sink ? '*' : ' ', sink->index, sink->name, (unsigned) sink->volume, sink_get_latency(sink), sink->monitor_source->index); + } + + return strbuf_tostring_free(s); +} diff --git a/src/sink.h b/src/sink.h index 1a9fb8ca..394abb5b 100644 --- a/src/sink.h +++ b/src/sink.h @@ -38,4 +38,10 @@ uint32_t sink_get_latency(struct sink *s); void sink_notify(struct sink*s); +char *sink_list_to_string(struct core *core); + +struct sink* sink_get_default(struct core *c); + + + #endif diff --git a/src/source.c b/src/source.c index c950b54d..44d77532 100644 --- a/src/source.c +++ b/src/source.c @@ -5,6 +5,7 @@ #include "source.h" #include "sourceoutput.h" +#include "strbuf.h" struct source* source_new(struct core *core, const char *name, const struct sample_spec *spec) { struct source *s; @@ -70,3 +71,37 @@ void source_post(struct source*s, struct memchunk *chunk) { idxset_foreach(s->outputs, do_post, chunk); } + +struct source* source_get_default(struct core *c) { + struct source *source; + assert(c); + + if ((source = idxset_get_by_index(c->sources, c->default_source_index))) + return source; + + if (!(source = idxset_first(c->sources, &c->default_source_index))) + return NULL; + + fprintf(stderr, "core: default source vanished, setting to %u.\n", source->index); + return source; +} + +char *source_list_to_string(struct core *c) { + struct strbuf *s; + struct source *source, *default_source; + uint32_t index = IDXSET_INVALID; + assert(c); + + s = strbuf_new(); + assert(s); + + strbuf_printf(s, "%u source(s) available.\n", idxset_ncontents(c->sources)); + + default_source = source_get_default(c); + + for (source = idxset_first(c->sources, &index); source; source = idxset_next(c->sources, &index)) + strbuf_printf(s, " %c index: %u, name: <%s>\n", source == default_source ? '*' : ' ', source->index, source->name); + + return strbuf_tostring_free(s); +} + diff --git a/src/source.h b/src/source.h index 1442b9f0..078fb1c9 100644 --- a/src/source.h +++ b/src/source.h @@ -29,4 +29,8 @@ void source_post(struct source*s, struct memchunk *b); void source_notify(struct source *s); +char *source_list_to_string(struct core *c); + +struct source* source_get_default(struct core *c); + #endif diff --git a/src/strbuf.c b/src/strbuf.c index 7c8b965d..97c451c1 100644 --- a/src/strbuf.c +++ b/src/strbuf.c @@ -1,6 +1,3 @@ -#ifndef foostrbufhfoo -#define foostrbufhfoo - #include #include #include @@ -55,6 +52,14 @@ char *strbuf_tostring(struct strbuf *sb) { return t; } +char *strbuf_tostring_free(struct strbuf *sb) { + char *t; + assert(sb); + t = strbuf_tostring(sb); + strbuf_free(sb); + return t; +} + void strbuf_puts(struct strbuf *sb, const char *t) { struct chunk *c; size_t l; @@ -118,5 +123,3 @@ int strbuf_printf(struct strbuf *sb, const char *format, ...) { size *= 2; } } - -#endif diff --git a/src/strbuf.h b/src/strbuf.h index 6ad582a3..f530a0dc 100644 --- a/src/strbuf.h +++ b/src/strbuf.h @@ -6,6 +6,7 @@ struct strbuf; struct strbuf *strbuf_new(void); void strbuf_free(struct strbuf *sb); char *strbuf_tostring(struct strbuf *sb); +char *strbuf_tostring_free(struct strbuf *sb); int strbuf_printf(struct strbuf *sb, const char *format, ...); void strbuf_puts(struct strbuf *sb, const char *t); -- cgit