/* * The function assumes that the following patterns have fixed sizes: * - "BeginString=" ("8=") is 2 bytes long * - "CheckSum=" ("10=") is 3 bytes long * - "MsgType=" ("35=") is 3 bytes long */ static bool checksum(struct fix_message *self, struct buffer *buffer) { const char *start; int offset; start = buffer_start(buffer); /* The number of bytes between tag MsgType and buffer's start */ offset = start - (self->msg_type - 3); /* * Checksum tag and its trailing delimiter increase * the message's length by seven bytes - "10=***\x01" */ if (buffer_size(buffer) + offset < self->body_length + 7) return false; /* Buffer's start will point to the CheckSum tag */ buffer_advance(buffer, self->body_length - offset); self->check_sum = parse_field(buffer, CheckSum); if (!self->check_sum) return false; if (!verify_checksum(self, buffer)) return false; /* Go back to analyze other fields */ buffer_advance(buffer, start - buffer_start(buffer)); return true; }
int fix_message_parse(struct fix_message *self, struct buffer *buffer) { unsigned long size; const char *start; self->head_buf = buffer; retry: start = buffer_start(buffer); size = buffer_size(buffer); if (!size) goto fail; if (!first_three_fields(self)) goto fail; if (!checksum(self, buffer)) goto fail; rest_of_message(self, buffer); return 0; fail: if (size > FIX_MAX_MESSAGE_SIZE) goto retry; buffer_advance(buffer, start - buffer_start(buffer)); return -1; }
int fix_message_parse(struct fix_message *self, struct buffer *buffer) { int ret = FIX_MSG_STATE_PARTIAL; unsigned long size; const char *start; self->head_buf = buffer; retry: start = buffer_start(buffer); size = buffer_size(buffer); if (!size) goto fail; ret = first_three_fields(self); if (ret) goto fail; ret = checksum(self, buffer); if (ret) goto fail; rest_of_message(self, buffer); return 0; fail: if (ret != FIX_MSG_STATE_PARTIAL) goto retry; buffer_advance(buffer, start - buffer_start(buffer)); return -1; }
reckless::detail::thread_input_buffer::thread_input_buffer(std::size_t size) : input_consumed_flag(false), size_(size), pinput_start_(buffer_start()), pinput_end_(buffer_start()) { }
char* reckless::detail::thread_input_buffer::wraparound() { #ifndef NDEBUG auto p = pinput_start_.load(std::memory_order_relaxed); auto marker = *reinterpret_cast<formatter_dispatch_function_t**>(p); assert(WRAPAROUND_MARKER == marker); #endif pinput_start_.store(buffer_start(), std::memory_order_relaxed); return buffer_start(); }
// Moves an input-buffer pointer forward by the given distance while // maintaining the invariant that: // // * p is aligned by FRAME_ALIGNMENT // * p never points at the end of the buffer; it always wraps around to the // beginning of the circular buffer. // // The distance must never be so great that the pointer moves *past* the end of // the buffer. To do so would be an error in our context, since no input frame // is allowed to be discontinuous. char* reckless::detail::thread_input_buffer::advance_frame_pointer(char* p, std::size_t distance) { assert(is_aligned(p)); assert(is_aligned(distance)); p += distance; assert(size_ >= static_cast<std::size_t>(p - buffer_start())); auto remaining = size_ - (p - buffer_start()); if(remaining == 0) p = buffer_start(); return p; }
//Scans the full the Cog and returns iterator. iterator scan_full_array( struct cog *cog) { if( cog->type == COG_BTREE ) { struct cog *a = cog->data.btree.lhs; struct cog *b = cog->data.btree.rhs; return iter_concat( scan_full_array(a), scan_full_array(b) ); } if( cog->type == COG_CONCAT ) { struct cog *a = cog->data.concat.lhs; struct cog *b = cog->data.concat.rhs; return iter_concat( scan_full_array(a), scan_full_array(b) ); } if( cog->type == COG_SORTEDARRAY ) { int start = cog->data.array.start; int len = cog->data.array.len; struct record *in = buffer_start(cog->data.array.records, start); struct buffer *out = buffer_alloc(cog->data.sortedarray.len + 1); int i, tgt; iterator ret; for(i = 0, tgt = 0; i < len; i++){ record_copy(&(in[i]), &(out->data[tgt])); tgt++; } ret = array_iter_alloc(out, 0, tgt); return ret; } if( cog->type == COG_ARRAY ) { int start = cog->data.array.start; int len = cog->data.array.len; struct record *in = buffer_start(cog->data.array.records, start); struct buffer *out = buffer_alloc(cog->data.array.len ); int i, tgt; iterator ret; for(i = 0, tgt = 0; i < len; i++){ record_copy(&(in[i]), &(out->data[tgt])); tgt++; } ret = array_iter_alloc(out, 0, tgt); return ret; } fprintf(stderr, "Invalid Cog Type %d\n", cog->type); exit(-1); }
struct itch50_message *itch50_message_decode(struct buffer *buf) { size_t available; size_t size; void *start; u8 type; available = buffer_size(buf); if (!available) return NULL; type = buffer_peek_8(buf); size = itch50_message_size(type); if (!size) return NULL; if (available < size) return NULL; start = buffer_start(buf); buffer_advance(buf, size); return start; }
int itch40_message_decode(struct buffer *buf, struct itch40_message *msg) { size_t available; size_t size; u8 type; available = buffer_size(buf); if (!available) return -1; type = buffer_peek_8(buf); size = itch40_message_size(type); if (!size) return -1; if (available < size) return -1; memcpy(msg, buffer_start(buf), size); buffer_advance(buf, size); return 0; }
int boe_message_decode(struct buffer *buf, struct boe_message *msg, size_t size) { u16 magic, len; void *start; size_t count; start = buffer_start(buf); magic = buffer_get_le16(buf); if (magic != BOE_MAGIC) return -1; len = buffer_get_le16(buf); count = BOE_MAGIC_LEN + len; if (count > size) count = size; memcpy(msg, start, count); buffer_advance(buf, len - BOE_MSG_LENGTH_LEN); return 0; }
static void generic_load(mpg123_handle *fr, char *arg, int state) { if(param.usebuffer) { buffer_resync(); if(mode == MODE_PAUSED && state != MODE_PAUSED) buffer_start(); } if(mode != MODE_STOPPED) { close_track(); mode = MODE_STOPPED; } if(!open_track(arg)) { generic_sendmsg("E Error opening stream: %s", arg); generic_sendmsg("P 0"); return; } mpg123_seek(fr, 0, SEEK_SET); /* This finds ID3v2 at beginning. */ if(mpg123_meta_check(fr) & MPG123_NEW_ID3) { generic_sendinfoid3(fr); mpg123_meta_free(fr); } else generic_sendinfo(arg); if(htd.icy_name.fill) generic_sendmsg("I ICY-NAME: %s", htd.icy_name.p); if(htd.icy_url.fill) generic_sendmsg("I ICY-URL: %s", htd.icy_url.p); mode = state; init = 1; generic_sendmsg(mode == MODE_PAUSED ? "P 1" : "P 2"); }
ssize_t buffer_xwrite(struct buffer *buf, int fd) { size_t count; void *start; start = buffer_start(buf); count = buffer_size(buf); return xwrite(fd, start, count); }
int fix_message_parse(struct fix_message *self, struct fix_dialect *dialect, struct buffer *buffer, unsigned long flags) { const char *start; int ret; self->head_buf = buffer; TRACE(LIBTRADING_FIX_MESSAGE_PARSE(self, dialect, buffer)); retry: ret = FIX_MSG_STATE_PARTIAL; start = buffer_start(buffer); if (!buffer_size(buffer)) goto fail; ret = first_three_fields(self, flags); if (ret) goto fail; ret = checksum(self, buffer, flags); if (ret) goto fail; rest_of_message(self, dialect, buffer); self->iov[0].iov_base = (void *)start; self->iov[0].iov_len = buffer_start(buffer) - start; TRACE(LIBTRADING_FIX_MESSAGE_PARSE_RET()); return 0; fail: if (ret != FIX_MSG_STATE_PARTIAL) goto retry; buffer_advance(buffer, start - buffer_start(buffer)); TRACE(LIBTRADING_FIX_MESSAGE_PARSE_ERR()); return -1; }
/* * The function assumes that the following patterns have fixed sizes: * - "BeginString=" ("8=") is 2 bytes long * - "CheckSum=" ("10=") is 3 bytes long * - "MsgType=" ("35=") is 3 bytes long */ static int checksum(struct fix_message *self, struct buffer *buffer, unsigned long flags) { const char *start; int offset; int ret; start = buffer_start(buffer); /* The number of bytes between tag MsgType and buffer's start */ offset = start - (self->msg_type - 3); /* * Checksum tag and its trailing delimiter increase * the message's length by seven bytes - "10=***\x01" */ if (buffer_size(buffer) + offset < self->body_length + 7) { ret = FIX_MSG_STATE_PARTIAL; goto exit; } if (flags & FIX_PARSE_FLAG_NO_CSUM) { ret = 0; goto exit; } /* Buffer's start will point to the CheckSum tag */ buffer_advance(buffer, self->body_length - offset); ret = match_field(buffer, CheckSum, &self->check_sum); if (ret) goto exit; if (!verify_checksum(self, buffer)) { ret = FIX_MSG_STATE_GARBLED; goto exit; } /* Go back to analyze other fields */ buffer_advance(buffer, start - buffer_start(buffer)); exit: return ret; }
char *buffer_find(struct buffer *buf, char c) { while (buffer_first_char(buf) != c) { if (!buffer_size(buf)) return NULL; buffer_advance(buf, 1); } return buffer_start(buf); }
void buffer_compact(struct buffer *buf) { size_t count; void *start; start = buffer_start(buf); count = buffer_size(buf); memmove(buf->data, start, count); buf->start = 0; buf->end = count; }
char *buffer_find(struct buffer *buf, u8 c) { while (true) { if (!buffer_size(buf)) return NULL; if (buffer_peek_8(buf) == c) break; buffer_advance(buf, 1); } return buffer_start(buf); }
bool do_read(struct epoll_event *p_entry, some_fd epoll_fd GCC_ATTR_UNUSED_PARAM) { g2_connection_t *w_entry = (g2_connection_t *)p_entry->data.ptr; ssize_t result = 0; bool ret_val = true; #ifdef DEBUG_DEVEL if(!w_entry->recv) { logg_posd(LOGF_DEBUG, "%s Ip: %p#I\tFDNum: %i\n", "no recv buffer!", &w_entry->remote_host, w_entry->com_socket); w_entry->flags.dismissed = true; return false; } #endif do { set_s_errno(0); result = my_epoll_recv(epoll_fd, w_entry->com_socket, buffer_start(*w_entry->recv), buffer_remaining(*w_entry->recv), 0); } while(-1 == result && EINTR == s_errno); switch(result) { default: w_entry->recv->pos += result; break; case 0: if(buffer_remaining(*w_entry->recv)) { if(EAGAIN != s_errno) { logg_posd(LOGF_DEVEL_OLD, "%s ERRNO=%i Ip: %p#I\tFDNum: %i\n", "EOF reached!", s_errno, &w_entry->remote_host, w_entry->com_socket); w_entry->flags.dismissed = true; ret_val = false; } else logg_devel("Nothing to read!\n"); } break; case -1: if(!(EAGAIN == s_errno || EWOULDBLOCK == s_errno)) { logg_serrno(LOGF_DEBUG, "write"); w_entry->flags.dismissed = true; ret_val = false; } break; } logg_develd_old("ret_val: %i\tpos: %u\tlim: %u\n", ret_val, w_entry->recv->pos, w_entry->recv->limit); return ret_val; }
static int soupbin3_packet_decode(struct buffer *buf, uint16_t len, struct soupbin3_packet *packet) { size_t size; void *start; start = buffer_start(buf); size = sizeof(uint16_t) + len; memcpy(packet, start - sizeof(uint16_t), size); buffer_advance(buf, len); return 0; }
static int parse_value(struct buffer *self, const char **value) { char *start, *end; start = buffer_start(self); end = buffer_find(self, 0x01); if (!end || *end != 0x01) return FIX_MSG_STATE_PARTIAL; buffer_advance(self, 1); *value = start; return 0; }
static const char *parse_value(struct buffer *self) { char *start, *end; start = buffer_start(self); end = buffer_find(self, 0x01); if (!end) return NULL; if (*end != 0x01) return NULL; buffer_advance(self, 1); return start; }
static int parse_tag(struct buffer *self, int *tag) { const char *delim; const char *start; const char *end; int ret; start = buffer_start(self); delim = buffer_find(self, '='); if (!delim || *delim != '=') return FIX_MSG_STATE_PARTIAL; ret = fix_uatoi(start, &end); if (end != delim) return FIX_MSG_STATE_GARBLED; buffer_advance(self, 1); *tag = ret; return 0; }
static int parse_tag(struct buffer *self) { const char *delim; const char *start; char *end; int ret; start = buffer_start(self); delim = buffer_find(self, '='); if (!delim) return 0; if (*delim != '=') return 0; ret = strtol(start, &end, 10); if (end != delim) return 0; buffer_advance(self, 1); return ret; }
int cmd_check(int argc, char *argv[]) { struct buffer *comp_buf, *uncomp_buf; z_stream stream; struct stat st; int fd; setlocale(LC_ALL, ""); if (argc < 2) usage(); parse_args(argc, argv); init_stream(&stream); fd = open(filename, O_RDONLY); if (fd < 0) die("%s: %s: %s\n", program, filename, strerror(errno)); if (fstat(fd, &st) < 0) die("%s: %s: %s\n", program, filename, strerror(errno)); comp_buf = buffer_mmap(fd, st.st_size); if (!comp_buf) die("%s: %s\n", program, strerror(errno)); stream.next_in = (void *) buffer_start(comp_buf); uncomp_buf = buffer_new(BUFFER_SIZE); if (!uncomp_buf) die("%s: %s\n", program, strerror(errno)); for (;;) { struct itch41_message *msg; retry_size: if (buffer_size(uncomp_buf) < sizeof(u16)) { ssize_t nr; buffer_compact(uncomp_buf); nr = buffer_inflate(comp_buf, uncomp_buf, &stream); if (nr < 0) die("%s: zlib error\n", program); if (!nr) break; if (show_progress) print_progress(comp_buf, st.st_size); goto retry_size; } buffer_advance(uncomp_buf, sizeof(u16)); retry_message: msg = itch41_message_decode(uncomp_buf); if (!msg) { ssize_t nr; buffer_compact(uncomp_buf); nr = buffer_inflate(comp_buf, uncomp_buf, &stream); if (nr < 0) die("%s: zlib error\n", program); if (!nr) break; if (show_progress) print_progress(comp_buf, st.st_size); goto retry_message; } if (verbose) printf("%c", msg->MessageType); stats[msg->MessageType - 'A']++; } printf("\n"); buffer_munmap(comp_buf); buffer_delete(uncomp_buf); if (close(fd) < 0) die("%s: %s: %s\n", program, filename, strerror(errno)); release_stream(&stream); print_stats(); return 0; }
char* reckless::detail::thread_input_buffer::allocate_input_frame(std::size_t size) { // Conceptually, we have the invariant that // pinput_start_ <= pinput_end_, // and the memory area after pinput_end is free for us to use for // allocating a frame. However, the fact that it's a circular buffer means // that: // // * The area after pinput_end is actually non-contiguous, wraps around // at the end of the buffer and ends at pinput_start. // // * Except, when pinput_end itself has fallen over the right edge and we // have the case pinput_end <= pinput_start. Then the *used* memory is // non-contiguous, and the free memory is contiguous (it still starts at // pinput_end and ends at pinput_start modulo circular buffer size). // // (This is easier to understand by drawing it on a paper than by reading // the comment text). auto mask = frame_alignment_mask(); size = (size + mask) & ~mask; // We can't write a frame that is larger than the entire capacity of the // input buffer. If you hit this assert then you either need to write a // smaller log entry, or you need to make the input buffer larger. assert(size < size_); while(true) { auto pinput_end = pinput_end_; // FIXME these asserts should / can be enabled again? assert(static_cast<std::size_t>(pinput_end - buffer_start()) < size_); assert(is_aligned(pinput_end)); // Even if we get an "old" value for pinput_start_ here, that's OK // because other threads will never cause the amount of available // buffer space to shrink. So either there is enough buffer space and // we're done, or there isn't and we'll wait for an input-consumption // event which creates a full memory barrier and hence gives us an // updated value for pinput_start_. So memory_order_relaxed should be // fine here. auto pinput_start = pinput_start_.load(std::memory_order_relaxed); std::ptrdiff_t free = pinput_start - pinput_end; if(free > 0) { // Free space is contiguous. // Technically, there is enough room if size == free. But the // problem with using the free space in this situation is that when // we increase pinput_end_ by size, we end up with pinput_start_ == // pinput_end_. Now, given that state, how do we know if the buffer // is completely filled or empty? So, it's easier to just check for // size < free instead of size <= free, and pretend we're out // of space if size == free. Same situation applies in the else // clause below. if(likely(static_cast<std::ptrdiff_t>(size) < free)) { pinput_end_ = advance_frame_pointer(pinput_end, size); return pinput_end; } else { // Not enough room. Wait for the output thread to consume some // input. wait_input_consumed(); } } else { // Free space is non-contiguous. // TODO should we use an end pointer instead of a size_? std::size_t free1 = size_ - (pinput_end - buffer_start()); if(likely(size < free1)) { // There's enough room in the first segment. pinput_end_ = advance_frame_pointer(pinput_end, size); return pinput_end; } else { std::size_t free2 = pinput_start - buffer_start(); if(likely(size < free2)) { // We don't have enough room for a continuous input frame // in the first segment (at the end of the circular // buffer), but there is enough room in the second segment // (at the beginning of the buffer). To instruct the output // thread to skip ahead to the second segment, we need to // put a marker value at the current position. We're // supposed to be guaranteed enough room for the wraparound // marker because frame alignment is at least the size of // the marker. *reinterpret_cast<formatter_dispatch_function_t**>(pinput_end_) = WRAPAROUND_MARKER; pinput_end_ = advance_frame_pointer(buffer_start(), size); return buffer_start(); } else { // Not enough room. Wait for the output thread to consume // some input. wait_input_consumed(); } } } } }
static int logg_internal(const enum loglevel level, const char *file, const char *func, const unsigned int line, int log_errno, const char *fmt, va_list args ) { struct big_buff *logg_buff; int ret_val, old_errno, retry_cnt; old_errno = errno; /* get our tls-print buf */ if(!(logg_buff = logg_get_buf())) { /* * hmmm, something went wrong, lets see, if we can get the message to * the user by other means */ return do_vlogging(level, fmt, args); } /* add time, if wanted, to buffer */ if(server.settings.logging.add_date_time) logg_buff->pos += add_time_to_buffer(buffer_start(*logg_buff), buffer_remaining(*logg_buff)); /* put out the "extra" stuff, if there */ if(file) { retry_cnt = 0; prefetch(strlpcpy); prefetch(file); prefetch(func); /* * calling snprintf for 2 * strcpy + an itoa is "nice" * but goes round the full stdio bloat: * snprintf->vsnprintf->vfprintf-> myriads of funcs to print */ do { char *sptr, *stmp, *rstart; size_t remaining; stmp = sptr = buffer_start(*logg_buff); remaining = buffer_remaining(*logg_buff); rstart = strlpcpy(sptr, file, remaining); remaining -= rstart - sptr; if(unlikely(remaining < 7)) goto realloc; sptr = rstart; *sptr++ = ':'; remaining--; rstart = strlpcpy(sptr, func, remaining); remaining -= rstart - sptr; if(unlikely(remaining < 18)) /* make sure we have enough space */ goto realloc; sptr = rstart; *sptr++ = '('; *sptr++ = ')'; *sptr++ = '@'; remaining -= 3; rstart = put_dec_trunc(sptr, line); /* 99,999 lines should be... */ strreverse(sptr, rstart - 1); remaining -= rstart - sptr; if(unlikely(remaining < 2)) goto realloc; sptr = rstart; *sptr++ = ':'; *sptr++ = ' '; logg_buff->pos += sptr - stmp; break; realloc: /* now we are in a slow path, no need to hurry */ { struct big_buff *tmp_buff; size_t len = strlen(file) + strlen(func) + 6 + 30 + strlen(fmt) * 3; len = ROUND_ALIGN(len * 2, 2048) + logg_buff->capacity; tmp_buff = realloc(logg_buff, sizeof(*logg_buff) + len); if(tmp_buff) { logg_buff = tmp_buff; logg_buff->limit = logg_buff->capacity = len; } else break; retry_cnt++; } } while(retry_cnt < 4); } ret_val = 0; retry_cnt = 0; /* format the message in tls */ do { size_t len = logg_buff->capacity; va_list tmp_valist; if(ret_val < 0) { len *= 2; if(++retry_cnt > 4) { ret_val = 0; break; } } else if((size_t)ret_val > buffer_remaining(*logg_buff)) len = ROUND_ALIGN((size_t)ret_val * 2, 1024); /* align to a k */ if(unlikely(len != logg_buff->capacity)) { struct big_buff *tmp_buf = realloc(logg_buff, sizeof(*logg_buff) + len); if(tmp_buf) { logg_buff = tmp_buf; logg_buff->limit = logg_buff->capacity = len; } else { ret_val = buffer_remaining(*logg_buff); break; } } /* put msg printed out in buffer */ va_copy(tmp_valist, args); ret_val = my_vsnprintf(buffer_start(*logg_buff), buffer_remaining(*logg_buff), fmt, tmp_valist); va_end(tmp_valist); /* error? repeat */ } while(unlikely(ret_val < 0 || (size_t)ret_val > buffer_remaining(*logg_buff))); logg_buff->pos += (size_t)ret_val; /* add errno string if wanted */ if(log_errno) { if(buffer_remaining(*logg_buff) < STRERROR_R_SIZE + 4) { size_t len = logg_buff->capacity * 2; struct big_buff *tmp_buff = realloc(logg_buff, sizeof(*logg_buff) + len); if(!tmp_buff) goto no_errno; logg_buff = tmp_buff; logg_buff->limit = logg_buff->capacity += len; } *buffer_start(*logg_buff) = ':'; logg_buff->pos++; *buffer_start(*logg_buff) = ' '; logg_buff->pos++; { #if defined STRERROR_R_CHAR_P || defined HAVE_MTSAFE_STRERROR || WIN32 || !(defined HAVE_STRERROR_R || HAVE_DECL_STRERROR_R-0 > 0) size_t err_str_len; # ifdef WIN32 const char *s = buffer_start(*logg_buff); if(!(err_str_len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, /* flags */ 0, /* pointer to other source */ old_errno, /* msg id */ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* language */ buffer_start(*logg_buff), /* buffer */ buffer_remaining(*logg_buff)-1, /* size */ 0 /* va_args */ ))) { s = "Unknown system error"; err_str_len = strlen(s) < buffer_remaining(*logg_buff)-2 ? strlen(s) : buffer_remaining(*logg_buff)-2; } # else # ifdef STRERROR_R_CHAR_P /* * the f***ing GNU-Version of strerror_r wich returns * a char * to the buffer.... * This sucks especially in conjunction with strnlen, * wich needs a #define __GNU_SOURCE, but conflicts * with this... */ const char *s = strerror_r(old_errno, buffer_start(*logg_buff), buffer_remaining(*logg_buff)-2); # else /* * Ol Solaris seems to have a static msgtable, so * strerror is threadsafe and we don't have a * _r version */ /* * we also simply fall into here if strerror is not thread * safe, but we have nothing else. * Since what should we do in this case... _sys_errlist * is a bsd extentions. */ const char *s = strerror(old_errno); # endif if(s) err_str_len = strnlen(s, buffer_remaining(*logg_buff)-2); else { s = "Unknown system error"; err_str_len = strlen(s) < (buffer_remaining(*logg_buff)-2) ? strlen(s) : buffer_remaining(*logg_buff)-2; } # endif if(s != buffer_start(*logg_buff)) my_memcpy(buffer_start(*logg_buff), s, err_str_len); logg_buff->pos += err_str_len; #else if(!strerror_r(old_errno, buffer_start(*logg_buff), buffer_remaining(*logg_buff))) // if(!strerror_s(buffer_start(*logg_buff), buffer_remaining(*logg_buff), old_errno)) logg_buff->pos += strnlen(buffer_start(*logg_buff), buffer_remaining(*logg_buff)); else { size_t err_l; const char *bs; if(EINVAL == errno) { err_l = str_size("Unknown errno value!"); bs = "Unknown errno value!"; } else if(ERANGE == errno) { err_l = str_size("errno msg to long for buffer!"); bs = "errno msg to long for buffer!"; } else { err_l = str_size("failure while retrieving errno msg!"); bs = "failure while retrieving errno msg!"; } err_l = (buffer_remaining(*logg_buff)-2) >= err_l ? err_l : (buffer_remaining(*logg_buff)-2); my_memcpy(buffer_start(*logg_buff), bs, err_l); logg_buff->pos += err_l; } #endif } *buffer_start(*logg_buff) = '\n'; logg_buff->pos++; *buffer_start(*logg_buff) = '\0'; } no_errno: /* output that stuff */ buffer_flip(*logg_buff); ret_val = do_logging(level, buffer_start(*logg_buff), buffer_remaining(*logg_buff)); logg_ret_buf(logg_buff); return ret_val; }
static void term_handle_input(mpg123_handle *fr, audio_output_t *ao, int do_delay) { int n = 1; /* long offset = 0; */ while(n > 0) { fd_set r; struct timeval t; char val; t.tv_sec=0; t.tv_usec=(do_delay) ? 10*1000 : 0; FD_ZERO(&r); FD_SET(0,&r); n = select(1,&r,NULL,NULL,&t); if(n > 0 && FD_ISSET(0,&r)) { if(read(0,&val,1) <= 0) break; switch(tolower(val)) { case MPG123_BACK_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr)); if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0) error1("Seek to begin failed: %s", mpg123_strerror(fr)); framenum=0; break; case MPG123_NEXT_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); /* was: plain_buffer_resync */ next_track(); break; case MPG123_QUIT_KEY: debug("QUIT"); if(stopped) { stopped = 0; if(param.usebuffer) { buffer_resync(); buffer_start(); } } set_intflag(); offset = 0; break; case MPG123_PAUSE_KEY: paused=1-paused; if(paused) { /* Not really sure if that is what is wanted This jumps in audio output, but has direct reaction to pausing loop. */ if(param.usebuffer) buffer_resync(); pause_recycle(fr); } if(stopped) { stopped=0; if(param.usebuffer) buffer_start(); } fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING); break; case MPG123_STOP_KEY: case ' ': /* when seeking while stopped and then resuming, I want to prevent the chirp from the past */ if(!param.usebuffer) ao->flush(ao); stopped=1-stopped; if(paused) { paused=0; offset -= pause_cycle; } if(param.usebuffer) { if(stopped) buffer_stop(); else { /* When we stopped buffer for seeking, we must resync. */ if(offset) buffer_resync(); buffer_start(); } } fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING); break; case MPG123_FINE_REWIND_KEY: if(param.usebuffer) seekmode(); offset--; break; case MPG123_FINE_FORWARD_KEY: seekmode(); offset++; break; case MPG123_REWIND_KEY: seekmode(); offset-=10; break; case MPG123_FORWARD_KEY: seekmode(); offset+=10; break; case MPG123_FAST_REWIND_KEY: seekmode(); offset-=50; break; case MPG123_FAST_FORWARD_KEY: seekmode(); offset+=50; break; case MPG123_VOL_UP_KEY: mpg123_volume_change(fr, 0.02); break; case MPG123_VOL_DOWN_KEY: mpg123_volume_change(fr, -0.02); break; case MPG123_PITCH_UP_KEY: case MPG123_PITCH_BUP_KEY: case MPG123_PITCH_DOWN_KEY: case MPG123_PITCH_BDOWN_KEY: case MPG123_PITCH_ZERO_KEY: { double new_pitch = param.pitch; switch(val) /* Not tolower here! */ { case MPG123_PITCH_UP_KEY: new_pitch += MPG123_PITCH_VAL; break; case MPG123_PITCH_BUP_KEY: new_pitch += MPG123_PITCH_BVAL; break; case MPG123_PITCH_DOWN_KEY: new_pitch -= MPG123_PITCH_VAL; break; case MPG123_PITCH_BDOWN_KEY: new_pitch -= MPG123_PITCH_BVAL; break; case MPG123_PITCH_ZERO_KEY: new_pitch = 0.0; break; } set_pitch(fr, ao, new_pitch); fprintf(stderr, "New pitch: %f\n", param.pitch); } break; case MPG123_VERBOSE_KEY: param.verbose++; if(param.verbose > VERBOSE_MAX) { param.verbose = 0; clear_stat(); } mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0); break; case MPG123_RVA_KEY: if(++param.rva > MPG123_RVA_MAX) param.rva = 0; mpg123_param(fr, MPG123_RVA, param.rva, 0); mpg123_volume_change(fr, 0.); break; case MPG123_PREV_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); /* was: plain_buffer_resync */ prev_track(); break; case MPG123_PLAYLIST_KEY: fprintf(stderr, "%s\nPlaylist (\">\" indicates current track):\n", param.verbose ? "\n" : ""); print_playlist(stderr, 1); fprintf(stderr, "\n"); break; case MPG123_TAG_KEY: fprintf(stderr, "%s\n", param.verbose ? "\n" : ""); print_id3_tag(fr, param.long_id3, stderr); fprintf(stderr, "\n"); break; case MPG123_MPEG_KEY: if(param.verbose) print_stat(fr,0,0); /* Make sure that we are talking about the correct frame. */ fprintf(stderr, "\n"); print_header(fr); fprintf(stderr, "\n"); break; case MPG123_HELP_KEY: { /* This is more than the one-liner before, but it's less spaghetti. */ int i; fprintf(stderr,"\n\n -= terminal control keys =-\n"); for(i=0; i<(sizeof(term_help)/sizeof(struct keydef)); ++i) { if(term_help[i].key2) fprintf(stderr, "[%c] or [%c]", term_help[i].key, term_help[i].key2); else fprintf(stderr, "[%c]", term_help[i].key); fprintf(stderr, "\t%s\n", term_help[i].desc); } fprintf(stderr, "\n"); } break; case MPG123_FRAME_INDEX_KEY: case MPG123_VARIOUS_INFO_KEY: if(param.verbose) fprintf(stderr, "\n"); switch(val) /* because of tolower() ... */ { case MPG123_FRAME_INDEX_KEY: print_index(fr); { long accurate; if(mpg123_getstate(fr, MPG123_ACCURATE, &accurate, NULL) == MPG123_OK) fprintf(stderr, "Accurate position: %s\n", (accurate == 0 ? "no" : "yes")); else error1("Unable to get state: %s", mpg123_strerror(fr)); } break; case MPG123_VARIOUS_INFO_KEY: { const char* curdec = mpg123_current_decoder(fr); if(curdec == NULL) fprintf(stderr, "Cannot get decoder info!\n"); else fprintf(stderr, "Active decoder: %s\n", curdec); } } break; default: ; } } } }
int control_generic (mpg123_handle *fr) { struct timeval tv; fd_set fds; int n; /* ThOr */ char alive = 1; char silent = 0; /* responses to stderr for frontends needing audio data from stdout */ if (param.remote_err) outstream = stderr; else outstream = stdout; #ifndef WIN32 setlinebuf(outstream); #else /* perhaps just use setvbuf as it's C89 */ /* fprintf(outstream, "You are on Win32 and want to use the control interface... tough luck: We need a replacement for select on STDIN first.\n"); return 0; setvbuf(outstream, (char*)NULL, _IOLBF, 0); */ #endif /* the command behaviour is different, so is the ID */ /* now also with version for command availability */ fprintf(outstream, "@R MPG123 (ThOr) v7\n"); #ifdef FIFO if(param.fifo) { if(param.fifo[0] == 0) { error("You wanted an empty FIFO name??"); return 1; } #ifndef WANT_WIN32_FIFO unlink(param.fifo); if(mkfifo(param.fifo, 0666) == -1) { error2("Failed to create FIFO at %s (%s)", param.fifo, strerror(errno)); return 1; } debug("going to open named pipe ... blocking until someone gives command"); #endif /* WANT_WIN32_FIFO */ #ifdef WANT_WIN32_FIFO control_file = win32_fifo_mkfifo(param.fifo); #else control_file = open(param.fifo,O_RDONLY); #endif /* WANT_WIN32_FIFO */ debug("opened"); } #endif while (alive) { tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(control_file, &fds); /* play frame if no command needs to be processed */ if (mode == MODE_PLAYING) { #ifdef WANT_WIN32_FIFO n = win32_fifo_read_peek(&tv); #else n = select(32, &fds, NULL, NULL, &tv); #endif if (n == 0) { if (!play_frame()) { /* When the track ended, user may want to keep it open (to seek back), so there is a decision between stopping and pausing at the end. */ if(param.keep_open) { mode = MODE_PAUSED; /* Hm, buffer should be stopped already, shouldn't it? */ if(param.usebuffer) buffer_stop(); generic_sendmsg("P 1"); } else { mode = MODE_STOPPED; close_track(); generic_sendmsg("P 0"); } continue; } if (init) { print_remote_header(fr); init = 0; } if(silent == 0) { generic_sendstat(fr); if(mpg123_meta_check(fr) & MPG123_NEW_ICY) { char *meta; if(mpg123_icy(fr, &meta) == MPG123_OK) generic_sendmsg("I ICY-META: %s", meta != NULL ? meta : "<nil>"); } } } } else { /* wait for command */ while (1) { #ifdef WANT_WIN32_FIFO n = win32_fifo_read_peek(NULL); #else n = select(32, &fds, NULL, NULL, NULL); #endif if (n > 0) break; } } /* on error */ if (n < 0) { fprintf(stderr, "Error waiting for command: %s\n", strerror(errno)); return 1; } /* read & process commands */ if (n > 0) { short int len = 1; /* length of buffer */ char *cmd, *arg; /* variables for parsing, */ char *comstr = NULL; /* gcc thinks that this could be used uninitialited... */ char buf[REMOTE_BUFFER_SIZE]; short int counter; char *next_comstr = buf; /* have it initialized for first command */ /* read as much as possible, maybe multiple commands */ /* When there is nothing to read (EOF) or even an error, it is the end */ #ifdef WANT_WIN32_FIFO len = win32_fifo_read(buf,REMOTE_BUFFER_SIZE); #else len = read(control_file, buf, REMOTE_BUFFER_SIZE); #endif if(len < 1) { #ifdef FIFO if(len == 0 && param.fifo) { debug("fifo ended... reopening"); #ifdef WANT_WIN32_FIFO win32_fifo_mkfifo(param.fifo); #else close(control_file); control_file = open(param.fifo,O_RDONLY|O_NONBLOCK); #endif if(control_file < 0){ error1("open of fifo failed... %s", strerror(errno)); break; } continue; } #endif if(len < 0) error1("command read error: %s", strerror(errno)); break; } debug1("read %i bytes of commands", len); /* one command on a line - separation by \n -> C strings in a row */ for(counter = 0; counter < len; ++counter) { /* line end is command end */ if( (buf[counter] == '\n') || (buf[counter] == '\r') ) { debug1("line end at counter=%i", counter); buf[counter] = 0; /* now it's a properly ending C string */ comstr = next_comstr; /* skip the additional line ender of \r\n or \n\r */ if( (counter < (len - 1)) && ((buf[counter+1] == '\n') || (buf[counter+1] == '\r')) ) buf[++counter] = 0; /* next "real" char is first of next command */ next_comstr = buf + counter+1; /* directly process the command now */ debug1("interpreting command: %s", comstr); if(strlen(comstr) == 0) continue; /* PAUSE */ if (!strcasecmp(comstr, "P") || !strcasecmp(comstr, "PAUSE")) { if(mode != MODE_STOPPED) { if (mode == MODE_PLAYING) { mode = MODE_PAUSED; if(param.usebuffer) buffer_stop(); generic_sendmsg("P 1"); } else { mode = MODE_PLAYING; if(param.usebuffer) buffer_start(); generic_sendmsg("P 2"); } } else generic_sendmsg("P 0"); continue; } /* STOP */ if (!strcasecmp(comstr, "S") || !strcasecmp(comstr, "STOP")) { if (mode != MODE_STOPPED) { if(param.usebuffer) { buffer_stop(); buffer_resync(); } close_track(); mode = MODE_STOPPED; generic_sendmsg("P 0"); } else generic_sendmsg("P 0"); continue; } /* SILENCE */ if(!strcasecmp(comstr, "SILENCE")) { silent = 1; generic_sendmsg("silence"); continue; } if(!strcasecmp(comstr, "T") || !strcasecmp(comstr, "TAG")) { generic_sendalltag(fr); continue; } if(!strcasecmp(comstr, "SCAN")) { if(mode != MODE_STOPPED) { if(mpg123_scan(fr) == MPG123_OK) generic_sendmsg("SCAN done"); else generic_sendmsg("E %s", mpg123_strerror(fr)); } else generic_sendmsg("E No track loaded!"); continue; } if(!strcasecmp(comstr, "SAMPLE")) { off_t pos = mpg123_tell(fr); off_t len = mpg123_length(fr); /* I need to have portable printf specifiers that do not truncate the type... more autoconf... */ if(len < 0) generic_sendmsg("E %s", mpg123_strerror(fr)); else generic_sendmsg("SAMPLE %li %li", (long)pos, (long)len); continue; } if(!strcasecmp(comstr, "SHOWEQ")) { int i; generic_sendmsg("SHOWEQ {"); for(i=0; i<32; ++i) { generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_LEFT, i, mpg123_geteq(fr, MPG123_LEFT, i)); generic_sendmsg("SHOWEQ %i : %i : %f", MPG123_RIGHT, i, mpg123_geteq(fr, MPG123_RIGHT, i)); } generic_sendmsg("SHOWEQ }"); continue; } if(!strcasecmp(comstr, "STATE")) { long val; generic_sendmsg("STATE {"); /* Get some state information bits and display them. */ if(mpg123_getstate(fr, MPG123_ACCURATE, &val, NULL) == MPG123_OK) generic_sendmsg("STATE accurate %li", val); generic_sendmsg("STATE }"); continue; } /* QUIT */ if (!strcasecmp(comstr, "Q") || !strcasecmp(comstr, "QUIT")){ alive = FALSE; continue; } /* some HELP */ if (!strcasecmp(comstr, "H") || !strcasecmp(comstr, "HELP")) { generic_sendmsg("H {"); generic_sendmsg("H HELP/H: command listing (LONG/SHORT forms), command case insensitve"); generic_sendmsg("H LOAD/L <trackname>: load and start playing resource <trackname>"); generic_sendmsg("H LOADPAUSED/LP <trackname>: load but do not start playing resource <trackname>"); generic_sendmsg("H LOADLIST <entry> <url>: load a playlist from given <url>, and display its entries, optionally load and play one of these specificed by the integer <entry> (<0: just list, 0: play last track, >0:play track with that position in list)"); generic_sendmsg("H PAUSE/P: pause playback"); generic_sendmsg("H STOP/S: stop playback (closes file)"); generic_sendmsg("H JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> or change position by offset, same in seconds if number followed by \"s\""); generic_sendmsg("H VOLUME/V <percent>: set volume in % (0..100...); float value"); generic_sendmsg("H RVA off|(mix|radio)|(album|audiophile): set rva mode"); generic_sendmsg("H EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel %i (left) or %i (right) or %i (both)", MPG123_LEFT, MPG123_RIGHT, MPG123_LR); generic_sendmsg("H EQFILE <filename>: load EQ settings from a file"); generic_sendmsg("H SHOWEQ: show all equalizer settings (as <channel> <band> <value> lines in a SHOWEQ block (like TAG))"); generic_sendmsg("H SEEK/K <sample>|<+offset>|<-offset>: jump to output sample position <samples> or change position by offset"); generic_sendmsg("H SCAN: scan through the file, building seek index"); generic_sendmsg("H SAMPLE: print out the sample position and total number of samples"); generic_sendmsg("H SEQ <bass> <mid> <treble>: simple eq setting..."); generic_sendmsg("H PITCH <[+|-]value>: adjust playback speed (+0.01 is 1 %% faster)"); generic_sendmsg("H SILENCE: be silent during playback (meaning silence in text form)"); generic_sendmsg("H STATE: Print auxiliary state info in several lines (just try it to see what info is there)."); generic_sendmsg("H TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names."); generic_sendmsg("H The output is multiple lines, begin marked by \"@T {\", end by \"@T }\"."); generic_sendmsg("H ID3v1 data is like in the @I info lines (see below), just with \"@T\" in front."); generic_sendmsg("H An ID3v2 data field is introduced via ([ ... ] means optional):"); generic_sendmsg("H @T ID3v2.<NAME>[ [lang(<LANG>)] desc(<description>)]:"); generic_sendmsg("H The lines of data follow with \"=\" prefixed:"); generic_sendmsg("H @T =<one line of content in UTF-8 encoding>"); generic_sendmsg("H meaning of the @S stream info:"); generic_sendmsg("H %s", remote_header_help); generic_sendmsg("H The @I lines after loading a track give some ID3 info, the format:"); generic_sendmsg("H @I ID3:artist album year comment genretext"); generic_sendmsg("H where artist,album and comment are exactly 30 characters each, year is 4 characters, genre text unspecified."); generic_sendmsg("H You will encounter \"@I ID3.genre:<number>\" and \"@I ID3.track:<number>\"."); generic_sendmsg("H Then, there is an excerpt of ID3v2 info in the structure"); generic_sendmsg("H @I ID3v2.title:Blabla bla Bla"); generic_sendmsg("H for every line of the \"title\" data field. Likewise for other fields (author, album, etc)."); generic_sendmsg("H }"); continue; } /* commands with arguments */ cmd = NULL; arg = NULL; cmd = strtok(comstr," \t"); /* get the main command */ arg = strtok(NULL,""); /* get the args */ if (cmd && strlen(cmd) && arg && strlen(arg)) { /* Simple EQ: SEQ <BASS> <MID> <TREBLE> */ if (!strcasecmp(cmd, "SEQ")) { double b,m,t; int cn; if(sscanf(arg, "%lf %lf %lf", &b, &m, &t) == 3) { /* Consider adding mpg123_seq()... but also, on could define a nicer courve for that. */ if ((t >= 0) && (t <= 3)) for(cn=0; cn < 1; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, b); if ((m >= 0) && (m <= 3)) for(cn=1; cn < 2; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, m); if ((b >= 0) && (b <= 3)) for(cn=2; cn < 32; ++cn) mpg123_eq(fr, MPG123_LEFT|MPG123_RIGHT, cn, t); generic_sendmsg("bass: %f mid: %f treble: %f", b, m, t); } else generic_sendmsg("E invalid arguments for SEQ: %s", arg); continue; } /* Equalizer control :) (JMG) */ if (!strcasecmp(cmd, "E") || !strcasecmp(cmd, "EQ")) { double e; /* ThOr: equalizer is of type real... whatever that is */ int c, v; /*generic_sendmsg("%s",updown);*/ if(sscanf(arg, "%i %i %lf", &c, &v, &e) == 3) { if(mpg123_eq(fr, c, v, e) == MPG123_OK) generic_sendmsg("%i : %i : %f", c, v, e); else generic_sendmsg("E failed to set eq: %s", mpg123_strerror(fr)); } else generic_sendmsg("E invalid arguments for EQ: %s", arg); continue; } if(!strcasecmp(cmd, "EQFILE")) { equalfile = arg; if(load_equalizer(fr) == 0) generic_sendmsg("EQFILE done"); else generic_sendmsg("E failed to parse given eq file"); continue; } /* SEEK to a sample offset */ if(!strcasecmp(cmd, "K") || !strcasecmp(cmd, "SEEK")) { off_t soff; char *spos = arg; int whence = SEEK_SET; if(mode == MODE_STOPPED) { generic_sendmsg("E No track loaded!"); continue; } soff = (off_t) atobigint(spos); if(spos[0] == '-' || spos[0] == '+') whence = SEEK_CUR; if(0 > (soff = mpg123_seek(fr, soff, whence))) { generic_sendmsg("E Error while seeking: %s", mpg123_strerror(fr)); mpg123_seek(fr, 0, SEEK_SET); } if(param.usebuffer) buffer_resync(); generic_sendmsg("K %li", (long)mpg123_tell(fr)); continue; } /* JUMP */ if (!strcasecmp(cmd, "J") || !strcasecmp(cmd, "JUMP")) { char *spos; off_t offset; double secs; spos = arg; if(mode == MODE_STOPPED) { generic_sendmsg("E No track loaded!"); continue; } if(spos[strlen(spos)-1] == 's' && sscanf(arg, "%lf", &secs) == 1) offset = mpg123_timeframe(fr, secs); else offset = atol(spos); /* totally replaced that stuff - it never fully worked a bit usure about why +pos -> spos+1 earlier... */ if (spos[0] == '-' || spos[0] == '+') offset += framenum; if(0 > (framenum = mpg123_seek_frame(fr, offset, SEEK_SET))) { generic_sendmsg("E Error while seeking"); mpg123_seek_frame(fr, 0, SEEK_SET); } if(param.usebuffer) buffer_resync(); generic_sendmsg("J %d", framenum); continue; } /* VOLUME in percent */ if(!strcasecmp(cmd, "V") || !strcasecmp(cmd, "VOLUME")) { double v; mpg123_volume(fr, atof(arg)/100); mpg123_getvolume(fr, &v, NULL, NULL); /* Necessary? */ generic_sendmsg("V %f%%", v * 100); continue; } /* PITCH (playback speed) in percent */ if(!strcasecmp(cmd, "PITCH")) { double p; if(sscanf(arg, "%lf", &p) == 1) { set_pitch(fr, ao, p); generic_sendmsg("PITCH %f", param.pitch); } else generic_sendmsg("E invalid arguments for PITCH: %s", arg); continue; } /* RVA mode */ if(!strcasecmp(cmd, "RVA")) { if(!strcasecmp(arg, "off")) param.rva = MPG123_RVA_OFF; else if(!strcasecmp(arg, "mix") || !strcasecmp(arg, "radio")) param.rva = MPG123_RVA_MIX; else if(!strcasecmp(arg, "album") || !strcasecmp(arg, "audiophile")) param.rva = MPG123_RVA_ALBUM; mpg123_volume_change(fr, 0.); generic_sendmsg("RVA %s", rva_name[param.rva]); continue; } /* LOAD - actually play */ if (!strcasecmp(cmd, "L") || !strcasecmp(cmd, "LOAD")){ generic_load(fr, arg, MODE_PLAYING); continue; } if (!strcasecmp(cmd, "L") || !strcasecmp(cmd, "LOADLIST")){ generic_loadlist(fr, arg); continue; } /* LOADPAUSED */ if (!strcasecmp(cmd, "LP") || !strcasecmp(cmd, "LOADPAUSED")){ generic_load(fr, arg, MODE_PAUSED); continue; } /* no command matched */ generic_sendmsg("E Unknown command: %s", cmd); /* unknown command */ } /* end commands with arguments */ else generic_sendmsg("E Unknown command or no arguments: %s", comstr); /* unknown command */ } /* end of single command processing */ } /* end of scanning the command buffer */ /* when last command had no \n... should I discard it? Ideally, I should remember the part and wait for next read() to get the rest up to a \n. But that can go to infinity. Too long commands too quickly are just bad. Cannot/Won't change that. So, discard the unfinished command and have fingers crossed that the rest of this unfinished one qualifies as "unknown". */ if(buf[len-1] != 0) { char lasti = buf[len-1]; buf[len-1] = 0; generic_sendmsg("E Unfinished command: %s%c", comstr, lasti); } } /* end command reading & processing */ } /* end main (alive) loop */ debug("going to end"); /* quit gracefully */ #ifndef NOXFERMEM if (param.usebuffer) { kill(buffer_pid, SIGINT); xfermem_done_writer(buffermem); waitpid(buffer_pid, NULL, 0); xfermem_done(buffermem); } #endif debug("closing control"); #ifdef FIFO #if WANT_WIN32_FIFO win32_fifo_close(); #else close(control_file); /* be it FIFO or STDIN */ if(param.fifo) unlink(param.fifo); #endif /* WANT_WIN32_FIFO */ #endif debug("control_generic returning"); return 0; }