mpg123-1.31.0

This commit is contained in:
Ozkan Sezer
2022-10-28 22:39:00 +03:00
parent b26727f0be
commit cd6cf37dee
53 changed files with 1275 additions and 1229 deletions

View File

@@ -118,17 +118,21 @@ if TERM_NONE
src_mpg123_SOURCES += src/term_none.c
endif
if NET123
src_mpg123_SOURCES += src/net123.h
endif
if NET123_EXEC
src_mpg123_SOURCES += src/net123.h src/net123_exec.c
src_mpg123_SOURCES += src/net123_exec.c
endif
if NET123_WINHTTP
src_mpg123_SOURCES += src/net123.h src/net123_winhttp.c
src_mpg123_SOURCES += src/net123_winhttp.c
src_mpg123_LDADD += -lwinhttp
endif
if NET123_WININET
src_mpg123_SOURCES += src/net123.h src/net123_wininet.c
src_mpg123_SOURCES += src/net123_wininet.c
src_mpg123_LDADD += -lwininet
endif

View File

@@ -179,7 +179,6 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
off_t rframes;
int spf;
double basevol, realvol;
char *icy;
long rate;
int framesize;
struct mpg123_frameinfo mi;
@@ -222,7 +221,8 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
/* Some sensible logic around offsets and time.
Buffering makes the relationships between the numbers non-trivial. */
rframes = frames-frame;
elapsed = decoded + offset*spf - buffered; /* May be negative, a countdown. */
// May be negative, a countdown. Buffer only confuses in paused (looping) mode, though.
elapsed = decoded + offset*spf - (paused ? 0 : buffered);
remain = elapsed > 0 ? length - elapsed : length;
if( MPG123_OK == mpg123_info(fr, &mi)
&& MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) )

View File

@@ -187,6 +187,9 @@
/* Define to 1 if you have the `sched_setscheduler' function. */
#undef HAVE_SCHED_SETSCHEDULER
/* Define to 1 if you have the `setenv' function. */
#undef HAVE_SETENV
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
@@ -214,9 +217,6 @@
/* Define to 1 if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H
/* Define to 1 if you have the <sndio.h> header file. */
#undef HAVE_SNDIO_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -289,6 +289,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
/* Define to 1 if you have the <wchar.h> header file. */
#undef HAVE_WCHAR_H
@@ -301,6 +304,9 @@
/* Define to 1 if you have the <wctype.h> header file. */
#undef HAVE_WCTYPE_H
/* Define to 1 if you have the <wincon.h> header file. */
#undef HAVE_WINCON_H
/* Define to 1 if you have the <windows.h> header file. */
#undef HAVE_WINDOWS_H
@@ -338,6 +344,15 @@
/* Define to for new net123 network stack. */
#undef NET123
/* Define for executable-based networking (for HTTPS). */
#undef NET123_EXEC
/* Define for winhttp networking (for HTTPS). */
#undef NET123_WINHTTP
/* Define for wininet networking (for HTTPS). */
#undef NET123_WININET
/* Define if network support is enabled. */
#undef NETWORK

View File

@@ -435,6 +435,10 @@ int control_generic (mpg123_handle *fr)
}
#endif
// Persist over loop iterations to remember unfinished commands.
char buf[REMOTE_BUFFER_SIZE]; // command buffer
short int last_len = 0; // length of partial command in there
while (alive)
{
tv.tv_sec = 0;
@@ -510,16 +514,15 @@ int control_generic (mpg123_handle *fr)
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);
len = win32_fifo_read(buf+last_len,REMOTE_BUFFER_SIZE-last_len);
#else
len = read(control_file, buf, REMOTE_BUFFER_SIZE);
len = read(control_file, buf+last_len, REMOTE_BUFFER_SIZE-last_len);
#endif
if(len < 1)
{
@@ -542,7 +545,9 @@ int control_generic (mpg123_handle *fr)
}
debug1("read %i bytes of commands", len);
len += last_len; // on top of remembered piece
/* one command on a line - separation by \n -> C strings in a row */
last_len = 0;
for(counter = 0; counter < len; ++counter)
{
/* line end is command end */
@@ -746,7 +751,6 @@ int control_generic (mpg123_handle *fr)
/* 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)
{
mpg123_eq_bands(fr, MPG123_LR, 0, 0, b);
@@ -891,7 +895,7 @@ int control_generic (mpg123_handle *fr)
if (!strcasecmp(cmd, "LP") || !strcasecmp(cmd, "LOADPAUSED")){ generic_load(fr, arg, MODE_PAUSED); continue; }
/* no command matched */
generic_sendstr(0, "E Unknown command: %s", cmd);
generic_send2str(0, "E Unknown command with arguments: %s %s", cmd, arg);
} /* end commands with arguments */
else generic_sendstr( 0, "E Unknown command or no arguments: %s"
, comstr );
@@ -899,30 +903,19 @@ int control_generic (mpg123_handle *fr)
} /* 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".
*/
// Last character not nulled if we did not use all command text.
if(buf[len-1] != 0)
{
// All that jazz because I did not reserve space for a zero.
char *last_command;
size_t last_len = len-(size_t)(next_comstr-buf);
last_command = malloc(last_len+1);
if(last_command)
if(next_comstr == buf && len == REMOTE_BUFFER_SIZE)
{
memcpy(last_command, next_comstr, last_len);
last_command[last_len] = 0;
generic_sendstr(0, "E Unfinished command: %s", last_command);
free(last_command);
generic_sendmsg("E Too long command, cannot parse.");
// Just skipping it, provoking further parsing erros, but maybe not fatal.
} else
{
last_len = len-(short)(next_comstr-buf);
mdebug("keeping %d bytes of old command", last_len);
memmove(buf, next_comstr, last_len);
}
else
generic_sendmsg("E Unfinished command: <DOOM>");
}
} /* end command reading & processing */
} /* end main (alive) loop */

View File

@@ -106,15 +106,29 @@ static int performoption (int argc, char *argv[], topt *opt, topt *opts)
return (GLO_NOARG);
loptarg = argv[loptind++]+loptchr;
loptchr = 0;
errno = 0;
if (opt->var) {
char *endptr = NULL;
if (opt->flags & GLO_CHAR) /* var is *char */
setcharoption(opt, loptarg);
else if(opt->flags & GLO_LONG)
*((long *) opt->var) = atol(loptarg);
else if(opt->flags & GLO_INT)
*((int *) opt->var) = atoi(loptarg);
else if(opt->flags & (GLO_LONG | GLO_INT))
{
long val = strtol(loptarg, &endptr, 10);
if(errno || endptr == loptarg || (endptr && *endptr))
return GLO_BADARG;
if(opt->flags & GLO_LONG)
*((long *) opt->var) = val;
else if(val <= INT_MAX && val >= INT_MIN)
*((int *) opt->var) = val;
else
return GLO_BADARG;
}
else if(opt->flags & GLO_DOUBLE)
*((double *) opt->var) = atof(loptarg);
{
*((double *) opt->var) = strtod(loptarg, &endptr);
if(errno || endptr == loptarg || (endptr && *endptr))
return GLO_BADARG;
}
else prog_error();
}
#if 0 /* Oliver: What was this for?! --ThOr */

View File

@@ -66,6 +66,7 @@ for .... no flag) */
#define GLO_UNKNOWN -1
#define GLO_NOARG -2
#define GLO_CONTINUE -3
#define GLO_BADARG -4
int getlopt (int argc, char *argv[], topt *opts);
// Helper to set a char parameter, avoiding memory leaks.

View File

@@ -153,74 +153,6 @@ debunk_result:
#ifdef NETWORK
#if !defined (WANT_WIN32_SOCKETS)
static int writestring (int fd, mpg123_string *string)
{
size_t result, bytes;
char *ptr = string->p;
bytes = string->fill ? string->fill-1 : 0;
while(bytes)
{
result = write(fd, ptr, bytes);
if(result < 0 && errno != EINTR)
{
merror("writing http string: %s", strerror(errno));
return FALSE;
}
else if(result == 0)
{
error("write: socket closed unexpectedly");
return FALSE;
}
ptr += result;
bytes -= result;
}
return TRUE;
}
static size_t readstring (mpg123_string *string, size_t maxlen, int fd)
{
int err;
debug2("Attempting readstring on %d for %"SIZE_P" bytes", fd, (size_p)maxlen);
string->fill = 0;
while(maxlen == 0 || string->fill < maxlen)
{
if(string->size-string->fill < 1)
if(!mpg123_grow_string(string, string->fill+4096))
{
error("Cannot allocate memory for reading.");
string->fill = 0;
return 0;
}
err = read(fd,string->p+string->fill,1);
/* Whoa... reading one byte at a time... one could ensure the line break in another way, but more work. */
if( err == 1)
{
string->fill++;
if(string->p[string->fill-1] == '\n') break;
}
else if(errno != EINTR)
{
error("Error reading from socket or unexpected EOF.");
string->fill = 0;
/* bail out to prevent endless loop */
return 0;
}
}
if(!mpg123_grow_string(string, string->fill+1))
{
string->fill=0;
}
else
{
string->p[string->fill] = 0;
string->fill++;
}
return string->fill;
}
#endif /* WANT_WIN32_SOCKETS */
void encode64 (char *source,char *destination)
{
@@ -383,11 +315,10 @@ int translate_url(const char *url, mpg123_string *purl)
return TRUE;
}
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, int *try_without_port)
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, const char * const *client_head)
{
char* ttemp;
int ret = TRUE;
const char *icy = param.talk_icy ? icy_yes : icy_no;
/* hm, my test redirection had troubles with line break before HTTP/1.0 */
if((ttemp = strchr(request->p,'\r')) != NULL){ *ttemp = 0; request->fill = ttemp-request->p+1; }
@@ -405,26 +336,14 @@ int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *por
if(host->fill)
{ /* Give virtual hosting a chance... adding the "Host: ... " line. */
debug2("Host: %s:%s", host->p, port->p);
if( mpg123_add_string(request, "Host: ")
if(!( mpg123_add_string(request, "Host: ")
&& mpg123_add_string(request, host->p)
&& ( *try_without_port || (
mpg123_add_string(request, ":")
&& mpg123_add_string(request, port->p) ))
&& mpg123_add_string(request, "\r\n") )
{
if(*try_without_port) *try_without_port = 0;
}
else return FALSE;
&& mpg123_add_string(request, ":")
&& mpg123_add_string(request, port->p)
&& mpg123_add_string(request, "\r\n") ))
return FALSE;
}
/* Acceptance, stream setup. */
if( !append_accept(request)
|| !mpg123_add_string(request, "\r\n")
|| !mpg123_add_string(request, CONN_HEAD)
|| !mpg123_add_string(request, icy)
|| !mpg123_add_string(request, "\r\n") )
return FALSE;
/* Authorization. */
if (httpauth1->fill || param.httpauth) {
char *buf;
@@ -456,72 +375,30 @@ int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *por
free(buf); /* Watch out for leaking if you introduce returns before this line. */
}
while(ret && *client_head)
{
ret = mpg123_add_string(request, *client_head);
if(ret)
ret = mpg123_add_string(request, "\r\n");
++client_head;
}
if(ret) ret = mpg123_add_string(request, "\r\n");
return ret;
}
#if !defined (WANT_WIN32_SOCKETS)
static int resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
{
debug1("request_url:%s", request_url->p);
/* initialized with full old url */
if(!mpg123_copy_string(request_url, purl)) return FALSE;
/* We may strip it down to a prefix ot totally. */
if(strncasecmp(response->p, "Location: http://", 17))
{ /* OK, only partial strip, need prefix for relative path. */
char* ptmp = NULL;
/* though it's not RFC (?), accept relative URIs as wget does */
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
/* not absolute uri, could still be server-absolute */
/* I prepend a part of the request... out of the request */
if(response->p[10] == '/')
{
/* only prepend http://server/ */
/* I null the first / after http:// */
ptmp = strchr(purl->p+7,'/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
}
else
{
/* prepend http://server/path/ */
/* now we want the last / */
ptmp = strrchr(purl->p+7, '/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
}
}
else purl->fill = 0;
debug1("prefix=%s", purl->fill ? purl->p : "");
if(!mpg123_add_string(purl, response->p+10)) return FALSE;
debug1(" purl: %s", purl->p);
debug1("old request_url: %s", request_url->p);
return TRUE;
}
int http_open(const char* url, struct httpdata *hd)
int http_open(const char* url, struct httpdata *hd, const char * const *client_head)
{
mpg123_string purl, host, port, path;
mpg123_string request, response, request_url;
mpg123_string request, request_url;
mpg123_string httpauth1;
int sock = -1;
int oom = 0;
int relocate, numrelocs = 0;
int got_location = FALSE;
/*
workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
this site's apache gives me a relocation to the same place when I give the port in Host request field
for the record: Apache/2.0.51 (Fedora)
*/
int try_without_port = 0;
mpg123_init_string(&purl);
mpg123_init_string(&host);
mpg123_init_string(&port);
mpg123_init_string(&path);
mpg123_init_string(&request);
mpg123_init_string(&response);
mpg123_init_string(&request_url);
mpg123_init_string(&httpauth1);
@@ -533,26 +410,6 @@ int http_open(const char* url, struct httpdata *hd)
/* Don't confuse the different auth strings... */
if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }
/* "GET http://" 11
* " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
* 26 + PACKAGE_NAME + PACKAGE_VERSION
* accept header + accept_length()
* "Authorization: Basic \r\n" 23
* "\r\n" 2
* ... plus the other predefined header lines
*/
/* Just use this estimate as first guess to reduce malloc calls in string library. */
{
size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION)
+ accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
if( !mpg123_grow_string(&request, length_estimate)
|| !mpg123_grow_string(&response,4096) )
{
oom=1; goto exit;
}
}
do
{
/* Storing the request url, with http:// prepended if needed. */
/* used to be url here... seemed wrong to me (when loop advanced...) */
@@ -585,7 +442,7 @@ int http_open(const char* url, struct httpdata *hd)
}
}
if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; }
if(!fill_request(&request, &host, &port, &httpauth1, client_head)){ oom=1; goto exit; }
httpauth1.fill = 0; /* We use the auth data from the URL only once. */
if (hd->proxystate >= PROXY_HOST)
@@ -599,105 +456,27 @@ int http_open(const char* url, struct httpdata *hd)
}
}
debug2("attempting to open_connection to %s:%s", host.p, port.p);
#ifdef WANT_WIN32_SOCKETS
sock = win32_net_open_connection(&host, &port);
#else
sock = open_connection(&host, &port);
#endif
if(sock < 0)
{
error1("Unable to establish connection to %s", host.fill ? host.p : "");
goto exit;
}
#define http_failure close(sock); sock=-1; goto exit;
if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
if(!writestring(sock, &request)){ http_failure; }
relocate = FALSE;
/* Arbitrary length limit here... */
#define safe_readstring \
readstring(&response, SIZE_MAX/16, sock); \
if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
{ \
error("HTTP response line exceeds max. length"); \
http_failure; \
} \
else if(response.fill == 0) \
{ \
error("readstring failed"); \
http_failure; \
} \
if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
safe_readstring;
#ifdef WANT_WIN32_SOCKETS
if(!win32_net_writestring (sock, &request))
#else
if(unintr_write(sock, request.p, request.fill-1) != request.fill-1)
#endif
{
char *sptr;
if((sptr = strchr(response.p, ' ')))
{
if(response.fill > sptr-response.p+2)
switch (sptr[1])
{
case '3':
relocate = TRUE;
case '2':
break;
default:
fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
http_failure;
}
else{ error("Too short response,"); http_failure; }
}
http_failure;
}
/* If we are relocated, we need to look out for a Location header. */
got_location = FALSE;
do
{
safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
if (!strncasecmp(response.p, "Location: ", 10))
{ /* It is a redirection! */
if(!resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }
if(!strcmp(purl.p, request_url.p))
{
warning("relocated to very same place! trying request again without host port");
try_without_port = 1;
}
got_location = TRUE;
}
else
{ /* We got a header line (or the closing empty line). */
char *tmp;
debug1("searching for header values... %s", response.p);
/* Not sure if I want to bail out on error here. */
/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
get_header_string(&response, "content-type", &hd->content_type);
get_header_string(&response, "icy-name", &hd->icy_name);
get_header_string(&response, "icy-url", &hd->icy_url);
/* watch out for icy-metaint */
if((tmp = get_header_val("icy-metaint", &response)))
{
hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
debug1("got icy-metaint %li", (long int)hd->icy_interval);
}
}
} while(response.p[0] != '\r' && response.p[0] != '\n');
if(relocate)
{
close(sock);
sock = -1;
/* Forget content type, might just relate to a displayed error page,
not the resource being redirected to. */
mpg123_free_string(&hd->content_type);
mpg123_init_string(&hd->content_type);
}
} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
if(relocate)
{
if(!got_location)
error("Server meant to redirect but failed to provide a location!");
else
error1("Too many HTTP relocations (%i).", numrelocs);
http_failure;
}
exit: /* The end as well as the exception handling point... */
@@ -708,22 +487,10 @@ exit: /* The end as well as the exception handling point... */
mpg123_free_string(&port);
mpg123_free_string(&path);
mpg123_free_string(&request);
mpg123_free_string(&response);
mpg123_free_string(&request_url);
mpg123_free_string(&httpauth1);
return sock;
}
#endif /*WANT_WIN32_SOCKETS*/
#else /* NETWORK */
/* stub */
int http_open (const char* url, struct httpdata *hd)
{
if(!param.quiet)
error("HTTP support not built in.");
return -1;
}
#endif
/* EOF */

View File

@@ -49,20 +49,17 @@ int debunk_mime(const char* mime);
int proxy_init(struct httpdata *hd);
int translate_url(const char *url, mpg123_string *purl);
size_t accept_length(void);
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, int *try_without_port);
int fill_request(mpg123_string *request, mpg123_string *host, mpg123_string *port, mpg123_string *httpauth1, const char * const *client_head);
void get_header_string(mpg123_string *response, const char *fieldname, mpg123_string *store);
char *get_header_val(const char *hname, mpg123_string *response);
/* needed for HTTP/1.1 non-pipelining mode */
/* #define CONN_HEAD "Connection: close\r\n" */
#define CONN_HEAD ""
#define icy_yes "Icy-MetaData: 1"
#define icy_no "Icy-MetaData: 0"
// Append an accept header line to the string, without line end.
int append_accept(mpg123_string *s);
/* takes url and content type string address, opens resource, returns fd for data, allocates and sets content type */
extern int http_open (const char* url, struct httpdata *hd);
// Open HTTP URL with internal network code.
int http_open(const char* url, struct httpdata *hd, const char * const *client_head);
#endif

View File

@@ -16,6 +16,7 @@
#define compat_fdopen INT123_compat_fdopen
#define compat_close INT123_compat_close
#define compat_fclose INT123_compat_fclose
#define compat_binmode INT123_compat_binmode
#define win32_wide_utf8 INT123_win32_wide_utf8
#define win32_wide_utf7 INT123_win32_wide_utf7
#define win32_utf8_wide INT123_win32_utf8_wide
@@ -277,9 +278,6 @@
#define buffer_drop INT123_buffer_drop
#define buffer_write INT123_buffer_write
#define buffer_fill INT123_buffer_fill
#define read_buf INT123_read_buf
#define xfer_write_string INT123_xfer_write_string
#define xfer_read_string INT123_xfer_read_string
#define xfermem_init INT123_xfermem_init
#define xfermem_init_writer INT123_xfermem_init_writer
#define xfermem_init_reader INT123_xfermem_init_reader
@@ -304,8 +302,6 @@
#define raw_formats INT123_raw_formats
#define wav_formats INT123_wav_formats
#define wav_drain INT123_wav_drain
#define write_parameters INT123_write_parameters
#define read_parameters INT123_read_parameters
#define stringlists_add INT123_stringlists_add
#define check_neon INT123_check_neon
#define dct64_3dnow INT123_dct64_3dnow

View File

@@ -35,7 +35,9 @@
ASM_NAME(dct36_3dnow):
pushl %ebp
movl %esp,%ebp
#if defined(PIC) && defined(__APPLE__)
// Not entirely sure about this PIC_GLOBAL_PTR here, but this is highly
// irrelevant, anyway. Anyone building 3DNow code for Android or Apple systems?
#ifdef PIC_GLOBAL_PTR
sub $4,%esp
#endif
pushl %esi
@@ -45,7 +47,7 @@ ASM_NAME(dct36_3dnow):
#define _EBX_ %edi
PREPARE_GOT
GET_GOT
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
#define _COS9_ 0(%edi)
#define _tfcos36_ 0(%eax)
mov GLOBAL_VAR_PTR(tfcos36), %eax
@@ -187,7 +189,7 @@ ASM_NAME(dct36_3dnow):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 0+_tfcos36_,%mm5
@@ -217,7 +219,7 @@ ASM_NAME(dct36_3dnow):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 32+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -265,7 +267,7 @@ ASM_NAME(dct36_3dnow):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 4+_tfcos36_,%mm5
@@ -295,7 +297,7 @@ ASM_NAME(dct36_3dnow):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 28+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -356,7 +358,7 @@ ASM_NAME(dct36_3dnow):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 8+_tfcos36_,%mm5
@@ -386,7 +388,7 @@ ASM_NAME(dct36_3dnow):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 24+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -446,7 +448,7 @@ ASM_NAME(dct36_3dnow):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 12+_tfcos36_,%mm5
@@ -476,7 +478,7 @@ ASM_NAME(dct36_3dnow):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 20+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -512,7 +514,7 @@ ASM_NAME(dct36_3dnow):
movq 64(%eax),%mm3
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 16+_tfcos36_,%mm5

View File

@@ -48,7 +48,9 @@
ASM_NAME(dct36_3dnowext):
pushl %ebp
movl %esp, %ebp
#if defined(PIC) && defined(__APPLE__)
// Not entirely sure about this PIC_GLOBAL_PTR here, but this is highly
// irrelevant, anyway. Anyone building 3DNow code for Android or Apple systems?
#ifdef PIC_GLOBAL_PTR
sub $4, %esp
#endif
pushl %esi
@@ -58,7 +60,7 @@ ASM_NAME(dct36_3dnowext):
#define _EBX_ %edi
PREPARE_GOT
GET_GOT
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
#define _COS9_ 0(%edi)
#define _tfcos36_ 0(%eax)
mov GLOBAL_VAR_PTR(tfcos36), %eax
@@ -198,7 +200,7 @@ ASM_NAME(dct36_3dnowext):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 0+_tfcos36_,%mm5
@@ -227,7 +229,7 @@ ASM_NAME(dct36_3dnowext):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 32+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -275,7 +277,7 @@ ASM_NAME(dct36_3dnowext):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 4+_tfcos36_,%mm5
@@ -305,7 +307,7 @@ ASM_NAME(dct36_3dnowext):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 28+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -365,7 +367,7 @@ ASM_NAME(dct36_3dnowext):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 8+_tfcos36_,%mm5
@@ -395,7 +397,7 @@ ASM_NAME(dct36_3dnowext):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 24+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -455,7 +457,7 @@ ASM_NAME(dct36_3dnowext):
movq %mm2,%mm4
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 12+_tfcos36_,%mm5
@@ -485,7 +487,7 @@ ASM_NAME(dct36_3dnowext):
pfsub %mm2,%mm4
movq %mm7,%mm5
punpckldq 20+_tfcos36_,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov 8(%ebp),%eax
#endif
pfmul %mm5,%mm4
@@ -520,7 +522,7 @@ ASM_NAME(dct36_3dnowext):
movq 64(%eax),%mm3
pfadd %mm3,%mm4
movq %mm7,%mm5
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov -4(%ebp),%eax
#endif
punpckldq 16+_tfcos36_,%mm5

View File

@@ -36,7 +36,7 @@ ASM_NAME(dct64_3dnow):
/* femms */
/* 1 */
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(pnts),%eax
movl (%eax),%eax
#else
@@ -140,7 +140,7 @@ ASM_NAME(dct64_3dnow):
movd %mm5,64(%ebx)
/* 2 */
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(pnts),%eax
movl 4(%eax),%eax
#else
@@ -248,7 +248,7 @@ ASM_NAME(dct64_3dnow):
movd %mm5,96(%esi)
/* 3 */
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(pnts),%eax
movl 8(%eax),%eax
#else
@@ -354,7 +354,7 @@ ASM_NAME(dct64_3dnow):
movd %mm6,112(%ebx)
/* 4 */
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(pnts),%eax
movl 12(%eax),%eax
#else
@@ -469,7 +469,7 @@ ASM_NAME(dct64_3dnow):
pi2fd %mm0,%mm0
movd %eax,%mm1
pi2fd %mm1,%mm1
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(pnts),%eax
movl 16(%eax),%eax
#else

View File

@@ -32,7 +32,7 @@ ASM_NAME(dct64_MMX):
fstps (%edx)
movl 276(%esp),%edi
flds 4(%eax)
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
movl GLOBAL_VAR_PTR(costab_mmxsse),%ebx
#else
leal GLOBAL_VAR(costab_mmxsse),%ebx

View File

@@ -77,7 +77,7 @@ ASM_NAME(dct64_sse):
subps %xmm0, %xmm5
subps %xmm1, %xmm4
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov GLOBAL_VAR_PTR(costab_mmxsse), %ecx
#else
lea GLOBAL_VAR(costab_mmxsse), %ecx

View File

@@ -77,7 +77,7 @@ ASM_NAME(dct64_real_sse):
subps %xmm0, %xmm5
subps %xmm1, %xmm4
#if defined(PIC) && defined(__APPLE__)
#ifdef PIC_GLOBAL_PTR
mov GLOBAL_VAR_PTR(costab_mmxsse), %eax
#else
lea GLOBAL_VAR(costab_mmxsse), %eax

View File

@@ -139,6 +139,11 @@ Lpic_base: \
#endif /* PIC variants */
// Apple and Android NDK seem both not to like direct global access.
#if defined(PIC) && (defined(__APPLE__) || defined(ANDROID))
#define PIC_GLOBAL_PTR
#endif
#endif /* OPT_X86 */
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__APPLE__)

View File

@@ -68,6 +68,9 @@ static void catch_interrupt (void)
intflag = TRUE;
}
static int xfer_write_string(out123_handle *ao, int who, const char *buf);
static int xfer_read_string(out123_handle *ao, int who, char **buf);
static int read_buf(int fd, void *addr, size_t size, byte *prebuf, int *preoff, int presize);
static int read_record(out123_handle *ao
, int who, void **buf, byte *prebuf, int *preoff, int presize, size_t *recsize);
static int buffer_loop(out123_handle *ao);
@@ -198,6 +201,47 @@ static int buffer_cmd_finish(out123_handle *ao)
}
}
/* Serialization of tunable parameters to communicate them between
main process and buffer. Make sure these two stay in sync ... */
static int write_parameters(out123_handle *ao, int who)
{
int fd = ao->buffermem->fd[who];
if(
GOOD_WRITEVAL(fd, ao->flags)
&& GOOD_WRITEVAL(fd, ao->preload)
&& GOOD_WRITEVAL(fd, ao->gain)
&& GOOD_WRITEVAL(fd, ao->device_buffer)
&& GOOD_WRITEVAL(fd, ao->verbose)
&& !xfer_write_string(ao, who, ao->name)
&& !xfer_write_string(ao, who, ao->bindir)
)
return 0;
else
return -1;
}
static int read_parameters(out123_handle *ao
, int who, byte *prebuf, int *preoff, int presize)
{
int fd = ao->buffermem->fd[who];
#define GOOD_READVAL_BUF(fd, val) \
!read_buf(fd, &val, sizeof(val), prebuf, preoff, presize)
if(
GOOD_READVAL_BUF(fd, ao->flags)
&& GOOD_READVAL_BUF(fd, ao->preload)
&& GOOD_READVAL_BUF(fd, ao->gain)
&& GOOD_READVAL_BUF(fd, ao->device_buffer)
&& GOOD_READVAL_BUF(fd, ao->verbose)
&& !read_record(ao, who, (void**)&ao->name, prebuf, preoff, presize, NULL)
&& !read_record(ao, who, (void**)&ao->bindir, prebuf, preoff, presize, NULL)
)
return 0;
else
return -1;
#undef GOOD_READVAL_BUF
}
int buffer_sync_param(out123_handle *ao)
{
int writerfd = ao->buffermem->fd[XF_WRITER];
@@ -524,7 +568,7 @@ int xfer_write_string(out123_handle *ao, int who, const char *buf)
}
int xfer_read_string(out123_handle *ao, int who, char **buf)
static int xfer_read_string(out123_handle *ao, int who, char **buf)
{
/* ao->errcode set in read_record() */
return read_record(ao, who, (void**)buf, NULL, NULL, 0, NULL)
@@ -536,7 +580,7 @@ int xfer_read_string(out123_handle *ao, int who, char **buf)
This assumes responsible use and avoids needless checking of input.
And, yes, it modifies the preoff argument!
Returns 0 on success, modifies prebuffer fill. */
int read_buf(int fd, void *addr, size_t size, byte *prebuf, int *preoff, int presize)
static int read_buf(int fd, void *addr, size_t size, byte *prebuf, int *preoff, int presize)
{
size_t need = size;

View File

@@ -54,13 +54,4 @@ size_t buffer_write(out123_handle *ao, void *buffer, size_t bytes);
/* Thin wrapper over xfermem giving the current buffer fill. */
size_t buffer_fill(out123_handle *ao);
/* Special handler to safely read values from command channel with
an additional buffer handed in. Exported for read_parameters(). */
int read_buf(int fd, void *addr, size_t size
, byte *prebuf, int *preoff, int presize);
/* Read/write strings from/to command channel. 0 on success. */
int xfer_write_string(out123_handle *ao, int who, const char *buf);
int xfer_read_string(out123_handle *ao, int who, char* *buf);
#endif

View File

@@ -361,49 +361,6 @@ out123_param_from(out123_handle *ao, out123_handle* from_ao)
return 0;
}
#ifndef NOXFERMEM
/* Serialization of tunable parameters to communicate them between
main process and buffer. Make sure these two stay in sync ... */
int write_parameters(out123_handle *ao, int who)
{
int fd = ao->buffermem->fd[who];
if(
GOOD_WRITEVAL(fd, ao->flags)
&& GOOD_WRITEVAL(fd, ao->preload)
&& GOOD_WRITEVAL(fd, ao->gain)
&& GOOD_WRITEVAL(fd, ao->device_buffer)
&& GOOD_WRITEVAL(fd, ao->verbose)
&& !xfer_write_string(ao, who, ao->name)
&& !xfer_write_string(ao, who, ao->bindir)
)
return 0;
else
return -1;
}
int read_parameters(out123_handle *ao
, int who, byte *prebuf, int *preoff, int presize)
{
int fd = ao->buffermem->fd[who];
#define GOOD_READVAL_BUF(fd, val) \
!read_buf(fd, &val, sizeof(val), prebuf, preoff, presize)
if(
GOOD_READVAL_BUF(fd, ao->flags)
&& GOOD_READVAL_BUF(fd, ao->preload)
&& GOOD_READVAL_BUF(fd, ao->gain)
&& GOOD_READVAL_BUF(fd, ao->device_buffer)
&& GOOD_READVAL_BUF(fd, ao->verbose)
&& !xfer_read_string(ao, who, &ao->name)
&& !xfer_read_string(ao, who, &ao->bindir)
)
return 0;
else
return -1;
#undef GOOD_READVAL_BUF
}
#endif
int attribute_align_arg
out123_open(out123_handle *ao, const char* driver, const char* device)
{
@@ -730,8 +687,14 @@ out123_play(out123_handle *ao, void *bytes, size_t count)
sum += written;
count -= written;
}
if(written < block && errno != EINTR)
{
if(written < block && errno != EINTR
&& errno != EAGAIN
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
// Not all platforms define it (or only in more modern POSIX modes).
// Standard says it is supposed to be a macro, so simple check here.
&& errno != EWOULDBLOCK
#endif
){
ao->errcode = OUT123_DEV_PLAY;
if(!AOQUIET)
merror( "Error in writing audio, wrote only %d of %d (%s?)!"

View File

@@ -18,9 +18,16 @@
NOTE: This particular num/size combination performs best under heavy
loads for my system, however this may not be true for any hardware/OS out there.
Generally, BUFFER_SIZE < 8k || NUM_BUFFERS > 16 || NUM_BUFFERS < 4 are not recommended.
Introducing user-settable device buffer. We fix 8 buffers, scale the individual
buffer size, rounded/truncated a bit to not be too odd. The old default of 64K
buffers leads to 2.97 s with CD-DA. Quite excessive. We will probably reduce this
soon.
*/
#define BUFFER_SIZE 0x10000
#define NUM_BUFFERS 8 /* total 512k roughly 2.5 sec of CD quality sound */
#define DEFAULT_DEVICE_BUFFER 0.25
// Buffers are multiples of this.
#define BUFFER_GRANULARITY 256
#define NUM_BUFFERS 8
static void wait_for_buffer(WAVEHDR* hdr, HANDLE hEvent);
static void drain_win32(out123_handle *ao);
@@ -29,6 +36,7 @@ static void drain_win32(out123_handle *ao);
struct queue_state
{
WAVEHDR buffer_headers[NUM_BUFFERS];
size_t bufsize;
/* The next buffer to be filled and put in playback */
int next_buffer;
/* Buffer playback completion event */
@@ -100,11 +108,18 @@ static int open_win32(out123_handle *ao)
ereturn(-1, "Unable to open wave output device.");
}
state->bufsize = (size_t)( (double)
(ao->device_buffer > 0. ? ao->device_buffer : DEFAULT_DEVICE_BUFFER)
* out_fmt.nAvgBytesPerSec / NUM_BUFFERS / BUFFER_GRANULARITY);
if(state->bufsize < 1)
state->bufsize = 1;
state->bufsize *= BUFFER_GRANULARITY;
/* Reset event from the "device open" message */
ResetEvent(state->play_done_event);
/* Allocate playback buffers */
for(i = 0; i < NUM_BUFFERS; i++)
if(!(state->buffer_headers[i].lpData = (LPSTR)malloc(BUFFER_SIZE)))
if(!(state->buffer_headers[i].lpData = (LPSTR)malloc(state->bufsize)))
{
ereturn(-1, "Out of memory for playback buffers.");
}
@@ -112,7 +127,7 @@ static int open_win32(out123_handle *ao)
{
/* Tell waveOutPrepareHeader the maximum value of dwBufferLength
we will ever send */
state->buffer_headers[i].dwBufferLength = BUFFER_SIZE;
state->buffer_headers[i].dwBufferLength = state->bufsize;
state->buffer_headers[i].dwFlags = 0;
res = waveOutPrepareHeader(state->waveout, &state->buffer_headers[i], sizeof(WAVEHDR));
if(res != MMSYSERR_NOERROR) ereturn(-1, "Can't write to audio output device (prepare).");
@@ -231,13 +246,13 @@ static int write_win32(out123_handle *ao, unsigned char *buf, int len)
wait_for_buffer(hdr, state->play_done_event);
/* Now see how much we want to stuff in and then stuff it in. */
bufill = BUFFER_SIZE - hdr->dwBufferLength;
bufill = state->bufsize - hdr->dwBufferLength;
if(len < bufill) bufill = len;
rest_len = len - bufill;
memcpy(hdr->lpData + hdr->dwBufferLength, buf, bufill);
hdr->dwBufferLength += bufill;
if(hdr->dwBufferLength == BUFFER_SIZE)
if(hdr->dwBufferLength == state->bufsize)
{ /* Send the buffer out when it's full. */
hdr->dwFlags |= WHDR_PREPARED;

View File

@@ -117,9 +117,5 @@ struct audio_format_name {
char *sname;
};
int write_parameters(out123_handle *ao, int fd);
int read_parameters(out123_handle *ao
, int fd, byte *prebuf, int *preoff, int presize);
#endif

View File

@@ -45,7 +45,7 @@ int utf8force = 0; // enforce UTF-8 workings
int utf8env = 0; // produce UTF-8 text output
int utf8loc = 0; // have actual UTF-8 locale (so that mbstowcs() works)
static int term_is_fun = -1;
//static int term_is_fun = -1;
static const char joker_symbol = '?';
static const char *uni_repl = "\xef\xbf\xbd";

View File

@@ -103,6 +103,7 @@ struct parameter param = {
,1024 /* resync_limit */
,0 /* smooth */
,0.0 /* pitch */
,0.5 // pauseloop
,0 /* appflags */
,NULL /* proxyurl */
,0 /* keep_open */
@@ -483,7 +484,7 @@ static void set_appflag(char *arg, topt *opts)
#if defined(NETWORK) || defined(NET123)
static void set_httpauth(char *arg, topt *opts)
{
param.httpauth = strdup(arg);
param.httpauth = compat_strdup(arg);
// Do not advertise the password for all system users.
memset(arg, 'x', strlen(arg));
}
@@ -717,6 +718,7 @@ topt opts[] = {
{'D', "delay", GLO_ARG | GLO_INT, 0, &param.delay, 0},
{0, "resync-limit", GLO_ARG | GLO_LONG, 0, &param.resync_limit, 0},
{0, "pitch", GLO_ARG|GLO_DOUBLE, 0, &param.pitch, 0},
{0, "pauseloop", GLO_ARG|GLO_DOUBLE, 0, &param.pauseloop, 0},
#ifdef NETWORK
{0, "ignore-mime", GLO_LONG, set_appflag, &appflag, MPG123APP_IGNORE_MIME },
#endif
@@ -1093,6 +1095,10 @@ int main(int sys_argc, char ** sys_argv)
print_outstr(stderr, prgName, 0, stderr_is_term);
fprintf(stderr, ": Missing argument for option \"%s\".\n", loptarg);
usage(1);
case GLO_BADARG:
print_outstr(stderr, prgName, 0, stderr_is_term);
fprintf(stderr, ": Bad option argument \"%s\".\n", loptarg);
usage(1);
}
/* Do this _after_ parameter parsing. */
utf8force = param.force_utf8;
@@ -1298,9 +1304,16 @@ int main(int sys_argc, char ** sys_argv)
ret = control_generic(mh);
safe_exit(ret);
}
if(param.term_ctrl == MAYBE)
param.term_ctrl = term_ctrl_default;
term_init();
{
int term_forced = param.term_ctrl == TRUE;
if(param.term_ctrl == MAYBE)
param.term_ctrl = term_ctrl_default;
if(term_init() && term_forced)
{
error("Aborting since explicitly requested terminal control is not available.");
safe_exit(99);
}
}
if(APPFLAG(MPG123APP_CONTINUE)) frames_left = param.frame_number;
while ((fname = get_next_file()))
@@ -1599,14 +1612,13 @@ static void long_usage(int err)
fprintf(o," -y --no-resync DISABLES resync on error (--resync is deprecated)\n");
fprintf(o," -F --no-frankenstein disable support for Frankenstein streams\n");
#if defined(NETWORK) || defined(NET123)
#ifdef NETWORK
fprintf(o," -p <f> --proxy <f> set WWW proxy\n");
#else
fprintf(o," -p <f> --proxy <f> override proxy environemnt variable for plain HTTP\n");
fprintf(o," --network <b> select network backend, available: auto");
const char **nb = net123_backends;
while(*nb){ fprintf(o, " %s", *nb++); }
fprintf(o, "\n");
#endif
fprintf(o," (auto meaning internal code for plain HTTP and the\n");
fprintf(o," first external option for HTTPS)\n");
fprintf(o," -u --auth set auth values for HTTP access\n");
fprintf(o," --auth-file set auth values for HTTP access from given file\n");
fprintf(o," --ignore-mime ignore HTTP MIME types (content-type)\n");
@@ -1635,6 +1647,7 @@ static void long_usage(int err)
fprintf(o," --list-devices list the available output devices for given output module\n");
fprintf(o," -a <d> --audiodevice <d> select audio device (depending on chosen module)\n");
fprintf(o," -s --stdout write raw audio to stdout (host native format)\n");
fprintf(o," -O <f> --outfile <f> write raw audio to given file (- is stdout)\n");
fprintf(o," -w <f> --wav <f> write samples as WAV file in <f> (- is stdout)\n");
fprintf(o," --au <f> write samples as Sun AU file in <f> (- is stdout)\n");
fprintf(o," --cdr <f> write samples as raw CD audio file in <f> (- is stdout)\n");
@@ -1703,6 +1716,7 @@ static void long_usage(int err)
#ifndef GENERIC
fprintf(o," --title set terminal title to filename\n");
#endif
fprintf(o," --pauseloop <s> loop interval in (fractional) seconds\n");
fprintf(o," --name <n> set instance name (used in various places)\n");
fprintf(o," --long-tag spacy id3 display with every item on a separate line\n");
fprintf(o," --lyrics show lyrics (from ID3v2 USLT frame)\n");

View File

@@ -97,6 +97,7 @@ struct parameter
long resync_limit;
int smooth;
double pitch; /* <0 or >0, 0.05 for 5% speedup. */
double pauseloop; // terminal control 'pause' loop length
unsigned long appflags; /* various switches for mpg123 application */
char *proxyurl;
int keep_open; /* Whether to keep files open after end reached, for remote control mode, perhaps terminal control, too. */

View File

@@ -39,12 +39,17 @@
#ifndef _MPG123_NET123_H_
#define _MPG123_NET123_H_
#include "config.h"
#include <sys/types.h>
// The network implementation defines the struct for private use.
// The purpose is just to keep enough context to be able to
// call net123_read() and net123_close() afterwards.
struct net123_handle_struct;
// stream handle for differing implementations, build-time plugins
struct net123_handle_struct
{
void *parts; // custom internal data
// callbacks
size_t (*read)(struct net123_handle_struct *nh, void *buf, size_t bufsize);
void (*close)(struct net123_handle_struct *nh);
};
typedef struct net123_handle_struct net123_handle;
extern const char *net123_backends[];
@@ -54,16 +59,16 @@ extern const char *net123_backends[];
// and then the raw data.
// client_head contains header lines to send with the request, without
// line ending
net123_handle *net123_open(const char *url, const char * const *client_head);
// Read data into buffer, return bytes read.
// This handles interrupts (EAGAIN, EINTR, ..) internally and only returns
// a short byte count on EOF or error. End of file or error is not distinguished:
// For the user, it only matters if there will be more bytes or not.
// Feel free to communicate errors via error() / merror() functions inside.
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize);
// Call that to free up resources, end processes.
void net123_close(net123_handle *nh);
// Variant for the external binding.
#ifdef NET123_EXEC
net123_handle *net123_open_exec(const char *url, const char * const *client_head);
#endif
#ifdef NET123_WININET
net123_handle *net123_open_wininet(const char *url, const char * const *client_head);
#endif
#ifdef NET123_WINHTTP
net123_handle *net123_open_winhttp(const char *url, const char * const *client_head);
#endif
#endif

View File

@@ -8,6 +8,9 @@
specifically if param.network_backend is set accordingly.
*/
// kill
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "config.h"
#include "net123.h"
@@ -34,18 +37,11 @@
// wget --user=... --password=...
// Alternatively: Have them in .netrc.
const char *net123_backends[] =
{
"wget"
, "curl"
, NULL
};
struct net123_handle_struct
typedef struct
{
int fd;
pid_t worker;
};
} exec_handle;
// Combine two given strings into one newly allocated one.
// Use: (--parameter=, value) -> --parameter=value
@@ -239,7 +235,36 @@ static char **curl_argv(const char *url, const char * const * client_head)
return argv;
}
net123_handle *net123_open(const char *url, const char * const * client_head)
static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize)
{
if(!nh || (bufsize && !buf))
return 0;
return unintr_read(((exec_handle*)nh->parts)->fd, buf, bufsize);
}
static void net123_close(net123_handle *nh)
{
if(!nh)
return;
exec_handle *eh = nh->parts;
if(eh->worker)
{
kill(eh->worker, SIGKILL);
errno = 0;
if(waitpid(eh->worker, NULL, 0) < 0)
merror("failed to wait for worker process: %s", strerror(errno));
else if(param.verbose > 1)
fprintf(stderr, "Note: network helper %"PRIiMAX" finished\n", (intmax_t)eh->worker);
}
if(eh->fd > -1)
close(eh->fd);
free(nh->parts);
free(nh);
}
net123_handle *net123_open_exec(const char *url, const char * const * client_head)
{
int use_curl = 0;
char * const curl_check_argv[] = { "curl", "--help", "all", NULL };
@@ -276,12 +301,21 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
}
int fd[2];
int hi = -1; // index of header value that might get a continuation line
net123_handle *nh = malloc(sizeof(net123_handle));
if(!nh)
exec_handle *eh = malloc(sizeof(exec_handle));
if(!nh || !eh)
{
if(nh)
free(nh);
if(eh)
free(eh);
return NULL;
nh->fd = -1;
nh->worker = 0;
}
nh->parts = eh;
nh->read = net123_read;
nh->close = net123_close;
eh->fd = -1;
eh->worker = 0;
errno = 0;
if(pipe(fd))
{
@@ -293,15 +327,15 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
compat_binmode(fd[0], TRUE);
compat_binmode(fd[1], TRUE);
nh->worker = fork();
if(nh->worker == -1)
eh->worker = fork();
if(eh->worker == -1)
{
merror("fork failed: %s", strerror(errno));
free(nh);
return NULL;
}
if(nh->worker == 0)
if(eh->worker == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
@@ -309,7 +343,6 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
dup2(infd, STDIN_FILENO);
// child
// Proxy environment variables can just be set in the user and inherited here, right?
int argc;
char **argv = use_curl ? curl_argv(url, client_head) : wget_argv(url, client_head);
if(!argv)
@@ -338,35 +371,9 @@ net123_handle *net123_open(const char *url, const char * const * client_head)
}
// parent
if(param.verbose > 1)
fprintf(stderr, "Note: started network helper with PID %"PRIiMAX"\n", (intmax_t)nh->worker);
fprintf(stderr, "Note: started network helper with PID %"PRIiMAX"\n", (intmax_t)eh->worker);
errno = 0;
close(fd[1]);
nh->fd = fd[0];
eh->fd = fd[0];
return nh;
}
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize)
{
if(!nh || (bufsize && !buf))
return 0;
return unintr_read(nh->fd, buf, bufsize);
}
void net123_close(net123_handle *nh)
{
if(!nh)
return;
if(nh->worker)
{
kill(nh->worker, SIGKILL);
errno = 0;
if(waitpid(nh->worker, NULL, 0) < 0)
merror("failed to wait for worker process: %s", strerror(errno));
else if(param.verbose > 1)
fprintf(stderr, "Note: network helper %"PRIiMAX" finished\n", (intmax_t)nh->worker);
}
if(nh->fd > -1)
close(nh->fd);
free(nh);
}

View File

@@ -5,13 +5,11 @@
#include <ws2tcpip.h>
#include <winhttp.h>
const char *net123_backends[] = { "(always winhttp)", NULL };
// The network implementation defines the struct for private use.
// The purpose is just to keep enough context to be able to
// call net123_read() and net123_close() afterwards.
#define URL_COMPONENTS_LENGTH 255
struct net123_handle_struct {
typedef struct {
HINTERNET session;
HINTERNET connect;
HINTERNET request;
@@ -26,7 +24,7 @@ struct net123_handle_struct {
size_t headers_pos, headers_len;
DWORD internetStatus, internetStatusLength;
LPVOID additionalInfo;
};
} winhttp_handle;
#define MPG123CONCAT_(x,y) x ## y
#define MPG123CONCAT(x,y) MPG123CONCAT_(x,y)
@@ -34,7 +32,7 @@ struct net123_handle_struct {
#define MPG123STRINGIFY(x) MPG123STRINGIFY_(x)
#define MPG123WSTR(x) MPG123CONCAT(L,MPG123STRINGIFY(x))
static DWORD wrap_auth(net123_handle *nh){
static DWORD wrap_auth(winhttp_handle *nh){
DWORD mode;
DWORD ret;
@@ -81,13 +79,16 @@ static void debug_crack(URL_COMPONENTS *comps){}
static
void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){
net123_handle *nh = (net123_handle *)dwContext;
winhttp_handle *nh = (winhttp_handle *)dwContext;
nh->internetStatus = dwInternetStatus;
nh->additionalInfo = lpvStatusInformation;
nh->internetStatusLength = dwStatusInformationLength;
}
net123_handle *net123_open(const char *url, const char * const *client_head){
static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize);
static void net123_close(net123_handle *nh);
net123_handle *net123_open_winhttp(const char *url, const char * const *client_head){
LPWSTR urlW = NULL, headers = NULL;
size_t ii;
WINBOOL res;
@@ -101,8 +102,18 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
win32_utf8_wide(url, &urlW, NULL);
if(urlW == NULL) goto cleanup;
net123_handle *ret = calloc(1, sizeof(net123_handle));
if (!ret) return ret;
winhttp_handle *ret = calloc(1, sizeof(winhttp_handle));
if (!ret) goto cleanup;
net123_handle *handle = calloc(1, sizeof(net123_handle));
if (!handle) {
free(ret);
goto cleanup;
}
handle->parts = ret;
handle->read = net123_read;
handle->close = net123_close;
ret->comps.dwStructSize = sizeof(ret->comps);
ret->comps.dwSchemeLength = 0;
@@ -206,13 +217,13 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
}
debug("net123_open OK");
return ret;
return handle;
cleanup:
debug("net123_open error");
if (urlW) free(urlW);
net123_close(ret);
ret = NULL;
return ret;
net123_close(handle);
handle = NULL;
return handle;
}
// Read data into buffer, return bytes read.
@@ -221,20 +232,21 @@ cleanup:
// For the user, it only matters if there will be more bytes or not.
// Feel free to communicate errors via error() / merror() functions inside.
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
winhttp_handle *h = nh->parts;
size_t ret;
size_t to_copy = nh->headers_len - nh->headers_pos;
size_t to_copy = h->headers_len - h->headers_pos;
DWORD bytesread = 0;
if(to_copy){
ret = to_copy <= bufsize ? to_copy : bufsize;
memcpy(buf, nh->headers + nh->headers_pos, ret);
nh->headers_pos += ret;
memcpy(buf, h->headers + h->headers_pos, ret);
h->headers_pos += ret;
return ret;
}
/* is this needed? */
to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize;
if(!WinHttpReadData(nh->request, buf, to_copy, &bytesread)){
if(!WinHttpReadData(h->request, buf, to_copy, &bytesread)){
return EOF;
}
return bytesread;
@@ -242,21 +254,26 @@ size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
// Call that to free up resources, end processes.
void net123_close(net123_handle *nh){
if(nh->headers) {
free(nh->headers);
nh->headers = NULL;
}
if(nh->request) {
WinHttpCloseHandle(nh->request);
nh->request = NULL;
}
if(nh->connect) {
WinHttpCloseHandle(nh->connect);
nh->connect = NULL;
}
if(nh->session) {
WinHttpCloseHandle(nh->session);
nh->session = NULL;
if (!nh) return;
winhttp_handle *h = nh->parts;
if(h) {
if(h->headers) {
free(h->headers);
h->headers = NULL;
}
if(h->request) {
WinHttpCloseHandle(h->request);
h->request = NULL;
}
if(h->connect) {
WinHttpCloseHandle(h->connect);
h->connect = NULL;
}
if(h->session) {
WinHttpCloseHandle(h->session);
h->session = NULL;
}
free(h);
}
free(nh);
}

View File

@@ -5,13 +5,11 @@
#include <ws2tcpip.h>
#include <wininet.h>
const char *net123_backends[] = { "(always wininet)", NULL };
// The network implementation defines the struct for private use.
// The purpose is just to keep enough context to be able to
// call net123_read() and net123_close() afterwards.
#define URL_COMPONENTS_LENGTH 255
struct net123_handle_struct {
typedef struct {
HINTERNET session;
HINTERNET connect;
HINTERNET request;
@@ -28,7 +26,7 @@ struct net123_handle_struct {
DWORD HttpQueryInfoIndex;
DWORD internetStatus, internetStatusLength;
LPVOID additionalInfo;
};
} wininet_handle;
#define MPG123CONCAT_(x,y) x ## y
#define MPG123CONCAT(x,y) MPG123CONCAT_(x,y)
@@ -60,13 +58,17 @@ static void debug_crack(URL_COMPONENTSW *comps){}
static
void WINAPI net123_ssl_errors(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength){
net123_handle *nh = (net123_handle *)dwContext;
debug("In net123_ssl_errors");
wininet_handle *nh = (wininet_handle *)(dwContext);
nh->internetStatus = dwInternetStatus;
nh->additionalInfo = lpvStatusInformation;
nh->internetStatusLength = dwStatusInformationLength;
}
net123_handle *net123_open(const char *url, const char * const *client_head){
static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize);
static void net123_close(net123_handle *nh);
net123_handle *net123_open_wininet(const char *url, const char * const *client_head){
LPWSTR urlW = NULL, headers = NULL;
size_t ii;
WINBOOL res;
@@ -78,51 +80,63 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
if(urlW == NULL) goto cleanup;
net123_handle *ret = calloc(1, sizeof(net123_handle));
if (!ret) return ret;
wininet_handle *wh = calloc(1, sizeof(wininet_handle));
if(!ret || !wh)
{
if(ret)
free(ret);
if(wh)
free(wh);
return NULL;
}
ret->comps.dwStructSize = sizeof(ret->comps);
ret->comps.dwSchemeLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwUserNameLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwPasswordLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwHostNameLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwUrlPathLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.dwExtraInfoLength = URL_COMPONENTS_LENGTH - 1;
ret->comps.lpszHostName = ret->lpszHostName;
ret->comps.lpszUserName = ret->lpszUserName;
ret->comps.lpszPassword = ret->lpszPassword;
ret->comps.lpszUrlPath = ret->lpszUrlPath;
ret->comps.lpszExtraInfo = ret->lpszExtraInfo;
ret->comps.lpszScheme = ret->lpszScheme;
ret->parts = wh;
ret->read = net123_read;
ret->close = net123_close;
wh->comps.dwStructSize = sizeof(wh->comps);
wh->comps.dwSchemeLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.dwUserNameLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.dwPasswordLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.dwHostNameLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.dwUrlPathLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.dwExtraInfoLength = URL_COMPONENTS_LENGTH - 1;
wh->comps.lpszHostName = wh->lpszHostName;
wh->comps.lpszUserName = wh->lpszUserName;
wh->comps.lpszPassword = wh->lpszPassword;
wh->comps.lpszUrlPath = wh->lpszUrlPath;
wh->comps.lpszExtraInfo = wh->lpszExtraInfo;
wh->comps.lpszScheme = wh->lpszScheme;
debug1("net123_open start crack %S", urlW);
if(!(res = InternetCrackUrlW(urlW, 0, 0, &ret->comps))) {
if(!(res = InternetCrackUrlW(urlW, 0, 0, &wh->comps))) {
debug1("net123_open crack fail %lu", GetLastError());
goto cleanup;
}
debug("net123_open crack OK");
debug_crack(&ret->comps);
debug_crack(&wh->comps);
ret->session = InternetOpenW(useragent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
wh->session = InternetOpenW(useragent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
free(urlW);
urlW = NULL;
debug("net123_open InternetOpenW OK");
if(!ret->session) goto cleanup;
if(!wh->session) goto cleanup;
debug2("net123_open InternetConnectW %S %u", ret->comps.lpszHostName, ret->comps.nPort);
ret->connect = InternetConnectW(ret->session, ret->comps.lpszHostName, ret->comps.nPort,
ret->comps.dwUserNameLength ? ret->comps.lpszUserName : NULL, ret->comps.dwPasswordLength ? ret->comps.lpszPassword : NULL,
debug2("net123_open InternetConnectW %S %u", wh->comps.lpszHostName, wh->comps.nPort);
wh->connect = InternetConnectW(wh->session, wh->comps.lpszHostName, wh->comps.nPort,
wh->comps.dwUserNameLength ? wh->comps.lpszUserName : NULL, wh->comps.dwPasswordLength ? wh->comps.lpszPassword : NULL,
INTERNET_SERVICE_HTTP, 0, 0);
if(!ret->connect) goto cleanup;
if(!wh->connect) goto cleanup;
debug("net123_open InternetConnectW OK");
debug1("HttpOpenRequestW GET %S", ret->comps.lpszUrlPath);
ret->request = HttpOpenRequestW(ret->connect, L"GET", ret->comps.lpszUrlPath, NULL, NULL, NULL, ret->comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)ret);
if(!ret->request) goto cleanup;
debug1("HttpOpenRequestW GET %S", wh->comps.lpszUrlPath);
wh->request = HttpOpenRequestW(wh->connect, L"GET", wh->comps.lpszUrlPath, NULL, NULL, NULL, wh->comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)wh);
if(!wh->request) goto cleanup;
debug("HttpOpenRequestW GET OK");
cb = InternetSetStatusCallback(ret->request, (INTERNET_STATUS_CALLBACK)net123_ssl_errors);
cb = InternetSetStatusCallback(wh->request, (INTERNET_STATUS_CALLBACK)net123_ssl_errors);
if(cb != NULL){
error1("InternetSetStatusCallback failed to install callback, errors might not be reported properly! (%lu)", GetLastError());
}
@@ -132,7 +146,7 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
if(!headers)
goto cleanup;
debug1("HttpAddRequestHeadersW add %S", headers);
res = HttpAddRequestHeadersW(ret->request, headers, (DWORD) -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
res = HttpAddRequestHeadersW(wh->request, headers, (DWORD) -1, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
debug2("HttpAddRequestHeadersW returns %u %lu", res, res ? 0 : GetLastError());
free(headers);
headers = NULL;
@@ -140,13 +154,14 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
debug("net123_open ADD HEADERS OK");
res = HttpSendRequestW(ret->request, NULL, 0, NULL, 0);
res = HttpSendRequestW(wh->request, NULL, 0, NULL, 0);
if (!res) {
res = GetLastError();
error1("HttpSendRequestW failed with %lu", res);
goto cleanup;
}
debug("HttpSendRequestW OK");
// dummy, cannot be null
headers = calloc(1,1);
@@ -155,18 +170,20 @@ net123_handle *net123_open(const char *url, const char * const *client_head){
error("Cannot allocate dummy buffer for HttpQueryInfoW");
goto cleanup;
}
res = HttpQueryInfoW(ret->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &ret->HttpQueryInfoIndex);
debug("Try HttpQueryInfoW pass 1");
res = HttpQueryInfoW(wh->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &wh->HttpQueryInfoIndex);
free(headers);
debug("HttpQueryInfoW pass 1 OK");
if(!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER && headerlen > 0) {
/* buffer size is in bytes, not including terminator */
headers = calloc(1, headerlen + sizeof(*headers));
if (!headers) goto cleanup;
res = HttpQueryInfoW(ret->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &ret->HttpQueryInfoIndex);
res = HttpQueryInfoW(wh->request, HTTP_QUERY_RAW_HEADERS_CRLF, headers, &headerlen, &wh->HttpQueryInfoIndex);
debug3("HttpQueryInfoW returned %u, err %u : %S", res, GetLastError(), headers ? headers : L"null");
win32_wide_utf7(headers, &ret->headers, &ret->headers_len);
win32_wide_utf7(headers, &wh->headers, &wh->headers_len);
/* bytes written, skip the terminating null, we want to stop at the \r\n\r\n */
ret->headers_len --;
wh->headers_len --;
free(headers);
headers = NULL;
} else {
@@ -184,21 +201,24 @@ cleanup:
return ret;
}
size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
static size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
if(!nh || !nh->parts)
return 0;
wininet_handle *wh = nh->parts;
size_t ret;
size_t to_copy = nh->headers_len - nh->headers_pos;
size_t to_copy = wh->headers_len - wh->headers_pos;
DWORD bytesread = 0;
if(to_copy){
ret = to_copy <= bufsize ? to_copy : bufsize;
memcpy(buf, nh->headers + nh->headers_pos, ret);
nh->headers_pos += ret;
memcpy(buf, wh->headers + wh->headers_pos, ret);
wh->headers_pos += ret;
return ret;
}
/* is this needed? */
to_copy = bufsize > ULONG_MAX ? ULONG_MAX : bufsize;
if(!InternetReadFile(nh->request, buf, to_copy, &bytesread)){
if(!InternetReadFile(wh->request, buf, to_copy, &bytesread)){
error1("InternetReadFile exited with %d", GetLastError());
return EOF;
}
@@ -206,22 +226,28 @@ size_t net123_read(net123_handle *nh, void *buf, size_t bufsize){
}
// Call that to free up resources, end processes.
void net123_close(net123_handle *nh){
if(nh->headers) {
free(nh->headers);
nh->headers = NULL;
static void net123_close(net123_handle *nh){
if(!nh)
return;
wininet_handle *wh = nh->parts;
if(!wh) /*???*/
return;
if(wh->headers) {
free(wh->headers);
wh->headers = NULL;
}
if(nh->request) {
InternetCloseHandle(nh->request);
nh->request = NULL;
if(wh->request) {
InternetCloseHandle(wh->request);
wh->request = NULL;
}
if(nh->connect) {
InternetCloseHandle(nh->connect);
nh->connect = NULL;
if(wh->connect) {
InternetCloseHandle(wh->connect);
wh->connect = NULL;
}
if(nh->session) {
InternetCloseHandle(nh->session);
nh->session = NULL;
if(wh->session) {
InternetCloseHandle(wh->session);
wh->session = NULL;
}
free(nh);
free(wh);
}

View File

@@ -1516,6 +1516,9 @@ int main(int sys_argc, char ** sys_argv)
case GLO_NOARG:
fprintf (stderr, ME": missing argument for parameter: %s\n", loptarg);
usage(1);
case GLO_BADARG:
fprintf(stderr, ME": bad option argument: %s\n", loptarg);
usage(1);
}
finish_endian();
mdebug("input byte flags: %ld, output byte flags: %ld", byte_in_flags, byte_out_flags);

View File

@@ -357,7 +357,8 @@ static int add_next_file (int argc, char *argv[], int args_utf8)
}
}
pl.entry = 0;
if(pl.file && pl.file->network)
#ifdef NET123
if(pl.file && pl.file->nh)
{
debug1("htd.content_type.p: %p", (void*) pl.file->htd.content_type.p);
if(!APPFLAG(MPG123APP_IGNORE_MIME) && pl.file->htd.content_type.p != NULL)
@@ -402,6 +403,7 @@ static int add_next_file (int argc, char *argv[], int args_utf8)
}
}
}
#endif
if(!pl.file)
{
param.listname = NULL; // why?

View File

@@ -4,11 +4,15 @@
This evolved into the generic I/O interposer for direct file or http stream
access, with explicit buffering for getline.
copyright 2010-2019 by the mpg123 project - free software under the terms of the LGPL 2.1
copyright 2010-2022 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
// setenv
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "streamdump.h"
#include <fcntl.h>
#include <errno.h>
@@ -18,6 +22,143 @@
#define O_BINARY 0
#endif
#ifndef HTTP_MAX_RELOCATIONS
#define HTTP_MAX_RELOCATIONS 20
#endif
#if defined(NETWORK) && !defined(NET123)
#error "NETWORK only with NET123 from now on!"
#endif
#if !defined(NETWORK) && defined(NET123)
#error "NET123 only with NETWORK from now on!"
#endif
#ifdef NETWORK
const char *net123_backends[] =
{
"internal"
#ifdef NET123_EXEC
, "wget"
, "curl"
#endif
#ifdef NET123_WINHTTP
, "winhttp"
#endif
#ifdef NET123_WININET
, "wininet"
#endif
, NULL
};
// Net123 variant for our internal code that has safer legacy support for HTTP shoutcast.
static size_t net123_read_internal( struct net123_handle_struct *nh,
void *buf, size_t bufsize )
{
if(!nh)
return 0;
int *fdp = nh->parts;
#ifdef WANT_WIN32_SOCKETS
return win32_net_read(*fdp, buf, bufsize);
#else
return unintr_read(*fdp, buf, bufsize);
#endif
}
static void net123_close_internal(struct net123_handle_struct *nh)
{
if(nh)
return;
int *fdp = nh->parts;
#ifdef WANT_WIN32_SOCKETS
if(*fdp != SOCKET_ERROR)
win32_net_close(*fdp);
#else
close(*fdp);
#endif
free(fdp);
free(nh);
}
static net123_handle *net123_open_internal( const char *url
, const char * const *client_head, struct httpdata *hd )
{
net123_handle *nh = malloc(sizeof(net123_handle));
if(!nh)
return NULL;
int *fdp = malloc(sizeof(int));
if(!fdp)
{
free(nh);
return NULL;
}
nh->parts = fdp;
nh->read = net123_read_internal;
nh->close = net123_close_internal;
// Handles win32_net internally.
*fdp = http_open(url, hd, client_head);
if(*fdp >= 0)
return nh;
free(fdp);
free(nh);
return NULL;
}
// Decide which backend to load.
static net123_handle *net123_open( const char *url
, const char * const *client_head, struct httpdata *hd )
{
int autochoose = !strcmp("auto", param.network_backend);
int https = !strncasecmp("https://", url, 8);
if(param.proxyurl)
{
if(strcmp(param.proxyurl, "none"))
{
#ifdef HAVE_SETENV
setenv("http_proxy", param.proxyurl, 1);
setenv("HTTP_PROXY", param.proxyurl, 1);
setenv("https_proxy", param.proxyurl, 1);
setenv("HTTPS_PROXY", param.proxyurl, 1);
#endif
} else
{
#ifdef HAVE_UNSETENV
unsetenv("http_proxy");
unsetenv("HTTP_PROXY");
unsetenv("https_proxy");
unsetenv("HTTPS_PROXY");
#endif
}
}
if( (autochoose && !https)
|| !strcmp("internal", param.network_backend) )
{
if(https && !param.quiet)
fprintf(stderr, "Note: HTTPS will fail with internal network code.\n");
return net123_open_internal(url, client_head, hd);
}
#ifdef NET123_EXEC
if( autochoose
|| !strcmp("wget", param.network_backend)
|| !strcmp("curl", param.network_backend) )
return net123_open_exec(url, client_head);
#endif
#ifdef NET123_WININET
if(autochoose || !strcmp("wininet", param.network_backend))
return net123_open_wininet(url, client_head);
#endif
#ifdef NET123_WINHTTP
if(autochoose || !strcmp("winhttp", param.network_backend))
return net123_open_winhttp(url, client_head);
#endif
merror("no network backend for %s", https ? "HTTPS" : "HTTP");
return NULL;
}
#endif
/* Stream dump descriptor. */
static int dump_fd = -1;
@@ -27,13 +168,9 @@ static int dump_fd = -1;
static ssize_t stream_read_raw(struct stream *sd, void *buf, size_t count)
{
ssize_t ret = -1;
#ifdef NET123
#ifdef NETWORK
if(sd->nh)
ret = net123_read(sd->nh, buf, count);
#endif
#ifdef WANT_WIN32_SOCKETS
if(sd->fd >= 0 && sd->network)
ret = win32_net_read(sd->fd, buf, count);
ret = (ssize_t) sd->nh->read(sd->nh, buf, count);
#endif
if(sd->fd >= 0) // plain file or network socket
ret = (ssize_t) unintr_read(sd->fd, buf, count);
@@ -75,8 +212,12 @@ static ssize_t stream_read(struct stream *sd, void *buf, size_t count)
static off_t stream_seek(struct stream *sd, off_t pos, int whence)
{
if(!sd || sd->network)
if(!sd)
return -1;
#ifdef NET123
if(sd->nh)
return -1;
#endif
return lseek(sd->fd, pos, whence);
}
@@ -147,15 +288,22 @@ ssize_t stream_getline(struct stream *sd, mpg123_string *line)
}
}
int stream_parse_headers(struct stream *sd)
#ifdef NETWORK
// Return 0 on success, non-zero when there is an error or more work to do.
// -1: error, 1: redirection to given location
static int stream_parse_headers(struct stream *sd, mpg123_string *location)
{
int ret = 0;
mpg123_string line;
mpg123_init_string(&line);
mpg123_string icyint;
mpg123_init_string(&icyint);
const char *head[] = { "content-type", "icy-name", "icy-url", "icy-metaint" };
mpg123_string *val[] = { &sd->htd.content_type, &sd->htd.icy_name, &sd->htd.icy_url, &icyint };
int redirect = 0;
location->fill = 0;
const char *head[] = { "content-type", "icy-name"
, "icy-url", "icy-metaint", "location" };
mpg123_string *val[] = { &sd->htd.content_type, &sd->htd.icy_name
, &sd->htd.icy_url, &icyint, location };
int hn = sizeof(head)/sizeof(char*);
int hi = -1;
int got_ok = 0;
@@ -171,6 +319,8 @@ int stream_parse_headers(struct stream *sd)
}
// React to HTTP error codes, but do not enforce an OK being sent as Shoutcast
// only produces very minimal headers, not even a HTTP response code.
// Well, ICY 200 OK could be there, but then we got other headers to know
// things are fine.
if(!strncasecmp("http/", line.p, 5))
{
// HTTP/1.1 200 OK
@@ -181,9 +331,17 @@ int stream_parse_headers(struct stream *sd)
++tok;
if(tok && *tok != '2')
{
merror("HTTP error response: %s", line.p);
ret = -1;
break;
if(*tok == '3')
{
redirect = ret = 1;
if(param.verbose > 2)
fprintf(stderr, "Note: HTTP redirect\n");
} else
{
merror("HTTP error response: %s", line.p);
ret = -1;
break;
}
} else if(tok && *tok == '2')
{
if(param.verbose > 2)
@@ -235,7 +393,7 @@ int stream_parse_headers(struct stream *sd)
continue;
}
}
if(icyint.fill)
if(!redirect && icyint.fill)
{
sd->htd.icy_interval = atol(icyint.p);
if(param.verbose > 1)
@@ -245,6 +403,9 @@ int stream_parse_headers(struct stream *sd)
{
error("no data at all from network resource");
ret = -1;
} else if(redirect && !location->fill)
{
error("redirect but no location given");
} else if(!got_ok)
{
error("missing positive server response");
@@ -255,11 +416,84 @@ int stream_parse_headers(struct stream *sd)
return ret;
}
// resolve relative locations given the initial full URL
// full URL ensured to either start with http:// or https://
// (case-insensitive), location non-empty
static void relocate_url(mpg123_string *location, const char *url)
{
if(!strncasecmp(location->p, "http://", 7) || !strncasecmp(location->p, "https://", 8))
return;
if(!url || (strncasecmp(url, "http://", 7) && strncasecmp(url, "https://", 8)))
{
mpg123_resize_string(location, 0);
return;
}
if(!param.quiet)
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
mpg123_string purl;
mpg123_string workbuf;
mpg123_init_string(&purl);
mpg123_init_string(&workbuf);
if(mpg123_set_string(&purl, url) && mpg123_move_string(location, &workbuf))
{
debug1("relocate request_url: %s", purl.p);
// location somewhat relative, either /some/path or even just some/path
char* ptmp = NULL;
// Though it's not RFC (?), accept relative URIs as wget does.
if(workbuf.p[0] == '/')
{
// server-absolute only prepend http://server/
// I null the first / after http:// or https://
size_t off = (purl.p[4] == 's') ? 8 : 7;
ptmp = strchr(purl.p+off,'/');
if(ptmp != NULL)
{
purl.fill = ptmp-purl.p+1;
purl.p[purl.fill-1] = 0;
}
}
else
{
// relative to current directory
// prepend http://server/path/
// first cutting off parameter stuff from URL
for(size_t i=0; i<purl.fill; ++i)
{
if(purl.p[i] == '?' || purl.p[i] == '#')
{
purl.p[i] = 0;
purl.fill = i+1;
break;
}
}
// now the last slash, keeping it
ptmp = strrchr(purl.p, '/');
if(ptmp != NULL)
{
purl.fill = ptmp-purl.p+2;
purl.p[purl.fill-1] = 0;
}
}
// only the prefix left
debug1("prefix=%s", purl.p);
mpg123_add_string(location, purl.p);
}
mpg123_add_string(location, workbuf.p);
mpg123_free_string(&workbuf);
mpg123_free_string(&purl);
}
#endif
static void stream_init(struct stream *sd)
{
sd->bufp = sd->buf;
sd->fill = 0;
sd->network = 0;
sd->fd = -1;
#ifdef NET123
sd->nh = NULL;
@@ -267,6 +501,24 @@ static void stream_init(struct stream *sd)
httpdata_init(&sd->htd);
}
// Clean up things for another connection.
// Does not reset the network flag.
static void stream_reset(struct stream *sd)
{
#ifdef NET123
if(sd->nh)
sd->nh->close(sd->nh);
sd->nh = NULL;
#endif
if(sd->fd >= 0) // plain file or network socket
close(sd->fd);
sd->fd = -1;
httpdata_free(&sd->htd);
httpdata_init(&sd->htd);
sd->bufp = sd->buf;
sd->fill = 0;
}
struct stream *stream_open(const char *url)
{
struct stream *sd = malloc(sizeof(struct stream));
@@ -279,10 +531,9 @@ struct stream *stream_open(const char *url)
sd->fd = STDIN_FILENO;
compat_binmode(STDIN_FILENO, TRUE);
}
#ifdef NET123
#ifdef NETWORK
else if(!strncasecmp("http://", url, 7) || !strncasecmp("https://", url, 8))
{
sd->network = 1;
// Network stream with header parsing.
const char *client_head[] = { NULL, NULL, NULL };
client_head[0] = param.talk_icy ? icy_yes : icy_no;
@@ -290,26 +541,51 @@ struct stream *stream_open(const char *url)
mpg123_init_string(&accept);
append_accept(&accept);
client_head[1] = accept.p;
sd->nh = net123_open(url, client_head);
if(!sd->nh || stream_parse_headers(sd))
mpg123_string location;
mpg123_string urlcopy;
mpg123_init_string(&location);
mpg123_init_string(&urlcopy);
int numrelocs = 0;
while(sd)
{
stream_close(sd);
return NULL;
}
}
#elif defined(NETWORK)
else if(!strncasecmp("http://", url, 7))
{
#ifdef WANT_WIN32_SOCKETS
sd->fd = win32_net_http_open(url, &sd->htd);
#else
sd->fd = http_open(url, &sd->htd);
#endif
if(sd->fd < 0)
{
stream_close(sd);
return NULL;
sd->nh = net123_open(url, client_head, &sd->htd);
if(!sd->nh)
{
stream_close(sd);
sd = NULL;
break;
}
location.fill = 0;
if(stream_parse_headers(sd, &location))
{
stream_reset(sd);
if(location.fill)
{
if(++numrelocs > HTTP_MAX_RELOCATIONS)
{
merror("too many HTTP redirections: %i", numrelocs);
url = NULL;
} else
{
relocate_url(&location, url); // resolve relative locations
mpg123_copy_string(&location, &urlcopy);
url = urlcopy.p;
}
} else
{
url = NULL;
}
if(!url)
{
stream_close(sd);
sd = NULL;
}
} else break; // Successful end.
}
mpg123_free_string(&urlcopy);
mpg123_free_string(&location);
mpg123_free_string(&accept);
// Either sd is NULL or we got a stream ready.
}
#endif
else
@@ -333,20 +609,7 @@ void stream_close(struct stream *sd)
{
if(!sd)
return;
#ifdef NET123
if(sd->nh)
net123_close(sd->nh);
#endif
#ifdef WANT_WIN32_SOCKETS
if(sd->fd >= 0 && sd->network)
{
if(sd->fd != SOCKET_ERROR)
win32_net_close(sd->fd);
}
#endif
if(sd->fd >= 0) // plain file or network socket
close(sd->fd);
httpdata_free(&sd->htd);
stream_reset(sd);
free(sd);
}
@@ -419,12 +682,7 @@ int dump_setup(struct stream *sd, mpg123_handle *mh)
ret = mpg123_open_handle(mh, sd);
} else
{
#ifdef WANT_WIN32_SOCKETS
if(sd->network)
win32_net_replace(mh);
else // ensure libmpg123 is using its own reader otherwise
#endif
mpg123_replace_reader(mh, NULL, NULL);
mpg123_replace_reader(mh, NULL, NULL);
ret = mpg123_open_fd(mh, sd->fd);
}
if(ret != MPG123_OK)

View File

@@ -18,6 +18,9 @@
#ifdef NET123
#include "net123.h"
#endif
#ifdef NETWORK
#include "httpget.h"
#endif
// The stream is either addressed via file descriptor or net123 handle.
struct stream
@@ -25,7 +28,6 @@ struct stream
char buf[256]; // buffer for getline
char *bufp; // read pointer in buffer
int fill; // bytes in buffer
int network; // flag to mark network streams (with httpdata)
int fd; // if > 0: plain file descriptor or win32 net socket
struct httpdata htd;
#ifdef NET123

View File

@@ -17,22 +17,26 @@
#include "debug.h"
static int term_enable = 0;
static const char *extrabreak = "";
int seeking = FALSE;
extern out123_handle *ao;
static const int helplen = 18;
#define HELPFMT "%-18s"
/* Hm, next step would be some system in this, plus configurability...
Two keys for everything? It's just stop/pause for now... */
struct keydef { const char key; const char key2; const char* desc; };
struct keydef term_help[] =
{
{ MPG123_STOP_KEY, ' ', "interrupt/restart playback (i.e. '(un)pause')" }
{ MPG123_STOP_KEY, ' ', "(un)pause playback" }
,{ MPG123_NEXT_KEY, 0, "next track" }
,{ MPG123_PREV_KEY, 0, "previous track" }
,{ MPG123_NEXT_DIR_KEY, 0, "next directory (next track until directory part changes)" }
,{ MPG123_PREV_DIR_KEY, 0, "previous directory (previous track until directory part changes)" }
,{ MPG123_BACK_KEY, 0, "back to beginning of track" }
,{ MPG123_PAUSE_KEY, 0, "loop around current position (don't combine with output buffer)" }
,{ MPG123_NEXT_DIR_KEY, 0, "next directory" }
,{ MPG123_PREV_DIR_KEY, 0, "previous directory" }
,{ MPG123_BACK_KEY, 0, "back to beginning" }
,{ MPG123_PAUSE_KEY, 0, "loop (--pauseloop)" }
,{ MPG123_FORWARD_KEY, 0, "forward" }
,{ MPG123_REWIND_KEY, 0, "rewind" }
,{ MPG123_FAST_FORWARD_KEY, 0, "fast forward" }
@@ -42,19 +46,19 @@ struct keydef term_help[] =
,{ MPG123_VOL_UP_KEY, 0, "volume up" }
,{ MPG123_VOL_DOWN_KEY, 0, "volume down" }
,{ MPG123_VOL_MUTE_KEY, 0, "(un)mute volume" }
,{ MPG123_RVA_KEY, 0, "RVA switch" }
,{ MPG123_VERBOSE_KEY, 0, "verbose switch" }
,{ MPG123_PLAYLIST_KEY, 0, "list current playlist, indicating current track there" }
,{ MPG123_TAG_KEY, 0, "display tag info (again)" }
,{ MPG123_MPEG_KEY, 0, "print MPEG header info (again)" }
,{ MPG123_PITCH_UP_KEY, MPG123_PITCH_BUP_KEY, "pitch up (small step, big step)" }
,{ MPG123_PITCH_DOWN_KEY, MPG123_PITCH_BDOWN_KEY, "pitch down (small step, big step)" }
,{ MPG123_PITCH_ZERO_KEY, 0, "reset pitch to zero" }
,{ MPG123_BOOKMARK_KEY, 0, "print out current position in playlist and track, for the benefit of some external tool to store bookmarks" }
,{ MPG123_RVA_KEY, 0, "cycle RVA modes" }
,{ MPG123_VERBOSE_KEY, 0, "cycle verbosity" }
,{ MPG123_PLAYLIST_KEY, 0, "show playlist" }
,{ MPG123_TAG_KEY, 0, "tag info" }
,{ MPG123_MPEG_KEY, 0, "MPEG header info" }
,{ MPG123_PITCH_UP_KEY, MPG123_PITCH_BUP_KEY, "pitch up + ++" }
,{ MPG123_PITCH_DOWN_KEY, MPG123_PITCH_BDOWN_KEY, "pitch down - --" }
,{ MPG123_PITCH_ZERO_KEY, 0, "zero pitch" }
,{ MPG123_BOOKMARK_KEY, 0, "print bookmark" }
,{ MPG123_HELP_KEY, 0, "this help" }
,{ MPG123_QUIT_KEY, 0, "quit" }
,{ MPG123_EQ_RESET_KEY, 0, "reset to a flat equalizer" }
,{ MPG123_EQ_SHOW_KEY, 0, "show our current rough equalizer settings" }
,{ MPG123_EQ_RESET_KEY, 0, "flat equalizer" }
,{ MPG123_EQ_SHOW_KEY, 0, "show equalizer" }
,{ MPG123_BASS_UP_KEY, 0, "more bass" }
,{ MPG123_BASS_DOWN_KEY, 0, "less bass" }
,{ MPG123_MID_UP_KEY, 0, "more mids" }
@@ -64,7 +68,7 @@ struct keydef term_help[] =
};
/* initialze terminal */
void term_init(void)
int term_init(void)
{
const char hide_cursor[] = "\x1b[?25l";
debug("term_init");
@@ -72,9 +76,11 @@ void term_init(void)
if(term_have_fun(STDERR_FILENO, param.term_visual))
fprintf(stderr, "%s", hide_cursor);
if(param.verbose)
extrabreak = "\n";
debug1("param.term_ctrl: %i", param.term_ctrl);
if(!param.term_ctrl)
return;
return 0;
term_enable = 0;
errno = 0;
@@ -84,9 +90,10 @@ void term_init(void)
merror("failed to set up terminal: %s", strerror(errno));
else
error("failed to set up terminal");
return;
return -1;
}
term_enable = 1;
return 0;
}
void term_hint(void)
@@ -127,7 +134,7 @@ void pause_recycle(mpg123_handle *fr)
{
/* Take care not to go backwards in time in steps of 1 frame
That is what the +1 is for. */
pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
pause_cycle=(int)(param.pauseloop/mpg123_tpf(fr));
offset-=pause_cycle;
}
@@ -153,7 +160,7 @@ off_t term_control(mpg123_handle *fr, out123_handle *ao)
do
{
off_t old_offset = offset;
term_handle_input(fr, ao, stopped|seeking);
term_handle_input(fr, ao, seeking);
if((offset < 0) && (-offset > framenum)) offset = - framenum;
if(param.verbose && offset != old_offset)
print_stat(fr,offset,ao,1,&param);
@@ -204,6 +211,20 @@ static void seekmode(mpg123_handle *mh, out123_handle *ao)
}
}
static void print_term_help(struct keydef *def)
{
if(def->key2)
{
if(isspace(def->key2))
fprintf(stderr, "%c '%c'", def->key, def->key2);
else
fprintf(stderr, "%c %c ", def->key, def->key2);
}
else fprintf(stderr, "%c ", def->key);
fprintf(stderr, " " HELPFMT, def->desc);
}
static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
{
debug1("term_handle_key: %c", val);
@@ -212,7 +233,7 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
case MPG123_BACK_KEY:
out123_pause(ao);
out123_drop(ao);
if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
if(paused) pause_cycle=(int)(param.pauseloop/mpg123_tpf(fr));
if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0)
error1("Seek to begin failed: %s", mpg123_strerror(fr));
@@ -232,8 +253,9 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
case MPG123_QUIT_KEY:
debug("QUIT");
if(stopped)
{ if(param.verbose)
print_stat(fr,0,ao,0,&param);
{
if(param.verbose)
print_stat(fr,0,ao,0,&param);
stopped = 0;
out123_pause(ao); /* no chance for annoying underrun warnings */
@@ -244,13 +266,23 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
break;
case MPG123_PAUSE_KEY:
paused=1-paused;
size_t buffered = out123_buffered(ao);
out123_pause(ao); /* underrun awareness */
out123_drop(ao);
if(paused)
{
/* Not really sure if that is what is wanted
This jumps in audio output, but has direct reaction to pausing loop. */
// Make output buffer react immediately, dropping decoded audio
// and (at least trying to) seeking back in input.
out123_param_float(ao, OUT123_PRELOAD, 0.);
if(buffered)
{
int framesize = 1;
if(!out123_getformat(ao, NULL, NULL, NULL, &framesize))
{
buffered /= framesize;
mpg123_seek(fr, -buffered, SEEK_CUR);
}
}
pause_recycle(fr);
}
else
@@ -326,7 +358,8 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
print_stat(fr,0,ao,0,&param);
// Assuming only changes happen via terminal control, these 3 values
// are what counts.
fprintf( stderr, "\n\nbass: %.3f\nmid: %.3f\ntreble: %.3f\n\n"
fprintf( stderr, "%s\nbass: %.3f\nmid: %.3f\ntreble: %.3f\n\n"
, extrabreak
, mpg123_geteq(fr, MPG123_LEFT, 0)
, mpg123_geteq(fr, MPG123_LEFT, 1)
, mpg123_geteq(fr, MPG123_LEFT, 2)
@@ -381,7 +414,9 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
{
param.verbose = 0;
clear_stat();
}
extrabreak = "";
} else
extrabreak = "\n";
mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0);
break;
case MPG123_RVA_KEY:
@@ -412,34 +447,38 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
case MPG123_TAG_KEY:
if(param.verbose)
print_stat(fr,0,ao,0,&param);
fprintf(stderr, "%s\n", param.verbose ? "\n" : "");
fprintf(stderr, "%s", extrabreak);
print_id3_tag(fr, param.long_id3, stderr, term_width(STDERR_FILENO));
break;
case MPG123_MPEG_KEY:
if(param.verbose)
print_stat(fr,0,ao,0,&param);
fprintf(stderr, "\n");
fprintf(stderr, "%s", extrabreak);
if(param.verbose > 1)
print_header(fr);
else
print_header_compact(fr);
fprintf(stderr, "\n");
break;
case MPG123_HELP_KEY:
{ /* This is more than the one-liner before, but it's less spaghetti. */
int i;
if(param.verbose)
print_stat(fr,0,ao,0,&param);
fprintf(stderr,"\n\n -= terminal control keys =-\n");
fprintf(stderr,"%s\n -= terminal control keys =-\n\n", extrabreak);
int linelen = term_width(STDERR_FILENO);
int colwidth = helplen+6;
int columns = linelen > colwidth ? ((linelen+2)/(colwidth+2)) : 1;
int j = 0;
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);
if(j)
fprintf(stderr, " ");
print_term_help(term_help+i);
j = (j+1)%columns;
if(!j)
fprintf(stderr, "\n");
}
fprintf(stderr, "\nAlso, the number row (starting at 1, ending at 0) gives you jump points into the current track at 10%% intervals.\n");
fprintf(stderr, "\n");
fprintf(stderr, "\n\nNumber row jumps in 10%% steps.\n\n");
}
break;
case MPG123_FRAME_INDEX_KEY:
@@ -508,7 +547,7 @@ static void term_handle_input(mpg123_handle *fr, out123_handle *ao, int do_delay
{
char val;
/* Do we really want that while loop? This means possibly handling multiple inputs that come very rapidly in one go. */
while(term_get_key(do_delay, &val))
while(term_get_key(stopped, do_delay, &val))
{
term_handle_key(fr, ao, val);
}

View File

@@ -12,8 +12,6 @@
#include "mpg123app.h"
#include "audio.h"
#define LOOP_CYCLES 0.500000 /* Loop time in sec */
/*
* Defines the keybindings in term.c - change to your
* own preferences.
@@ -87,7 +85,7 @@
#define MPG123_TERM_USR2 "f"
void term_init(void);
int term_init(void); // -1 on error, 0 success or no terminal desired
void term_exit(void);
off_t term_control(mpg123_handle *mh, out123_handle *ao);
void term_hint(void); /* Print a message hinting at terminal usage. */

View File

@@ -25,7 +25,7 @@ void term_restore(void)
{
}
int term_get_key(int do_delay, char *val)
int term_get_key(int stopped, int do_delay, char *val)
{
return 0;
}

View File

@@ -8,6 +8,10 @@
initially written by Thomas Orgis
*/
// ctermid
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200112L
#include "config.h"
#ifdef __OS2__
@@ -176,7 +180,7 @@ int term_setup(void)
/* Get the next pressed key, if any.
Returns 1 when there is a key, 0 if not. */
int term_get_key(int do_delay, char *val)
int term_get_key(int stopped, int do_delay, char *val)
{
#ifdef __OS2__
KBDKEYINFO key;
@@ -184,7 +188,7 @@ int term_get_key(int do_delay, char *val)
key.chScan = 0;
if(do_delay)
DosSleep(10);
if(!KbdCharIn(&key,IO_NOWAIT,0) && key.chChar)
if(!KbdCharIn(&key,(stopped) ? IO_WAIT : IO_NOWAIT,0) && key.chChar)
{
*val = key.chChar;
return 1;
@@ -207,7 +211,8 @@ int term_get_key(int do_delay, char *val)
FD_ZERO(&r);
FD_SET(term_fd,&r);
if(select(term_fd+1,&r,NULL,NULL,&t) > 0 && FD_ISSET(term_fd,&r))
/* No timeout if stopped */
if(select(term_fd+1,&r,NULL,NULL,(stopped) ? NULL : &t) > 0 && FD_ISSET(term_fd,&r))
{
if(read(term_fd,val,1) <= 0)
return 0; /* Well, we couldn't read the key, so there is none. */

View File

@@ -90,7 +90,7 @@ int term_present(void){
/* Get the next pressed key, if any.
Returns 1 when there is a key, 0 if not. */
int term_get_key(int do_delay, char *val){
int term_get_key(int stopped, int do_delay, char *val){
INPUT_RECORD record;
HANDLE input;
DWORD res;
@@ -99,7 +99,7 @@ int term_get_key(int do_delay, char *val){
if(input == NULL || input == INVALID_HANDLE_VALUE)
return 0;
while(WaitForSingleObject(input, do_delay ? 10 : 0) == WAIT_OBJECT_0){
while(WaitForSingleObject(input, stopped ? INFINITE : (do_delay ? 10 : 0)) == WAIT_OBJECT_0){
do_delay = 0;
if(!ReadConsoleInput(input, &record, 1, &res))
return 0;

View File

@@ -47,6 +47,6 @@ void term_restore(void);
* \param val address to store character to
* \return 1 if there is a key, 0 if not
*/
int term_get_key(int do_delay, char *val);
int term_get_key(int stopped, int do_delay, char *val);
#endif

View File

@@ -86,7 +86,7 @@ int main(int argc, char **argv)
fprintf(stderr,"1to1 non-indexed seek errors: %"SIZE_P" / %"SIZE_P"\n", (size_p)errs[1],(size_p)samples);
fprintf(stderr,"NtoM indexed seek errors: %"SIZE_P" / %"SIZE_P"\n", (size_p)errs_ntom[0],(size_p)samples);
fprintf(stderr,"NtoM non-indexed seek errors: %"SIZE_P" / %"SIZE_P"\n", (size_p)errs_ntom[1],(size_p)samples);
fprintf(stderr,"Errors in getting first sample again: %"SIZE_P"\n", first_sample_errs);
fprintf(stderr,"Errors in getting first sample again: %"SIZE_P"\n", (size_p)first_sample_errs);
fprintf(stderr,"\n");
if(ret == 0)

View File

@@ -1,14 +1,7 @@
#include "config.h"
#include "mpg123.h"
#include "win32_support.h"
#include "mpg123app.h"
#include "httpget.h"
#include "resolver.h"
#include "compat.h"
#include <errno.h>
#include "debug.h"
#if defined (WANT_WIN32_SOCKETS)
#ifdef DEBUG
#define msgme(x) win32_net_msg(x,__FILE__,__LINE__)
#define msgme1 win32_net_msg(1,__FILE__,__LINE__)
@@ -115,28 +108,6 @@ static int get_sock_ch (int sock)
return (((int) c)&0xff);
return -1;
}
/* Addapted from from newlib*/
char *win32_net_fgets(char *s, int n, int stream)
{
char c = 0;
char *buf;
buf = s;
debug1("Pseudo net fgets attempts to read %d bytes from network.", n - 1);
while (--n > 0 && (c = get_sock_ch (stream)) != -1)
{
*s++ = c;
if (c == '\n' || c == '\r')
break;
}
debug1("Pseudo net fgets got %"SIZE_P" bytes.", (size_p)(s - buf));
if (c == -1 && s == buf)
{
debug("Pseudo net fgets met a premature end.");
return NULL;
}
*s = 0;
return buf;
}
ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte)
{
@@ -148,18 +119,6 @@ ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte)
return ret;
}
off_t win32_net_lseek (int a, off_t b, int c)
{
debug("lseek on a socket called!");
return -1;
}
void win32_net_replace (mpg123_handle *fr)
{
debug("win32_net_replace ran");
mpg123_replace_reader(fr, win32_net_read, win32_net_lseek);
}
static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
{
debug("win32_net_timeout_connect ran");
@@ -239,7 +198,7 @@ static int win32_net_timeout_connect(int sockfd, const struct sockaddr *serv_add
}
}
static int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
{
struct addrinfo hints;
struct addrinfo *addr, *addrlist;
@@ -286,52 +245,10 @@ static int win32_net_open_connection(mpg123_string *host, mpg123_string *port)
}
freeaddrinfo(addrlist);
return 1;
return ws.local_socket == SOCKET_ERROR ? -1 : 1;
}
static size_t win32_net_readstring (mpg123_string *string, size_t maxlen, int fd)
{
debug2("Attempting readstring on %d for %"SIZE_P" bytes", fd, (size_p)maxlen);
int err;
string->fill = 0;
while(maxlen == 0 || string->fill < maxlen)
{
if(string->size-string->fill < 1)
if(!mpg123_grow_string(string, string->fill+4096))
{
error("Cannot allocate memory for reading.");
string->fill = 0;
return 0;
}
err = win32_net_read(0,string->p+string->fill,1); /*fd is ignored */
/* Whoa... reading one byte at a time... one could ensure the line break in another way, but more work. */
if( err == 1)
{
string->fill++;
if(string->p[string->fill-1] == '\n') break;
}
else if(errno != EINTR)
{
error("Error reading from socket or unexpected EOF.");
string->fill = 0;
/* bail out to prevent endless loop */
return 0;
}
}
if(!mpg123_grow_string(string, string->fill+1))
{
string->fill=0;
}
else
{
string->p[string->fill] = 0;
string->fill++;
}
return string->fill;
}
static int win32_net_writestring (int fd, mpg123_string *string)
int win32_net_writestring (int fd, mpg123_string *string)
{
size_t result, bytes;
char *ptr = string->p;
@@ -355,253 +272,3 @@ static int win32_net_writestring (int fd, mpg123_string *string)
}
return TRUE;
}
static int win32_net_resolve_redirect(mpg123_string *response, mpg123_string *request_url, mpg123_string *purl)
{
debug1("request_url:%s", request_url->p);
/* initialized with full old url */
if(!mpg123_copy_string(request_url, purl)) return FALSE;
/* We may strip it down to a prefix ot totally. */
if(strncasecmp(response->p, "Location: http://", 17))
{ /* OK, only partial strip, need prefix for relative path. */
char* ptmp = NULL;
/* though it's not RFC (?), accept relative URIs as wget does */
fprintf(stderr, "NOTE: no complete URL in redirect, constructing one\n");
/* not absolute uri, could still be server-absolute */
/* I prepend a part of the request... out of the request */
if(response->p[10] == '/')
{
/* only prepend http://server/ */
/* I null the first / after http:// */
ptmp = strchr(purl->p+7,'/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+1; purl->p[purl->fill-1] = 0; }
}
else
{
/* prepend http://server/path/ */
/* now we want the last / */
ptmp = strrchr(purl->p+7, '/');
if(ptmp != NULL){ purl->fill = ptmp-purl->p+2; purl->p[purl->fill-1] = 0; }
}
}
else purl->fill = 0;
debug1("prefix=%s", purl->fill ? purl->p : "");
if(!mpg123_add_string(purl, response->p+10)) return FALSE;
debug1(" purl: %s", purl->p);
debug1("old request_url: %s", request_url->p);
return TRUE;
}
int win32_net_http_open(const char* url, struct httpdata *hd)
{
mpg123_string purl, host, port, path;
mpg123_string request, response, request_url;
mpg123_string httpauth1;
ws.local_socket = SOCKET_ERROR;
int oom = 0;
int relocate, numrelocs = 0;
int got_location = FALSE;
/*
workaround for http://www.global24music.com/rautemusik/files/extreme/isdn.pls
this site's apache gives me a relocation to the same place when I give the port in Host request field
for the record: Apache/2.0.51 (Fedora)
*/
int try_without_port = 0;
mpg123_init_string(&purl);
mpg123_init_string(&host);
mpg123_init_string(&port);
mpg123_init_string(&path);
mpg123_init_string(&request);
mpg123_init_string(&response);
mpg123_init_string(&request_url);
mpg123_init_string(&httpauth1);
/* Get initial info for proxy server. Once. */
if(hd->proxystate == PROXY_UNKNOWN && !proxy_init(hd)) goto exit;
if(!translate_url(url, &purl)){ oom=1; goto exit; }
/* Don't confuse the different auth strings... */
if(!split_url(&purl, &httpauth1, NULL, NULL, NULL) ){ oom=1; goto exit; }
/* "GET http://" 11
* " HTTP/1.0\r\nUser-Agent: <PACKAGE_NAME>/<PACKAGE_VERSION>\r\n"
* 26 + PACKAGE_NAME + PACKAGE_VERSION
* accept header + accept_length()
* "Authorization: Basic \r\n" 23
* "\r\n" 2
* ... plus the other predefined header lines
*/
/* Just use this estimate as first guess to reduce malloc calls in string library. */
{
size_t length_estimate = 62 + strlen(PACKAGE_NAME) + strlen(PACKAGE_VERSION)
+ accept_length() + strlen(CONN_HEAD) + strlen(icy_yes) + purl.fill;
if( !mpg123_grow_string(&request, length_estimate)
|| !mpg123_grow_string(&response,4096) )
{
oom=1; goto exit;
}
}
do
{
/* Storing the request url, with http:// prepended if needed. */
/* used to be url here... seemed wrong to me (when loop advanced...) */
if(strncasecmp(purl.p, "http://", 7) != 0) mpg123_set_string(&request_url, "http://");
else mpg123_set_string(&request_url, "");
mpg123_add_string(&request_url, purl.p);
if(!split_url(&purl, NULL, &host, &port, &path)){ oom=1; goto exit; }
if (hd->proxystate >= PROXY_HOST)
{
/* We will connect to proxy, full URL goes into the request. */
if( !mpg123_set_string(&request, "GET ")
|| !mpg123_add_string(&request, request_url.p) )
{
oom=1; goto exit;
}
}
else
{
/* We will connect to the host from the URL and only the path goes into the request. */
if( !mpg123_set_string(&request, "GET ")
|| !mpg123_add_string(&request, path.p) )
{
oom=1; goto exit;
}
}
if(!fill_request(&request, &host, &port, &httpauth1, &try_without_port)){ oom=1; goto exit; }
httpauth1.fill = 0; /* We use the auth data from the URL only once. */
if (hd->proxystate >= PROXY_HOST)
{
if( !mpg123_copy_string(&hd->proxyhost, &host)
|| !mpg123_copy_string(&hd->proxyport, &port) )
{
oom=1; goto exit;
}
}
debug2("attempting to open_connection to %s:%s", host.p, port.p);
win32_net_open_connection(&host, &port);
if(ws.local_socket == SOCKET_ERROR)
{
error1("Unable to establish connection to %s", host.fill ? host.p : "");
goto exit;
}
debug("win32_net_open_connection succeed");
#define http_failure win32_net_close(ws.local_socket); ws.local_socket=SOCKET_ERROR; goto exit;
if(param.verbose > 2) fprintf(stderr, "HTTP request:\n%s\n",request.p);
if(!win32_net_writestring(ws.local_socket, &request)){ http_failure; }
debug("Skipping fdopen for WSA sockets");
relocate = FALSE;
/* Arbitrary length limit here... */
#define safe_readstring \
win32_net_readstring(&response, SIZE_MAX/16, -1); \
if(response.fill > SIZE_MAX/16) /* > because of appended zero. */ \
{ \
error("HTTP response line exceeds max. length"); \
http_failure; \
} \
else if(response.fill == 0) \
{ \
error("readstring failed"); \
http_failure; \
} \
if(param.verbose > 2) fprintf(stderr, "HTTP in: %s", response.p);
safe_readstring;
{
char *sptr;
if((sptr = strchr(response.p, ' ')))
{
if(response.fill > sptr-response.p+2)
switch (sptr[1])
{
case '3':
relocate = TRUE;
case '2':
break;
default:
fprintf (stderr, "HTTP request failed: %s", sptr+1); /* '\n' is included */
http_failure;
}
else{ error("Too short response,"); http_failure; }
}
}
/* If we are relocated, we need to look out for a Location header. */
got_location = FALSE;
do
{
safe_readstring; /* Think about that: Should we really error out when we get nothing? Could be that the server forgot the trailing empty line... */
if (!strncasecmp(response.p, "Location: ", 10))
{ /* It is a redirection! */
if(!win32_net_resolve_redirect(&response, &request_url, &purl)){ oom=1, http_failure; }
if(!strcmp(purl.p, request_url.p))
{
warning("relocated to very same place! trying request again without host port");
try_without_port = 1;
}
got_location = TRUE;
}
else
{ /* We got a header line (or the closing empty line). */
char *tmp;
debug1("searching for header values... %s", response.p);
/* Not sure if I want to bail out on error here. */
/* Also: What text encoding are these strings in? Doesn't need to be plain ASCII... */
get_header_string(&response, "content-type", &hd->content_type);
get_header_string(&response, "icy-name", &hd->icy_name);
get_header_string(&response, "icy-url", &hd->icy_url);
/* watch out for icy-metaint */
if((tmp = get_header_val("icy-metaint", &response)))
{
hd->icy_interval = (off_t) atol(tmp); /* atoll ? */
debug1("got icy-metaint %li", (long int)hd->icy_interval);
}
}
} while(response.p[0] != '\r' && response.p[0] != '\n');
if (relocate) { win32_net_close(ws.local_socket); ws.local_socket=SOCKET_ERROR; }
} while(relocate && got_location && purl.fill && numrelocs++ < HTTP_MAX_RELOCATIONS);
if(relocate)
{
if(!got_location)
error("Server meant to redirect but failed to provide a location!");
else
error1("Too many HTTP relocations (%i).", numrelocs);
http_failure;
}
exit: /* The end as well as the exception handling point... */
if(oom) error("Apparently, I ran out of memory or had some bad input data...");
mpg123_free_string(&purl);
mpg123_free_string(&host);
mpg123_free_string(&port);
mpg123_free_string(&path);
mpg123_free_string(&request);
mpg123_free_string(&response);
mpg123_free_string(&request_url);
mpg123_free_string(&httpauth1);
if (ws.local_socket == SOCKET_ERROR || oom)
return -1;
else
return 1;
}
#else
int win32_net_http_open(const char* url, struct httpdata *hd)
{
return -1;
}
#endif /*WANT_WIN32_SOCKETS */

View File

@@ -9,7 +9,6 @@
#include "config.h"
#include "mpg123.h"
#include "httpget.h"
#ifdef HAVE_WINDOWS_H
#define WIN32_LEAN_AND_MEAN 1
@@ -32,15 +31,18 @@
Note: Do not treat return values as valid file/socket handles, they only indicate success/failure.
file descriptors are ignored, only the local ws.local_socket is used for storing socket handle,
so the socket handle is always associated with the last call to win32_net_http_open
TODO: Move the socket descriptor/state struct into streamdump.c, which wraps all network
connections. Stored in in nh->parts, it enables multiple sockets being opened.
*/
/**
* Opens an http URL
* @param[in] url URL to open
* @param[out] hd http data info
* @param[in] host to connect to
* @param[in] port to use
* @return -1 for failure, 1 for success
*/
int win32_net_http_open(const char* url, struct httpdata *hd);
int win32_net_open_connection(mpg123_string *host, mpg123_string *port);
/**
* Reads from network socket
@@ -61,13 +63,12 @@ ssize_t win32_net_read (int fildes, void *buf, size_t nbyte);
ssize_t win32_net_write (int fildes, const void *buf, size_t nbyte);
/**
* Similar to fgets - get a string from a stream
* @param[out] s buffer to Write to
* @param[in] n bytes of data to read.
* @param[in] stream ignored for compatiblity, last open connection is used.
* @return pointer to s if successful, NULL if failture
* Writes a whole mpg123_string to the network socket
* @param[in] filedes Value is ignored, last open connection is used.
* @param[in] string the string to write
* @return TRUE if successful, FALS on error
*/
char *win32_net_fgets(char *s, int n, int stream);
int win32_net_writestring (int filedes, mpg123_string *string);
/**
* Initialize Winsock 2.2.
@@ -84,12 +85,6 @@ void win32_net_deinit (void);
* @param[in] sock value is ignored.
*/
void win32_net_close (int sock);
/**
* Set reader callback for mpg123_open_fd
* @param[in] fr pointer to a mpg123_handle struct.
*/
void win32_net_replace (mpg123_handle *fr);
#endif
#ifdef WANT_WIN32_UNICODE