diff --git a/src/hostapi/jack/pa_jack.c b/src/hostapi/jack/pa_jack.c index 0dad24f..88926f4 100644 --- a/src/hostapi/jack/pa_jack.c +++ b/src/hostapi/jack/pa_jack.c @@ -76,6 +76,7 @@ static pthread_t mainThread_; static char *jackErr_ = NULL; static const char* clientName_ = "PortAudio"; +static const char* port_regex_suffix = ":.*"; #define STRINGIZE_HELPER(expr) #expr #define STRINGIZE(expr) STRINGIZE_HELPER(expr) @@ -451,6 +452,38 @@ BlockingWaitEmpty( PaStream *s ) /* ---- jack driver ---- */ +/* copy null terminated string source to destination, escaping regex characters with '\\' in the process */ +static void copy_string_and_escape_regex_chars( char *destination, const char *source, size_t destbuffersize ) +{ + assert( destination != source ); + assert( destbuffersize > 0 ); + + char *dest = destination; + /* dest_stop is the last location that we can null-terminate the string */ + char *dest_stop = destination + (destbuffersize - 1); + + const char *src = source; + + while ( *src != '\0' && dest != dest_stop ) + { + const char c = *src; + if ( strchr( "\\()[]{}*+?|$^.", c ) != NULL ) + { + if( (dest + 1) == dest_stop ) + break; /* only proceed if we can write both c and the escape */ + + *dest = '\\'; + dest++; + } + *dest = c; + dest++; + + src++; + } + + *dest = '\0'; +} + /* BuildDeviceList(): * * The process of determining a list of PortAudio "devices" from @@ -472,7 +505,12 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) const char **jack_ports = NULL; char **client_names = NULL; - char *regex_pattern = NULL; + char *port_regex_string = NULL; + char *device_name_regex_escaped = NULL; + // In the worst case scenario, every character would be escaped, doubling the string size. + // Add 1 for null terminator. + size_t device_name_regex_escaped_size = jack_client_name_size() * 2 + 1; + size_t port_regex_size = device_name_regex_escaped_size + strlen(port_regex_suffix); int port_index, client_index, i; double globalSampleRate; regex_t port_regex; @@ -490,7 +528,9 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) * associated with the previous list */ PaUtil_FreeAllAllocations( jackApi->deviceInfoMemory ); - regex_pattern = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, jack_client_name_size() + 3 ); + port_regex_string = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, port_regex_size ); + device_name_regex_escaped = PaUtil_GroupAllocateMemory( + jackApi->deviceInfoMemory, device_name_regex_escaped_size ); tmp_client_name = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, jack_client_name_size() ); /* We can only retrieve the list of clients indirectly, by first @@ -512,6 +552,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) int client_seen = FALSE; regmatch_t match_info; const char *port = jack_ports[port_index]; + PA_DEBUG(( "JACK port found: %s\n", port )); /* extract the client name from the port name, using a regex * that parses the clientname:portname syntax */ @@ -584,11 +625,14 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) /* To determine how many input and output channels are available, * we re-query jackd with more specific parameters. */ - - sprintf( regex_pattern, "%s:.*", client_names[client_index] ); + copy_string_and_escape_regex_chars( device_name_regex_escaped, + client_names[client_index], + device_name_regex_escaped_size ); + strncpy( port_regex_string, device_name_regex_escaped, port_regex_size ); + strncat( port_regex_string, port_regex_suffix, port_regex_size ); /* ... what are your output ports (that we could input from)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, + clientPorts = jack_get_ports( jackApi->jack_client, port_regex_string, JACK_PORT_TYPE_FILTER, JackPortIsOutput); curDevInfo->maxInputChannels = 0; curDevInfo->defaultLowInputLatency = 0.; @@ -609,7 +653,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) } /* ... what are your input ports (that we could output to)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, + clientPorts = jack_get_ports( jackApi->jack_client, port_regex_string, JACK_PORT_TYPE_FILTER, JackPortIsInput); curDevInfo->maxOutputChannels = 0; curDevInfo->defaultLowOutputLatency = 0.; @@ -629,6 +673,11 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) free(clientPorts); } + PA_DEBUG(( "Adding JACK device %s with %d input channels and %d output channels\n", + client_names[client_index], + curDevInfo->maxInputChannels, + curDevInfo->maxOutputChannels )); + /* Add this client to the list of devices */ commonApi->deviceInfos[client_index] = curDevInfo; ++commonApi->info.deviceCount; @@ -1080,8 +1129,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; PaJackStream *stream = NULL; char *port_string = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, jack_port_name_size() ); - unsigned long regexSz = jack_client_name_size() + 3; - char *regex_pattern = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, regexSz ); + // In the worst case every character would be escaped which would double the string length. + // Add 1 for null terminator + size_t regex_escaped_client_name_length = jack_client_name_size() * 2 + 1; + char *regex_escaped_client_name = PaUtil_GroupAllocateMemory( + jackHostApi->deviceInfoMemory, regex_escaped_client_name_length ); + unsigned long regex_size = jack_client_name_size() * 2 + 1 + strlen(port_regex_suffix); + char *regex_pattern = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, regex_size ); const char **jack_ports = NULL; /* int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); */ int i; @@ -1239,7 +1293,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, int err = 0; /* Get output ports of our capture device */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name ); + copy_string_and_escape_regex_chars( regex_escaped_client_name, + hostApi->deviceInfos[ inputParameters->device ]->name, + regex_escaped_client_name_length ); + strncpy( regex_pattern, regex_escaped_client_name, regex_size ); + strncat( regex_pattern, port_regex_suffix, regex_size ); UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, JACK_PORT_TYPE_FILTER, JackPortIsOutput ), paUnanticipatedHostError ); for( i = 0; i < inputChannelCount && jack_ports[i]; i++ ) @@ -1263,7 +1321,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, int err = 0; /* Get input ports of our playback device */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name ); + copy_string_and_escape_regex_chars( regex_escaped_client_name, + hostApi->deviceInfos[ outputParameters->device ]->name, + regex_escaped_client_name_length ); + strncpy( regex_pattern, regex_escaped_client_name, regex_size ); + strncat( regex_pattern, port_regex_suffix, regex_size ); UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, JACK_PORT_TYPE_FILTER, JackPortIsInput ), paUnanticipatedHostError ); for( i = 0; i < outputChannelCount && jack_ports[i]; i++ ) @@ -1769,3 +1831,4 @@ PaError PaJack_GetClientName(const char** clientName) error: return result; } +