From: Andre Noll Date: Sat, 25 May 2013 12:37:41 +0000 (+0200) Subject: Merge branch 't/doc' X-Git-Tag: v0.4.13~34 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=e065f3eec9be99d82da855bf3da38d8e86752fef;hp=e82be065a135dd946a9b0f50514735c3526ad994 Merge branch 't/doc' Simple enough and cooking since 2013-05-05. e82be0 manual: Add a new paragraph for installing dependencies. 1d6f5f manual: Add missing references of two optional packages. 4a6061 check_wav.c: Fix documentation of check_wav_post_select(). f9c625 Add two missing Copyright headers and file annotations. 16af9d Add documentation of public functions in check_wav.c. --- diff --git a/CREDITS b/CREDITS index 1d8ad0ce..ec4e5362 100644 --- a/CREDITS +++ b/CREDITS @@ -40,7 +40,7 @@ Loren Merritt (FFmpeg) Simon Morlat (ortp) -Christof Müller (bug reports) +Christof Müller (bug reports) M. Hari Nezumi (AudioCompress) @@ -50,7 +50,7 @@ Manuel Odendahl (poc) Guillaume Outters (mosx-mpg123) -Christian Reißmann (design) +Christian Reißmann (design) Gerrit Renker (dccp improvements, IPv6 support) diff --git a/Makefile.in b/Makefile.in index d577ce29..f3dfea00 100644 --- a/Makefile.in +++ b/Makefile.in @@ -79,6 +79,8 @@ CPPFLAGS += -I/usr/local/include CPPFLAGS += -I$(cmdline_dir) CPPFLAGS += @osl_cppflags@ +LDFLAGS += @clock_gettime_ldflags@ + man_pages := $(patsubst %, $(man_dir)/%.1, @executables@) autocrap := config.h.in configure @@ -244,47 +246,47 @@ endif 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' diff --git a/NEWS b/NEWS index f323df1d..56f84753 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,11 @@ 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. + ----------------------------------------- 0.4.12 (2012-12-20) "volatile relativity" ----------------------------------------- diff --git a/aacdec_filter.c b/aacdec_filter.c index 55f7ea25..3ff90834 100644 --- a/aacdec_filter.c +++ b/aacdec_filter.c @@ -80,7 +80,7 @@ static void aacdec_close(struct filter_node *fn) 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; @@ -91,12 +91,11 @@ static void aacdec_post_select(__a_unused struct sched *s, struct task *t) 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); @@ -123,7 +122,7 @@ next_buffer: ret = -E_AACDEC_INIT; if (NeAACDecInit2(padd->handle, p, padd->decoder_length, &rate, - &channels) < 0) + &channels) != 0) goto out; } padd->sample_rate = rate; @@ -203,8 +202,8 @@ out: } err: assert(ret < 0); - t->error = ret; btr_remove_node(&fn->btrn); + return ret; } /** diff --git a/afh.c b/afh.c index aab664c2..4c65d7c1 100644 --- a/afh.c +++ b/afh.c @@ -7,7 +7,6 @@ /** \file afh.c Paraslash's standalone audio format handler tool. */ #include -#include #include "para.h" #include "string.h" diff --git a/afh_common.c b/afh_common.c index 83ea3808..6c161a7c 100644 --- a/afh_common.c +++ b/afh_common.c @@ -7,7 +7,6 @@ /** \file afh_common.c Common audio format handler functions. */ #include /* mmap */ -#include /* gettimeofday */ #include #include @@ -109,8 +108,7 @@ void afh_init(void) { int i; - PARA_INFO_LOG("supported audio formats: %s\n", - SERVER_AUDIO_FORMATS); + PARA_INFO_LOG("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS); FOR_EACH_AUDIO_FORMAT(i) { PARA_NOTICE_LOG("initializing %s handler\n", audio_format_name(i)); diff --git a/afh_recv.c b/afh_recv.c index febda8a9..774ae88c 100644 --- a/afh_recv.c +++ b/afh_recv.c @@ -173,7 +173,7 @@ static void afh_recv_pre_select(struct sched *s, struct task *t) 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; @@ -233,7 +233,7 @@ out: btr_remove_node(&rn->btrn); pard->current_chunk = pard->first_chunk; } - t->error = ret; + return ret; } /** diff --git a/afs.c b/afs.c index eb18708c..2f521291 100644 --- a/afs.c +++ b/afs.c @@ -723,9 +723,9 @@ static void signal_pre_select(struct sched *s, struct task *t) 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"); @@ -733,20 +733,20 @@ static void afs_signal_post_select(struct sched *s, struct task *t) } 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) @@ -915,7 +915,7 @@ err: /** 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; @@ -923,16 +923,13 @@ static void command_post_select(struct sched *s, struct task *t) 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) { @@ -953,17 +950,18 @@ static void command_post_select(struct sched *s, struct task *t) 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) diff --git a/alsa_write.c b/alsa_write.c index ba844db2..88e48b45 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "para.h" #include "fd.h" @@ -165,17 +164,18 @@ static int alsa_init(struct private_alsa_write_data *pad, 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); } @@ -236,7 +236,7 @@ static void alsa_close(struct writer_node *wn) 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); @@ -253,7 +253,7 @@ static void alsa_write_post_select(__a_unused struct sched *s, 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 */ @@ -266,17 +266,17 @@ again: 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; @@ -297,7 +297,7 @@ again: 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) { @@ -311,7 +311,7 @@ again: 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); @@ -320,14 +320,14 @@ again: 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) diff --git a/amp_filter.c b/amp_filter.c index 94849663..a6bfca25 100644 --- a/amp_filter.c +++ b/amp_filter.c @@ -22,7 +22,6 @@ extern char *stat_item_values[NUM_STAT_ITEMS]; /** Data specific to the amplify filter. */ struct private_amp_data { - /** Points to the configuration data for this instance of this filter. */ /** Amplification factor. */ unsigned amp; }; @@ -63,7 +62,7 @@ static void amp_open(struct filter_node *fn) 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; @@ -74,16 +73,15 @@ static void amp_post_select(__a_unused struct sched *s, struct task *t) 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; @@ -111,12 +109,11 @@ next_buffer: 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) diff --git a/ao_write.c b/ao_write.c index ffe86699..9d204ff3 100644 --- a/ao_write.c +++ b/ao_write.c @@ -276,7 +276,7 @@ fail: 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); @@ -291,7 +291,7 @@ static void aow_post_select(__a_unused struct sched *s, 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); @@ -312,7 +312,7 @@ static void aow_post_select(__a_unused struct sched *s, 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); @@ -334,7 +334,7 @@ remove_thread_btrn: 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) diff --git a/audioc.c b/audioc.c index abd76359..74fb11cb 100644 --- a/audioc.c +++ b/audioc.c @@ -106,7 +106,7 @@ static void audioc_pre_select(struct sched *s, struct task *t) 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); @@ -115,7 +115,7 @@ static void audioc_post_select(struct sched *s, struct task *t) 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); @@ -124,14 +124,14 @@ static void audioc_post_select(struct sched *s, struct task *t) 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 = { diff --git a/audiod.c b/audiod.c index 4f2d4151..d2c66f2b 100644 --- a/audiod.c +++ b/audiod.c @@ -496,7 +496,6 @@ static void open_filters(struct slot_info *s) 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)); @@ -992,7 +991,7 @@ static void signal_pre_select(struct sched *s, struct task *t) 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; @@ -1004,6 +1003,7 @@ static void signal_post_select(struct sched *s, __a_unused struct task *t) 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) @@ -1019,7 +1019,7 @@ static void command_pre_select(struct sched *s, struct task *t) 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); @@ -1036,6 +1036,7 @@ static void command_post_select(struct sched *s, struct task *t) 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) @@ -1089,32 +1090,44 @@ static void set_stat_task_restart_barrier(unsigned seconds) 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); } /* @@ -1123,12 +1136,11 @@ static void try_to_close_slot(int 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); @@ -1151,10 +1163,13 @@ static void start_stop_decoders(void) 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; @@ -1177,7 +1192,7 @@ 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); @@ -1250,6 +1265,7 @@ static void status_post_select(struct sched *s, struct task *t) st->last_status_read = *now; out: start_stop_decoders(); + return 0; } static void init_status_task(struct status_task *st) diff --git a/buffer_tree.c b/buffer_tree.c index bb854124..f0a7b224 100644 --- a/buffer_tree.c +++ b/buffer_tree.c @@ -386,7 +386,8 @@ void btr_add_output(char *buf, size_t size, struct btr_node *btrn) { struct btr_buffer *btrb; - assert(size != 0); + if (size == 0) + return; if (list_empty(&btrn->children)) { free(buf); return; @@ -415,7 +416,8 @@ void btr_add_output_dont_free(const char *buf, size_t size, struct btr_node *btr { struct btr_buffer *btrb; - assert(size != 0); + if (size == 0) + return; if (list_empty(&btrn->children)) return; btrb = new_btrb((char *)buf, size); @@ -442,7 +444,8 @@ void btr_add_output_pool(struct btr_pool *btrp, size_t size, char *buf; size_t avail; - assert(size != 0); + if (size == 0) + return; if (list_empty(&btrn->children)) return; avail = btr_pool_get_buffer(btrp, &buf); diff --git a/client.c b/client.c index 715df9c6..90dc432b 100644 --- a/client.c +++ b/client.c @@ -54,7 +54,7 @@ static void exec_pre_select(struct sched *s, struct task *t) 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; @@ -63,10 +63,8 @@ static void exec_post_select(__a_unused struct sched *s, struct task *t) 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; @@ -76,6 +74,7 @@ static void exec_post_select(__a_unused struct sched *s, struct task *t) et->result_buf[et->result_size - 1] = '\0'; out: btr_consume(btrn, sz); + return 0; } static int make_client_argv(const char *line) @@ -531,23 +530,21 @@ __noreturn static void print_completions(void) #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 = { diff --git a/client_common.c b/client_common.c index 7bdb4738..8ea41922 100644 --- a/client_common.c +++ b/client_common.c @@ -326,7 +326,7 @@ static int send_sb_command(struct client_task *ct) * * \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; @@ -338,7 +338,7 @@ static void client_post_select(struct sched *s, struct task *t) 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); @@ -346,10 +346,10 @@ static void client_post_select(struct sched *s, struct task *t) 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); @@ -360,7 +360,7 @@ static void client_post_select(struct sched *s, struct task *t) 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 @@ -406,7 +406,7 @@ static void client_post_select(struct sched *s, struct task *t) 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) { @@ -447,20 +447,20 @@ static void client_post_select(struct sched *s, struct task *t) 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; @@ -475,7 +475,7 @@ static void client_post_select(struct sched *s, struct task *t) if (ret < 0) goto out; ct->status = CL_SENT_COMMAND; - return; + return 0; } case CL_SENT_COMMAND: { @@ -501,7 +501,7 @@ static void client_post_select(struct sched *s, struct task *t) 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); @@ -517,15 +517,15 @@ static void client_post_select(struct sched *s, struct task *t) 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: { @@ -534,13 +534,13 @@ static void client_post_select(struct sched *s, struct task *t) 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); @@ -559,13 +559,13 @@ static void client_post_select(struct sched *s, struct task *t) } } 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; } /** diff --git a/command.c b/command.c index 39470fa9..ec822c82 100644 --- a/command.c +++ b/command.c @@ -8,7 +8,6 @@ #include #include -#include #include #include @@ -124,7 +123,7 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly, 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 @@ -411,7 +410,7 @@ static int com_si(struct command_context *cc) mmd->num_commands, mmd->num_connects, conf.loglevel_arg, - SERVER_AUDIO_FORMATS, + AUDIO_FORMAT_HANDLERS, sender_info ); mutex_unlock(mmd_mutex); diff --git a/compress_filter.c b/compress_filter.c index 04377b75..d7162791 100644 --- a/compress_filter.c +++ b/compress_filter.c @@ -41,7 +41,7 @@ static void compress_close(struct filter_node *fn) 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; @@ -56,12 +56,11 @@ static void compress_post_select(__a_unused struct sched *s, struct task *t) //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 */ @@ -108,8 +107,8 @@ next_buffer: goto next_buffer; err: assert(ret < 0); - t->error = ret; btr_remove_node(&fn->btrn); + return ret; } /** TODO: Add sanity checks */ diff --git a/configure.ac b/configure.ac index b9875747..135a0bd9 100644 --- a/configure.ac +++ b/configure.ac @@ -247,6 +247,18 @@ if test x$ac_cv_have_working_snprintf$ac_cv_have_working_vsnprintf != "xyesyes"; 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" @@ -289,7 +301,7 @@ else acl udp_send" server_ldflags="" - server_audio_formats="mp3 wma" + audio_format_handlers="mp3 wma" AC_SUBST(osl_cppflags) server_ldflags="$server_ldflags $osl_libs -losl" fi @@ -471,12 +483,17 @@ fi 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 @@ -637,7 +654,7 @@ if test "$have_vorbis" = "yes"; then recv_errlist_objs="$recv_errlist_objs ogg_afh" audiod_audio_formats="$audiod_audio_formats ogg" - server_audio_formats="$server_audio_formats ogg" + audio_format_handlers="$audio_format_handlers ogg" else AC_MSG_WARN([no ogg/vorbis $msg]) fi @@ -661,7 +678,7 @@ if test "$have_speex" = "yes"; then recv_errlist_objs="$recv_errlist_objs spx_afh spx_common" audiod_audio_formats="$audiod_audio_formats spx" - server_audio_formats="$server_audio_formats spx" + audio_format_handlers="$audio_format_handlers spx" else AC_MSG_WARN([no ogg/speex $msg]) fi @@ -705,7 +722,7 @@ if test "$have_faad" = "yes"; then recv_ldflags="$afh_ldflags $faad_libs -lfaad" audiod_audio_formats="$audiod_audio_formats aac" - server_audio_formats="$server_audio_formats aac" + audio_format_handlers="$audio_format_handlers aac" filters="$filters aacdec" AC_SUBST(faad_cppflags) else @@ -836,7 +853,7 @@ if test "$have_flac" = "yes"; then afh_ldflags="$afh_ldflags $flac_libs -lFLAC" recv_ldflags="$afh_ldflags $flac_libs -lFLAC" filters="$filters flacdec" - server_audio_formats="$server_audio_formats flac" + audio_format_handlers="$audio_format_handlers flac" audiod_audio_formats="$audiod_audio_formats flac" AC_SUBST(flac_cppflags) else @@ -1186,7 +1203,7 @@ done AC_DEFINE_UNQUOTED(STATUS_ITEM_ARRAY, [$result], [char * array of all status items]) -AC_DEFINE_UNQUOTED(SERVER_AUDIO_FORMATS, "$server_audio_formats", +AC_DEFINE_UNQUOTED(AUDIO_FORMAT_HANDLERS, "$audio_format_handlers", [formats supported by para_server and para_afh]) AC_SUBST(executables, add_para($executables)) @@ -1243,6 +1260,7 @@ AC_DEFINE_UNQUOTED(INIT_AUDIOC_ERRLISTS, 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) @@ -1251,8 +1269,6 @@ AC_SUBST(play_ldflags, $play_ldflags) 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) @@ -1284,10 +1300,10 @@ paraslash configuration: ~~~~~~~~~~~~~~~~~~~~~~~~ unix socket credentials: $have_ucred readline (interactive CLIs): $have_readline -audio formats supported by para_server/para_afh: $server_audio_formats +audio formats handlers: $audio_format_handlers id3 version2 support: $have_libid3tag -filters supported by para_audiod/para_filter: $filters -writers supported by para_audiod/para_write: $writers +filters: $filters +writers: $writers optional executables: $extras $mixer_summary ]) diff --git a/daemon.c b/daemon.c index 29b00ed2..18ad1568 100644 --- a/daemon.c +++ b/daemon.c @@ -10,7 +10,6 @@ #include #include /* getgrnam() */ #include -#include #include #include "para.h" @@ -374,7 +373,7 @@ __printf_2_3 void daemon_log(int ll, const char* fmt,...) 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]; diff --git a/dccp_recv.c b/dccp_recv.c index eb442f30..1f9df189 100644 --- a/dccp_recv.c +++ b/dccp_recv.c @@ -124,7 +124,7 @@ static void dccp_recv_pre_select(struct sched *s, struct task *t) 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; @@ -152,10 +152,9 @@ static void dccp_recv_post_select(struct sched *s, struct task *t) 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) diff --git a/fecdec_filter.c b/fecdec_filter.c index d5a708cf..2f0a6746 100644 --- a/fecdec_filter.c +++ b/fecdec_filter.c @@ -436,7 +436,7 @@ static void fecdec_close(struct filter_node *fn) 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; @@ -468,9 +468,9 @@ next_buffer: 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) diff --git a/file_write.c b/file_write.c index a12867d5..cea21cb3 100644 --- a/file_write.c +++ b/file_write.c @@ -8,7 +8,6 @@ #include #include -#include #include "para.h" #include "list.h" @@ -38,10 +37,8 @@ struct private_file_write_data { __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); @@ -100,7 +97,7 @@ static void file_write_close(struct writer_node *wn) 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); @@ -121,18 +118,18 @@ static void file_write_post_select(__a_unused struct sched *s, 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) diff --git a/filter.h b/filter.h index eefa6f69..9c57904e 100644 --- a/filter.h +++ b/filter.h @@ -109,7 +109,7 @@ struct filter { * 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. * diff --git a/filter_common.c b/filter_common.c index 415824ad..907912fd 100644 --- a/filter_common.c +++ b/filter_common.c @@ -123,13 +123,13 @@ void print_filter_helps(int detailed) } /** - * 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. */ diff --git a/flacdec_filter.c b/flacdec_filter.c index b0f499c3..09b319a0 100644 --- a/flacdec_filter.c +++ b/flacdec_filter.c @@ -216,7 +216,7 @@ static void flacdec_pre_select(struct sched *s, struct task *t) 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; @@ -224,7 +224,7 @@ static void flacdec_post_select(__a_unused struct sched *s, struct task *t) 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; @@ -255,9 +255,9 @@ static void flacdec_post_select(__a_unused struct sched *s, struct task *t) 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) diff --git a/grab_client.c b/grab_client.c index a800b70b..ecc16fc8 100644 --- a/grab_client.c +++ b/grab_client.c @@ -108,7 +108,7 @@ static void gc_pre_select(struct sched *s, struct task *t) * 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. @@ -184,7 +184,7 @@ static int gc_close(struct grab_client *gc, int err) 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; @@ -192,10 +192,9 @@ static void gc_post_select(__a_unused struct sched *s, struct task *t) 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); @@ -205,10 +204,10 @@ static void gc_post_select(__a_unused struct sched *s, struct task *t) 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) diff --git a/gui.c b/gui.c index a4ee7275..631e7cc5 100644 --- a/gui.c +++ b/gui.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "gui.cmdline.h" #include "para.h" @@ -305,11 +306,18 @@ static void add_spaces(WINDOW* win, unsigned int num) 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; @@ -391,11 +399,14 @@ static int first_visible_rbe(unsigned *lines) 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; @@ -403,16 +414,22 @@ static int draw_top_rbe(unsigned *lines) 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; } @@ -444,10 +461,15 @@ out: 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); @@ -1162,7 +1184,7 @@ static void com_scroll_top(void) 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) { @@ -1193,7 +1215,7 @@ static void com_page_down(void) 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; @@ -1514,6 +1536,7 @@ int main(int argc, char *argv[]) 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(); diff --git a/http_recv.c b/http_recv.c index a913d3a2..f1d85930 100644 --- a/http_recv.c +++ b/http_recv.c @@ -60,7 +60,6 @@ static void http_recv_pre_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; - t->error = 0; if (generic_recv_pre_select(s, t) <= 0) return; if (phd->status == HTTP_CONNECTED) @@ -74,7 +73,7 @@ static void http_recv_pre_select(struct sched *s, struct task *t) * 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; @@ -90,11 +89,11 @@ static void http_recv_post_select(struct sched *s, struct task *t) 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); @@ -102,17 +101,17 @@ static void http_recv_post_select(struct sched *s, struct task *t) 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); @@ -128,10 +127,9 @@ static void http_recv_post_select(struct sched *s, struct task *t) 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) diff --git a/interactive.c b/interactive.c index bc3a7c1e..f2819012 100644 --- a/interactive.c +++ b/interactive.c @@ -307,7 +307,7 @@ static void i9e_line_handler(char *line) } } -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; @@ -361,7 +361,7 @@ rm_btrn: 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) diff --git a/mp3_afh.c b/mp3_afh.c index 64709821..ece13e1f 100644 --- a/mp3_afh.c +++ b/mp3_afh.c @@ -72,11 +72,11 @@ static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mon #include -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) @@ -85,7 +85,7 @@ 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); @@ -101,7 +101,7 @@ static char *get_string(union id3_field *field) { 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 && \ diff --git a/mp3dec_filter.c b/mp3dec_filter.c index bd1f0d35..98627504 100644 --- a/mp3dec_filter.c +++ b/mp3dec_filter.c @@ -77,7 +77,7 @@ static void mp3dec_close(struct filter_node *fn) #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; @@ -88,13 +88,12 @@ static void mp3dec_post_select(__a_unused struct sched *s, struct task *t) 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); /* @@ -120,7 +119,7 @@ next_frame: } if (loaded == 0) goto next_buffer; - return; + return 0; } pmd->sample_rate = pmd->frame.header.samplerate; pmd->channels = MAD_NCHANNELS(&pmd->frame.header); @@ -140,12 +139,12 @@ decode: 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); @@ -165,8 +164,8 @@ decode: 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) diff --git a/oggdec_filter.c b/oggdec_filter.c index b405f869..f41150f2 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -199,7 +199,7 @@ static void ogg_pre_select(struct sched *s, struct task *t) 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; @@ -217,7 +217,7 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t) } 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; @@ -254,12 +254,12 @@ static void ogg_post_select(__a_unused struct sched *s, struct task *t) 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; } /** diff --git a/oss_write.c b/oss_write.c index 57bdcfa6..cd327243 100644 --- a/oss_write.c +++ b/oss_write.c @@ -157,7 +157,7 @@ err_free: 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); @@ -181,7 +181,7 @@ static void oss_post_select(__a_unused struct sched *s, 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); @@ -199,9 +199,9 @@ static void oss_post_select(__a_unused struct sched *s, 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) diff --git a/osx_write.c b/osx_write.c index d38e2fb1..41b19030 100644 --- a/osx_write.c +++ b/osx_write.c @@ -301,7 +301,7 @@ static void osx_write_pre_select(struct sched *s, struct task *t) 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; @@ -314,7 +314,7 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t) 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); @@ -337,7 +337,7 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t) ret = 0; mutex_unlock(powd->mutex); if (ret >= 0) - return; + return 0; fail: assert(ret < 0); if (powd && powd->callback_btrn) { @@ -348,7 +348,7 @@ fail: } btr_remove_node(&wn->btrn); PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); - t->error = ret; + return ret; } /** diff --git a/para.h b/para.h index 4208ae6a..edab4871 100644 --- a/para.h +++ b/para.h @@ -109,6 +109,7 @@ void ms2tv(const long unsigned n, struct timeval *tv); 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}; diff --git a/play.c b/play.c index cca203b5..2cb0536d 100644 --- a/play.c +++ b/play.c @@ -7,7 +7,6 @@ /** \file play.c Paraslash's standalone player. */ #include -#include #include #include @@ -1196,7 +1195,7 @@ static unsigned get_time_string(struct play_task *pt, char **result) ); } -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; @@ -1204,7 +1203,7 @@ static void play_post_select(struct sched *s, struct task *t) ret = eof_cleanup(pt); if (ret < 0) { pt->rq = CRT_TERM_RQ; - return; + return 0; } ret = session_post_select(s, t); if (ret < 0) @@ -1234,7 +1233,7 @@ static void play_post_select(struct sched *s, struct task *t) } ret = 1; out: - t->error = ret; + return ret; } /** @@ -1255,7 +1254,7 @@ int main(int argc, char *argv[]) filter_init(); writer_init(); - gettimeofday(now, NULL); + clock_get_realtime(now); sched.default_timeout.tv_sec = 5; parse_config_or_die(argc, argv); diff --git a/prebuffer_filter.c b/prebuffer_filter.c index e1afff5a..0759d678 100644 --- a/prebuffer_filter.c +++ b/prebuffer_filter.c @@ -37,7 +37,6 @@ static void prebuffer_pre_select(struct sched *s, struct task *t) struct prebuffer_filter_args_info *conf = ppd->conf; struct timeval diff; - t->error = 0; if (iqs == 0) return; if (ppd->barrier.tv_sec == 0) { @@ -57,7 +56,7 @@ static void prebuffer_close(struct filter_node *fn) 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; @@ -65,15 +64,14 @@ static void prebuffer_post_select(__a_unused struct sched *s, struct task *t) 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) diff --git a/recv.h b/recv.h index f70cbbe8..cc44a602 100644 --- a/recv.h +++ b/recv.h @@ -115,7 +115,7 @@ struct receiver { * * \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; diff --git a/resample_filter.c b/resample_filter.c index ed9945ae..bd8ece91 100644 --- a/resample_filter.c +++ b/resample_filter.c @@ -202,7 +202,7 @@ static int resample_frames(int16_t *in, size_t num_frames, bool have_more, 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); @@ -231,7 +231,8 @@ static void resample_post_select(__a_unused struct sched *s, struct task *t) * 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); @@ -246,14 +247,14 @@ static void resample_post_select(__a_unused struct sched *s, struct task *t) 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) diff --git a/sched.c b/sched.c index 7ee77027..d42e1498 100644 --- a/sched.c +++ b/sched.c @@ -8,7 +8,6 @@ #include #include -#include #include "para.h" #include "ipc.h" @@ -50,20 +49,12 @@ static inline bool timeout_is_zero(struct sched *s) 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); } } @@ -71,14 +62,14 @@ static void sched_preselect(struct sched *s) 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) @@ -128,30 +119,23 @@ again: 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; diff --git a/sched.h b/sched.h index 7a5a4f86..9c5c098e 100644 --- a/sched.h +++ b/sched.h @@ -55,11 +55,13 @@ struct task { */ 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. */ @@ -76,7 +78,7 @@ struct task { * 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; diff --git a/server.c b/server.c index 2595d9c4..f3d5237f 100644 --- a/server.c +++ b/server.c @@ -67,7 +67,6 @@ */ #include -#include #include #include @@ -279,13 +278,13 @@ static void handle_sighup(void) 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; @@ -330,6 +329,7 @@ cleanup: shm_detach(mmd); exit(EXIT_FAILURE); } + return 0; } static void init_signal_task(void) @@ -358,7 +358,7 @@ static void command_pre_select(struct sched *s, struct task *t) 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); @@ -392,7 +392,7 @@ static void command_post_select(struct sched *s, struct task *t) if (child_pid) { close(new_fd); /* parent keeps accepting connections */ - return; + return 0; } /* mmd might already have changed at this point */ free(chunk_table); @@ -406,10 +406,12 @@ static void command_post_select(struct sched *s, struct task *t) 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) @@ -493,7 +495,7 @@ static void server_init(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 */ diff --git a/spxdec_filter.c b/spxdec_filter.c index 6bd41146..9bba042c 100644 --- a/spxdec_filter.c +++ b/spxdec_filter.c @@ -238,7 +238,7 @@ static int compute_skip_samples(ogg_page *og, struct private_spxdec_data *psd) 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; @@ -249,7 +249,6 @@ static void speexdec_post_select(__a_unused struct sched *s, struct task *t) 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) { @@ -290,10 +289,9 @@ next_buffer: goto next_buffer; ret = ns; fail: - if (ret < 0) { - t->error = ret; + if (ret < 0) btr_remove_node(&fn->btrn); - } + return ret; } /** diff --git a/stdin.c b/stdin.c index fd803ae5..b25a0ba0 100644 --- a/stdin.c +++ b/stdin.c @@ -33,7 +33,6 @@ static void stdin_pre_select(struct sched *s, struct task *t) 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); @@ -55,22 +54,21 @@ static void stdin_pre_select(struct sched *s, struct task *t) * 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; /* * 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 @@ -81,11 +79,11 @@ static void stdin_post_select(struct sched *s, struct task *t) if (n > 0) btr_add_output_pool(sit->btrp, n, sit->btrn); if (ret >= 0) - return; + return 0; err: btr_remove_node(&sit->btrn); //btr_pool_free(sit->btrp); - t->error = ret; + return ret; } /** diff --git a/stdout.c b/stdout.c index 9c7e64e7..abf3d06f 100644 --- a/stdout.c +++ b/stdout.c @@ -30,7 +30,6 @@ static void stdout_pre_select(struct sched *s, struct task *t) 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); @@ -47,7 +46,7 @@ static void stdout_pre_select(struct sched *s, struct task *t) * 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; @@ -55,14 +54,13 @@ static void stdout_post_select(struct sched *s, struct task *t) 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; for (;;) { sz = btr_next_buffer(btrn, &buf); @@ -76,7 +74,7 @@ static void stdout_post_select(struct sched *s, struct task *t) out: if (ret < 0) btr_remove_node(&sot->btrn); - t->error = ret; + return ret; } /** * Initialize a stdout task structure with default values. diff --git a/string.c b/string.c index c001b15d..dfcfa2cd 100644 --- a/string.c +++ b/string.c @@ -6,12 +6,18 @@ /** \file string.c Memory allocation and string handling functions. */ -#include /* gettimeofday */ +#define _GNU_SOURCE + #include #include /* uname() */ + #include #include +#include +#include +#include + #include "para.h" #include "string.h" #include "error.h" @@ -982,3 +988,145 @@ char *key_value_copy(const char *src, size_t len, const char *key) 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; +} diff --git a/string.h b/string.h index d4627f33..d45c9199 100644 --- a/string.h +++ b/string.h @@ -91,3 +91,5 @@ void freep(void *arg); 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); diff --git a/time.c b/time.c index 18b5a35f..6f6dd49e 100644 --- a/time.c +++ b/time.c @@ -191,3 +191,40 @@ void compute_chunk_time(long unsigned chunk_num, 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 + gettimeofday(tv, NULL); +#endif /* HAVE_CLOCK_GETTIME */ + return tv; +} diff --git a/udp_recv.c b/udp_recv.c index 6c5ee026..f9c70ec8 100644 --- a/udp_recv.c +++ b/udp_recv.c @@ -49,7 +49,7 @@ static int udp_check_eof(size_t sz, struct iovec iov[2]) 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; @@ -83,12 +83,12 @@ static void udp_recv_post_select(__a_unused struct sched *s, struct task *t) } 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) diff --git a/udp_send.c b/udp_send.c index b41c0ebf..7930f092 100644 --- a/udp_send.c +++ b/udp_send.c @@ -8,7 +8,7 @@ #include -#include +#include #include #include #include diff --git a/vss.c b/vss.c index cfa64a14..aec357ab 100644 --- a/vss.c +++ b/vss.c @@ -1114,12 +1114,11 @@ static void vss_send(struct vss_task *vsst) } } -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; @@ -1151,6 +1150,7 @@ static void vss_post_select(struct sched *s, struct task *t) (vss_next() && vss_playing())) tv_add(now, &vsst->announce_tv, &vsst->data_send_barrier); vss_send(vsst); + return 0; } /** diff --git a/wav_filter.c b/wav_filter.c index 716d8dd9..83b81fb2 100644 --- a/wav_filter.c +++ b/wav_filter.c @@ -74,7 +74,7 @@ static void wav_pre_select(struct sched *s, struct task *t) 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; @@ -83,12 +83,11 @@ static void wav_post_select(__a_unused struct sched *s, struct task *t) 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) { @@ -113,13 +112,13 @@ static void wav_post_select(__a_unused struct sched *s, struct task *t) 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; } /** diff --git a/web/header.html b/web/header.html index 464db042..f7cb2776 100644 --- a/web/header.html +++ b/web/header.html @@ -2,7 +2,7 @@ "http://www.w3.org/TR/html4/loose.dtd"> - + Paraslash diff --git a/web/header2.html b/web/header2.html index c8de7afd..94fbcad2 100644 --- a/web/header2.html +++ b/web/header2.html @@ -2,7 +2,7 @@ "http://www.w3.org/TR/html4/loose.dtd"> - + Paraslash diff --git a/web/manual.m4 b/web/manual.m4 index 43d5f28a..3f5f148f 100644 --- a/web/manual.m4 +++ b/web/manual.m4 @@ -77,11 +77,11 @@ The paraslash executables *para_server* -para_server streams binary audio data (MP3, OGG/Vorbis, OGG/Speex, -M4A, WMA files) over local and/or remote networks. It listens on a -TCP port and accepts commands such as play, stop, pause, next from -authenticated clients. There are many more commands though, see the -man page of para_server for a description of all commands. +para_server streams binary audio data (MP3, ...) over local and/or +remote networks. It listens on a TCP port and accepts commands such +as play, stop, pause, next from authenticated clients. There are +many more commands though, see the man page of para_server for a +description of all commands. It supports three built-in network streaming protocols (senders/receivers): HTTP, DCCP, or UDP. This is explained in more diff --git a/wma.h b/wma.h index 33e34a35..f0ba7f63 100644 --- a/wma.h +++ b/wma.h @@ -25,7 +25,7 @@ struct asf_header_info { }; /* wma_common.c */ -int wma_log2(unsigned int v); +__a_const int wma_log2(unsigned int v); const char *search_pattern(const char *pattern, int pattern_len, const char *buf, int buf_size); int read_asf_header(const char *buf, int loaded, struct asf_header_info *ahi); diff --git a/wmadec_filter.c b/wmadec_filter.c index 20f9df44..8b751f04 100644 --- a/wmadec_filter.c +++ b/wmadec_filter.c @@ -17,7 +17,6 @@ #define _XOPEN_SOURCE 600 -#include #include #include #include @@ -1211,7 +1210,7 @@ static int wmadec_execute(struct btr_node *btrn, const char *cmd, char **result) #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; @@ -1222,12 +1221,11 @@ static void wmadec_post_select(__a_unused struct sched *s, struct task *t) 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; @@ -1263,11 +1261,11 @@ next_buffer: 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) diff --git a/write.c b/write.c index 3deef147..866ea433 100644 --- a/write.c +++ b/write.c @@ -86,10 +86,10 @@ static void write_pre_select(struct sched *s, struct task *t) 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) diff --git a/write.h b/write.h index 2573ba5c..82921608 100644 --- a/write.h +++ b/write.h @@ -65,7 +65,7 @@ struct writer { * * 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. * diff --git a/write_common.c b/write_common.c index a4f908ac..44ccf88a 100644 --- a/write_common.c +++ b/write_common.c @@ -113,8 +113,8 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent, .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); }