9e32ff oss mixer: Improve error message.
Simon Morlat <simon.morlat@linphone.org> (ortp)
-Christof Müller (bug reports)
+Christof Müller (bug reports)
M. Hari Nezumi <magenta@trikuare.cx> (AudioCompress)
Guillaume Outters <guillaume.outters@free.fr> (mosx-mpg123)
-Christian Reißmann (design)
+Christian Reißmann (design)
Gerrit Renker <gerrit@erg.abdn.ac.uk> (dccp improvements, IPv6 support)
CPPFLAGS += -I$(cmdline_dir)
CPPFLAGS += @osl_cppflags@
+LDFLAGS += @clock_gettime_ldflags@
+
man_pages := $(patsubst %, $(man_dir)/%.1, @executables@)
autocrap := config.h.in configure
para_recv: $(recv_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) $(recv_objs) -o $@ @recv_ldflags@
+ $(Q) $(CC) $(recv_objs) -o $@ @recv_ldflags@ $(LDFLAGS)
para_filter: $(filter_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) $(filter_objs) -o $@ @filter_ldflags@
+ $(Q) $(CC) $(filter_objs) -o $@ @filter_ldflags@ $(LDFLAGS)
para_client: $(client_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(client_objs) @client_ldflags@
+ $(Q) $(CC) -o $@ $(client_objs) @client_ldflags@ $(LDFLAGS)
para_gui: $(gui_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(gui_objs) -lcurses
+ $(Q) $(CC) -o $@ $(gui_objs) @gui_ldflags@ $(LDFLAGS)
para_audiod: $(audiod_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(audiod_objs) @audiod_ldflags@
+ $(Q) $(CC) -o $@ $(audiod_objs) @audiod_ldflags@ $(LDFLAGS)
para_audioc: $(audioc_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(audioc_objs) @audioc_ldflags@
+ $(Q) $(CC) -o $@ $(audioc_objs) @audioc_ldflags@ $(LDFLAGS)
para_fade: $(fade_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(fade_objs) @fade_ldflags@
+ $(Q) $(CC) -o $@ $(fade_objs) @fade_ldflags@ $(LDFLAGS)
para_server: $(server_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(server_objs) @server_ldflags@
+ $(Q) $(CC) -o $@ $(server_objs) @server_ldflags@ $(LDFLAGS)
para_write: $(write_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(write_objs) @write_ldflags@
+ $(Q) $(CC) -o $@ $(write_objs) @write_ldflags@ $(LDFLAGS)
para_afh: $(afh_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(afh_objs) @afh_ldflags@
+ $(Q) $(CC) -o $@ $(afh_objs) @afh_ldflags@ $(LDFLAGS)
para_play: $(play_objs)
@[ -z "$(Q)" ] || echo 'LD $@'
- $(Q) $(CC) $(LDFLAGS) -o $@ $(play_objs) @play_ldflags@
+ $(Q) $(CC) -o $@ $(play_objs) @play_ldflags@ $(LDFLAGS)
clean:
@[ -z "$(Q)" ] || echo 'CLEAN'
0.?.? (to be announced) "spectral gravity"
------------------------------------------
+ - UTF8 support for para_gui and the mp3 audio format handler.
+ - Scheduler improvements and fixes.
+ - The obsolete gettimeofday() function has been replaced
+ by clock_gettime() on systems which support it.
+ - Speed and usability improvements for para_gui.
+ - para_client now restores the fd flags of stdin and stdout
+ on shutdown
+
-----------------------------------------
0.4.12 (2012-12-20) "volatile relativity"
-----------------------------------------
fn->private_data = NULL;
}
-static void aacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int aacdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
size_t len, skip, consumed, loaded;
next_buffer:
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, (char **)&inbuf);
len = PARA_MIN(len, (size_t)8192);
}
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
/**
/** \file afh.c Paraslash's standalone audio format handler tool. */
#include <regex.h>
-#include <sys/time.h>
#include "para.h"
#include "string.h"
/** \file afh_common.c Common audio format handler functions. */
#include <sys/mman.h> /* mmap */
-#include <sys/time.h> /* gettimeofday */
#include <sys/types.h>
#include <regex.h>
sched_request_barrier_or_min_delay(&chunk_time, s);
}
-static void afh_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int afh_recv_post_select(__a_unused struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct afh_recv_args_info *conf = rn->conf;
btr_remove_node(&rn->btrn);
pard->current_chunk = pard->first_chunk;
}
- t->error = ret;
+ return ret;
}
/**
para_fd_set(st->fd, &s->rfds, &s->max_fileno);
}
-static void afs_signal_post_select(struct sched *s, struct task *t)
+static int afs_signal_post_select(struct sched *s, __a_unused struct task *t)
{
- int signum;
+ int signum, ret;
if (getppid() == 1) {
PARA_EMERG_LOG("para_server died\n");
}
signum = para_next_signal(&s->rfds);
if (signum == 0)
- return;
+ return 0;
if (signum == SIGHUP) {
close_afs_tables();
parse_config_or_die(1);
- t->error = open_afs_tables();
- if (t->error < 0)
- return;
+ ret = open_afs_tables();
+ if (ret < 0)
+ return ret;
init_admissible_files(current_mop);
- return;
+ return 0;
}
PARA_EMERG_LOG("terminating on signal %d\n", signum);
shutdown:
task_notify_all(s, E_AFS_SIGNAL);
- t->error = -E_AFS_SIGNAL;
+ return -E_AFS_SIGNAL;
}
static void register_signal_task(struct sched *s)
/** Shutdown connection if query has not arrived until this many seconds. */
#define AFS_CLIENT_TIMEOUT 3
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
struct command_task *ct = container_of(t, struct command_task, task);
struct sockaddr_un unix_addr;
int fd, ret;
ret = task_get_notification(t);
- if (ret < 0) {
- t->error = ret;
- return;
- }
+ if (ret < 0)
+ return ret;
ret = execute_server_command(&s->rfds);
if (ret < 0) {
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
task_notify_all(s, -ret);
- t->error = ret;
- return;
+ return ret;
}
/* Check the list of connected clients. */
list_for_each_entry_safe(client, tmp, &afs_client_list, node) {
if (ret < 0)
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret <= 0)
- return;
+ return 0;
ret = mark_fd_nonblocking(fd);
if (ret < 0) {
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
close(fd);
- return;
+ return 0;
}
client = para_malloc(sizeof(*client));
client->fd = fd;
client->connect_time = *now;
para_list_add(&client->node, &afs_client_list);
+ return 0;
}
static void register_command_task(uint32_t cookie, struct sched *s)
H: no amplification, 64 means the amplitude should be multiplied
H: by a factor of two, 128 by three and so on.
H:
-H: This value is used by the compress filter.
+H: This value is used by the amp filter.
H:
H: -v Verbose mode. Explain what is being done.
H:
#include <regex.h>
#include <sys/types.h>
#include <alsa/asoundlib.h>
-#include <sys/time.h>
#include "para.h"
#include "fd.h"
goto fail;
ret = snd_output_buffer_open(&output_log);
if (ret == 0) {
- char *buf;
+ char *buf, *p;
+ size_t sz;
PARA_INFO_LOG("dumping alsa configuration\n");
snd_pcm_dump(pad->handle, output_log);
- snd_output_buffer_string(output_log, &buf);
- for (;;) {
- char *p = strchr(buf, '\n');
- if (!p) /* omit last output line, it's empty */
+ sz = snd_output_buffer_string(output_log, &buf);
+ for (p = buf; p < buf + sz;) {
+ char *q = memchr(p, '\n', buf + sz - p);
+ if (!q)
break;
- *p = '\0';
- PARA_INFO_LOG("%s\n", buf);
- buf = p + 1;
+ *q = '\0';
+ PARA_INFO_LOG("%s\n", p);
+ p = q + 1;
}
snd_output_close(output_log);
}
free(pad);
}
-static void alsa_write_post_select(__a_unused struct sched *s,
+static int alsa_write_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
again:
ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, wn->min_iqs);
bytes = btr_next_buffer(btrn, &data);
if (ret < 0 || bytes < wn->min_iqs) { /* eof */
PARA_DEBUG_LOG("waiting for device to drain\n");
tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
&pad->drain_barrier);
- return;
+ return 0;
}
if (tv_diff(now, &pad->drain_barrier, NULL) > 0)
goto err;
- return;
+ return 0;
}
if (!pad) {
int32_t val;
if (bytes == 0) /* no data available */
- return;
+ return 0;
pad = para_calloc(sizeof(*pad));
get_btr_sample_rate(btrn, &val);
pad->sample_rate = val;
goto again;
}
if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
- return;
+ return 0;
frames = bytes / pad->bytes_per_frame;
frames = snd_pcm_writei(pad->handle, data, frames);
if (frames == 0 || frames == -EAGAIN) {
char buf[100];
if (read(pad->poll_fd, buf, 100))
do_nothing;
- return;
+ return 0;
}
if (frames > 0) {
btr_consume(btrn, frames * pad->bytes_per_frame);
if (frames == -EPIPE) {
PARA_WARNING_LOG("underrun (tried to write %zu bytes)\n", bytes);
snd_pcm_prepare(pad->handle);
- return;
+ return 0;
}
PARA_ERROR_LOG("alsa write error: %s\n", snd_strerror(-frames));
ret = -E_ALSA;
err:
assert(ret < 0);
btr_remove_node(&wn->btrn);
- t->error = ret;
+ return ret;
}
__malloc static void *alsa_parse_config_or_die(int argc, char **argv)
pad->amp, pad->amp / 64.0 + 1.0);
}
-static void amp_post_select(__a_unused struct sched *s, struct task *t)
+static int amp_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_amp_data *pad = fn->private_data;
bool inplace = btr_inplace_ok(btrn);
if (pad->amp == 0) { /* no amplification */
- t->error = -E_AMP_ZERO_AMP;
btr_splice_out_node(btrn);
- return;
+ return -E_AMP_ZERO_AMP;
}
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
in_bytes = btr_next_buffer(btrn, (char **)&in);
len = in_bytes / 2;
btr_consume(btrn, len * 2);
btr_add_output((char *)out, len * 2, btrn);
}
- t->error = 0;
goto next_buffer;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void amp_free_config(void *conf)
return -E_AO_PTHREAD;
}
-static void aow_post_select(__a_unused struct sched *s,
+static int aow_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
if (ret < 0)
goto remove_btrn;
if (ret == 0)
- return;
+ return 0;
get_btr_sample_rate(wn->btrn, &rate);
get_btr_channels(wn->btrn, &ch);
get_btr_sample_format(wn->btrn, &format);
ret = aow_create_thread(wn);
if (ret < 0)
goto remove_thread_btrn;
- return;
+ return 0;
}
pthread_mutex_lock(&pawd->mutex);
ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
remove_btrn:
btr_remove_node(&wn->btrn);
out:
- t->error = ret;
+ return ret;
}
__malloc static void *aow_parse_config_or_die(int argc, char **argv)
para_fd_set(at->fd, &s->rfds, &s->max_fileno);
}
-static void audioc_post_select(struct sched *s, struct task *t)
+static int audioc_post_select(struct sched *s, struct task *t)
{
char *buf = NULL;
struct audioc_task *at = container_of(t, struct audioc_task, task);
if (ret < 0)
goto out;
if (!FD_ISSET(at->fd, &s->rfds))
- return;
+ return 0;
buf = para_malloc(conf.bufsize_arg);
ret = recv_bin_buffer(at->fd, buf, conf.bufsize_arg);
PARA_DEBUG_LOG("recv: %d\n", ret);
if (ret < 0)
goto out;
btr_add_output(buf, ret, at->btrn);
- return;
+ return 0;
out:
if (ret < 0) {
free(buf);
btr_remove_node(&at->btrn);
close(at->fd);
}
- t->error = ret;
+ return ret;
}
static struct audioc_task audioc_task = {
fn->conf = a->filter_conf[i];
fn->task.pre_select = f->pre_select;
fn->task.post_select = f->post_select;
-
fn->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = f->name, .parent = parent,
.handler = f->execute, .context = fn));
para_fd_set(st->fd, &s->rfds, &s->max_fileno);
}
-static void signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused struct task *t)
{
int signum;
PARA_EMERG_LOG("terminating on signal %d\n", signum);
clean_exit(EXIT_FAILURE, "caught deadly signal");
}
+ return 0;
}
static void signal_setup_default(struct signal_task *st)
para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
}
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
int ret;
struct command_task *ct = container_of(t, struct command_task, task);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
audiod_status_dump();
+ return 0;
}
static void init_command_task(struct command_task *ct)
tv_add(now, &delay, &stat_task->restart_barrier);
}
-static void try_to_close_slot(int slot_num)
+static bool must_close_slot(int slot_num)
{
struct slot_info *s = &slot[slot_num];
struct audio_format_info *a = afi + s->format;
int i;
if (s->format < 0)
- return;
+ return false;
if (s->receiver_node && s->receiver_node->task.error >= 0)
- return;
+ return false;
for (i = 0; i < a->num_filters; i++)
if (s->fns && s->fns[i].task.error >= 0)
- return;
+ return false;
if (a->num_writers > 0) {
for (i = 0; i < a->num_writers; i++)
if (s->wns && s->wns[i].task.error >= 0)
- return;
+ return false;
} else {
if (s->wns && s->wns[0].task.error >= 0)
- return;
+ return false;
+ }
+ return true;
+}
+
+static void close_unused_slots(void)
+{
+ int i;
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = slot + i;
+ if (!must_close_slot(i))
+ continue;
+ PARA_INFO_LOG("closing slot %d\n", i);
+ close_writers(s);
+ close_filters(s);
+ close_receiver(i);
+ clear_slot(i);
}
- PARA_INFO_LOG("closing slot %d\n", slot_num);
- close_writers(s);
- close_filters(s);
- close_receiver(slot_num);
- clear_slot(slot_num);
}
/*
*/
static void start_stop_decoders(void)
{
- int i, ret;
+ int ret;
struct slot_info *sl;
struct audio_format_info *a;
- FOR_EACH_SLOT(i)
- try_to_close_slot(i);
+ close_unused_slots();
if (audiod_status != AUDIOD_ON ||
!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
return notify_receivers(E_NOT_PLAYING);
static void status_pre_select(struct sched *s, struct task *t)
{
struct status_task *st = container_of(t, struct status_task, task);
- int ret, cafn = stat_task->current_audio_format_num;
+ int i, ret, cafn = stat_task->current_audio_format_num;
if (must_start_decoder())
goto min_delay;
+ FOR_EACH_SLOT(i)
+ if (must_close_slot(i))
+ goto min_delay;
ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
if (ret > 0)
goto min_delay;
}
/* restart the client task if necessary */
-static void status_post_select(struct sched *s, struct task *t)
+static int status_post_select(struct sched *s, struct task *t)
{
struct status_task *st = container_of(t, struct status_task, task);
st->last_status_read = *now;
out:
start_stop_decoders();
+ return 0;
}
static void init_status_task(struct status_task *st)
/** Length of a standard wav header. */
#define WAV_HEADER_LEN 44
+/** The possible states of a check_wav instance. */
enum check_wav_state {
+ /** Initial state, less than \p WAV_HEADER_LEN bytes available. */
CWS_NEED_HEADER,
+ /** Wav hader was detected. */
CWS_HAVE_HEADER,
+ /** First part of the stream did not look like a wav header. */
CWS_NO_HEADER,
};
unsigned sample_rate;
};
+/**
+ * Set select timeout according to the given context.
+ *
+ * \param s Contains the timeval that should be set.
+ * \param cwc Contains a pointer to the buffer tree node.
+ *
+ * This requests a minimal timeout from the scheduler if btrn of \a cwc is not
+ * idle.
+ */
void check_wav_pre_select(struct sched *s, struct check_wav_context *cwc)
{
int ret = btr_node_status(cwc->btrn, cwc->min_iqs, BTR_NT_INTERNAL);
return 1;
}
+/**
+ * Filter out the wav header, pushdown everything else.
+ *
+ * \param cwc The context of this instance.
+ *
+ * This function looks at the first \p WAV_HEADER_SIZE bytes of the input queue
+ * of the btrn of \a cwc. If they look like a wav header, the function extracts
+ * the information of interest and swallows this part of the stream. Otherwise
+ * it is pushed down to all children. In either case the rest of the input is
+ * pushed down as well.
+ *
+ * Once the first part has been processed this way, the state of the instance
+ * changes from \p CWS_NEED_HEADER to \p CWS_HAVE_HEADER or \p CWS_NO_HEADER.
+ *
+ * \return Standard.
+ */
int check_wav_post_select(struct check_wav_context *cwc)
{
struct btr_node *btrn = cwc->btrn;
return ret;
}
+/**
+ * Allocate and set up a new check_wav instance.
+ *
+ * \param parent This buffer tree node will be the parent of the new node.
+ * \param child The child of the new node.
+ * \param params Default values and options.
+ * \param cw_btrn A pointer to the check wav node is returned here.
+ *
+ * This function also sets up the ->execute handler of the btrn so that all
+ * children of this node can figure out channel count, sample rate, etc.
+ *
+ * \return The (opaque) handle of the newly created check_wav instance. It is
+ * supposed to be passed to \ref check_wav_pre_select() and \ref
+ * check_wav_post_select().
+ *
+ * \sa \ref btr_new_node.
+ */
struct check_wav_context *check_wav_init(struct btr_node *parent,
struct btr_node *child, struct wav_params *params,
struct btr_node **cw_btrn)
return cwc;
}
+/**
+ * Dellocate all ressources of a check_wav instance.
+ *
+ * \param cwc Determines the instance to shut down.
+ *
+ * This function may only be called after check_wav_post_select() has returned
+ * negative.
+ */
void check_wav_shutdown(struct check_wav_context *cwc)
{
free(cwc);
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file check_wav.h Detect, process and cut a wav header. */
+
struct check_wav_context;
/**
sched_min_delay(s);
}
-static void exec_post_select(__a_unused struct sched *s, struct task *t)
+static int exec_post_select(__a_unused struct sched *s, struct task *t)
{
struct exec_task *et = container_of(t, struct exec_task, task);
struct btr_node *btrn = et->btrn;
int ret;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
- if (ret <= 0) {
- t->error = ret;
- return;
- }
+ if (ret <= 0)
+ return ret;
sz = btr_next_buffer(btrn, &buf);
if (sz <= 1)
goto out;
et->result_buf[et->result_size - 1] = '\0';
out:
btr_consume(btrn, sz);
+ return 0;
}
static int make_client_argv(const char *line)
#endif /* HAVE_READLINE */
-static void supervisor_post_select(struct sched *s, struct task *t)
+static int supervisor_post_select(struct sched *s, __a_unused struct task *t)
{
- if (ct->task.error < 0) {
- t->error = ct->task.error;
- return;
- }
+ if (ct->task.error < 0)
+ return ct->task.error;
if (ct->status == CL_SENDING) {
stdin_set_defaults(&sit);
register_task(s, &sit.task);
- t->error = -E_TASK_STARTED;
- return;
+ return -E_TASK_STARTED;
}
if (ct->status == CL_RECEIVING) {
stdout_set_defaults(&sot);
register_task(s, &sot.task);
- t->error = -E_TASK_STARTED; return;
+ return -E_TASK_STARTED;
}
+ return 0;
}
static struct task svt = {
*
* \sa struct sched, struct task.
*/
-static void client_post_select(struct sched *s, struct task *t)
+static int client_post_select(struct sched *s, struct task *t)
{
struct client_task *ct = container_of(t, struct client_task, task);
struct btr_node *btrn = ct->btrn;
if (ret < 0)
goto out;
if (ct->scc.fd < 0)
- return;
+ return 0;
switch (ct->status) {
case CL_CONNECTED: /* receive welcome message */
ret = client_recv_buffer(ct, &s->rfds, buf, sizeof(buf), &n);
goto out;
ct->features = parse_features(buf);
ct->status = CL_RECEIVED_WELCOME;
- return;
+ return 0;
case CL_RECEIVED_WELCOME: /* send auth command */
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
if (has_feature("sideband", ct)) {
ct->use_sideband = true;
sprintf(buf, AUTH_REQUEST_MSG "%s sideband", ct->user);
if (ret < 0)
goto out;
ct->status = CL_SENT_AUTH;
- return;
+ return 0;
case CL_SENT_AUTH:
/*
* Receive challenge and session keys, decrypt the challenge and
hash_to_asc(ct->challenge_hash, buf);
PARA_INFO_LOG("--> %s\n", buf);
ct->status = CL_RECEIVED_CHALLENGE;
- return;
+ return 0;
}
case CL_RECEIVED_CHALLENGE:
if (ct->use_sideband) {
if (!strstr(buf, PROCEED_MSG))
goto out;
ct->status = CL_RECEIVED_PROCEED;
- return;
+ return 0;
}
case CL_RECEIVED_PROCEED: /* concat args and send command */
{
int i;
char *command = NULL;
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
if (ct->use_sideband) {
ret = send_sb_command(ct);
if (ret <= 0)
goto out;
ct->status = CL_SENT_COMMAND;
- return;
+ return 0;
}
for (i = 0; i < ct->conf.inputs_num; i++) {
char *tmp = command;
if (ret < 0)
goto out;
ct->status = CL_SENT_COMMAND;
- return;
+ return 0;
}
case CL_SENT_COMMAND:
{
if (strstr(buf2, AWAITING_DATA_MSG)) {
free(buf2);
ct->status = CL_SENDING;
- return;
+ return 0;
}
ct->status = CL_RECEIVING;
btr_add_output(buf2, n, btrn);
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
if (!FD_ISSET(ct->scc.fd, &s->wfds))
- return;
+ return 0;
sz = btr_next_buffer(btrn, &buf2);
ret = sc_send_bin_buffer(&ct->scc, buf2, sz);
if (ret < 0)
goto out;
btr_consume(btrn, sz);
- return;
+ return 0;
}
case CL_RECEIVING:
{
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
/*
* The FD_ISSET() is not strictly necessary, but is allows us
* to skip the malloc below if there is nothing to read anyway.
*/
if (!FD_ISSET(ct->scc.fd, &s->rfds))
- return;
+ return 0;
if (ct->use_sideband) {
struct sb_buffer sbb;
ret = recv_sb(ct, &s->rfds, &sbb);
}
}
out:
- t->error = ret;
if (ret < 0) {
if (!ct->use_sideband && ret != -E_SERVER_EOF &&
ret != -E_BTR_EOF && ret != -E_EOF)
- PARA_ERROR_LOG("%s\n", para_strerror(-t->error));
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
btr_remove_node(&ct->btrn);
}
+ return ret;
}
/**
#include <regex.h>
#include <signal.h>
-#include <sys/time.h>
#include <sys/types.h>
#include <osl.h>
localtime_r(&nmmd->mtime, &mtime_tm);
strftime(mtime, 29, "%b %d %Y", &mtime_tm);
}
- gettimeofday(¤t_time, NULL);
+ clock_get_realtime(¤t_time);
/*
* The calls to WRITE_STATUS_ITEM() below never fail because
* b->max_size is zero (unlimited), see para_printf(). However, clang
free(fn->private_data);
}
-static void compress_post_select(__a_unused struct sched *s, struct task *t)
+static int compress_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_compress_data *pcd = fn->private_data;
//inplace = false;
next_buffer:
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
length = btr_next_buffer(btrn, &inbuf) & ~(size_t)1;
if (length == 0) { /* eof and 1 byte available */
goto next_buffer;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
/** TODO: Add sanity checks */
client_ldflags=""
gui_cmdline_objs="add_cmdline(gui)"
-gui_errlist_objs="exec signal string stat ringbuffer fd gui gui_theme"
+gui_errlist_objs="exec signal string stat ringbuffer fd gui gui_theme time"
gui_objs="$gui_cmdline_objs $gui_errlist_objs"
play_errlist_objs="play fd sched ggo buffer_tree time string net
afh_recv afh_common
AC_MSG_ERROR([fatal: buggy snprintf() detected])
fi])
AX_FUNC_SNPRINTF()
+################################################################## clock_gettime
+clock_gettime_lib=
+AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
+ AC_CHECK_LIB([rt], [clock_gettime], [clock_gettime_lib=rt], [], [])
+])
+if test -n "$clock_gettime_lib"; then
+ AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [
+ define to 1 if clock_gettime() is supported])
+fi
+if test "$clock_gettime_lib" = "rt"; then
+ AC_SUBST(clock_gettime_ldflags, -lrt)
+fi
########################################################################### osl
have_osl=yes
OLD_CPPFLAGS="$CPPFLAGS"
AC_CHECK_HEADER(curses.h, [], [
have_curses="no"
])
-AC_CHECK_LIB([curses], [initscr], [], [
- have_curses="no"
-])
+gui_ldflags="$curses_libs"
+AC_CHECK_LIB([ncursesw], [initscr],
+ [gui_ldflags="$curses_libs -lncursesw"], [
+ AC_CHECK_LIB([curses], [initscr],
+ [gui_ldflags="$curses_libs -lcurses"],
+ [have_curses="no"]
+ )
+ ]
+)
if test "$have_curses" = "yes"; then
AC_SUBST(curses_cppflags)
- AC_DEFINE(HAVE_NCURSES, 1, [define to 1 to turn on curses support])
extras="$extras gui"
executables="$executables gui"
else
objlist_to_errlist($audioc_errlist_objs), errors used by para_audioc)
AC_SUBST(gui_objs, add_dot_o($gui_objs))
+AC_SUBST(gui_ldflags, $gui_ldflags)
AC_DEFINE_UNQUOTED(INIT_GUI_ERRLISTS,
objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
AC_DEFINE_UNQUOTED(INIT_PLAY_ERRLISTS,
objlist_to_errlist($play_errlist_objs), errors used by para_play)
-AC_MSG_NOTICE(play objs: $play_objs)
-
enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
enum of supported filters)
#include <pwd.h>
#include <sys/types.h> /* getgrnam() */
#include <grp.h>
-#include <sys/time.h>
#include <signal.h>
#include "para.h"
fprintf(fp, "%s", color);
if (log_time || log_timing) {
struct timeval tv;
- gettimeofday(&tv, NULL);
+ clock_get_realtime(&tv);
if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */
time_t t1 = tv.tv_sec;
char str[100];
para_fd_set(rn->fd, &s->rfds, &s->max_fileno);
}
-static void dccp_recv_post_select(struct sched *s, struct task *t)
+static int dccp_recv_post_select(struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct btr_node *btrn = rn->btrn;
btr_add_output_pool(rn->btrp, num_bytes - iov[0].iov_len, btrn);
}
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
+ if (ret < 0)
+ btr_remove_node(&rn->btrn);
+ return ret;
}
static void dccp_recv_free_config(void *conf)
fn->private_data = NULL;
}
-static void fecdec_post_select(__a_unused struct sched *s, struct task *t)
+static int fecdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
btr_consume(btrn, FEC_HEADER_SIZE + h.slice_bytes);
goto next_buffer;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
static void fecdec_open(struct filter_node *fn)
#include <regex.h>
#include <sys/types.h>
-#include <sys/time.h>
#include "para.h"
#include "list.h"
__must_check __malloc static char *random_filename(void)
{
char *result, *home = para_homedir();
- struct timeval tv;
- gettimeofday(&tv, NULL);
- srandom(tv.tv_usec);
+ srandom(clock_get_realtime(NULL)->tv_usec);
result = make_message("%s/.paraslash/%08lu", home,
para_random(99999999));
free(home);
free(pfwd);
}
-static void file_write_post_select(__a_unused struct sched *s,
+static int file_write_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
goto out;
}
if (!FD_ISSET(pfwd->fd, &s->wfds))
- return;
+ return 0;
bytes = btr_next_buffer(btrn, &buf);
assert(bytes > 0);
//PARA_INFO_LOG("writing %zu\n", bytes);
- ret = write(pfwd->fd, buf, bytes);
+ ret = xwrite(pfwd->fd, buf, bytes);
if (ret < 0)
goto out;
btr_consume(btrn, ret);
out:
if (ret < 0)
btr_remove_node(&wn->btrn);
- t->error = ret;
+ return ret;
}
__malloc static void *file_write_parse_config_or_die(int argc, char **argv)
* post_select function is supposed to set t->error to a (negative)
* error code.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/**
* Answer a buffer tree query.
*
}
/**
- * Set select timeout of the the scheduler.
+ * Set select timeout of the scheduler.
*
* \param s The scheduler.
* \param t The task struct of this filter.
*
* This looks at the status of the btr node of the filter. If data is available
- * in the input queue of the filter, or if an error occured, a minimal timeout
+ * in the input queue of the filter, or if an error occurred, a minimal timeout
* for the next select call is requested from the scheduler. Otherwise the
* scheduler timeout is left unchanged.
*/
return sched_min_delay(s);
}
-static void flacdec_post_select(__a_unused struct sched *s, struct task *t)
+static int flacdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_flacdec_data *pfd = fn->private_data;
int ret;
if (output_queue_full(btrn))
- return;
+ return 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
goto out;
fn->min_iqs = 0;
ret = 1;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
static void flacdec_close(struct filter_node *fn)
* We need this forward declaration as post_select() needs
* activate_grab_client and vice versa.
*/
-static void gc_post_select(struct sched *s, struct task *t);
+static int gc_post_select(struct sched *s, struct task *t);
/**
* Move a grab client to the active list and start it.
return 0;
}
-static void gc_post_select(__a_unused struct sched *s, struct task *t)
+static int gc_post_select(__a_unused struct sched *s, struct task *t)
{
struct grab_client *gc = container_of(t, struct grab_client, task);
struct btr_node *btrn = gc->btrn;
size_t sz;
char *buf;
- t->error = 0;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
if (ret < 0)
goto err;
sz = btr_next_buffer(btrn, &buf);
goto err;
if (ret > 0)
btr_consume(btrn, ret);
- return;
+ return 0;
err:
gc_close(gc, ret);
- t->error = ret;
+ return ret;
}
static int gc_check_args(int argc, char **argv, struct grab_client *gc)
#include <signal.h>
#include <sys/types.h>
#include <curses.h>
+#include <locale.h>
+#include <sys/time.h>
#include "gui.cmdline.h"
#include "para.h"
static unsigned scroll_position;
-static int cmd_died, curses_active;
+static int curses_active;
static pid_t cmd_pid;
static int command_fds[2];
return file_exists(tmp)? tmp: NULL;
}
-/*
- * print num spaces to curses window
- */
+/* Print given number of spaces to curses window. */
static void add_spaces(WINDOW* win, unsigned int num)
{
- while (num > 0) {
- num--;
- waddstr(win, " ");
+ char space[] = " ";
+ unsigned sz = sizeof(space);
+
+ while (num >= sz) {
+ waddstr(win, space);
+ num -= sz;
+ }
+ if (num > 0) {
+ assert(num < sz);
+ space[num] = '\0';
+ waddstr(win, space);
}
}
static int align_str(WINDOW* win, char *str, unsigned int len,
unsigned int align)
{
- int i, num; /* of spaces */
+ int ret, i, num; /* of spaces */
+ size_t width;
if (!win || !str)
- return -1;
- num = len - strlen(str);
+ return 0;
+ ret = strwidth(str, &width);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ width = 0;
+ str[0] = '\0';
+ }
+ num = len - width;
if (num < 0) {
str[len] = '\0';
num = 0;
return RINGBUFFER_SIZE - 1;
}
+/*
+returns number of first visible rbe, *lines is the number of lines drawn.
+ */
static int draw_top_rbe(unsigned *lines)
{
- unsigned len;
- int offset, fvr = first_visible_rbe(lines);
+ int ret, fvr = first_visible_rbe(lines);
struct rb_entry *rbe;
+ size_t bytes_to_skip, cells_to_skip, width;
if (fvr < 0)
return -1;
rbe = ringbuffer_get(bot_win_rb, fvr);
if (!rbe)
return -1;
- len = strlen(rbe->msg);
if (*lines > bot.lines) {
- /* first rbe is only partially visible */
- offset = (*lines - bot.lines) * bot.cols;
- assert(offset <= len);
- } else
- offset = 0;
+ /* rbe is partially visible multi-line */
+ cells_to_skip = (*lines - bot.lines) * bot.cols;
+ ret = skip_cells(rbe->msg, cells_to_skip, &bytes_to_skip);
+ if (ret < 0)
+ return ret;
+ ret = strwidth(rbe->msg + bytes_to_skip, &width);
+ if (ret < 0)
+ return ret;
+ } else {
+ bytes_to_skip = 0;
+ width = rbe->len;
+ }
wattron(bot.win, COLOR_PAIR(rbe->color));
- waddstr(bot.win, rbe->msg + offset);
- *lines = NUM_LINES(len - offset);
+ waddstr(bot.win, rbe->msg + bytes_to_skip);
+ *lines = NUM_LINES(width);
return fvr;
}
static void rb_add_entry(int color, char *msg)
{
- struct rb_entry *old, *new = para_malloc(sizeof(struct rb_entry));
+ struct rb_entry *old, *new;
int x, y;
+ size_t len;
+
+ if (strwidth(msg, &len) < 0)
+ return;
+ new = para_malloc(sizeof(struct rb_entry));
new->color = color;
- new->len = strlen(msg);
+ new->len = len;
new->msg = msg;
old = ringbuffer_add(bot_win_rb, new);
// fprintf(stderr, "added: %s\n", new->msg);
para_sigaction(SIGHUP, SIG_IGN);
}
-__noreturn static void do_exit(int ret)
+/* kill every process in the process group and exit */
+__noreturn static void kill_pg_and_die(int ret)
{
para_sigaction(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
__noreturn static void finish(int ret)
{
shutdown_curses();
- do_exit(ret);
+ kill_pg_and_die(ret);
}
/*
va_start(argp, fmt);
vfprintf(outfd, fmt, argp);
va_end(argp);
- do_exit(ret);
+ kill_pg_and_die(ret);
}
static void print_welcome(void)
ret = para_reap_child(&pid);
if (ret <= 0)
return;
- if (pid == cmd_pid) {
+ if (pid == cmd_pid)
cmd_pid = 0;
- cmd_died = 1;
- }
goto reap_next_child;
}
}
}
-static int open_stat_pipe(void)
+static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv)
{
- static int init = 1;
+ static struct timeval next_exec, atm, diff;
int ret, fds[3] = {0, 1, 0};
pid_t pid;
- if (init)
- init = 0;
- else
- /*
- * Sleep a bit to avoid a busy loop. As the call to sleep() may
- * be interrupted by SIGCHLD, we simply wait until the call
- * succeeds.
- */
- while (sleep(2))
- ; /* nothing */
+ if (stat_pipe >= 0)
+ goto success;
+ /* Avoid busy loop */
+ gettimeofday(&atm, NULL);
+ if (tv_diff(&next_exec, &atm, &diff) > 0) {
+ if (tv_diff(&diff, tv, NULL) < 0)
+ *tv = diff;
+ return;
+ }
+ next_exec.tv_sec = atm.tv_sec + 2;
ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
if (ret < 0)
- return ret;
+ return;
ret = mark_fd_nonblocking(fds[1]);
- if (ret >= 0)
- return fds[1];
- close(fds[1]);
- return ret;
+ if (ret < 0) {
+ close(fds[1]);
+ return;
+ }
+ stat_pipe = fds[1];
+success:
+ para_fd_set(stat_pipe, rfds, max_fileno);
}
-#define COMMAND_BUF_SIZE 4096
+#define COMMAND_BUF_SIZE 32768
/*
* This is the core select loop. Besides the (internal) signal
char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
int cbo[2] = {0, 0}; /* command buf offsets */
struct timeval tv;
+ unsigned flags[2] = {0, 0}; /* for for_each_line() */
repeat:
tv.tv_sec = conf.timeout_arg / 1000;
// ret = refresh_status();
FD_ZERO(&rfds);
max_fileno = 0;
- if (stat_pipe < 0)
- stat_pipe = open_stat_pipe();
- if (stat_pipe >= 0)
- para_fd_set(stat_pipe, &rfds, &max_fileno);
+ status_pre_select(&rfds, &max_fileno, &tv);
/* signal pipe */
para_fd_set(signal_pipe, &rfds, &max_fileno);
/* command pipe only for COMMAND_MODE */
if (command_fds[1] >= 0)
para_fd_set(command_fds[1], &rfds, &max_fileno);
}
+ if (mode == GETCH_MODE || mode == COMMAND_MODE)
+ para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
if (ret <= 0)
goto check_return; /* skip fd checks */
COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
cbo[i] += sz;
sz = cbo[i];
- cbo[i] = for_each_line(command_buf[i], cbo[i],
+ cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
add_output_line, &i);
- if (sz != cbo[i])
+ if (sz != cbo[i]) { /* at least one line found */
wrefresh(bot.win);
+ flags[i] = 0;
+ }
if (ret < 0) {
PARA_NOTICE_LOG("closing command fd %d: %s",
i, para_strerror(-ret));
close(command_fds[i]);
command_fds[i] = -1;
+ flags[i] = 0;
if (command_fds[!i] < 0) /* both fds closed */
return 0;
}
+ if (cbo[i] == COMMAND_BUF_SIZE - 1) {
+ PARA_NOTICE_LOG("discarding overlong line");
+ cbo[i] = 0;
+ flags[i] = FELF_DISCARD_FIRST;
+ }
}
}
ret = read_stat_pipe(&rfds);
return ret;
break;
case EXTERNAL_MODE:
- if (cmd_died) {
- cmd_died = 0;
+ if (cmd_pid == 0)
return 0;
- }
}
goto repeat;
}
shutdown_curses();
if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
return;
- cmd_died = 0;
do_select(EXTERNAL_MODE);
init_curses();
}
struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
if (!rbe)
break;
- lines += NUM_LINES(strlen(rbe->msg));
+ lines += NUM_LINES(rbe->len);
}
i++;
if (lines > 0 && scroll_position != i) {
struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
if (!rbe)
break;
- lines += NUM_LINES(strlen(rbe->msg));
+ lines += NUM_LINES(rbe->len);
}
if (lines) {
scroll_position = i;
top.lines = theme.top_lines_default;
setup_signal_handling();
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
+ setlocale(LC_CTYPE, "");
initscr(); /* needed only once, always successful */
init_curses();
print_welcome();
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct private_http_recv_data *phd = rn->private_data;
- t->error = 0;
if (generic_recv_pre_select(s, t) <= 0)
return;
if (phd->status == HTTP_CONNECTED)
* area with data read from the socket. In any case, update the state of the
* connection if necessary.
*/
-static void http_recv_post_select(struct sched *s, struct task *t)
+static int http_recv_post_select(struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct private_http_recv_data *phd = rn->private_data;
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
if (phd->status == HTTP_CONNECTED) {
char *rq;
if (!FD_ISSET(rn->fd, &s->wfds))
- return;
+ return 0;
rq = make_request_msg();
PARA_INFO_LOG("sending http request\n");
ret = write_va_buffer(rn->fd, "%s", rq);
if (ret < 0)
goto out;
phd->status = HTTP_SENT_GET_REQUEST;
- return;
+ return 0;
}
if (phd->status == HTTP_SENT_GET_REQUEST) {
ret = read_pattern(rn->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG), &s->rfds);
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
PARA_INFO_LOG("received ok msg, streaming\n");
phd->status = HTTP_STREAMING;
- return;
+ return 0;
}
ret = -E_HTTP_RECV_OVERRUN;
iovcnt = btr_pool_get_buffers(rn->btrp, iov);
btr_add_output_pool(rn->btrp, num_bytes - iov[0].iov_len, btrn);
}
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
+ if (ret < 0)
+ btr_remove_node(&rn->btrn);
+ return ret;
}
static void http_recv_close(struct receiver_node *rn)
}
}
-static void i9e_post_select(__a_unused struct sched *s, struct task *t)
+static int i9e_post_select(__a_unused struct sched *s, __a_unused struct task *t)
{
int ret;
struct i9e_client_info *ici = i9ep->ici;
wipe_bottom_line();
out:
i9ep->caught_sigint = false;
- t->error = ret;
+ return ret;
}
static void i9e_pre_select(struct sched *s, __a_unused struct task *t)
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mix.h Mixer API (used by para_fade). */
+
/**
* Opaque structure which corresponds to an instance of a mixer.
*
if (!*mood_name)
return -E_DUMMY_ROW;
mlpd.m = alloc_new_mood(mood_name);
- ret = for_each_line_ro(mood_def.data, mood_def.size,
+ ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
parse_mood_line, &mlpd);
osl_close_disk_object(&mood_def);
if (ret < 0) {
ret = para_printf(pb, "checking mood %s...\n", mood_name);
if (ret < 0)
goto out;
- ret = for_each_line_ro(mood_def.data, mood_def.size,
+ ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
parse_mood_line, &mlpd);
if (ret < 0)
para_printf(pb, "%s line %u: %s\n", mood_name, mlpd.line_num,
#include <id3tag.h>
-static char *get_latin1(id3_ucs4_t const *string)
+static char *get_utf8(id3_ucs4_t const *string)
{
if (!string)
return NULL;
- return (char *)id3_ucs4_latin1duplicate(string);
+ return (char *)id3_ucs4_utf8duplicate(string);
}
static char *get_stringlist(union id3_field *field)
char *result = NULL;
for (k = 0; k < nstrings; k++) {
- char *tmp = (char *)get_latin1(id3_field_getstrings(field, k));
+ char *tmp = (char *)get_utf8(id3_field_getstrings(field, k));
if (result) {
char *tmp2 = result;
result = make_message("%s %s", tmp2, tmp);
{
id3_ucs4_t const *string = id3_field_getfullstring(field);
- return get_latin1(string);
+ return get_utf8(string);
}
#define FOR_EACH_FIELD(f, j, fr) for (j = 0; j < (fr)->nfields && \
#define MP3DEC_MAX_FRAME 8192
-static void mp3dec_post_select(__a_unused struct sched *s, struct task *t)
+static int mp3dec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
int i, ret;
next_buffer:
pmd->stream.error = 0;
- t->error = 0;
iqs = btr_get_input_queue_size(btrn);
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, &inbuffer);
/*
}
if (loaded == 0)
goto next_buffer;
- return;
+ return 0;
}
pmd->sample_rate = pmd->frame.header.samplerate;
pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
if (fn->min_iqs > MP3DEC_MAX_FRAME)
goto err;
mp3dec_consume(btrn, &pmd->stream, len);
- return;
+ return 0;
}
if (pmd->stream.error != MAD_ERROR_BADDATAPTR)
goto decode;
mp3dec_consume(btrn, &pmd->stream, len);
- return;
+ return 0;
}
fn->min_iqs = 0;
mad_synth_frame(&pmd->synth, &pmd->frame);
goto next_frame;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void mp3dec_open(struct filter_node *fn)
sched_min_delay(s);
}
-static void ogg_post_select(__a_unused struct sched *s, struct task *t)
+static int ogg_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_oggdec_data *pod = fn->private_data;
} else if (ret == 0 && !pod->have_more) /* nothing to do */
goto out;
if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
- return;
+ return 0;
if (!pod->vf) {
if (ret <= 0)
goto out;
if (ret == OV_HOLE) /* avoid buffer underruns */
fn->min_iqs = 9000;
if (ret >= 0 || ret == OV_HOLE)
- return;
+ return 0;
ret = -E_OGGDEC_BADLINK;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&fn->btrn);
+ return ret;
}
/**
return ret;
}
-static void oss_post_select(__a_unused struct sched *s,
+static int oss_post_select(__a_unused struct sched *s,
struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
ret = oss_init(wn, rate, ch, format);
if (ret < 0)
goto out;
- return;
+ return 0;
}
btr_merge(btrn, wn->min_iqs);
bytes = btr_next_buffer(btrn, &data);
btr_consume(btrn, ret);
ret = 0;
out:
- t->error = ret;
if (ret < 0)
btr_remove_node(&wn->btrn);
+ return ret;
}
__malloc static void *oss_parse_config_or_die(int argc, char **argv)
sched_request_timeout_ms(50, s);
}
-static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
+static int osx_write_post_select(__a_unused struct sched *s, struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
struct private_osx_write_data *powd = wn->private_data;
if (!powd) {
ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret == 0)
- return;
+ return 0;
if (ret < 0)
goto fail;
ret = core_audio_init(wn);
ret = 0;
mutex_unlock(powd->mutex);
if (ret >= 0)
- return;
+ return 0;
fail:
assert(ret < 0);
if (powd && powd->callback_btrn) {
}
btr_remove_node(&wn->btrn);
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
- t->error = ret;
+ return ret;
}
/**
void compute_chunk_time(long unsigned chunk_num,
struct timeval *chunk_tv, struct timeval *stream_start,
struct timeval *result);
+struct timeval *clock_get_realtime(struct timeval *tv);
/** The enum of all status items. */
enum status_items {STATUS_ITEM_ENUM NUM_STAT_ITEMS};
/** \file play.c Paraslash's standalone player. */
#include <regex.h>
-#include <sys/time.h>
#include <fnmatch.h>
#include <signal.h>
);
}
-static void play_post_select(struct sched *s, struct task *t)
+static int play_post_select(struct sched *s, struct task *t)
{
struct play_task *pt = container_of(t, struct play_task, task);
int ret;
ret = eof_cleanup(pt);
if (ret < 0) {
pt->rq = CRT_TERM_RQ;
- return;
+ return 0;
}
ret = session_post_select(s, t);
if (ret < 0)
}
ret = 1;
out:
- t->error = ret;
+ return ret;
}
/**
filter_init();
writer_init();
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
sched.default_timeout.tv_sec = 5;
parse_config_or_die(argc, argv);
if (ret < 0)
goto err;
playlist->length = 0;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- add_playlist_entry, playlist);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, add_playlist_entry, playlist);
osl_close_disk_object(&playlist_def);
if (ret < 0)
goto err;
ret = para_printf(pb, "checking playlist %s...\n", playlist_name);
if (ret < 0)
return ret;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- check_playlist_path, pb);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, check_playlist_path, pb);
}
osl_close_disk_object(&playlist_def);
return ret;
ret = pl_get_def_by_name(current_playlist.name, &playlist_def);
if (ret < 0)
return ret;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- search_path, new_path);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, search_path, new_path);
osl_close_disk_object(&playlist_def);
is_admissible = (ret < 0);
if (was_admissible && is_admissible)
struct prebuffer_filter_args_info *conf = ppd->conf;
struct timeval diff;
- t->error = 0;
if (iqs == 0)
return;
if (ppd->barrier.tv_sec == 0) {
free(fn->private_data);
}
-static void prebuffer_post_select(__a_unused struct sched *s, struct task *t)
+static int prebuffer_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
struct private_prebuffer_data *ppd = fn->private_data;
struct prebuffer_filter_args_info *conf = ppd->conf;
- t->error = 0;
if (ppd->barrier.tv_sec == 0)
- return;
+ return 0;
if (tv_diff(now, &ppd->barrier, NULL) < 0)
- return;
+ return 0;
if (iqs < conf->size_arg)
- return;
- btr_splice_out_node(btrn);
- t->error = -E_PREBUFFER_SUCCESS;
+ return 0;
+ btr_splice_out_node(fn->btrn);
+ return -E_PREBUFFER_SUCCESS;
}
static int prebuffer_parse_config(int argc, char **argv, void **config)
*
* \sa select(2), struct receiver.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/** The two help texts of this receiver. */
struct ggo_help help;
return data.input_frames_used;
}
-static void resample_post_select(__a_unused struct sched *s, struct task *t)
+static int resample_post_select(__a_unused struct sched *s, struct task *t)
{
int ret;
struct filter_node *fn = container_of(t, struct filter_node, task);
* btr exec mechanism for the destination samplerate and the
* channel count.
*/
- return btr_pushdown(btrn);
+ btr_pushdown(btrn);
+ return 0;
}
btr_merge(btrn, fn->min_iqs);
in_bytes = btr_next_buffer(btrn, (char **)&in);
goto out;
btr_consume(btrn, ret * 2 * ctx->channels);
btr_add_output((char *)out, num_frames * 2 * ctx->channels, btrn);
- return;
+ return 0;
out:
if (ret < 0) {
- t->error = ret;
btr_remove_node(&fn->btrn);
/* This releases the check_wav btr node */
check_wav_post_select(ctx->cwc);
}
+ return ret;
}
static int resample_parse_config(int argc, char **argv, void **config)
#include <regex.h>
#include <assert.h>
-#include <sys/time.h>
#include "para.h"
#include "ipc.h"
static void sched_preselect(struct sched *s)
{
struct task *t, *tmp;
+
list_for_each_entry_safe(t, tmp, &s->pre_select_list, pre_select_node) {
- if (t->error < 0) {
- unregister_task(t);
- continue;
- }
- if (t->notification != 0) {
+ if (t->notification != 0)
sched_min_delay(s);
- break;
- }
- if (!t->pre_select)
- continue;
- t->pre_select(s, t);
- if (timeout_is_zero(s))
- break;
+ if (t->pre_select)
+ t->pre_select(s, t);
}
}
static inline void call_post_select(struct sched *s, struct task *t)
{
#ifndef SCHED_DEBUG
- t->post_select(s, t);
+ t->error = t->post_select(s, t);
#else
struct timeval t1, t2, diff;
unsigned long pst;
- gettimeofday(&t1, NULL);
- t->post_select(s, t);
- gettimeofday(&t2, NULL);
+ clock_get_realtime(&t1);
+ t->error = t->post_select(s, t);
+ clock_get_realtime(&t2);
tv_diff(&t1, &t2, &diff);
pst = tv2ms(&diff);
if (pst > 50)
FD_ZERO(&s->wfds);
s->select_timeout = s->default_timeout;
s->max_fileno = -1;
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
sched_preselect(s);
- if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_list))
- return 0;
- if (!timeout_is_zero(s)) {
- ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
- &s->select_timeout);
- if (ret < 0)
- return ret;
- if (ret == 0) {
- /*
- * APUE: Be careful not to check the descriptor sets on return
- * unless the return value is greater than zero. The return
- * state of the descriptor sets is implementation dependent if
- * either a signal is caught or the timer expires.
- */
- FD_ZERO(&s->rfds);
- FD_ZERO(&s->wfds);
- }
- gettimeofday(now, NULL);
- } else {
+ ret = s->select_function(s->max_fileno + 1, &s->rfds, &s->wfds,
+ &s->select_timeout);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ /*
+ * APUE: Be careful not to check the descriptor sets on return
+ * unless the return value is greater than zero. The return
+ * state of the descriptor sets is implementation dependent if
+ * either a signal is caught or the timer expires.
+ */
FD_ZERO(&s->rfds);
FD_ZERO(&s->wfds);
}
+ clock_get_realtime(now);
sched_post_select(s);
if (list_empty(&s->pre_select_list) && list_empty(&s->post_select_list))
return 0;
*/
void (*pre_select)(struct sched *s, struct task *t);
/**
- * The postselect hook of \a t.
+ * The post select hook of \a t.
*
- * Evaluate and act upon the results of the previous select call.
+ * Its purpose is to evaluate and act upon the results of the previous
+ * select call. If this function returns a negative value, the
+ * scheduler unregisters the task.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/** Whether this task is in error state. */
int error;
/** Position of the task in the pre_select list of the scheduler. */
* This is set by the scheduler at the beginning of its main loop. It may be
* used (read-only) from everywhere. As none of the functions called by the
* scheduler are allowed to block, this value should be accurate enough so that
- * there is no need to call gettimeofday() directly.
+ * there is no need to call clock_gettime() directly.
*/
extern struct timeval *now;
*/
#include <signal.h>
-#include <sys/time.h>
#include <regex.h>
#include <osl.h>
kill(mmd->afs_pid, SIGHUP);
}
-static void signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused struct task *t)
{
int signum = para_next_signal(&s->rfds);
switch (signum) {
case 0:
- return;
+ return 0;
case SIGHUP:
handle_sighup();
break;
shm_detach(mmd);
exit(EXIT_FAILURE);
}
+ return 0;
}
static void init_signal_task(void)
para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
}
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
struct server_command_task *sct = container_of(t, struct server_command_task, task);
if (child_pid) {
close(new_fd);
/* parent keeps accepting connections */
- return;
+ return 0;
}
/* mmd might already have changed at this point */
free(chunk_table);
for (i = sct->argc - 1; i >= 0; i--)
memset(sct->argv[i], 0, strlen(sct->argv[i]));
sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
- return handle_connect(new_fd, peer_name);
+ handle_connect(new_fd, peer_name);
+ /* never reached*/
out:
if (ret < 0)
PARA_CRIT_LOG("%s\n", para_strerror(-ret));
+ return 0;
}
static void init_server_command_task(int argc, char **argv)
log_welcome("para_server");
init_ipc_or_die(); /* init mmd struct and mmd->lock */
/* make sure, the global now pointer is uptodate */
- gettimeofday(now, NULL);
+ clock_get_realtime(now);
set_server_start_time(now);
init_user_list(user_list_file);
/* become daemon */
return ret;
}
-static void speexdec_post_select(__a_unused struct sched *s, struct task *t)
+static int speexdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct private_spxdec_data *psd = fn->private_data;
size_t nbytes;
next_buffer:
- t->error = 0;
ret = ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
btr_merge(btrn, fn->min_iqs);
if (!psd->shi.state) {
goto next_buffer;
ret = ns;
fail:
- if (ret < 0) {
- t->error = ret;
+ if (ret < 0)
btr_remove_node(&fn->btrn);
- }
+ return ret;
}
/**
struct stdin_task *sit = container_of(t, struct stdin_task, task);
int ret;
- t->error = 0;
ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
if (ret < 0)
sched_min_delay(s);
* appears to be readable, data is read from stdin and fed into the buffer
* tree.
*/
-static void stdin_post_select(struct sched *s, struct task *t)
+static int stdin_post_select(struct sched *s, struct task *t)
{
struct stdin_task *sit = container_of(t, struct stdin_task, task);
ssize_t ret;
size_t sz, n;
char *buf = NULL;
- t->error = 0;
ret = btr_node_status(sit->btrn, 0, BTR_NT_ROOT);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
sz = btr_pool_get_buffer(sit->btrp, &buf);
if (sz == 0)
- return;
+ return 0;
+ if (sit->must_set_nonblock_flag) {
+ ret = mark_fd_nonblocking(STDIN_FILENO);
+ if (ret < 0)
+ goto err;
+ sit->must_set_nonblock_flag = false;
+ }
/*
* Do not use the maximal size to avoid having only a single buffer
* reference for the whole pool. This is bad because if that single
if (n > 0)
btr_add_output_pool(sit->btrp, n, sit->btrn);
if (ret >= 0)
- return;
+ return 0;
err:
btr_remove_node(&sit->btrn);
+ /* Revert to blocking mode if necessary. */
+ fcntl(STDIN_FILENO, F_SETFL, sit->fd_flags);
//btr_pool_free(sit->btrp);
- t->error = ret;
+ return ret;
}
/**
* \param sit The stdin task structure.
*
* This fills in the pre/post select function pointers of the task structure
- * given by \a sit. Moreover, the stdin file desctiptor is set to nonblocking
- * mode, and a buffer tree is created.
+ * given by \a sit and creates a buffer tree for I/O.
*/
void stdin_set_defaults(struct stdin_task *sit)
{
sit->task.post_select = stdin_post_select;
sit->btrp = btr_pool_new("stdin", 128 * 1024);
sprintf(sit->task.status, "stdin reader");
- ret = mark_fd_nonblocking(STDIN_FILENO);
- if (ret >= 0)
- return;
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
+ /*
+ * Both STDIN_FILENO and STDOUT_FILENO may refer to the same open file
+ * description (the terminal), and thus share the same file status
+ * flags. In order to not interfere with the stdout task, we only get
+ * the file status flags for STDIN here and save a copy. The nonblock
+ * flag is set later on the first read.
+ */
+ ret = fcntl(STDIN_FILENO, F_GETFL);
+ if (ret < 0) {
+ PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ sit->fd_flags = ret;
+ sit->must_set_nonblock_flag = (sit->fd_flags & O_NONBLOCK) == 0;
}
struct btr_node *btrn;
/** Use a buffer pool to minimize memcpy due to alignment problems. */
struct btr_pool *btrp;
+ /** The descriptor flags of STDIN at startup. */
+ int fd_flags;
+ /** Whether we have to set STDIN to nonblocking mode. */
+ bool must_set_nonblock_flag;
};
void stdin_set_defaults(struct stdin_task *sit);
struct stdout_task *sot = container_of(t, struct stdout_task, task);
int ret;
- t->error = 0;
ret = btr_node_status(sot->btrn, 0, BTR_NT_LEAF);
if (ret > 0)
para_fd_set(STDOUT_FILENO, &s->wfds, &s->max_fileno);
* This function writes input data from the buffer tree to stdout if \p
* STDOUT_FILENO is writable.
*/
-static void stdout_post_select(struct sched *s, struct task *t)
+static int stdout_post_select(struct sched *s, struct task *t)
{
struct stdout_task *sot = container_of(t, struct stdout_task, task);
struct btr_node *btrn = sot->btrn;
char *buf;
size_t sz;
- t->error = 0;
ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
if (ret < 0)
goto out;
if (ret == 0)
- return;
+ return 0;
if (!FD_ISSET(STDOUT_FILENO, &s->wfds))
- return;
+ return 0;
+ if (sot->must_set_nonblock_flag) {
+ ret = mark_fd_nonblocking(STDOUT_FILENO);
+ if (ret < 0)
+ goto out;
+ sot->must_set_nonblock_flag = false;
+ }
for (;;) {
sz = btr_next_buffer(btrn, &buf);
if (sz == 0)
btr_consume(btrn, ret);
}
out:
- if (ret < 0)
+ if (ret < 0) {
btr_remove_node(&sot->btrn);
- t->error = ret;
+ /* Revert to blocking mode if necessary. */
+ fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
+ }
+ return ret;
}
/**
* Initialize a stdout task structure with default values.
* \param sot The stdout task structure.
*
* This fills in the pre/post select function pointers of the task structure
- * given by \a sot and sets the stdout file descriptor to nonblocking mode.
+ * given by \a sot.
*/
void stdout_set_defaults(struct stdout_task *sot)
{
sot->task.pre_select = stdout_pre_select;
sot->task.post_select = stdout_post_select;
sprintf(sot->task.status, "stdout");
- ret = mark_fd_nonblocking(STDOUT_FILENO);
- if (ret >= 0)
- return;
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
+
+ /* See stdin.c for details. */
+ ret = fcntl(STDOUT_FILENO, F_GETFL);
+ if (ret < 0) {
+ PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ sot->fd_flags = ret;
+ sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0;
}
struct task task;
/** Stdout is always a leaf node in the buffer tree. */
struct btr_node *btrn;
+ /** The descriptor flags of STDOUT at startup. */
+ int fd_flags;
+ /** Whether we have to set STDOUT to nonblocking mode. */
+ bool must_set_nonblock_flag;
};
void stdout_set_defaults(struct stdout_task *sot);
/** \file string.c Memory allocation and string handling functions. */
-#include <sys/time.h> /* gettimeofday */
+#define _GNU_SOURCE
+
#include <pwd.h>
#include <sys/utsname.h> /* uname() */
+
#include <string.h>
#include <regex.h>
+#include <langinfo.h>
+#include <wchar.h>
+#include <wctype.h>
+
#include "para.h"
#include "string.h"
#include "error.h"
__printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
{
int ret;
- size_t size;
+ size_t size = 150;
va_list aq;
+ *result = para_malloc(size + 1);
va_copy(aq, ap);
- ret = vsnprintf(NULL, 0, fmt, aq);
+ ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
assert(ret >= 0);
+ if (ret < size) /* OK */
+ return ret;
size = ret + 1;
- *result = para_malloc(size);
+ *result = para_realloc(*result, size);
va_copy(aq, ap);
ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
}
/**
- * Used to distinguish between read-only and read-write mode.
+ * Call a custom function for each complete line.
+ *
+ * \param flags Any combination of flags defined in \ref for_each_line_flags.
+ * \param buf The buffer containing data separated by newlines.
+ * \param size The number of bytes in \a buf.
+ * \param line_handler The custom function.
+ * \param private_data Pointer passed to \a line_handler.
+ *
+ * For each complete line in \p buf, \p line_handler is called. The first
+ * argument to \p line_handler is (a copy of) the current line, and \p
+ * private_data is passed as the second argument. If the \p FELF_READ_ONLY
+ * flag is unset, a pointer into \a buf is passed to the line handler,
+ * otherwise a pointer to a copy of the current line is passed instead. This
+ * copy is freed immediately after the line handler returns.
+ *
+ * The function returns if \p line_handler returns a negative value or no more
+ * lines are in the buffer. The rest of the buffer (last chunk containing an
+ * incomplete line) is moved to the beginning of the buffer if FELF_READ_ONLY is
+ * unset.
*
- * \sa for_each_line(), for_each_line_ro().
+ * \return On success this function returns the number of bytes not handled to
+ * \p line_handler. The only possible error is a negative return value from the
+ * line handler. In this case processing stops and the return value of the line
+ * handler is returned to indicate failure.
+ *
+ * \sa \ref for_each_line_flags.
*/
-enum for_each_line_modes{
- /** Activate read-only mode. */
- LINE_MODE_RO,
- /** Activate read-write mode. */
- LINE_MODE_RW
-};
-
-static int for_each_complete_line(enum for_each_line_modes mode, char *buf,
- size_t size, line_handler_t *line_handler, void *private_data)
+int for_each_line(unsigned flags, char *buf, size_t size,
+ line_handler_t *line_handler, void *private_data)
{
char *start = buf, *end;
int ret, i, num_lines = 0;
} else
end = next_cr;
num_lines++;
- if (!line_handler) {
- start = ++end;
- continue;
- }
- if (mode == LINE_MODE_RO) {
- size_t s = end - start;
- char *b = para_malloc(s + 1);
- memcpy(b, start, s);
- b[s] = '\0';
-// PARA_NOTICE_LOG("b: %s, start: %s\n", b, start);
- ret = line_handler(b, private_data);
- free(b);
- } else {
- *end = '\0';
- ret = line_handler(start, private_data);
+ if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
+ if (flags & FELF_READ_ONLY) {
+ size_t s = end - start;
+ char *b = para_malloc(s + 1);
+ memcpy(b, start, s);
+ b[s] = '\0';
+ ret = line_handler(b, private_data);
+ free(b);
+ } else {
+ *end = '\0';
+ ret = line_handler(start, private_data);
+ }
+ if (ret < 0)
+ return ret;
}
- if (ret < 0)
- return ret;
start = ++end;
}
- if (!line_handler || mode == LINE_MODE_RO)
- return num_lines;
i = buf + size - start;
- if (i && i != size)
+ if (i && i != size && !(flags & FELF_READ_ONLY))
memmove(buf, start, i);
return i;
}
-/**
- * Call a custom function for each complete line.
- *
- * \param buf The buffer containing data separated by newlines.
- * \param size The number of bytes in \a buf.
- * \param line_handler The custom function.
- * \param private_data Pointer passed to \a line_handler.
- *
- * If \p line_handler is \p NULL, the function returns the number of complete
- * lines in \p buf. Otherwise, \p line_handler is called for each complete
- * line in \p buf. The first argument to \p line_handler is the current line,
- * and \p private_data is passed as the second argument. The function returns
- * if \p line_handler returns a negative value or no more lines are in the
- * buffer. The rest of the buffer (last chunk containing an incomplete line)
- * is moved to the beginning of the buffer.
- *
- * \return If \p line_handler is not \p NULL, this function returns the number
- * of bytes not handled to \p line_handler on success, or the negative return
- * value of the \p line_handler on errors.
- *
- * \sa for_each_line_ro().
- */
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data)
-{
- return for_each_complete_line(LINE_MODE_RW, buf, size, line_handler,
- private_data);
-}
-
-/**
- * Call a custom function for each complete line.
- *
- * \param buf Same meaning as in \p for_each_line().
- * \param size Same meaning as in \p for_each_line().
- * \param line_handler Same meaning as in \p for_each_line().
- * \param private_data Same meaning as in \p for_each_line().
- *
- * This function behaves like \p for_each_line(), but \a buf is left unchanged.
- *
- * \return On success, the function returns the number of complete lines in \p
- * buf, otherwise the (negative) return value of \p line_handler is returned.
- *
- * \sa for_each_line().
- */
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data)
-{
- return for_each_complete_line(LINE_MODE_RO, buf, size, line_handler,
- private_data);
-}
-
/** Return the hex characters of the lower 4 bits. */
#define hex(a) (hexchar[(a) & 15])
return NULL;
return safe_strdup(src + keylen + 1, len - keylen - 1);
}
+
+static bool utf8_mode(void)
+{
+ static bool initialized, have_utf8;
+
+ if (!initialized) {
+ char *info = nl_langinfo(CODESET);
+ have_utf8 = (info && strcmp(info, "UTF-8") == 0);
+ initialized = true;
+ PARA_INFO_LOG("%susing UTF-8 character encoding\n",
+ have_utf8? "" : "not ");
+ }
+ return have_utf8;
+}
+
+/*
+ * glibc's wcswidth returns -1 if the string contains a tab character, which
+ * makes the function next to useless. The two functions below are taken from
+ * mutt.
+ */
+
+#define IsWPrint(wc) (iswprint(wc) || wc >= 0xa0)
+
+static int mutt_wcwidth(wchar_t wc, size_t pos)
+{
+ int n;
+
+ if (wc == 0x09) /* tab */
+ return (pos | 7) + 1 - pos;
+ n = wcwidth(wc);
+ if (IsWPrint(wc) && n > 0)
+ return n;
+ if (!(wc & ~0x7f))
+ return 2;
+ if (!(wc & ~0xffff))
+ return 6;
+ return 10;
+}
+
+static size_t mutt_wcswidth(const wchar_t *s, size_t n)
+{
+ size_t w = 0;
+
+ while (n--)
+ w += mutt_wcwidth(*s++, w);
+ return w;
+}
+
+/**
+ * Skip a given number of cells at the beginning of a string.
+ *
+ * \param s The input string.
+ * \param cells_to_skip Desired number of cells that should be skipped.
+ * \param bytes_to_skip Result.
+ *
+ * This function computes how many input bytes must be skipped to advance a
+ * string by the given width. If the current character encoding is not UTF-8,
+ * this is simply the given number of cells, i.e. \a cells_to_skip. Otherwise,
+ * \a s is treated as a multibyte string and on successful return, \a s +
+ * bytes_to_skip points to the start of a multibyte string such that the total
+ * width of the multibyte characters that are skipped by advancing \a s that
+ * many bytes equals at least \a cells_to_skip.
+ *
+ * \return Standard.
+ */
+int skip_cells(const char *s, size_t cells_to_skip, size_t *bytes_to_skip)
+{
+ wchar_t wc;
+ mbstate_t ps;
+ size_t n, bytes_parsed, cells_skipped;
+
+ *bytes_to_skip = 0;
+ if (cells_to_skip == 0)
+ return 0;
+ if (!utf8_mode()) {
+ *bytes_to_skip = cells_to_skip;
+ return 0;
+ }
+ bytes_parsed = cells_skipped = 0;
+ memset(&ps, 0, sizeof(ps));
+ n = strlen(s);
+ while (cells_to_skip > cells_skipped) {
+ size_t mbret;
+
+ mbret = mbrtowc(&wc, s + bytes_parsed, n - bytes_parsed, &ps);
+ assert(mbret != 0);
+ if (mbret == (size_t)-1 || mbret == (size_t)-2)
+ return -ERRNO_TO_PARA_ERROR(EILSEQ);
+ bytes_parsed += mbret;
+ cells_skipped += mutt_wcwidth(wc, cells_skipped);
+ }
+ *bytes_to_skip = bytes_parsed;
+ return 1;
+}
+
+/**
+ * Compute the width of an UTF-8 string.
+ *
+ * \param s The string.
+ * \param result The width of \a s is returned here.
+ *
+ * If not in UTF8-mode. this function is just a wrapper for strlen(3).
+ * Otherwise \a s is treated as an UTF-8 string and its display width is
+ * computed. Note that this function may fail if the underlying call to
+ * mbsrtowcs(3) fails, so the caller must check the return value.
+ *
+ * \sa nl_langinfo(3), wcswidth(3).
+ *
+ * \return Standard.
+ */
+__must_check int strwidth(const char *s, size_t *result)
+{
+ const char *src = s;
+ mbstate_t state;
+ static wchar_t *dest;
+ size_t num_wchars;
+
+ /*
+ * Never call any log function here. This may result in an endless loop
+ * as para_gui's para_log() calls this function.
+ */
+
+ if (!utf8_mode()) {
+ *result = strlen(s);
+ return 0;
+ }
+ memset(&state, 0, sizeof(state));
+ *result = 0;
+ num_wchars = mbsrtowcs(NULL, &src, 0, &state);
+ if (num_wchars == (size_t)-1)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ if (num_wchars == 0)
+ return 0;
+ dest = para_malloc(num_wchars * sizeof(*dest));
+ src = s;
+ memset(&state, 0, sizeof(state));
+ num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
+ assert(num_wchars > 0 && num_wchars != (size_t)-1);
+ *result = mutt_wcswidth(dest, num_wchars);
+ free(dest);
+ return 1;
+}
void *private_data;
};
+/**
+ * Controls the behavior of for_each_line().
+ *
+ * \sa for_each_line().
+ */
+enum for_each_line_flags {
+ /** Activate read-only mode. */
+ FELF_READ_ONLY = 1 << 0,
+ /** Don't call line handler for the first input line. */
+ FELF_DISCARD_FIRST = 1 << 1,
+};
+
+/** Used for \ref for_each_line(). */
+typedef int line_handler_t(char *, void *);
+int for_each_line(unsigned flags, char *buf, size_t size,
+ line_handler_t *line_handler, void *private_data);
+
/**
* Write the contents of a status item to a para_buffer.
*
__must_check __malloc char *para_homedir(void);
__malloc char *para_hostname(void);
__printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...);
-/** Used for for_each_line() and for_each_line_ro(). */
-typedef int line_handler_t(char *, void *);
-int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data);
-int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler,
- void *private_data);
int para_atoi64(const char *str, int64_t *result);
int para_atoi32(const char *str, int32_t *value);
int get_loglevel_by_name(const char *txt);
int compute_word_num(const char *buf, const char *delim, int offset);
char *safe_strdup(const char *src, size_t len);
char *key_value_copy(const char *src, size_t len, const char *key);
+int skip_cells(const char *s, size_t cells_to_skip, size_t *result);
+__must_check int strwidth(const char *s, size_t *result);
tv_scale(chunk_num, chunk_tv, &tmp);
tv_add(&tmp, stream_start, result);
}
+
+/**
+ * Retrieve the time of the realtime clock.
+ *
+ * \param tv Where to store the result.
+ *
+ * Gets the current value of the system-wide real-time clock (identified by id
+ * \p CLOCK_REALTIME). If \a tv is \p NULL, the value is stored in a static
+ * buffer, otherwise it is stored at the location given by \a tv.
+ *
+ * \return This function aborts on errors. On success it returns a pointer to
+ * memory containing the current time.
+ *
+ * \sa clock_gettime(2), gettimeofday(2).
+ */
+struct timeval *clock_get_realtime(struct timeval *tv)
+{
+ static struct timeval user_friendly;
+
+ if (!tv)
+ tv = &user_friendly;
+#ifdef HAVE_CLOCK_GETTIME
+ {
+ struct timespec t;
+ int ret;
+
+ ret = clock_gettime(CLOCK_REALTIME, &t);
+ assert(ret == 0);
+ tv->tv_sec = t.tv_sec;
+ tv->tv_usec = t.tv_nsec / 1000;
+ }
+#else
+ #include <sys/time.h>
+ gettimeofday(tv, NULL);
+#endif /* HAVE_CLOCK_GETTIME */
+ return tv;
+}
return -E_RECV_EOF;
}
-static void udp_recv_post_select(__a_unused struct sched *s, struct task *t)
+static int udp_recv_post_select(__a_unused struct sched *s, struct task *t)
{
struct receiver_node *rn = container_of(t, struct receiver_node, task);
struct btr_node *btrn = rn->btrn;
}
ret = readv_ret;
out:
- if (ret >= 0)
- return;
- btr_remove_node(&rn->btrn);
- t->error = ret;
- close(rn->fd);
- rn->fd = -1;
+ if (ret < 0) {
+ btr_remove_node(&rn->btrn);
+ close(rn->fd);
+ rn->fd = -1;
+ }
+ return ret;
}
static void udp_recv_close(struct receiver_node *rn)
#include <regex.h>
-#include <sys/time.h>
+#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/udp.h>
#include <net/if.h>
}
}
-static void vss_post_select(struct sched *s, struct task *t)
+static int vss_post_select(struct sched *s, struct task *t)
{
int ret, i;
struct vss_task *vsst = container_of(t, struct vss_task, task);
-
if (mmd->sender_cmd_data.cmd_num >= 0) {
int num = mmd->sender_cmd_data.cmd_num,
sender_num = mmd->sender_cmd_data.sender_num;
(vss_next() && vss_playing()))
tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier);
vss_send(vsst);
+ return 0;
}
/**
sched_min_delay(s);
}
-static void wav_post_select(__a_unused struct sched *s, struct task *t)
+static int wav_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct btr_node *btrn = fn->btrn;
char *header, *buf;
int32_t rate, ch;
- t->error = 0;
if (iqs == 0) {
ret = -E_WAV_EOF;
if (btr_no_parent(btrn))
goto err;
- return;
+ return 0;
}
ret = btr_exec_up(btrn, "sample_rate", &buf);
if (ret < 0) {
btr_add_output(header, WAV_HEADER_LEN, btrn);
ret = -E_WAV_SUCCESS;
err:
- t->error = ret;
if (ret == -E_WAV_SUCCESS)
btr_splice_out_node(btrn);
else {
btr_remove_node(&fn->btrn);
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
}
+ return ret;
}
/**
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<title>Paraslash</title>
<LINK href="para.css" REL="stylesheet" TYPE="text/css">
<link rel="shortcut icon" href="paraslash.ico">
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
- <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
+ <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<title>Paraslash</title>
<LINK href="../../para.css" REL="stylesheet" TYPE="text/css">
<link rel="shortcut icon" href="../../paraslash.ico">
Requirements
~~~~~~~~~~~~
+For the impatient:
-In any case you'll need
+ git clone git://git.tuebingen.mpg.de/osl
+ cd osl && make && sudo make install && sudo ldconfig
+ sudo apt-get install autoconf libssl-dev help2man gengetopt \
+ libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
+ libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev \
+ libasound2-dev libao-dev libreadline-dev libncurses-dev
+
+Detailed description: In any case you'll need
- XREFERENCE(http://systemlinux.org/~maan/osl/, libosl).
The _object storage layer_ library is used by para_server. To
or decode files encoded with the _Free Lossless Audio Codec_,
libFLAC (libFLAC-dev) must be installed.
+ - XREFERENCE(http://www.mega-nerd.com/SRC/index.html,
+ libsamplerate). The resample filter will only be compiled if
+ this library is installed. Debian package: libsamplerate-dev.
+
- XREFERENCE(ftp://ftp.alsa-project.org/pub/lib/, alsa-lib). On
Linux, you'll need to have ALSA's development package
libasound2-dev installed.
libao). Needed to build the ao writer (ESD, PulseAudio,...).
Debian package: libao-dev.
+ - XREFERENCE(ftp://ftp.gnu.org/pub/gnu/ncurses, curses). Needed
+ for para_gui. Debian package: libncurses-dev.
+
- XREFERENCE(http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html,
GNU Readline). If this library (libreadline-dev) is installed,
para_client, para_audioc and para_play support interactive
#define _XOPEN_SOURCE 600
-#include <sys/time.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
-static void wmadec_post_select(__a_unused struct sched *s, struct task *t)
+static int wmadec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
int ret, converted, out_size;
next_buffer:
converted = 0;
- t->error = 0;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (ret < 0)
goto err;
if (ret == 0)
- return;
+ return 0;
btr_merge(btrn, fn->min_iqs);
len = btr_next_buffer(btrn, (char **)&in);
ret = -E_WMADEC_EOF;
converted += ret + WMA_FRAME_SKIP;
success:
btr_consume(btrn, converted);
- return;
+ return 0;
err:
assert(ret < 0);
- t->error = ret;
btr_remove_node(&fn->btrn);
+ return ret;
}
static void wmadec_open(struct filter_node *fn)
check_wav_pre_select(s, wt->cwc);
}
-static void write_post_select(__a_unused struct sched *s, struct task *t)
+static int write_post_select(__a_unused struct sched *s, struct task *t)
{
struct write_task *wt = container_of(t, struct write_task, task);
- t->error = check_wav_post_select(wt->cwc);
+ return check_wav_post_select(wt->cwc);
}
static int setup_and_schedule(void)
*
* Called from the post_select function of the writer node's task.
*/
- void (*post_select)(struct sched *s, struct task *t);
+ int (*post_select)(struct sched *s, struct task *t);
/**
* Close one instance of the writer.
*
.handler = w->execute, .context = wn));
strcpy(wn->task.status, name);
free(name);
- wn->task.post_select = w->post_select;
wn->task.pre_select = w->pre_select;
+ wn->task.post_select = w->post_select;
register_task(s, &wn->task);
}