Cooking since 2014-04-03.
* t/audiod_com_version:
audiod: Implement version command.
audiod: Trival spelling fix for com_tasks().
DEBUG_CPPFLAGS += -Wall -Wno-sign-compare -Wno-unknown-pragmas
DEBUG_CPPFLAGS += -Wformat-security
DEBUG_CPPFLAGS += -Wmissing-format-attribute
-DEBUG_CPPFLAGS += -Wdeclaration-after-statement
ifeq ($(uname_s),Linux)
CPPFLAGS += -fdata-sections -ffunction-sections
NEWS
====
----------------------------------------------
-0.5.2 (to be announced) "orthogonal interior"
----------------------------------------------
+-------------------------------------------------
+0.5.3 (to be released) "symbolic synchronization"
+-------------------------------------------------
+
+ - Various alsa-related fixes, mostly for the raspberry pi.
+ - The test suite has been extended to include sanity checks
+ for the generated man pages.
+ - ao_writer fixes. This writer was in a quite bad shape. Many
+ serious bugs have been fixed.
+ - new audiod command: version.
+
+----------------------------------------
+0.5.2 (2014-04-11) "orthogonal interior"
+----------------------------------------
The new sync filter, the AES_CTR128 stream cipher and the overhauled
network code are the highlights of this release. It also includes a
- The man pages of para_audiod, para_filter, para_recv, and
para_write contain the relevant options for receivers, filters,
writers. This broke in 0.5.0.
+ - ogg/vorbis latency improvements.
- Improved user manual.
- Minor fixes to avoid clang warnings.
+Downloads: ./releases/paraslash-0.5.2.tar.bz2 (tarball),
+./releases/paraslash-0.5.2.tar.bz2.asc (signature)
+
------------------------------------------
0.5.1 (2013-12-20) "temporary implication"
------------------------------------------
mixer_channel, h->card, snd_strerror(ret));
return -E_ALSA_MIX_RANGE;
}
- if (h->pmin < 0 || h->pmax < 0 || h->pmin >= h->pmax) {
+ if (h->pmin >= h->pmax) {
PARA_NOTICE_LOG("alsa reported %s range %ld-%ld (%s)\n",
mixer_channel, h->pmin, h->pmax, h->card);
return -E_ALSA_MIX_RANGE;
snd_pcm_t *handle;
/** Determined and set by alsa_init(). */
int bytes_per_frame;
- /**
- * The sample rate given by command line option or the decoder
- * of the writer node group.
+ /*
+ * If the sample rate is not given at the command line and no wav
+ * header was detected, the btr exec mechanism is employed to query the
+ * ancestor buffer tree nodes for this information. In a typical setup
+ * the decoder passes the sample rate back to the alsa writer.
+ *
+ * \sa \ref btr_exec_up().
*/
unsigned sample_rate;
- snd_pcm_format_t sample_format;
- /**
- * The number of channels, given by command line option or the
- * decoder of the writer node group.
+ /*
+ * The sample format (8/16 bit, signed/unsigned, little/big endian) is
+ * determined in the same way as the \a sample_rate.
*/
+ snd_pcm_format_t sample_format;
+ /* The number of channels, again determined like \a sample_rate. */
unsigned channels;
+ /* time until buffer underrun occurs, in milliseconds */
+ unsigned buffer_time;
struct timeval drain_barrier;
/* File descriptor for select(). */
int poll_fd;
snd_pcm_uframes_t start_threshold, stop_threshold;
snd_pcm_uframes_t buffer_size, period_size;
snd_output_t *output_log;
- unsigned buffer_time;
int ret;
const char *msg;
+ unsigned period_time;
PARA_INFO_LOG("opening %s\n", conf->device_arg);
msg = "unable to open pcm";
&pad->sample_rate, NULL);
if (ret < 0)
goto fail;
- msg = "unable to get buffer time";
- ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time,
- NULL);
- if (ret < 0 || buffer_time == 0)
- goto fail;
- /* buffer at most 500 milliseconds */
- buffer_time = PARA_MIN(buffer_time, 500U * 1000U);
+ /* alsa wants microseconds */
+ pad->buffer_time = conf->buffer_time_arg * 1000;
msg = "could not set buffer time";
ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
- &buffer_time, NULL);
+ &pad->buffer_time, NULL);
+ if (ret < 0)
+ goto fail;
+ pad->buffer_time /= 1000; /* we prefer milliseconds */
+ period_time = pad->buffer_time * 250; /* buffer time / 4 */
+ msg = "could not set period time";
+ ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams,
+ &period_time, 0);
if (ret < 0)
goto fail;
+
msg = "unable to install hw params";
ret = snd_pcm_hw_params(pad->handle, hwparams);
if (ret < 0)
if (ret == 0) {
char *buf, *p;
size_t sz;
- PARA_INFO_LOG("dumping alsa configuration\n");
+ PARA_DEBUG_LOG("dumping alsa configuration\n");
snd_pcm_dump(pad->handle, output_log);
+ snd_pcm_hw_params_dump(hwparams, output_log);
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;
*q = '\0';
- PARA_INFO_LOG("%s\n", p);
+ PARA_DEBUG_LOG("%s\n", p);
p = q + 1;
}
snd_output_close(output_log);
sched_request_barrier_or_min_delay(&pad->drain_barrier, s);
return;
}
+ /* wait at most 50% of the buffer time */
+ sched_request_timeout_ms(pad->buffer_time / 2, s);
ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1);
if (ret < 0) {
PARA_ERROR_LOG("could not get alsa poll fd: %s\n",
snd_strerror(-ret));
- t->error = -E_ALSA;
return;
}
pad->poll_fd = pfd.fd;
wn->min_iqs = pad->bytes_per_frame;
goto again;
}
- if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds))
- return 0;
frames = bytes / pad->bytes_per_frame;
frames = snd_pcm_writei(pad->handle, data, frames);
if (frames == 0 || frames == -EAGAIN) {
- /*
- * The alsa poll fd was ready for IO but we failed to write to
- * the alsa handle. This means another event is pending. We
- * don't care about that but we have to dispatch the event in
- * order to avoid a busy loop. So we simply read from the poll
- * fd.
- */
char buf[100];
- if (read(pad->poll_fd, buf, 100))
- do_nothing;
+ if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds))
+ if (read(pad->poll_fd, buf, 100))
+ do_nothing;
return 0;
}
if (frames > 0) {
pthread_t thread;
pthread_attr_t attr;
+ /* The mutex and the condition variable serialize access to ->btrn */
pthread_mutex_t mutex;
pthread_cond_t data_available;
struct btr_node *thread_btrn;
if (!pawd)
return;
+ assert(!pawd->thread_btrn);
ao_close(pawd->dev);
free(pawd);
wn->private_data = NULL;
- ao_shutdown();
}
static void aow_pre_select(struct sched *s, struct task *t)
{
struct writer_node *wn = container_of(t, struct writer_node, task);
- int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+ struct private_aow_data *pawd = wn->private_data;
+ int ret;
- if (ret == 0)
- return;
+ if (!pawd) { /* not yet started */
+ assert(wn->btrn);
+ ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+ if (ret != 0)
+ goto min_delay;
+ return; /* no data available */
+ }
+ if (!wn->btrn) { /* EOF */
+ if (!pawd->thread_btrn) /* ready to exit */
+ goto min_delay;
+ /* wait for the play thread to terminate */
+ goto timeout;
+ }
+ pthread_mutex_lock(&pawd->mutex);
+ ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+ pthread_mutex_unlock(&pawd->mutex);
+ if (ret != 0)
+ goto min_delay;
+ /*
+ * Even though the node status is zero, we might have data available,
+ * but the output buffer is full. If we don't set a timeout here, we
+ * are woken up only if new data arrives, which might be too late and
+ * result in a buffer underrun in the playing thread. To avoid this we
+ * never sleep longer than the (default) buffer time.
+ */
+timeout:
+ return sched_request_timeout_ms(20, s);
+min_delay:
sched_min_delay(s);
}
struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
struct ao_write_args_info *conf = wn->conf;
- ao_initialize();
if (conf->driver_given) {
ret = -E_AO_BAD_DRIVER;
id = ao_driver_id(conf->driver_arg);
char *data;
int ret;
+ pthread_mutex_lock(&pawd->mutex);
for (;;) {
- /*
- * Lock mutex and wait for signal. pthread_cond_wait() will
- * automatically and atomically unlock mutex while it waits.
- */
- pthread_mutex_lock(&pawd->mutex);
for (;;) {
ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret < 0)
- goto unlock;
+ goto fail;
if (ret > 0) {
btr_merge(btrn, wn->min_iqs);
bytes = btr_next_buffer(btrn, &data);
break;
/* eof and less than a single frame available */
ret = -E_WRITE_COMMON_EOF;
- goto unlock;
+ goto fail;
}
- //PARA_CRIT_LOG("waiting for data\n");
- //usleep(1000);
- //pthread_mutex_unlock(&pawd->mutex);
- pthread_cond_wait(&pawd->data_available, &pawd->mutex);
+ /*
+ * No data available, go to sleep and wait for the main
+ * thread to wake us up. pthread_cond_wait() unlocks
+ * the mutex while it waits and locks it again upon
+ * return.
+ */
+ ret = pthread_cond_wait(&pawd->data_available,
+ &pawd->mutex);
+ /* pthread_cond_wait() can never fail here */
+ assert(ret == 0);
}
- pthread_mutex_unlock(&pawd->mutex);
assert(frames > 0);
bytes = frames * pawd->bytes_per_frame;
- ret = -E_AO_PLAY;
- if (ao_play(pawd->dev, data, bytes) == 0) /* failure */
- goto out;
+ pthread_mutex_unlock(&pawd->mutex);
+ ret = ao_play(pawd->dev, data, bytes);
+ pthread_mutex_lock(&pawd->mutex);
+ if (ret == 0) { /* failure */
+ ret = -E_AO_PLAY;
+ goto fail;
+ }
btr_consume(btrn, bytes);
}
-unlock:
- pthread_mutex_unlock(&pawd->mutex);
-out:
+fail:
+ btr_remove_node(&pawd->thread_btrn);
assert(ret < 0);
PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+ pthread_mutex_unlock(&pawd->mutex);
pthread_exit(NULL);
}
goto remove_thread_btrn;
return 0;
}
+ if (!wn->btrn) {
+ if (!pawd->thread_btrn) {
+ pthread_join(pawd->thread, NULL);
+ return -E_AO_EOF;
+ }
+ PARA_INFO_LOG("waiting for play thread to terminate\n");
+ return 0;
+ }
pthread_mutex_lock(&pawd->mutex);
ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
if (ret > 0) {
btr_pushdown(wn->btrn);
- pthread_cond_signal(&pawd->data_available);
+ if (pthread_cond_signal(&pawd->data_available) != 0) {
+ ret = -E_AO_PTHREAD;
+ PARA_ERROR_LOG("pthread_cond_signal() failed\n");
+ goto remove_thread_btrn;
+ }
}
- pthread_mutex_unlock(&pawd->mutex);
- if (ret >= 0)
+ if (ret >= 0) {
+ pthread_mutex_unlock(&pawd->mutex);
goto out;
- pthread_mutex_lock(&pawd->mutex);
+ }
btr_remove_node(&wn->btrn);
- PARA_INFO_LOG("waiting for thread to terminate\n");
pthread_cond_signal(&pawd->data_available);
pthread_mutex_unlock(&pawd->mutex);
- pthread_join(pawd->thread, NULL);
+ return 0;
remove_thread_btrn:
btr_remove_node(&pawd->thread_btrn);
remove_btrn:
dh[num_lines] = NULL;
w->help.detailed_help = (const char **)dh;
ao_write_cmdline_parser_free(&dummy);
- ao_shutdown();
}
*/
void __noreturn clean_exit(int status, const char *msg)
{
- int i;
-
if (socket_name)
unlink(socket_name);
close_stat_pipe();
- FOR_EACH_SLOT(i)
- close_slot(i);
+ close_unused_slots();
audiod_cmdline_parser_free(&conf);
close_stat_clients();
PARA_EMERG_LOG("%s\n", msg);
btr_drop_buffer_reference(br);
}
+static void btr_free_node(struct btr_node *btrn)
+{
+ free(btrn->name);
+ free(btrn);
+}
+
/**
* Remove a node from a buffer tree.
*
btr_drain(btrn);
if (btrn->parent)
list_del(&btrn->node);
- free(btrn->name);
- free(btrn);
+ btr_free_node(btrn);
out:
*btrnp = NULL;
}
list_del(&ch->node);
}
assert(list_empty(&btrn->children));
+ btr_free_node(btrn);
*btrnp = NULL;
}
schedule(&command_sched);
*result = exec_task.result_buf;
btr_remove_node(&exec_task.btrn);
- client_disconnect(ct);
ret = 1;
out:
btr_remove_node(&exec_task.btrn);
{
int ret;
- client_disconnect(ct);
PARA_DEBUG_LOG("line: %s\n", line);
ret = make_client_argv(line);
if (ret <= 0)
char **features;
};
-void client_disconnect(struct client_task *ct);
void client_close(struct client_task *ct);
int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
int *loglevel);
/** The size of the receiving buffer. */
#define CLIENT_BUFSIZE 4000
-/**
- * Close the connection to para_server and deallocate per-command resources.
- *
- * \param ct The client task.
- *
- * This frees all resources of the current command but keeps the configuration
- * in \p ct->conf.
- *
- * \sa \ref client_close().
- */
-void client_disconnect(struct client_task *ct)
-{
- if (!ct)
- return;
- if (ct->scc.fd >= 0)
- close(ct->scc.fd);
- free_argv(ct->features);
- ct->features = NULL;
- sc_free(ct->scc.recv);
- ct->scc.recv = NULL;
- sc_free(ct->scc.send);
- ct->scc.send = NULL;
- btr_remove_node(&ct->btrn[0]);
- btr_remove_node(&ct->btrn[1]);
-}
-
/**
* Close the connection to para_server and free all resources.
*
* \param ct Pointer to the client data.
*
- * \sa \ref client_open(), \ref client_disconnect().
+ * \sa \ref client_open().
*/
void client_close(struct client_task *ct)
{
if (!ct)
return;
- client_disconnect(ct);
free(ct->user);
free(ct->config_file);
free(ct->key_file);
btr_remove_node(&ct->btrn[1]);
if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ if (ct->scc.fd >= 0) {
+ close(ct->scc.fd);
+ ct->scc.fd = -1;
+ }
+ free_argv(ct->features);
+ ct->features = NULL;
+ sc_free(ct->scc.recv);
+ ct->scc.recv = NULL;
+ sc_free(ct->scc.send);
+ ct->scc.send = NULL;
return ret;
}
fi
if test "$have_readline" = "yes"; then
- :
+ AC_CHECK_DECL(
+ [rl_free_keymap],
+ [AC_DEFINE(RL_FREE_KEYMAP_DECLARED, 1, readline >= 6.3)],
+ [],
+ [
+ #include <stdio.h>
+ #include <readline/readline.h>
+ ]
+ )
AC_SUBST(readline_cppflags)
AC_SUBST(readline_ldflags)
AC_DEFINE(HAVE_READLINE, 1, define to 1 to turn on readline support)
switch (state) {
case 0:
- if (target) {
- if (tarindex >= targsize)
- return -E_BASE64;
- target[tarindex] = (pos - Base64) << 2;
- }
+ if (tarindex >= targsize)
+ return -E_BASE64;
+ target[tarindex] = (pos - Base64) << 2;
state = 1;
break;
case 1:
- if (target) {
- if (tarindex + 1 >= targsize)
- return -E_BASE64;
- target[tarindex] |= (pos - Base64) >> 4;
- target[tarindex+1] = ((pos - Base64) & 0x0f)
- << 4 ;
- }
+ if (tarindex + 1 >= targsize)
+ return -E_BASE64;
+ target[tarindex] |= (pos - Base64) >> 4;
+ target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
tarindex++;
state = 2;
break;
case 2:
- if (target) {
- if (tarindex + 1 >= targsize)
- return -E_BASE64;
- target[tarindex] |= (pos - Base64) >> 2;
- target[tarindex+1] = ((pos - Base64) & 0x03)
- << 6;
- }
+ if (tarindex + 1 >= targsize)
+ return -E_BASE64;
+ target[tarindex] |= (pos - Base64) >> 2;
+ target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
tarindex++;
state = 3;
break;
case 3:
- if (target) {
- if (tarindex >= targsize)
- return -E_BASE64;
- target[tarindex] |= (pos - Base64);
- }
+ if (tarindex >= targsize)
+ return -E_BASE64;
+ target[tarindex] |= pos - Base64;
tarindex++;
state = 0;
break;
* zeros. If we don't check them, they become a
* subliminal channel.
*/
- if (target && target[tarindex] != 0)
+ if (target[tarindex] != 0)
return -E_BASE64;
}
} else {
PARA_ERROR(AO_PLAY, "ao_play() failed"), \
PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
PARA_ERROR(AO_PTHREAD, "pthread error"), \
+ PARA_ERROR(AO_EOF, "ao: end of file"), \
#define COMPRESS_FILTER_ERRORS \
static int meta_eof_cb(FLAC__IOHandle handle)
{
struct private_flac_afh_data *pfad = handle;
- return pfad->fpos == pfad->map_bytes - 1;
+ return pfad->fpos == pfad->map_bytes;
}
static int meta_close_cb(FLAC__IOHandle __a_unused handle)
para_install_sighandler(SIGCHLD);
para_install_sighandler(SIGWINCH);
para_install_sighandler(SIGUSR1);
- para_sigaction(SIGHUP, SIG_IGN);
}
/* kill every process in the process group and exit */
fprintf(i9ep->stderr_stream, "\r");
}
+#ifndef RL_FREE_KEYMAP_DECLARED
/**
* Free all storage associated with a keymap.
*
* \param keymap The keymap to deallocate.
*/
void rl_free_keymap(Keymap keymap);
+#endif
/**
* Reset the terminal and save the in-memory command line history.
option "device" d
#~~~~~~~~~~~~~~~~
"set PCM device"
-string typestr="device"
-default="default"
+string typestr = "device"
+default = "default"
optional
-details="
- On systems with dmix, a better choice than the default
- value might be to use \"plug:swmix\".
+details = "
+ Check for the presence of a /proc/asound/ directory to see if
+ ALSA is present in your kernel. The file /proc/asound/devices
+ contains all devices ALSA knows about.
"
+
+option "buffer-time" B
+#~~~~~~~~~~~~~~~~~~~~~
+"duration of the ALSA buffer"
+int typestr = "milliseconds"
+default = "170"
+optional
+details = "
+ This is only a hint as ALSA might pick a slightly different
+ time, depending on the sound hardware. The chosen value is
+ shown in debug output as BUFFER_TIME.
+
+ If synchronization between multiple clients is desired,
+ the same buffer time should be configured for all clients.
+"
+
</qu>
+
0, /* no initial bytes */
ovc); /* the ov_open_callbacks */
if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) {
- /* this might be due to the input buffer being too small */
+ /* maybe the input buffer is too small */
if (!btr_no_parent(btrn)) {
fn->min_iqs += 1000;
iqs = btr_get_input_queue_size(btrn);
ret = 0;
if (iqs < fn->min_iqs)
goto out;
- PARA_CRIT_LOG("iqs: %zu\n", iqs);
btr_merge(btrn, fn->min_iqs);
pod->converted = 0;
goto open;
break;
fn->min_iqs = 0;
have += ret;
- if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
- continue;
- if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
+ if (have >= OGGDEC_OUTPUT_CHUNK_SIZE)
break;
- btr_add_output(buf, have, btrn);
- buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
- have = 0;
}
pod->have_more = (ret > 0);
if (have > 0) {
if (ret != 0)
return sched_min_delay(s);
- if (ctx->have_more)
+ if (!ctx->have_more)
return;
if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE)
return sched_min_delay(s);
test_options += --trash-dir $(trash_dir)
test_options += --executables "$(prefixed_executables)"
test_options += --objects "$(basename $(all_objs))"
+test_options += --man-dir $(man_dir)
ifdef V
ifeq ("$(origin V)", "command line")
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='Parse generated man pages.
+
+Simple sanity checks of some man pages.
+
+* para_audiod: Check that list of audiod commands is present
+* para_server: Check that list of server/afs commands is present
+* para_play: Check that list of play commands is present
+
+* para_{recv,filter,write,audiod}: Check for presence of
+filter/receiver/writer options as appropriate '
+
+. ${0%/*}/test-lib.sh
+
+rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer
+
+grep_man()
+{
+ local regex="$1" exe="$2"
+ tr '\n' ' ' < "$o_man_dir/para_$exe.1" | grep -q "$regex"
+}
+
+# check that options of all reveivers/filters/writers are contained
+# in the man pages
+
+regex="$rfw_regex"
+test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv"
+test_expect_success 'para_filter: filter options' "grep_man '$regex' filter"
+test_expect_success 'para_write: writer options' "grep_man '$regex' write"
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+ test_skip 'para_audiod' "missing object(s): $result"
+else
+ test_expect_success 'para_audiod: recv/filter/writer options' \
+ "grep_man '$regex' audiod"
+fi
+
+# check various command lists
+
+test_require_objects "audiod"
+if [[ -n "$result" ]]; then
+ test_skip 'para_audiod' "missing object(s): $result"
+else
+ regex='LIST OF AUDIOD COMMANDS.\{200,\}'
+ test_expect_success 'para_audiod: command list' \
+ "grep_man '$regex' audiod"
+fi
+
+test_require_objects "server"
+missing_objects="$result"
+if [[ -n "$missing_objects" ]]; then
+ test_skip "para_server" "missing object(s): $missing_objects"
+else
+ regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS'
+ test_expect_success 'para_server: server/afs commands' \
+ "grep_man '$regex' server"
+fi
+
+# para_play is always built
+regex='LIST OF COMMANDS.\{100,\}'
+test_expect_success 'para_play: play commands' "grep_man '$regex' play"
+regex="$rfw_regex"
+test_done
if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
export TERM=$ORIGINAL_TERM
case "$1" in
- error) tput bold; tput setaf 1;;
- skip) tput setaf 5;;
+ error) tput $C_BOLD; tput $C_SETAF 1;;
+ skip) tput $C_SETAF 5;;
ok)
(($o_verbose == 0)) && return
- tput setaf 2;;
- pass) tput bold; tput setaf 2;;
- info) tput setaf 3;;
+ tput $C_SETAF 2;;
+ pass) tput $C_BOLD; tput $C_SETAF 2;;
+ info) tput $C_SETAF 3;;
run)
(($o_verbose == 0)) && return
- tput setaf 6;;
+ tput $C_SETAF 6;;
esac
fi
shift
printf "%s\n" "$*"
if [[ "$o_nocolor" != "true" && -n "$1" ]]; then
- tput sgr0
+ tput $C_SGR0
export TERM=dumb
fi
}
result="false"
[[ "$TERM" == "dumb" ]] && return
[[ -t 1 ]] || return
- tput bold >/dev/null 2>&1 || return
- tput setaf 1 >/dev/null 2>&1 || return
- tput sgr0 >/dev/null 2>&1 || return
+ C_BOLD='bold'
+ tput $C_BOLD &>/dev/null || {
+ C_BOLD='md'
+ tput $C_BOLD &>/dev/null
+ } || return
+ C_SETAF='setaf'
+ tput $C_SETAF 1 &>/dev/null || {
+ C_SETAF='AF'
+ tput $C_SETAF 1 &>/dev/null
+ } || return
+ C_SGR0='sgr0'
+ tput $C_SGR0 >/dev/null 2>&1 || {
+ C_SGR0='me'
+ tput $C_SGR0 &>/dev/null
+ } || return
result="true"
}
-v=1|--verbose=1) o_verbose="1"; shift;;
-v|--verbose|-v=2|--verbose=2) o_verbose="2"; shift;;
--no-color) o_nocolor="true"; shift;;
+ --man-dir) export o_man_dir="$2"; shift; shift;;
--results-dir) o_results_dir="$2"; shift; shift;;
--trash-dir) o_trash_dir="$2"; shift; shift;;
--executables-dir) export o_executables_dir="$2"; shift; shift;;
[[ -z "$o_results_dir" ]] && o_results_dir="$test_dir/test-results"
[[ -z "$o_executables_dir" ]] && o_executables_dir="$test_dir/.."
[[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes"
+ [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1"
# we want alsolute paths because relative paths become invalid
# after changing to the trash dir
[[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir"
[[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir"
+ [[ -n "${o_man_dir##/*}" ]] && o_man_dir="$wd/$o_man_dir"
[[ -n "${o_trash_dir##/*}" ]] && o_trash_dir="$wd/$o_trash_dir"
mkdir -p "$o_results_dir"
static char buf[512];
snprintf(buf, sizeof(buf) - 1, "%s\n"
- "Copyright (C) 2013-2014 Andre Noll\n"
+ "Copyright (C) 2014 Andre Noll\n"
"This is free software with ABSOLUTELY NO WARRANTY."
" See COPYING for details.\n"
"Report bugs to <maan@systemlinux.org>.\n"
static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti)
{
const char *p, *end = buf + buf_size, *q;
- uint16_t len1, len2, len3, len4, len5;
+ uint16_t len1, len2, len3, len4;
p = search_pattern(comment_header, sizeof(comment_header),
buf, buf_size);
p += 2;
len4 = read_u16(p);
p += 2;
- len5 = read_u16(p);
+ /* ignore length of the rating information */
p += 2;
if (p + len1 >= end)
goto next;
if (p + len2 >= end)
goto next;
ti->artist = get_str16(p, len2);
- p += len2 + len3 + len4;
- if (p + len5 >= end)
+ p += len2 + len3;
+ if (p + len4 >= end)
goto next;
- ti->comment = get_str16(p, len5);
+ ti->comment = get_str16(p, len4);
next:
p = search_pattern(extended_content_header, sizeof(extended_content_header),
buf, buf_size);