* This file is part of DisOrder.
* Copyright (C) 2006-2008 Richard Kettlewell
*
- * This program is free software; you can redistribute it and/or modify
+ * 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
+ * the Free Software Foundation, either version 3 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.
- *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file lib/eclient.c
* @brief Client code for event-driven programs
struct operation {
struct operation *next; /**< @brief next operation */
char *cmd; /**< @brief command to send or 0 */
+ char **body; /**< @brief command body */
operation_callback *opcallback; /**< @brief internal completion callback */
void (*completed)(); /**< @brief user completion callback or 0 */
void *v; /**< @brief data for COMPLETED */
/** @brief Protocol version */
int protocol;
+
+ /** @brief True if enabled */
+ int enabled;
};
/* Forward declarations ******************************************************/
operation_callback *opcallback,
void (*completed)(),
void *v,
+ int nbody,
+ char **body,
const char *cmd,
...);
static void log_opcallback(disorder_eclient *c, struct operation *op);
static void logentry_state(disorder_eclient *c, int nvec, char **vec);
static void logentry_volume(disorder_eclient *c, int nvec, char **vec);
static void logentry_rescanned(disorder_eclient *c, int nvec, char **vec);
+static void logentry_user_add(disorder_eclient *c, int nvec, char **vec);
+static void logentry_user_confirm(disorder_eclient *c, int nvec, char **vec);
+static void logentry_user_delete(disorder_eclient *c, int nvec, char **vec);
+static void logentry_user_edit(disorder_eclient *c, int nvec, char **vec);
+static void logentry_rights_changed(disorder_eclient *c, int nvec, char **vec);
+static void logentry_adopted(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_created(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_deleted(disorder_eclient *c, int nvec, char **vec);
+static void logentry_playlist_modified(disorder_eclient *c, int nvec, char **vec);
+static void logentry_global_pref(disorder_eclient *c, int nvec, char **vec);
/* Tables ********************************************************************/
/** @brief Table for parsing log entries */
static const struct logentry_handler logentry_handlers[] = {
#define LE(X, MIN, MAX) { #X, MIN, MAX, logentry_##X }
+ LE(adopted, 2, 2),
LE(completed, 1, 1),
LE(failed, 2, 2),
+ LE(global_pref, 1, 2),
LE(moved, 1, 1),
LE(playing, 1, 2),
+ LE(playlist_created, 2, 2),
+ LE(playlist_deleted, 1, 1),
+ LE(playlist_modified, 2, 2),
LE(queue, 2, INT_MAX),
LE(recent_added, 2, INT_MAX),
LE(recent_removed, 1, 1),
LE(removed, 1, 2),
LE(rescanned, 0, 0),
+ LE(rights_changed, 1, 1),
LE(scratched, 2, 2),
LE(state, 1, 1),
+ LE(user_add, 1, 1),
+ LE(user_confirm, 1, 1),
+ LE(user_delete, 1, 1),
+ LE(user_edit, 2, 2),
LE(volume, 2, 2)
};
c->callbacks = cb;
c->u = u;
c->opstail = &c->ops;
+ c->enabled = 1;
vector_init(&c->vec);
dynstr_init(&c->input);
dynstr_init(&c->output);
c->log_callbacks->state(c->log_v, c->statebits);
}
+/** @brief Permit new connection activity */
+void disorder_eclient_enable_connect(disorder_eclient *c) {
+ c->enabled = 1;
+}
+
+/** @brief Suppress new connection activity */
+void disorder_eclient_disable_connect(disorder_eclient *c) {
+ c->enabled = 0;
+}
+
/** @brief Return current state */
unsigned long disorder_eclient_state(const disorder_eclient *c) {
return c->statebits | (c->state > state_connected ? DISORDER_CONNECTED : 0);
/* State machine *************************************************************/
+/** @brief Send an operation (into the output buffer)
+ * @param op Operation to send
+ */
+static void op_send(struct operation *op) {
+ disorder_eclient *const c = op->client;
+ put(c, op->cmd, strlen(op->cmd));
+ if(op->body) {
+ for(int n = 0; op->body[n]; ++n) {
+ if(op->body[n][0] == '.')
+ put(c, ".", 1);
+ put(c, op->body[n], strlen(op->body[n]));
+ put(c, "\n", 1);
+ }
+ put(c, ".\n", 2);
+ }
+ op->sent = 1;
+}
+
/** @brief Called when there's something to do
* @param c Client
* @param mode bitmap of @ref DISORDER_POLL_READ and/or @ref DISORDER_POLL_WRITE.
/* If there is no password yet then we cannot connect */
if(!config->password) {
comms_error(c, "no password is configured");
+ c->enabled = 0;
return;
}
- start_connect(c);
+ /* Only try to connect if enabled */
+ if(c->enabled)
+ start_connect(c);
/* might now be state_disconnected (on error), state_connecting (slow
* connect) or state_connected (fast connect). If state_disconnected then
* we just rely on a periodic callback from the event loop sometime. */
D(("state_connected"));
/* We just connected. Initiate the authentication protocol. */
stash_command(c, 1/*queuejump*/, authbanner_opcallback,
- 0/*completed*/, 0/*v*/, 0/*cmd*/);
+ 0/*completed*/, 0/*v*/, -1/*nbody*/, 0/*body*/, 0/*cmd*/);
/* We never stay is state_connected very long. We could in principle jump
* straight to state_cmdresponse since there's actually no command to
* send, but that would arguably be cheating. */
if(c->authenticated) {
/* Transmit all unsent operations */
for(op = c->ops; op; op = op->next) {
- if(!op->sent) {
- put(c, op->cmd, strlen(op->cmd));
- op->sent = 1;
- }
+ if(!op->sent)
+ op_send(op);
}
} else {
/* Just send the head operation */
- if(c->ops->cmd && !c->ops->sent) {
- put(c, c->ops->cmd, strlen(c->ops->cmd));
- c->ops->sent = 1;
- }
+ if(c->ops->cmd && !c->ops->sent)
+ op_send(c->ops);
}
/* Awaiting response for the operation at the head of the list */
c->state = state_cmdresponse;
/* Queue up a byte to send */
if(c->state == state_log
&& c->output.nvec == 0
- && time(&now) - c->last_prod > LOG_PROD_INTERVAL) {
+ && xtime(&now) - c->last_prod > LOG_PROD_INTERVAL) {
put(c, "x", 1);
c->last_prod = now;
}
socklen_t len;
D(("start_connect"));
- if((len = find_server(&sa, &c->ident)) == (socklen_t)-1)
+ if((len = find_server(config, &sa, &c->ident)) == (socklen_t)-1)
return comms_error(c, "cannot look up server"); /* TODO better error */
if(c->fd != -1) {
xclose(c->fd);
return;
}
stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/,
+ -1/*nbody*/, 0/*body*/,
"user", quoteutf8(config->username), quoteutf8(res),
(char *)0);
}
if(c->rc / 100 != 2) {
/* Wrong password or something. We cannot proceed. */
protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ c->enabled = 0;
disorder_eclient_close(c);
return;
}
if(c->log_callbacks && !(c->ops && c->ops->opcallback == log_opcallback))
/* We are a log client, switch to logging mode */
stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, c->log_v,
+ -1/*nbody*/, 0/*body*/,
"log", (char *)0);
}
&& line[1] >= '0' && line[1] <= '9'
&& line[2] >= '0' && line[2] <= '9'
&& line[3] == ' '))
- fatal(0, "invalid response from server: %s", line);
+ disorder_fatal(0, "invalid response from server: %s", line);
c->rc = (line[0] * 10 + line[1]) * 10 + line[2] - 111 * '0';
c->line = line;
switch(c->rc % 10) {
operation_callback *opcallback,
void (*completed)(),
void *v,
+ int nbody,
+ char **body,
int ncmd,
char **cmd) {
struct operation *op = xmalloc(sizeof *op);
op->cmd = d.vec;
} else
op->cmd = 0; /* usually, awaiting challenge */
+ if(nbody >= 0) {
+ op->body = xcalloc(nbody + 1, sizeof (char *));
+ for(n = 0; n < nbody; ++n)
+ op->body[n] = xstrdup(body[n]);
+ op->body[n] = 0;
+ } else
+ op->body = NULL;
op->opcallback = opcallback;
op->completed = completed;
op->v = v;
operation_callback *opcallback,
void (*completed)(),
void *v,
+ int nbody,
+ char **body,
const char *cmd, va_list ap) {
char *arg;
struct vector vec;
while((arg = va_arg(ap, char *)))
vector_append(&vec, arg);
stash_command_vector(c, queuejump, opcallback, completed, v,
- vec.nvec, vec.vec);
+ nbody, body, vec.nvec, vec.vec);
} else
- stash_command_vector(c, queuejump, opcallback, completed, v, 0, 0);
+ stash_command_vector(c, queuejump, opcallback, completed, v,
+ nbody, body,
+ 0, 0);
}
static void stash_command(disorder_eclient *c,
operation_callback *opcallback,
void (*completed)(),
void *v,
+ int nbody,
+ char **body,
const char *cmd,
...) {
va_list ap;
va_start(ap, cmd);
- vstash_command(c, queuejump, opcallback, completed, v, cmd, ap);
+ vstash_command(c, queuejump, opcallback, completed, v, nbody, body, cmd, ap);
va_end(ap);
}
= (disorder_eclient_string_response *)op->completed;
D(("string_response_callback"));
- if(c->rc / 100 == 2 || c->rc == 555) {
- if(op->completed) {
+ if(completed) {
+ if(c->rc / 100 == 2 || c->rc == 555) {
if(c->rc == 555)
completed(op->v, NULL, NULL);
else if(c->protocol >= 2) {
completed(op->v, "error parsing response", NULL);
} else
completed(op->v, NULL, c->line + 4);
- }
- } else
- completed(op->v, errorstring(c), NULL);
+ } else
+ completed(op->v, errorstring(c), NULL);
+ }
}
/* for commands with a simple integer response */
= (disorder_eclient_no_response *)op->completed;
D(("no_response_callback"));
- if(c->rc / 100 == 2)
- completed(op->v, NULL);
- else
- completed(op->v, errorstring(c));
+ if(completed) {
+ if(c->rc / 100 == 2)
+ completed(op->v, NULL);
+ else
+ completed(op->v, errorstring(c));
+ }
}
/* error callback for queue_unmarshall */
for(n = 0; n < c->vec.nvec; ++n) {
q = xmalloc(sizeof *q);
D(("queue_unmarshall %s", c->vec.vec[n]));
- if(!queue_unmarshall(q, c->vec.vec[n], eclient_queue_error, op)) {
+ if(!queue_unmarshall(q, c->vec.vec[n], NULL, op)) {
q->prev = qlast;
*qtail = q;
qtail = &q->next;
switch(c->rc % 10) {
case 2:
if(queue_unmarshall(q = xmalloc(sizeof *q), c->line + 4,
- eclient_queue_error, c))
+ NULL, c))
completed(op->v, "cannot parse result", 0);
else
completed(op->v, 0, q);
/* for commands that expect a list of some sort */
static void list_response_opcallback(disorder_eclient *c,
struct operation *op) {
+ disorder_eclient_list_response *const completed =
+ (disorder_eclient_list_response *)op->completed;
+
D(("list_response_callback"));
- if(c->rc / 100 == 2) {
- if(op->completed)
- ((disorder_eclient_list_response *)op->completed)(op->v,
- c->vec.nvec,
- c->vec.vec);
- } else
- /* TODO don't use protocol_error here */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ if(c->rc / 100 == 2)
+ completed(op->v, NULL, c->vec.nvec, c->vec.vec);
+ else if(c->rc == 555)
+ completed(op->v, NULL, -1, NULL);
+ else
+ completed(op->v, errorstring(c), 0, 0);
}
/* for volume */
va_list ap;
va_start(ap, cmd);
- vstash_command(c, 0/*queuejump*/, opcallback, completed, v, cmd, ap);
+ vstash_command(c, 0/*queuejump*/, opcallback, completed, v, -1, 0, cmd, ap);
+ va_end(ap);
+ /* Give the state machine a kick, since we might be in state_idle */
+ disorder_eclient_polled(c, 0);
+ return 0;
+}
+
+static int simple_body(disorder_eclient *c,
+ operation_callback *opcallback,
+ void (*completed)(),
+ void *v,
+ int nbody,
+ char **body,
+ const char *cmd, ...) {
+ va_list ap;
+
+ va_start(ap, cmd);
+ vstash_command(c, 0/*queuejump*/, opcallback, completed, v, nbody, body, cmd, ap);
va_end(ap);
/* Give the state machine a kick, since we might be in state_idle */
disorder_eclient_polled(c, 0);
"play", track, (char *)0);
}
+int disorder_eclient_playafter(disorder_eclient *c,
+ const char *target,
+ int ntracks,
+ const char **tracks,
+ disorder_eclient_no_response *completed,
+ void *v) {
+ struct vector vec;
+ int n;
+
+ if(!target)
+ target = "";
+ vector_init(&vec);
+ vector_append(&vec, (char *)"playafter");
+ vector_append(&vec, (char *)target);
+ for(n = 0; n < ntracks; ++n)
+ vector_append(&vec, (char *)tracks[n]);
+ stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
+ -1, 0, vec.nvec, vec.vec);
+ disorder_eclient_polled(c, 0);
+ return 0;
+}
+
int disorder_eclient_pause(disorder_eclient *c,
disorder_eclient_no_response *completed,
void *v) {
for(n = 0; n < nids; ++n)
vector_append(&vec, (char *)ids[n]);
stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
- vec.nvec, vec.vec);
+ -1, 0, vec.nvec, vec.vec);
disorder_eclient_polled(c, 0);
return 0;
}
"nop", (char *)0);
}
+int disorder_eclient_get_global(disorder_eclient *c,
+ disorder_eclient_string_response *completed,
+ const char *pref,
+ void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v,
+ "get-global", pref, (char *)0);
+}
+
+int disorder_eclient_set_global(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *pref,
+ const char *value,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "set-global", pref, value, (char *)0);
+}
+
+int disorder_eclient_unset_global(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *pref,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "unset-global", pref, (char *)0);
+}
+
/** @brief Get the last @p max added tracks
* @param c Client
* @param completed Called with list
static void rtp_response_opcallback(disorder_eclient *c,
struct operation *op) {
+ disorder_eclient_list_response *const completed =
+ (disorder_eclient_list_response *)op->completed;
D(("rtp_response_opcallback"));
if(c->rc / 100 == 2) {
- if(op->completed) {
- int nvec;
- char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0);
+ int nvec;
+ char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0);
- ((disorder_eclient_list_response *)op->completed)(op->v, nvec, vec);
- }
+ if(vec)
+ completed(op->v, NULL, nvec, vec);
+ else
+ completed(op->v, "error parsing response", 0, 0);
} else
- /* TODO don't use protocol_error here */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ completed(op->v, errorstring(c), 0, 0);
}
/** @brief Determine the RTP target address
"adduser", user, password, rights, (char *)0);
}
+/** @brief Adopt a track
+ * @param c Client
+ * @param completed Called on completion
+ * @param id Track ID
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_adopt(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *id,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "adopt", id, (char *)0);
+}
+
+/** @brief Get the list of playlists
+ * @param c Client
+ * @param completed Called with list of playlists
+ * @param v Passed to @p completed
+ *
+ * The playlist list is not sorted in any particular order.
+ */
+int disorder_eclient_playlists(disorder_eclient *c,
+ disorder_eclient_list_response *completed,
+ void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v,
+ "playlists", (char *)0);
+}
+
+/** @brief Delete a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to delete
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_delete(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *playlist,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "playlist-delete", playlist, (char *)0);
+}
+
+/** @brief Lock a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to lock
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_lock(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *playlist,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "playlist-lock", playlist, (char *)0);
+}
+
+/** @brief Unlock the locked a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_unlock(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "playlist-unlock", (char *)0);
+}
+
+/** @brief Set a playlist's sharing
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to modify
+ * @param sharing @c "public" or @c "private"
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_set_share(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *playlist,
+ const char *sharing,
+ void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v,
+ "playlist-set-share", playlist, sharing, (char *)0);
+}
+
+/** @brief Get a playlist's sharing
+ * @param c Client
+ * @param completed Called with sharing status
+ * @param playlist Playlist to inspect
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_get_share(disorder_eclient *c,
+ disorder_eclient_string_response *completed,
+ const char *playlist,
+ void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v,
+ "playlist-get-share", playlist, (char *)0);
+}
+
+/** @brief Set a playlist
+ * @param c Client
+ * @param completed Called on completion
+ * @param playlist Playlist to modify
+ * @param tracks List of tracks
+ * @param ntracks Number of tracks
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_set(disorder_eclient *c,
+ disorder_eclient_no_response *completed,
+ const char *playlist,
+ char **tracks,
+ int ntracks,
+ void *v) {
+ return simple_body(c, no_response_opcallback, (void (*)())completed, v,
+ ntracks, tracks,
+ "playlist-set", playlist, (char *)0);
+}
+
+/** @brief Get a playlist's contents
+ * @param c Client
+ * @param completed Called with playlist contents
+ * @param playlist Playlist to inspect
+ * @param v Passed to @p completed
+ */
+int disorder_eclient_playlist_get(disorder_eclient *c,
+ disorder_eclient_list_response *completed,
+ const char *playlist,
+ void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v,
+ "playlist-get", playlist, (char *)0);
+}
+
/* Log clients ***************************************************************/
/** @brief Monitor the server log
if(c->log_callbacks->state)
c->log_callbacks->state(c->log_v, c->statebits);
stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, v,
- "log", (char *)0);
+ -1, 0, "log", (char *)0);
disorder_eclient_polled(c, 0);
return 0;
}
return;
}
/* TODO: do something with the time */
+ //fprintf(stderr, "log key: %s\n", vec[1]);
n = TABLE_FIND(logentry_handlers, name, vec[1]);
- if(n < 0) return; /* probably a future command */
+ if(n < 0) {
+ //fprintf(stderr, "...not found\n");
+ return; /* probably a future command */
+ }
vec += 2;
nvec -= 2;
- if(nvec < logentry_handlers[n].min || nvec > logentry_handlers[n].max)
+ if(nvec < logentry_handlers[n].min || nvec > logentry_handlers[n].max) {
+ //fprintf(stderr, "...wrong # args\n");
return;
+ }
logentry_handlers[n].handler(c, nvec, vec);
}
c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
}
+static void logentry_user_add(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->user_add)
+ c->log_callbacks->user_add(c->log_v, vec[0]);
+}
+
+static void logentry_user_confirm(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->user_confirm)
+ c->log_callbacks->user_confirm(c->log_v, vec[0]);
+}
+
+static void logentry_user_delete(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->user_delete)
+ c->log_callbacks->user_delete(c->log_v, vec[0]);
+}
+
+static void logentry_user_edit(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->user_edit)
+ c->log_callbacks->user_edit(c->log_v, vec[0], vec[1]);
+}
+
+static void logentry_rights_changed(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->rights_changed) {
+ rights_type r;
+ if(!parse_rights(vec[0], &r, 0/*report*/))
+ c->log_callbacks->rights_changed(c->log_v, r);
+ }
+}
+
+static void logentry_playlist_created(disorder_eclient *c,
+ int attribute((unused)) nvec,
+ char **vec) {
+ if(c->log_callbacks->playlist_created)
+ c->log_callbacks->playlist_created(c->log_v, vec[0], vec[1]);
+}
+
+static void logentry_playlist_deleted(disorder_eclient *c,
+ int attribute((unused)) nvec,
+ char **vec) {
+ if(c->log_callbacks->playlist_deleted)
+ c->log_callbacks->playlist_deleted(c->log_v, vec[0]);
+}
+
+static void logentry_playlist_modified(disorder_eclient *c,
+ int attribute((unused)) nvec,
+ char **vec) {
+ if(c->log_callbacks->playlist_modified)
+ c->log_callbacks->playlist_modified(c->log_v, vec[0], vec[1]);
+}
+
static const struct {
unsigned long bit;
const char *enable;
return d->vec;
}
+static void logentry_adopted(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->adopted)
+ c->log_callbacks->adopted(c->log_v, vec[0], vec[1]);
+}
+
+static void logentry_global_pref(disorder_eclient *c,
+ int nvec, char **vec) {
+ if(c->log_callbacks->global_pref)
+ c->log_callbacks->global_pref(c->log_v, vec[0], nvec > 1 ? vec[1] : 0);
+}
+
/*
Local Variables:
c-basic-offset:2