output/recorder: dynamic file name
This commit is contained in:
252
src/util/format.c
Normal file
252
src/util/format.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* music player command (mpc)
|
||||
* Copyright (C) 2003-2015 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Reallocate the given string and append the source string.
|
||||
*/
|
||||
gcc_malloc
|
||||
static char *
|
||||
string_append(char *dest, const char *src, size_t len)
|
||||
{
|
||||
size_t destlen = dest != NULL
|
||||
? strlen(dest)
|
||||
: 0;
|
||||
|
||||
dest = realloc(dest, destlen + len + 1);
|
||||
memcpy(dest + destlen, src, len);
|
||||
dest[destlen + len] = '\0';
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the format string until the current group is closed by either
|
||||
* '&', '|' or ']' (supports nesting).
|
||||
*/
|
||||
gcc_pure
|
||||
static const char *
|
||||
skip_format(const char *p)
|
||||
{
|
||||
unsigned stack = 0;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '[')
|
||||
stack++;
|
||||
else if (*p == '#' && p[1] != '\0')
|
||||
/* skip escaped stuff */
|
||||
++p;
|
||||
else if (stack > 0) {
|
||||
if (*p == ']')
|
||||
--stack;
|
||||
} else if (*p == '&' || *p == '|' || *p == ']')
|
||||
break;
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *
|
||||
format_object2(const char *format, const char **last, const void *object,
|
||||
const char *(*getter)(const void *object, const char *name))
|
||||
{
|
||||
char *ret = NULL;
|
||||
const char *p;
|
||||
bool found = false;
|
||||
|
||||
for (p = format; *p != '\0';) {
|
||||
switch (p[0]) {
|
||||
case '|':
|
||||
++p;
|
||||
if (!found) {
|
||||
/* nothing found yet: try the next
|
||||
section */
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
} else
|
||||
/* already found a value: skip the
|
||||
next section */
|
||||
p = skip_format(p);
|
||||
break;
|
||||
|
||||
case '&':
|
||||
++p;
|
||||
if (!found)
|
||||
/* nothing found yet, so skip this
|
||||
section */
|
||||
p = skip_format(p);
|
||||
else
|
||||
/* we found something yet, but it will
|
||||
only be used if the next section
|
||||
also found something, so reset the
|
||||
flag */
|
||||
found = false;
|
||||
break;
|
||||
|
||||
case '[': {
|
||||
char *t = format_object2(p + 1, &p, object, getter);
|
||||
if (t != NULL) {
|
||||
ret = string_append(ret, t, strlen(t));
|
||||
free(t);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if (last != NULL)
|
||||
*last = p + 1;
|
||||
if (!found) {
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
return ret;
|
||||
|
||||
case '\\': {
|
||||
/* take care of escape sequences */
|
||||
char ltemp;
|
||||
switch (p[1]) {
|
||||
case 'a':
|
||||
ltemp = '\a';
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
ltemp = '\b';
|
||||
break;
|
||||
|
||||
case 't':
|
||||
ltemp = '\t';
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
ltemp = '\n';
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
ltemp = '\v';
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
ltemp = '\f';
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
ltemp = '\r';
|
||||
break;
|
||||
|
||||
case '[':
|
||||
case ']':
|
||||
ltemp = p[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown escape: copy the
|
||||
backslash */
|
||||
ltemp = p[0];
|
||||
--p;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = string_append(ret, <emp, 1);
|
||||
p += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case '%': {
|
||||
/* find the extent of this format specifier
|
||||
(stop at \0, ' ', or esc) */
|
||||
const char *end = p + 1;
|
||||
while (*end >= 'a' && *end <= 'z')
|
||||
++end;
|
||||
|
||||
const size_t length = end - p + 1;
|
||||
|
||||
if (*end != '%') {
|
||||
ret = string_append(ret, p, length - 1);
|
||||
p = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
char name[32];
|
||||
if (length > (int)sizeof(name)) {
|
||||
ret = string_append(ret, p, length);
|
||||
p = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(name, p + 1, length - 2);
|
||||
name[length - 2] = 0;
|
||||
|
||||
const char *value = getter(object, name);
|
||||
size_t value_length;
|
||||
if (value != NULL) {
|
||||
if (*value != 0)
|
||||
found = true;
|
||||
value_length = strlen(value);
|
||||
} else {
|
||||
/* unknown variable: copy verbatim
|
||||
from format string */
|
||||
value = p;
|
||||
value_length = length;
|
||||
}
|
||||
|
||||
ret = string_append(ret, value, value_length);
|
||||
|
||||
/* advance past the specifier */
|
||||
p = end + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case '#':
|
||||
/* let the escape character escape itself */
|
||||
if (p[1] != '\0') {
|
||||
ret = string_append(ret, p + 1, 1);
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
/* pass-through non-escaped portions of the format string */
|
||||
ret = string_append(ret, p, 1);
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
if (last != NULL)
|
||||
*last = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
format_object(const char *format, const void *object,
|
||||
const char *(*getter)(const void *object, const char *name))
|
||||
{
|
||||
return format_object2(format, NULL, object, getter);
|
||||
}
|
51
src/util/format.h
Normal file
51
src/util/format.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* music player command (mpc)
|
||||
* Copyright (C) 2003-2015 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPC_FORMAT_H
|
||||
#define MPC_FORMAT_H
|
||||
|
||||
#include "Compiler.h"
|
||||
|
||||
struct mpd_song;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Pretty-print an object into a string using the given format
|
||||
* specification.
|
||||
*
|
||||
* @param format the format string
|
||||
* @param object the object
|
||||
* @param getter a getter function that extracts a value from the object
|
||||
* @return the resulting string to be freed by free(); NULL if
|
||||
* no format string group produced any output
|
||||
*/
|
||||
gcc_malloc
|
||||
char *
|
||||
format_object(const char *format, const void *object,
|
||||
const char *(*getter)(const void *object, const char *name));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user