view src/audacious/tuple_formatter.c @ 3284:70789a2a5a65 trunk

documentation fixes
author William Pitcock <nenolod@atheme-project.org>
date Thu, 09 Aug 2007 06:21:09 -0500
parents 6062ef95818b
children 740c6f845554
line wrap: on
line source

/*
 * Audacious
 * Copyright (c) 2007 William Pitcock
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; under version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses>.
 *
 * The Audacious team does not consider modular code linking to
 * Audacious or using our public API to be a derived work.
 */

#include <glib.h>
#include <mowgli.h>

#include "tuple.h"
#include "tuple_formatter.h"

/*
 * the tuple formatter:
 *
 * this is a data-driven meta-language which eventually hopes to be
 * turing complete.
 *
 * language constructs follow the following basic rules:
 *   - begin with ${
 *   - end with }
 *
 * language constructs:
 *   - ${field}: prints a field
 *   - ${?field:expr}: evaluates expr if field exists
 *   - ${=field,"value"}: defines field in the currently iterated
 *                        tuple as string value of "value"
 *   - ${=field,value}: defines field in the currently iterated
 *                      tuple as integer value of "value"
 *   - ${==field,field:expr}: evaluates expr if both fields are the same
 *   - ${!=field,field:expr}: evaluates expr if both fields are not the same
 *
 * everything else is treated as raw text.
 * additionally, plugins can add additional instructions!
 */

typedef struct {
    Tuple *tuple;
    GString str;
} TupleFormatterContext;

/* processes a construct, e.g. "${?artist:artist is defined}" would
   return "artist is defined" if artist is defined. */
gchar *
tuple_formatter_process_construct(Tuple *tuple, const gchar *string)
{
    TupleFormatterContext *ctx;
    const gchar *iter;
    gchar *out;

    g_return_val_if_fail(tuple != NULL, NULL);
    g_return_val_if_fail(string != NULL, NULL);

    ctx = g_new0(TupleFormatterContext, 1);

    for (iter = string; *iter != '\0'; iter++)
    {
        /* if it's raw text, just copy the byte */
        if (*iter != '$')
            g_string_append_c(&ctx->str, *iter);
        else if (*(iter + 1) == '{')
        {
            GString expression = {};
            GString argument = {};
            GString *sel = &expression;
            gchar *result;

            for (iter += 2; *iter != '\0'; iter++)
            {
                switch(*iter)
                {
                case '}':
                    break;
                case ':':
                    sel = &argument;
                    break;
                default:
                    g_string_append_c(sel, *iter);
                    break;
                }
            }

            if (expression.len == 0)
                continue;

            result = tuple_formatter_process_expr(tuple, expression.str, argument.len ? argument.str : NULL);
            if (result != NULL)
            {
                g_string_append(&ctx->str, result);
                g_free(result);
            }

            g_free(expression.str);
            g_free(argument.str);
        }
    }

    out = g_strdup(ctx->str.str);
    g_free(ctx->str.str);
    g_free(ctx);

    return out;
}

static GList *tuple_formatter_expr_list = NULL;

typedef struct {
    const gchar *name;
    gboolean (*func)(Tuple *tuple, const gchar *expression);
} TupleFormatterExpression;

/* processes an expression and optional argument pair. */
gchar *
tuple_formatter_process_expr(Tuple *tuple, const gchar *expression, 
    const gchar *argument)
{
    TupleFormatterExpression *expr = NULL;
    GList *iter;

    g_return_val_if_fail(tuple != NULL, NULL);
    g_return_val_if_fail(expression != NULL, NULL);

    for (iter = tuple_formatter_expr_list; iter != NULL; iter = iter->next)
    {
        TupleFormatterExpression *tmp = (TupleFormatterExpression *) iter->data;

        if (g_str_has_prefix(tmp->name, expression) == TRUE)
        {
            expr = tmp;
            expression += strlen(tmp->name);
        }
    }

    /* ${artist} */
    if (expr == NULL && argument == NULL)
    {
        TupleValueType type = tuple_get_value_type(tuple, expression);

        switch(type)
        {
        case TUPLE_STRING:
             return g_strdup(tuple_get_string(tuple, expression));
             break;
        case TUPLE_INT:
             return g_strdup_printf("%d", tuple_get_int(tuple, expression));
             break;
        case TUPLE_UNKNOWN:
        default:
             return NULL;
        }
    }
    else if (expr != NULL)
    {
        if (expr->func(tuple, expression) == TRUE && argument != NULL)
            return tuple_formatter_process_construct(tuple, argument);
    }

    return NULL;
}

/* registers a formatter */
void
tuple_formatter_register_expression(const gchar *keyword,
	gboolean (*func)(Tuple *tuple, const gchar *argument))
{
    TupleFormatterExpression *expr;

    g_return_if_fail(keyword != NULL);
    g_return_if_fail(func != NULL);

    expr = g_new0(TupleFormatterExpression, 1);
    expr->name = keyword;
    expr->func = func;

    tuple_formatter_expr_list = g_list_append(tuple_formatter_expr_list, expr);
}

/* builtin-keyword: ${?arg}, returns TRUE if <arg> exists. */
static gboolean
tuple_formatter_expression_exists(Tuple *tuple, const gchar *expression)
{
    return (tuple_get_value_type(tuple, expression) != TUPLE_UNKNOWN) ? TRUE : FALSE;
}

/* processes a string containing instructions. does initialization phases
   if not already done */
gchar *
tuple_formatter_process_string(Tuple *tuple, const gchar *string)
{
    static gboolean initialized = FALSE;

    if (initialized == FALSE)
    {
        tuple_formatter_register_expression("?", tuple_formatter_expression_exists);
        initialized = TRUE;
    }

    return tuple_formatter_process_string(tuple, string);
}