mirror of
https://git.planet-casio.com/Lephenixnoir/fxsdk.git
synced 2024-12-29 13:03:37 +01:00
194 lines
4.7 KiB
C
194 lines
4.7 KiB
C
#include "filter.h"
|
|
#include "util.h"
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
//---
|
|
// Property parser
|
|
//---
|
|
|
|
/* skip_spaces(): Skip spaces, returns true if end of string is reached */
|
|
bool skip_spaces(char const **input)
|
|
{
|
|
while(isspace(**input)) (*input)++;
|
|
return (**input == 0);
|
|
}
|
|
/* isword(): Identify valid word characters for the filter */
|
|
bool isword(int c)
|
|
{
|
|
return c && !strchr(" \t\n,;=", c);
|
|
}
|
|
/* read_word(): Copy the next word in the string, assumes word is non-empty */
|
|
char *read_word(char const **input)
|
|
{
|
|
char const *str = *input;
|
|
while(**input && isword(**input)) (*input)++;
|
|
return strndup(str, *input - str);
|
|
}
|
|
|
|
enum {
|
|
T_END, /* End of string */
|
|
T_PROP, /* Property; (*name) and (*value) are set */
|
|
T_COMMA = ',', /* Comma character (property separator) */
|
|
T_SEMI = ';', /* Semicolon character (option separator) */
|
|
};
|
|
|
|
/* lex(): Read a token from the input source
|
|
Returns the token type, updates (*input) and sets (*name) and (*value) to
|
|
either NULL or some freshly-allocated copies of the name and (optional)
|
|
value of the property (always NULL unless T_PROP is returned). The caller
|
|
should free() both. */
|
|
static int lex(char const **input, char **name, char **value)
|
|
{
|
|
*name = *value = NULL;
|
|
if(skip_spaces(input)) return T_END;
|
|
|
|
if(**input == ',' || **input == ';') {
|
|
(*input)++;
|
|
return (*input)[-1];
|
|
}
|
|
|
|
if(!isword(**input)) {
|
|
wrn("expected property name in filter, skipping '%c'", **input);
|
|
(*input)++;
|
|
return lex(input, name, value);
|
|
}
|
|
*name = read_word(input);
|
|
if(skip_spaces(input) || **input != '=')
|
|
return T_PROP;
|
|
|
|
(*input)++;
|
|
if(skip_spaces(input))
|
|
wrn("no value after '=' in filter property '%s'", *name);
|
|
else if(!isword(**input))
|
|
wrn("ignoring invalid value for filter property '%s'", *name);
|
|
else
|
|
*value = read_word(input);
|
|
return T_PROP;
|
|
}
|
|
|
|
filter_t *filter_parse(char const *input)
|
|
{
|
|
char *name=NULL, *value=NULL;
|
|
int t;
|
|
|
|
/* Create an initial filter with a single otion */
|
|
filter_t *filter = malloc(sizeof *filter);
|
|
if(!filter) return NULL;
|
|
filter->options = calloc(1, sizeof(properties_t));
|
|
if(!filter->options) {
|
|
free(filter);
|
|
return NULL;
|
|
}
|
|
filter->length = 1;
|
|
|
|
/* Current option */
|
|
properties_t *option = &filter->options[0];
|
|
|
|
while((t = lex(&input, &name, &value)) != T_END) {
|
|
/* Ignore property separators (tokens are already separated) */
|
|
if(t == ',') continue;
|
|
|
|
/* Add a new option in the filter */
|
|
if(t == ';') {
|
|
size_t new_size = (filter->length + 1) * sizeof(properties_t);
|
|
properties_t *new_options = realloc(filter->options, new_size);
|
|
if(!new_options) continue;
|
|
|
|
filter->options = new_options;
|
|
option = &filter->options[filter->length++];
|
|
*option = (properties_t){ 0 };
|
|
continue;
|
|
}
|
|
|
|
/* Add a new property to the current option */
|
|
if(!strcmp(name, "p7") && !value)
|
|
option->p7 = true;
|
|
else if(!strcmp(name, "mass_storage") && !value)
|
|
option->mass_storage = true;
|
|
else if(!strcmp(name, "series_cg") && !value)
|
|
option->series_cg = true;
|
|
else if(!strcmp(name, "series_g3") && !value)
|
|
option->series_g3 = true;
|
|
else if(!strcmp(name, "serial_number") && value) {
|
|
option->serial_number = strdup(value);
|
|
}
|
|
else wrn("ignoring invalid filter property: '%s' %s value", name,
|
|
value ? "with" : "without");
|
|
|
|
free(name);
|
|
free(value);
|
|
}
|
|
|
|
return filter;
|
|
}
|
|
|
|
void filter_free(filter_t *filter)
|
|
{
|
|
if(!filter) return;
|
|
|
|
for(size_t i = 0; i < filter->length; i++)
|
|
free(filter->options[i].serial_number);
|
|
|
|
free(filter->options);
|
|
free(filter);
|
|
}
|
|
|
|
//---
|
|
// Filtering API
|
|
//---
|
|
|
|
void filter_clean_libusb(filter_t *filter)
|
|
{
|
|
if(!filter) return;
|
|
|
|
for(size_t i = 0; i < filter->length; i++) {
|
|
properties_t *prop = &filter->options[i];
|
|
|
|
/* Suppress series_cg and series_g3, which are based off the USB Mass
|
|
Storage metadata provided only by UDisks2 */
|
|
if(prop->series_cg) {
|
|
wrn("ignoring series_cg in libusb filter (cannot be detected)");
|
|
prop->series_cg = false;
|
|
}
|
|
if(prop->series_g3) {
|
|
wrn("ignoring series_g3 in libusb filter (cannot be detected)");
|
|
prop->series_g3 = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void filter_clean_udisks2(filter_t *filter)
|
|
{
|
|
/* Every property can be used */
|
|
(void)filter;
|
|
}
|
|
|
|
bool filter_match(properties_t const *props, filter_t const *filter)
|
|
{
|
|
/* No filter is a pass-through */
|
|
if(!filter || !filter->length)
|
|
return true;
|
|
|
|
for(size_t i = 0; i < filter->length; i++) {
|
|
if(properties_match(props, &filter->options[i]))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void filter_print(FILE *fp, filter_t const *filter)
|
|
{
|
|
#define output(...) { \
|
|
if(sep) fprintf(fp, ", "); \
|
|
fprintf(fp, __VA_ARGS__); \
|
|
sep = true; \
|
|
}
|
|
|
|
for(size_t i = 0; i < filter->length; i++) {
|
|
if(i > 0) printf("; ");
|
|
properties_t *prop = &filter->options[i];
|
|
properties_print(fp, prop);
|
|
}
|
|
}
|