[pulseaudio-discuss] [PATCH 5/5] pactl: Implement list message-handlers

Tanu Kaskinen tanuk at iki.fi
Fri Jan 12 15:40:52 UTC 2018


On Sun, 2017-10-29 at 20:51 +0100, Georg Chini wrote:
> For better readability, "pactl list message-handlers" is introduced which
> prints a formatted output of "pactl send-message /core list-handlers".
> 
> The patch also adds the function pa_split_message_response() for easy
> parsing of the message response string.
> ---
>  man/pactl.1.xml.in               |  2 +-
>  shell-completion/bash/pulseaudio |  2 +-
>  shell-completion/zsh/_pulseaudio |  1 +
>  src/pulsecore/core-util.c        | 34 +++++++++++++++++++++++++++++++++
>  src/pulsecore/core-util.h        |  1 +
>  src/utils/pactl.c                | 41 ++++++++++++++++++++++++++++++++++++++--
>  6 files changed, 77 insertions(+), 4 deletions(-)
> 
> diff --git a/man/pactl.1.xml.in b/man/pactl.1.xml.in
> index 9669aca9..e444f973 100644
> --- a/man/pactl.1.xml.in
> +++ b/man/pactl.1.xml.in
> @@ -76,7 +76,7 @@ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
>      <option>
>        <p><opt>list</opt> [<arg>short</arg>] [<arg>TYPE</arg>]</p>
>        <optdesc><p>Dump all currently loaded modules, available sinks, sources, streams, etc.  <arg>TYPE</arg> must be one of:
> -      modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards.  If not specified, all info is listed.  If
> +      modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers.  If not specified, all info is listed.  If
>        short is given, output is in a tabular format, for easy parsing by scripts.</p></optdesc>
>      </option>
>  
> diff --git a/shell-completion/bash/pulseaudio b/shell-completion/bash/pulseaudio
> index 797ec067..24d2aa1c 100644
> --- a/shell-completion/bash/pulseaudio
> +++ b/shell-completion/bash/pulseaudio
> @@ -113,7 +113,7 @@ _pactl() {
>      local comps
>      local flags='-h --help --version -s --server= --client-name='
>      local list_types='short sinks sources sink-inputs source-outputs cards
> -                    modules samples clients'
> +                    modules samples clients message-handlers'
>      local commands=(stat info list exit upload-sample play-sample remove-sample
>                      load-module unload-module move-sink-input move-source-output
>                      suspend-sink suspend-source set-card-profile set-sink-port
> diff --git a/shell-completion/zsh/_pulseaudio b/shell-completion/zsh/_pulseaudio
> index a2817ebb..c24d0387 100644
> --- a/shell-completion/zsh/_pulseaudio
> +++ b/shell-completion/zsh/_pulseaudio
> @@ -285,6 +285,7 @@ _pactl_completion() {
>                  'clients: list connected clients'
>                  'samples: list samples'
>                  'cards: list available cards'
> +                'message-handlers: list available message-handlers'
>              )
>  
>              if ((CURRENT == 2)); then
> diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
> index d4cfa20c..e7587f38 100644
> --- a/src/pulsecore/core-util.c
> +++ b/src/pulsecore/core-util.c
> @@ -1140,6 +1140,40 @@ const char *pa_split_spaces_in_place(const char *c, int *n, const char **state)
>      return current;
>  }
>  
> +/* Split the specified string into elements. An element is defined as
> + * a sub-string between curly braces. The function is needed to parse
> + * the response of messages. Each time it is called returns a newly
> + * allocated string with pa_xmalloc(). The variable state points to,
> + * should be initialized to NULL before the first call. */
> +char *pa_split_message_response(const char *c, const char **state) {

There are three cases where we need to parse this data format: message
parameters, message responses and signal parameters. Therefore the
parsing function(s) should be named in some generic manner.

Clients are going to need the parsing function(s), so this should go to
libpulse.

I would also prefer more fine-grained functions than just one single
split function. When parsing lists, this split function will
unnecessarily malloc the list before mallocing the individual list
elements.

I already provided an example about how I'd like the parsing to work.
Apparently you didn't like that for some reason? I'll copy the example
here again:

const char *state = message_parameters;

if (pa_message_read_start_list(&state) < 0)
    fail("Expected a list.");

while (pa_message_read_end_list(&state) < 0) {
    char *path;
    char *description;

    if (pa_message_read_path(&state, &path) < 0)
        fail("Expected a path.");
    if (pa_message_read_string(&state, &description) < 0)
        fail("Expected a string.")

    /* Do something with path and description. */

    pa_xfree(description);
    pa_xfree(path);
}

> +    const char *current = *state ? *state : c;
> +    const char *start_pos;
> +    uint32_t open_braces = 1;
> +
> +    if (!*current || *c == 0)
> +        return NULL;
> +
> +    current = strchr(current, '{');
> +    if (!current)
> +        return NULL;
> +
> +    start_pos = current + 1;
> +

I think it would be slightly easier to follow the logic if open_braces
was initialized here, just before the while block.

> +    while (open_braces != 0 && *current != 0) {
> +        current++;
> +        if (*current == '{')
> +            open_braces++;
> +        if (*current == '}')
> +            open_braces--;
> +    }

This is going to have to handle escaping.

> +
> +    pa_assert(open_braces == 0);

Invalid input shouldn't cause crashing. This will crash if the input
has too few closing brackets.

> +
> +    *state = current + 1;
> +
> +    return pa_xstrndup(start_pos, current - start_pos);
> +}
> +
>  PA_STATIC_TLS_DECLARE(signame, pa_xfree);
>  
>  /* Return the name of an UNIX signal. Similar to Solaris sig2str() */
> diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
> index e28b6aa7..947dfc13 100644
> --- a/src/pulsecore/core-util.h
> +++ b/src/pulsecore/core-util.h
> @@ -113,6 +113,7 @@ char *pa_split(const char *c, const char *delimiters, const char **state);
>  const char *pa_split_in_place(const char *c, const char *delimiters, int *n, const char **state);
>  char *pa_split_spaces(const char *c, const char **state);
>  const char *pa_split_spaces_in_place(const char *c, int *n, const char **state);
> +char *pa_split_message_response(const char *c, const char **state);
>  
>  char *pa_strip_nl(char *s);
>  char *pa_strip(char *s);
> diff --git a/src/utils/pactl.c b/src/utils/pactl.c
> index 2e15b189..c7dc57ac 100644
> --- a/src/utils/pactl.c
> +++ b/src/utils/pactl.c
> @@ -854,6 +854,34 @@ static void string_callback(pa_context *c, int success, const char *response, vo
>      complete_action();
>  }
>  
> +static void list_handlers_callback(pa_context *c, int success, const char *response, void *userdata) {
> +    const char *state = NULL;
> +    char *element;
> +    char *handler_name;
> +    char *description;
> +
> +    if (success < 0) {

Just a reminder that this will have to be changed if you change the
success parameter semantics as I suggested earlier.

> +        pa_log(_("Send message failed: %s"), pa_strerror(pa_context_errno(c)));

This could be more specific, e.g. "The list-handlers message failed:
%s".

> +        quit(1);
> +        return;
> +    }

Unless short_list_format is set, there should be some heading
indicating that the following list contains message handlers (the
heading is useful with the plain "pactl list" command that will print
many kinds of information). Or alternatively (approximately) follow the
same format as with other types (I think that would be better):

Message Handler /foo/bar
        Description: Something something something
Message Handler /quux/weng
        Description: Coconuts are ok

> +
> +    while ((element = pa_split_message_response(response, &state))) {
> +       const char *state2 = NULL;
> +       handler_name = pa_split_message_response(element, &state2);

Error handling is missing.

> +       description = pa_split_message_response(element, &state2);

Error handling is missing.

> +       if (short_list_format)
> +           printf("%s\n", handler_name);
> +       else
> +           printf("Name: %s     Description: %s\n", handler_name, description);
> +       pa_xfree(element);
> +       pa_xfree(handler_name);
> +       pa_xfree(description);
> +    }
> +
> +    complete_action();
> +}

-- 
Tanu

https://liberapay.com/tanuk
https://www.patreon.com/tanuk


More information about the pulseaudio-discuss mailing list