+ return 0;
+
+ if (sot->must_set_nonblock_flag) {
+ ret = mark_fd_nonblocking(STDOUT_FILENO);
+ if (ret < 0)
+ goto out;
+ sot->must_set_nonblock_flag = false;
+ }
+ for (;;) {
+ sz = btr_next_buffer(btrn, &buf);
+ if (sz == 0)
+ break;
+ ret = xwrite(STDOUT_FILENO, buf, sz);
+ if (ret <= 0)
+ break;
+ btr_consume(btrn, ret);
+ }
+out:
+ if (ret < 0) {
+ btr_remove_node(&sot->btrn);
+ /* Revert to blocking mode if necessary. */
+ fcntl(STDOUT_FILENO, F_SETFL, sot->fd_flags);
+ }
+ return ret;
+}
+
+/**
+ * Register a stdout task structure.
+ *
+ * \param sot The stdout task structure to register.
+ * \param s The task will be added to this scheduler's task list.
+ *
+ * This sets up \a sot and registers a task with \a sot as context pointer.
+ */
+void stdout_task_register(struct stdout_task *sot, struct sched *s)
+{
+ int ret;
+ struct task_info ti = {
+ .pre_select = stdout_pre_select,
+ .post_select = stdout_post_select,
+ .context = sot,
+ .name = "stdout",
+ };
+
+ /* See \ref stdin.c for details. */
+ ret = fcntl(STDOUT_FILENO, F_GETFL);
+ if (ret < 0) {
+ PARA_EMERG_LOG("F_GETFL: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ sot->fd_flags = ret;
+ sot->must_set_nonblock_flag = (sot->fd_flags & O_NONBLOCK) == 0
+ && !isatty(STDOUT_FILENO);
+ sot->task = task_register(&ti, s);