#include <fnmatch.h>
#include <sys/shm.h>
#include <osl.h>
+#include <lopsub.h>
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
unsigned char *hash;
};
-/** The flags accepted by the ls command. */
-enum ls_flags {
- /** -p */
- LS_FLAG_FULL_PATH = 1,
- /** -a */
- LS_FLAG_ADMISSIBLE_ONLY = 2,
- /** -r */
- LS_FLAG_REVERSE = 4,
- /** -d */
- LS_FLAG_UNIXDATE = 8,
-};
-
/**
* The size of the individual output fields of the ls command.
*
/** Data passed from the ls command handler to its callback function. */
struct ls_options {
- /** The given command line flags. */
- unsigned flags;
- /** The sorting method given at the command line. */
+ struct lls_parse_result *lpr;
+ /* Derived from lpr */
enum ls_sorting_method sorting;
- /** The given listing mode (short, long, verbose, mbox). */
+ /* Derived from lpr */
enum ls_listing_mode mode;
- /** The arguments passed to the ls command. */
- char **patterns;
- /** Number of non-option arguments. */
- int num_patterns;
/** Used for long listing mode to align the output fields. */
struct ls_widths widths;
/** Size of the \a data array. */
NUM_AFT_COLUMNS
};
-/**
- * Compare two osl objects pointing to hash values.
- *
- * \param obj1 Pointer to the first hash object.
- * \param obj2 Pointer to the second hash object.
- *
- * \return The values required for an osl compare function.
- *
- * \sa osl_compare_func, uint32_compare().
- */
-static int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+/* compare function for the hash column */
+static int aft_hash_compare(const struct osl_object *obj1,
+ const struct osl_object *obj2)
{
- return hash_compare((unsigned char *)obj1->data, (unsigned char *)obj2->data);
+ return hash_compare((unsigned char *)obj1->data,
+ (unsigned char *)obj2->data);
}
static struct osl_column_description aft_cols[] = {
static int get_local_time(uint64_t *seconds, char *buf, size_t size,
time_t current_time, enum ls_listing_mode lm)
{
- struct tm t;
+ struct tm *tm;
+ /*
+ * Omit year but show time if the given value is closer to the current
+ * time than this many seconds.
+ */
+ const time_t m = 6 * 30 * 24 * 3600; /* six months */
- if (!localtime_r((time_t *)seconds, &t))
+ tm = localtime((time_t *)seconds);
+ if (!tm)
return -E_LOCALTIME;
if (lm == LS_MODE_MBOX) {
- if (!strftime(buf, size, "%c", &t))
+ if (!strftime(buf, size, "%c", tm))
return -E_STRFTIME;
return 1;
}
- if (*seconds + 6 * 30 * 24 * 3600 > current_time) {
- if (!strftime(buf, size, "%b %e %k:%M", &t))
+ if (*seconds > current_time - m && *seconds < current_time + m) {
+ if (!strftime(buf, size, "%b %e %k:%M", tm))
return -E_STRFTIME;
return 1;
}
- if (!strftime(buf, size, "%b %e %Y", &t))
+ /*
+ * If the given time is more than six month away from the current time,
+ * we print only the year. The additional space character in the format
+ * string below makes the formated date align nicely with dates that
+ * contain the time (those written by the above strftime() statement).
+ */
+ if (!strftime(buf, size, "%b %e %Y", tm))
return -E_STRFTIME;
return 1;
}
if (!hours) { /* m:ss or mm:ss */
max_width = opts->mode == LS_MODE_LONG?
opts->widths.duration_width : 4;
- sprintf(buf, "%*u:%02u", max_width - 3, mins, seconds % 60);
+ sprintf(buf, "%*u:%02d", max_width - 3, mins, seconds % 60);
} else { /* more than one hour => h:mm:ss, hh:mm:ss, hhh:mm:ss, ... */
max_width = opts->mode == LS_MODE_LONG?
opts->widths.duration_width : 7;
- sprintf(buf, "%*u:%02u:%02u", max_width - 6, hours, mins,
+ sprintf(buf, "%*u:%02u:%02d", max_width - 6, hours, mins,
seconds % 60);
}
}
char *att_text;
int ret;
- ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap);
- if (ret < 0)
- return ret;
+ WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_BITMAP, "%s\n", att_bitmap);
ret = get_attribute_text(&afsi->attributes, " ", &att_text);
if (ret < 0)
return ret;
- ret = WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text);
+ WRITE_STATUS_ITEM(b, SI_ATTRIBUTES_TXT, "%s\n", att_text);
free(att_text);
return ret;
}
-static int write_lyrics_items(struct para_buffer *b, struct afs_info *afsi)
+static void write_lyrics_items(struct para_buffer *b, struct afs_info *afsi)
{
char *lyrics_name;
- int ret;
- ret = WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id);
- if (ret < 0)
- return ret;
+ WRITE_STATUS_ITEM(b, SI_LYRICS_ID, "%u\n", afsi->lyrics_id);
lyr_get_name_by_id(afsi->lyrics_id, &lyrics_name);
- return WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name?
+ WRITE_STATUS_ITEM(b, SI_LYRICS_NAME, "%s\n", lyrics_name?
lyrics_name : "(none)");
}
-static int write_image_items(struct para_buffer *b, struct afs_info *afsi)
+static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
{
char *image_name;
- int ret;
- ret = WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id);
- if (ret < 0)
- return ret;
+ WRITE_STATUS_ITEM(b, SI_IMAGE_ID, "%u\n", afsi->image_id);
img_get_name_by_id(afsi->image_id, &image_name);
- return WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name?
+ WRITE_STATUS_ITEM(b, SI_IMAGE_NAME, "%s\n", image_name?
image_name : "(none)");
}
-static int write_filename_items(struct para_buffer *b, const char *path,
- unsigned flags)
+static void write_filename_items(struct para_buffer *b, const char *path,
+ bool basename)
{
char *val;
- int ret;
- if (!(flags & LS_FLAG_FULL_PATH))
- return WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
- ret = WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path);
- if (ret < 0)
- return ret;
+ if (basename) {
+ WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
+ return;
+ }
+ WRITE_STATUS_ITEM(b, SI_PATH, "%s\n", path);
val = para_basename(path);
- ret = WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : "");
- if (ret < 0)
- return ret;
+ WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", val? val : "");
val = para_dirname(path);
- ret = WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : "");
+ WRITE_STATUS_ITEM(b, SI_DIRECTORY, "%s\n", val? val : "");
free(val);
- return ret;
}
static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
AFTCOL_CHUNKS, &chunk_table_obj));
if (ret < 0)
return ret;
- ret = para_printf(b, "%s\n"
+ para_printf(b, "%s\n"
"chunk_time: %lu:%lu\nchunk_offsets: ",
d->path,
(long unsigned) d->afhi.chunk_tv.tv_sec,
(long unsigned) d->afhi.chunk_tv.tv_usec
);
- if (ret < 0)
- goto out;
buf = chunk_table_obj.data;
- for (i = 0; i <= d->afhi.chunks_total; i++) {
- ret = para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
- if (ret < 0)
- goto out;
- }
- ret = para_printf(b, "\n");
-out:
+ for (i = 0; i <= d->afhi.chunks_total; i++)
+ para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
+ para_printf(b, "\n");
+ ret = 1;
osl_close_disk_object(&chunk_table_obj);
return ret;
}
-static int write_score(struct para_buffer *b, struct ls_data *d,
- struct ls_options *opts)
-{
- if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/
- return 0;
- return WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
-}
-
static int print_list_item(struct ls_data *d, struct ls_options *opts,
struct para_buffer *b, time_t current_time)
{
+ const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(LS, ADMISSIBLE, opts->lpr);
+ const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME, opts->lpr);
+ const struct lls_opt_result *r_d = SERVER_CMD_OPT_RESULT(LS, UNIX_DATE, opts->lpr);
int ret;
char att_buf[65];
char last_played_time[30];
char asc_hash[2 * HASH_SIZE + 1];
if (opts->mode == LS_MODE_SHORT) {
- ret = para_printf(b, "%s\n", d->path);
+ para_printf(b, "%s\n", d->path);
+ ret = 1;
goto out;
}
if (opts->mode == LS_MODE_CHUNKS) {
goto out;
}
get_attribute_bitmap(&afsi->attributes, att_buf);
- if (opts->flags & LS_FLAG_UNIXDATE)
+ if (lls_opt_given(r_d))
sprintf(last_played_time, "%llu",
(long long unsigned)afsi->last_played);
else {
get_duration_buf(afhi->seconds_total, duration_buf, opts);
if (opts->mode == LS_MODE_LONG) {
struct ls_widths *w = &opts->widths;
- if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY) {
- ret = para_printf(b, "%*li ",
- opts->widths.score_width, d->score);
- if (ret < 0)
- goto out;
- }
- ret = para_printf(b,
+ if (lls_opt_given(r_a))
+ para_printf(b, "%*li ", opts->widths.score_width,
+ d->score);
+ para_printf(b,
"%s " /* attributes */
"%*u " /* amp */
- "%*d " /* image_id */
- "%*d " /* lyrics_id */
- "%*d " /* bitrate */
+ "%*u " /* image_id */
+ "%*u " /* lyrics_id */
+ "%*u " /* bitrate */
"%*s " /* audio format */
- "%*d " /* frequency */
- "%d " /* channels */
+ "%*u " /* frequency */
+ "%u " /* channels */
"%s " /* duration */
- "%*d " /* num_played */
+ "%*u " /* num_played */
"%s " /* last_played */
"%s\n", /* path */
att_buf,
last_played_time,
d->path
);
+ ret = 1;
goto out;
}
if (opts->mode == LS_MODE_MBOX) {
const char *bn = para_basename(d->path);
- ret = para_printf(b,
+ para_printf(b,
"From foo@localhost %s\n"
"Received: from\nTo: bar\nFrom: a\n"
"Subject: %s\n\n",
last_played_time,
bn? bn : "?");
- if (ret < 0)
- goto out;
}
- ret = write_filename_items(b, d->path, opts->flags);
- if (ret < 0)
- goto out;
- ret = write_score(b, d, opts);
- if (ret < 0)
- goto out;
+ write_filename_items(b, d->path, lls_opt_given(r_b));
+ if (lls_opt_given(r_a))
+ WRITE_STATUS_ITEM(b, SI_SCORE, "%li\n", d->score);
ret = write_attribute_items(b, att_buf, afsi);
if (ret < 0)
goto out;
- ret = write_image_items(b, afsi);
- if (ret < 0)
- goto out;
- ret = write_lyrics_items(b, afsi);
- if (ret < 0)
- goto out;
+ write_image_items(b, afsi);
+ write_lyrics_items(b, afsi);
hash_to_asc(d->hash, asc_hash);
- ret = WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n",
+ WRITE_STATUS_ITEM(b, SI_HASH, "%s\n", asc_hash);
+ WRITE_STATUS_ITEM(b, SI_BITRATE, "%dkbit/s\n", afhi->bitrate);
+ WRITE_STATUS_ITEM(b, SI_FORMAT, "%s\n",
audio_format_name(afsi->audio_format_id));
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%lu\n",
+ WRITE_STATUS_ITEM(b, SI_FREQUENCY, "%dHz\n", afhi->frequency);
+ WRITE_STATUS_ITEM(b, SI_CHANNELS, "%d\n", afhi->channels);
+ WRITE_STATUS_ITEM(b, SI_DURATION, "%s\n", duration_buf);
+ WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n",
afhi->seconds_total);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n",
- tv2ms(&afhi->chunk_tv));
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%lu\n",
+ WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
+ WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%u\n", afsi->num_played);
+ WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
+ WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
+ WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
afhi->chunks_total);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album);
- if (ret < 0)
- goto out;
- ret = WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment);
- if (ret < 0)
- goto out;
+ WRITE_STATUS_ITEM(b, SI_TECHINFO, "%s\n", afhi->techinfo);
+ WRITE_STATUS_ITEM(b, SI_ARTIST, "%s\n", afhi->tags.artist);
+ WRITE_STATUS_ITEM(b, SI_TITLE, "%s\n", afhi->tags.title);
+ WRITE_STATUS_ITEM(b, SI_YEAR, "%s\n", afhi->tags.year);
+ WRITE_STATUS_ITEM(b, SI_ALBUM, "%s\n", afhi->tags.album);
+ WRITE_STATUS_ITEM(b, SI_COMMENT, "%s\n", afhi->tags.comment);
if (opts->mode == LS_MODE_MBOX) {
struct osl_object lyrics_def;
lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def);
if (lyrics_def.data) {
- ret = para_printf(b, "Lyrics:\n~~~~~~~\n%s",
+ para_printf(b, "Lyrics:\n~~~~~~~\n%s",
(char *)lyrics_def.data);
osl_close_disk_object(&lyrics_def);
}
ret = strftime(mtime_str, 29, "%b %d %Y", &mtime_tm);
assert(ret > 0); /* number of bytes placed in mtime_str */
out:
- /* We don't care too much about errors here */
- (void)WRITE_STATUS_ITEM(pb, SI_MTIME, "%s\n", mtime_str);
- (void)WRITE_STATUS_ITEM(pb, SI_FILE_SIZE, "%ld\n", statbuf.st_size / 1024);
+ WRITE_STATUS_ITEM(pb, SI_MTIME, "%s\n", mtime_str);
+ WRITE_STATUS_ITEM(pb, SI_FILE_SIZE, "%ld\n", statbuf.st_size / 1024);
}
static int make_status_items(void)
{
- struct ls_options opts = {
- .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
- .mode = LS_MODE_VERBOSE,
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+ char *argv[] = {"ls", "--admissible", "--listing-mode=verbose"};
+ struct ls_options opts = {.mode = LS_MODE_VERBOSE};
struct para_buffer pb = {.max_size = shm_get_shmmax() - 1};
time_t current_time;
int ret;
+ ret = lls_parse(ARRAY_SIZE(argv), argv, cmd, &opts.lpr, NULL);
+ assert(ret >= 0);
time(¤t_time);
ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
if (ret < 0)
- return ret;
+ goto out;
make_inode_status_items(&pb);
free(status_items);
status_items = pb.buf;
+
memset(&pb, 0, sizeof(pb));
pb.max_size = shm_get_shmmax() - 1;
pb.flags = PBF_SIZE_PREFIX;
make_inode_status_items(&pb);
free(parser_friendly_status_items);
parser_friendly_status_items = pb.buf;
- return 1;
+ ret = 1;
+out:
+ lls_free_parse_result(opts.lpr, cmd);
+ return ret;
}
/**
* No need to update the status items as the AFSI_CHANGE event will
* recreate them.
*/
- afs_event(AFSI_CHANGE, NULL, &aced);
+ ret = afs_event(AFSI_CHANGE, NULL, &aced);
+ if (ret < 0)
+ goto out;
ret = save_afd(afd);
out:
free(afd->afhi.chunk_table);
return strcmp(d1->path, d2->path);
}
+static inline bool admissible_only(struct ls_options *opts)
+{
+ return SERVER_CMD_OPT_GIVEN(LS, ADMISSIBLE, opts->lpr)
+ || opts->sorting == LS_SORT_BY_SCORE;
+}
+
static int sort_matching_paths(struct ls_options *options)
{
+ const struct lls_opt_result *r_b = SERVER_CMD_OPT_RESULT(LS, BASENAME,
+ options->lpr);
size_t nmemb = options->num_matching_paths;
size_t size = sizeof(*options->data_ptr);
int (*compar)(const void *, const void *);
options->data_ptr[i] = options->data + i;
/* In these cases the array is already sorted */
- if (options->sorting == LS_SORT_BY_PATH
- && !(options->flags & LS_FLAG_ADMISSIBLE_ONLY)
- && (options->flags & LS_FLAG_FULL_PATH))
- return 1;
- if (options->sorting == LS_SORT_BY_SCORE &&
- options->flags & LS_FLAG_ADMISSIBLE_ONLY)
- return 1;
+ if (admissible_only(options)) {
+ if (options->sorting == LS_SORT_BY_SCORE)
+ return 1;
+ } else {
+ if (options->sorting == LS_SORT_BY_PATH && !lls_opt_given(r_b))
+ return 1;
+ }
switch (options->sorting) {
case LS_SORT_BY_PATH:
{
int ret, i;
struct ls_options *options = ls_opts;
+ bool basename_given = SERVER_CMD_OPT_GIVEN(LS, BASENAME, options->lpr);
struct ls_data *d;
struct ls_widths *w;
unsigned short num_digits;
- unsigned tmp;
+ unsigned tmp, num_inputs;
struct osl_row *aft_row;
long score;
char *path;
- if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+ if (admissible_only(options)) {
ret = get_score_and_aft_row(row, &score, &aft_row);
if (ret < 0)
return ret;
ret = get_audio_file_path_of_row(aft_row, &path);
if (ret < 0)
return ret;
- if (!(options->flags & LS_FLAG_FULL_PATH)) {
+ if (basename_given) {
char *p = strrchr(path, '/');
if (p)
path = p + 1;
}
- if (options->num_patterns) {
- for (i = 0; i < options->num_patterns; i++) {
- ret = fnmatch(options->patterns[i], path, 0);
+ num_inputs = lls_num_inputs(options->lpr);
+ if (num_inputs > 0) {
+ for (i = 0; i < num_inputs; i++) {
+ ret = fnmatch(lls_input(i, options->lpr), path, 0);
if (!ret)
break;
if (ret == FNM_NOMATCH)
continue;
return -E_FNMATCH;
}
- if (i >= options->num_patterns) /* no match */
+ if (i >= num_inputs) /* no match */
return 1;
}
tmp = options->num_matching_paths++;
w->amp_width = PARA_MAX(w->amp_width, num_digits);
num_digits = strlen(audio_format_name(d->afsi.audio_format_id));
w->audio_format_width = PARA_MAX(w->audio_format_width, num_digits);
- if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
+ if (admissible_only(options)) {
GET_NUM_DIGITS(score, &num_digits);
num_digits++; /* add one for the sign (space or "-") */
w->score_width = PARA_MAX(w->score_width, num_digits);
return ret;
}
-static void com_ls_callback(int fd, const struct osl_object *query)
+static int com_ls_callback(struct afs_callback_arg *aca)
{
- struct ls_options *opts = query->data;
- char *p, *pattern_start = (char *)query->data + sizeof(*opts);
- struct para_buffer b = {
- .max_size = shm_get_shmmax(),
- .flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0,
- .max_size_handler = afs_max_size_handler,
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- }
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+ struct ls_options *opts = aca->query.data;
int i = 0, ret;
time_t current_time;
+ const struct lls_opt_result *r_r;
- if (opts->num_patterns) {
- opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
- for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
- opts->patterns[i] = p;
- p += strlen(p) + 1;
- }
- } else
- opts->patterns = NULL;
- if (opts->flags & LS_FLAG_ADMISSIBLE_ONLY)
+ ret = lls_deserialize_parse_result(
+ (char *)aca->query.data + sizeof(*opts), cmd, &opts->lpr);
+ assert(ret >= 0);
+ r_r = SERVER_CMD_OPT_RESULT(LS, REVERSE, opts->lpr);
+
+ aca->pbout.flags = (opts->mode == LS_MODE_PARSER)? PBF_SIZE_PREFIX : 0;
+ if (admissible_only(opts))
ret = admissible_file_loop(opts, prepare_ls_row);
else
ret = osl(osl_rbtree_loop(audio_file_table, AFTCOL_PATH, opts,
if (ret < 0)
goto out;
if (opts->num_matching_paths == 0) {
- if (opts->num_patterns > 0)
- para_printf(&b, "no matches\n");
+ ret = lls_num_inputs(opts->lpr) > 0? -E_NO_MATCH : 0;
goto out;
}
ret = sort_matching_paths(opts);
if (ret < 0)
goto out;
time(¤t_time);
- if (opts->flags & LS_FLAG_REVERSE)
+ if (lls_opt_given(r_r))
for (i = opts->num_matching_paths - 1; i >= 0; i--) {
- ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
+ ret = print_list_item(opts->data_ptr[i], opts,
+ &aca->pbout, current_time);
if (ret < 0)
goto out;
}
else
for (i = 0; i < opts->num_matching_paths; i++) {
- ret = print_list_item(opts->data_ptr[i], opts, &b, current_time);
+ ret = print_list_item(opts->data_ptr[i], opts,
+ &aca->pbout, current_time);
if (ret < 0)
goto out;
}
out:
- if (b.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, b.buf, b.offset);
- free(b.buf);
+ lls_free_parse_result(opts->lpr, cmd);
free(opts->data);
free(opts->data_ptr);
- free(opts->patterns);
+ return ret;
}
-/*
- * TODO: flags -h (sort by hash)
- */
-int com_ls(struct command_context *cc)
+/* TODO: flags -h (sort by hash) */
+static int com_ls(struct command_context *cc, struct lls_parse_result *lpr)
{
- int i, ret;
- unsigned flags = 0;
- enum ls_sorting_method sort = LS_SORT_BY_PATH;
- enum ls_listing_mode mode = LS_MODE_SHORT;
- struct ls_options opts = {.patterns = NULL};
- struct osl_object query = {.data = &opts, .size = sizeof(opts)};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-l", 2)) {
- if (!*(arg + 2)) {
- mode = LS_MODE_LONG;
- continue;
- }
- if (*(arg + 3))
- return -E_AFT_SYNTAX;
- switch(*(arg + 2)) {
- case 's':
- mode = LS_MODE_SHORT;
- continue;
- case 'l':
- mode = LS_MODE_LONG;
- continue;
- case 'v':
- mode = LS_MODE_VERBOSE;
- continue;
- case 'm':
- mode = LS_MODE_MBOX;
- continue;
- case 'c':
- mode = LS_MODE_CHUNKS;
- continue;
- case 'p':
- mode = LS_MODE_PARSER;
- continue;
- default:
- return -E_AFT_SYNTAX;
- }
- }
- if (!strcmp(arg, "-p")) {
- flags |= LS_FLAG_FULL_PATH;
- continue;
- }
- if (!strcmp(arg, "-a")) {
- flags |= LS_FLAG_ADMISSIBLE_ONLY;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- flags |= LS_FLAG_REVERSE;
- continue;
- }
- if (!strcmp(arg, "-d")) {
- flags |= LS_FLAG_UNIXDATE;
- continue;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
+ struct ls_options *opts;
+ struct osl_object query;
+ const struct lls_opt_result *r_l = SERVER_CMD_OPT_RESULT(LS, LISTING_MODE,
+ lpr);
+ const struct lls_opt_result *r_s = SERVER_CMD_OPT_RESULT(LS, SORT, lpr);
+ int ret;
+ char *slpr;
+
+ ret = lls_serialize_parse_result(lpr, cmd, NULL, &query.size);
+ assert(ret >= 0);
+ query.size += sizeof(*opts);
+ query.data = para_malloc(query.size);
+ opts = query.data;
+ memset(opts, 0, sizeof(*opts));
+ slpr = query.data + sizeof(*opts);
+ ret = lls_serialize_parse_result(lpr, cmd, &slpr, NULL);
+ assert(ret >= 0);
+ opts->mode = LS_MODE_SHORT;
+ opts->sorting = LS_SORT_BY_PATH;
+ if (lls_opt_given(r_l)) {
+ const char *val = lls_string_val(0, r_l);
+ if (!strcmp(val, "l") || !strcmp(val, "long"))
+ opts->mode = LS_MODE_LONG;
+ else if (!strcmp(val, "s") || !strcmp(val, "short"))
+ opts->mode = LS_MODE_SHORT;
+ else if (!strcmp(val, "v") || !strcmp(val, "verbose"))
+ opts->mode = LS_MODE_VERBOSE;
+ else if (!strcmp(val, "m") || !strcmp(val, "mbox"))
+ opts->mode = LS_MODE_MBOX;
+ else if (!strcmp(val, "c") || !strcmp(val, "chunk-table"))
+ opts->mode = LS_MODE_MBOX;
+ else if (!strcmp(val, "p") || !strcmp(val, "parser-friendly"))
+ opts->mode = LS_MODE_PARSER;
+ else {
+ ret = -E_AFT_SYNTAX;
+ goto out;
}
- if (!strncmp(arg, "-s", 2)) {
- if (!*(arg + 2) || *(arg + 3))
- return -E_AFT_SYNTAX;
- switch(*(arg + 2)) {
- case 'p':
- sort = LS_SORT_BY_PATH;
- continue;
- case 's': /* -ss implies -a */
- sort = LS_SORT_BY_SCORE;
- flags |= LS_FLAG_ADMISSIBLE_ONLY;
- continue;
- case 'l':
- sort = LS_SORT_BY_LAST_PLAYED;
- continue;
- case 'n':
- sort = LS_SORT_BY_NUM_PLAYED;
- continue;
- case 'f':
- sort = LS_SORT_BY_FREQUENCY;
- continue;
- case 'c':
- sort = LS_SORT_BY_CHANNELS;
- continue;
- case 'i':
- sort = LS_SORT_BY_IMAGE_ID;
- continue;
- case 'y':
- sort = LS_SORT_BY_LYRICS_ID;
- continue;
- case 'b':
- sort = LS_SORT_BY_BITRATE;
- continue;
- case 'd':
- sort = LS_SORT_BY_DURATION;
- continue;
- case 'a':
- sort = LS_SORT_BY_AUDIO_FORMAT;
- continue;
- default:
- return -E_AFT_SYNTAX;
- }
+ }
+ if (lls_opt_given(r_s)) {
+ const char *val = lls_string_val(0, r_s);
+ if (!strcmp(val, "p") || !strcmp(val, "path"))
+ opts->sorting = LS_SORT_BY_PATH;
+ else if (!strcmp(val, "s") || !strcmp(val, "score"))
+ opts->sorting = LS_SORT_BY_SCORE;
+ else if (!strcmp(val, "l") || !strcmp(val, "lastplayed"))
+ opts->sorting = LS_SORT_BY_LAST_PLAYED;
+ else if (!strcmp(val, "n") || !strcmp(val, "numplayed"))
+ opts->sorting = LS_SORT_BY_NUM_PLAYED;
+ else if (!strcmp(val, "f") || !strcmp(val, "frquency"))
+ opts->sorting = LS_SORT_BY_FREQUENCY;
+ else if (!strcmp(val, "c") || !strcmp(val, "channels"))
+ opts->sorting = LS_SORT_BY_CHANNELS;
+ else if (!strcmp(val, "i") || !strcmp(val, "image-id"))
+ opts->sorting = LS_SORT_BY_IMAGE_ID;
+ else if (!strcmp(val, "y") || !strcmp(val, "lyrics-id"))
+ opts->sorting = LS_SORT_BY_LYRICS_ID;
+ else if (!strcmp(val, "b") || !strcmp(val, "bitrate"))
+ opts->sorting = LS_SORT_BY_BITRATE;
+ else if (!strcmp(val, "d") || !strcmp(val, "duration"))
+ opts->sorting = LS_SORT_BY_DURATION;
+ else if (!strcmp(val, "a") || !strcmp(val, "audio-format"))
+ opts->sorting = LS_SORT_BY_AUDIO_FORMAT;
+ else {
+ ret = -E_AFT_SYNTAX;
+ goto out;
}
- return -E_AFT_SYNTAX;
}
- opts.flags = flags;
- opts.sorting = sort;
- opts.mode = mode;
- opts.num_patterns = cc->argc - i;
- ret = send_option_arg_callback_request(&query, opts.num_patterns,
- cc->argv + i, com_ls_callback, afs_cb_result_handler, cc);
+ ret = send_callback_request(com_ls_callback, &query,
+ afs_cb_result_handler, cc);
+out:
+ free(query.data);
return ret;
}
+EXPORT_SERVER_CMD_HANDLER(ls);
/**
* Call the given function for each file in the audio file table.
func));
}
-static struct osl_row *find_hash_sister(unsigned char *hash)
+static int find_hash_sister(unsigned char *hash, struct osl_row **result)
{
- const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
- struct osl_row *row;
+ int ret = aft_get_row_of_hash(hash, result);
- osl_get_row(audio_file_table, AFTCOL_HASH, &obj, &row);
- return row;
+ if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+ return 0;
+ return ret;
+}
+
+static int find_path_brother(const char *path, struct osl_row **result)
+{
+ int ret = aft_get_row_of_path(path, result);
+
+ if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+ return 0;
+ return ret;
}
/** The format of the data stored by save_audio_file_data(). */
enum com_add_buffer_offsets {
- /** afhi (if present) starts here. */
+ /* afhi (if present) starts at this offset. */
CAB_AFHI_OFFSET_POS = 0,
/** Start of the chunk table (if present). */
- CAB_CHUNKS_OFFSET_POS = 2,
+ CAB_CHUNKS_OFFSET_POS = 4,
+ /** Start of the (serialized) lopsub parse result. */
+ CAB_LPR_OFFSET = 8,
/** Audio format id. */
- CAB_AUDIO_FORMAT_OFFSET = 4,
- /** Flags given to the add command. */
- CAB_FLAGS_OFFSET = 5,
+ CAB_AUDIO_FORMAT_ID_OFFSET = 12,
/** The hash of the audio file being added. */
- CAB_HASH_OFFSET = 9,
+ CAB_HASH_OFFSET = 13,
/** Start of the path of the audio file. */
CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH_SIZE),
};
* handler info won't be stored in the buffer.
*/
static void save_add_callback_buffer(unsigned char *hash, const char *path,
- struct afh_info *afhi, uint32_t flags,
+ struct afh_info *afhi, const char *slpr, size_t slpr_size,
uint8_t audio_format_num, struct osl_object *obj)
{
size_t path_len = strlen(path) + 1;
size_t afhi_size = sizeof_afhi_buf(afhi);
size_t size = CAB_PATH_OFFSET + path_len + afhi_size
- + sizeof_chunk_table(afhi);
+ + sizeof_chunk_table(afhi) + slpr_size;
char *buf = para_malloc(size);
- uint16_t pos;
-
- write_u8(buf + CAB_AUDIO_FORMAT_OFFSET, audio_format_num);
- write_u32(buf + CAB_FLAGS_OFFSET, flags);
+ uint32_t pos;
+ assert(size <= ~(uint32_t)0);
+ write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
strcpy(buf + CAB_PATH_OFFSET, path);
-
pos = CAB_PATH_OFFSET + path_len;
- PARA_DEBUG_LOG("size: %zu, afhi starts at %d\n", size, pos);
- PARA_DEBUG_LOG("last afhi byte: %p, pos %zu\n", buf + pos + afhi_size - 1,
- pos + afhi_size - 1);
- write_u16(buf + CAB_AFHI_OFFSET_POS, pos);
+ write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
save_afhi(afhi, buf + pos);
-
pos += afhi_size;
- PARA_DEBUG_LOG("size: %zu, chunks start at %d\n", size, pos);
- write_u16(buf + CAB_CHUNKS_OFFSET_POS, pos);
- if (afhi)
+ write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
+ if (afhi) {
save_chunk_table(afhi, buf + pos);
- PARA_DEBUG_LOG("last byte in buf: %p\n", buf + size - 1);
+ pos += sizeof_chunk_table(afhi);
+ }
+ write_u32(buf + CAB_LPR_OFFSET, pos);
+ memcpy(buf + pos, slpr, slpr_size);
+ assert(pos + slpr_size == size);
obj->data = buf;
obj->size = size;
}
*/
-/** Flags passed to the add command. */
-enum com_add_flags {
- /** Skip paths that exist already. */
- ADD_FLAG_LAZY = 1,
- /** Force adding. */
- ADD_FLAG_FORCE = 2,
- /** Print what is being done. */
- ADD_FLAG_VERBOSE = 4,
- /** Try to add files with unknown suffixes. */
- ADD_FLAG_ALL = 8,
-};
-
-static void com_add_callback(int fd, const struct osl_object *query)
+static int com_add_callback(struct afs_callback_arg *aca)
{
- char *buf = query->data, *path;
+ char *buf = aca->query.data, *path;
struct osl_row *pb, *aft_row;
struct osl_row *hs;
struct osl_object objs[NUM_AFT_COLUMNS];
char asc[2 * HASH_SIZE + 1];
int ret;
char afsi_buf[AFSI_SIZE];
- uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
+ char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
struct afs_info default_afsi = {.last_played = 0};
- struct para_buffer msg = {
- .max_size = shm_get_shmmax(),
- .max_size_handler = afs_max_size_handler,
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- }
- };
uint16_t afhi_offset, chunks_offset;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+ const struct lls_opt_result *r_f, *r_v;
+
+ ret = lls_deserialize_parse_result(slpr, cmd, &aca->lpr);
+ assert(ret >= 0);
+ r_f = SERVER_CMD_OPT_RESULT(ADD, FORCE, aca->lpr);
+ r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
hash = (unsigned char *)buf + CAB_HASH_OFFSET;
hash_to_asc(hash, asc);
objs[AFTCOL_PATH].size = strlen(path) + 1;
PARA_INFO_LOG("request to add %s\n", path);
- hs = find_hash_sister(hash);
- ret = aft_get_row_of_path(path, &pb);
- if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+ ret = find_hash_sister(hash, &hs);
+ if (ret < 0)
goto out;
- if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
- if (flags & ADD_FLAG_VERBOSE)
- ret = para_printf(&msg, "ignoring duplicate\n");
- else
- ret = 1;
+ ret = find_path_brother(path, &pb);
+ if (ret < 0)
+ goto out;
+ if (hs && pb && hs == pb && !lls_opt_given(r_f)) {
+ if (lls_opt_given(r_v))
+ para_printf(&aca->pbout, "ignoring duplicate\n");
+ ret = 1;
goto out;
}
if (hs && hs != pb) {
struct osl_object obj;
if (pb) { /* hs trumps pb, remove pb */
- if (flags & ADD_FLAG_VERBOSE) {
- ret = para_printf(&msg, "removing %s\n", path);
- if (ret < 0)
- goto out;
- }
- afs_event(AUDIO_FILE_REMOVE, &msg, pb);
+ if (lls_opt_given(r_v))
+ para_printf(&aca->pbout, "removing %s\n", path);
+ ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, pb);
+ if (ret < 0)
+ goto out;
ret = osl(osl_del_row(audio_file_table, pb));
if (ret < 0)
goto out;
pb = NULL;
}
/* file rename, update hs' path */
- if (flags & ADD_FLAG_VERBOSE) {
+ if (lls_opt_given(r_v)) {
ret = osl(osl_get_object(audio_file_table, hs,
AFTCOL_PATH, &obj));
if (ret < 0)
goto out;
- ret = para_printf(&msg, "renamed from %s\n", (char *)obj.data);
- if (ret < 0)
- goto out;
+ para_printf(&aca->pbout, "renamed from %s\n",
+ (char *)obj.data);
}
ret = osl(osl_update_object(audio_file_table, hs, AFTCOL_PATH,
&objs[AFTCOL_PATH]));
if (ret < 0)
goto out;
- afs_event(AUDIO_FILE_RENAME, &msg, hs);
- if (!(flags & ADD_FLAG_FORCE))
+ ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
+ if (ret < 0)
+ goto out;
+ if (!lls_opt_given(r_f))
goto out;
}
/* no hs or force mode, child must have sent afhi */
- afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
- chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
+ afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
+ chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
objs[AFTCOL_AFHI].data = buf + afhi_offset;
objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
if (!objs[AFTCOL_AFHI].size) /* "impossible" */
goto out;
objs[AFTCOL_CHUNKS].data = buf + chunks_offset;
- objs[AFTCOL_CHUNKS].size = query->size - chunks_offset;
+ objs[AFTCOL_CHUNKS].size = aca->query.size - chunks_offset;
if (pb && !hs) { /* update pb's hash */
char old_asc[2 * HASH_SIZE + 1];
unsigned char *old_hash;
if (ret < 0)
goto out;
hash_to_asc(old_hash, old_asc);
- if (flags & ADD_FLAG_VERBOSE) {
- ret = para_printf(&msg, "file change: %s -> %s\n",
+ if (lls_opt_given(r_v))
+ para_printf(&aca->pbout, "file change: %s -> %s\n",
old_asc, asc);
- if (ret < 0)
- goto out;
- }
- ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
- &objs[AFTCOL_HASH]);
+ ret = osl(osl_update_object(audio_file_table, pb, AFTCOL_HASH,
+ &objs[AFTCOL_HASH]));
if (ret < 0)
goto out;
}
if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
struct osl_row *row = pb? pb : hs;
/* update afhi and chunk_table */
- if (flags & ADD_FLAG_VERBOSE) {
- ret = para_printf(&msg, "updating afhi and chunk table\n");
- if (ret < 0)
- goto out;
- }
+ if (lls_opt_given(r_v))
+ para_printf(&aca->pbout,
+ "updating afhi and chunk table\n");
ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI,
&objs[AFTCOL_AFHI]));
if (ret < 0)
&objs[AFTCOL_CHUNKS]));
if (ret < 0)
goto out;
- afs_event(AFHI_CHANGE, &msg, row);
+ ret = afs_event(AFHI_CHANGE, &aca->pbout, row);
goto out;
}
/* new entry, use default afsi */
- if (flags & ADD_FLAG_VERBOSE) {
- ret = para_printf(&msg, "new file\n");
- if (ret < 0)
- goto out;
- }
+ if (lls_opt_given(r_v))
+ para_printf(&aca->pbout, "new file\n");
default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
- default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_OFFSET);
+ default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET);
objs[AFTCOL_AFSI].data = &afsi_buf;
objs[AFTCOL_AFSI].size = AFSI_SIZE;
save_afsi(&default_afsi, &objs[AFTCOL_AFSI]);
ret = osl(osl_add_and_get_row(audio_file_table, objs, &aft_row));
- afs_event(AUDIO_FILE_ADD, &msg, aft_row);
+ if (ret < 0)
+ goto out;
+ ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
out:
if (ret < 0)
- para_printf(&msg, "%s\n", para_strerror(-ret));
- if (msg.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, msg.buf, msg.offset);
- free(msg.buf);
+ para_printf(&aca->pbout, "could not add %s\n", path);
+ lls_free_parse_result(aca->lpr, cmd);
+ return ret;
}
-/** Used by com_add(). */
+/* Used by com_add(). */
struct private_add_data {
- /** The pointer passed to the original command handler. */
+ /* The pointer passed to the original command handler. */
struct command_context *cc;
- /** The given add flags. */
- uint32_t flags;
+ /* Contains the flags given at the command line. */
+ struct lls_parse_result *lpr;
+ /* Serialized lopsub parse result. */
+ char *slpr;
+ /* Number of bytes. */
+ size_t slpr_size;
};
-static void path_brother_callback(int fd, const struct osl_object *query)
+static int path_brother_callback(struct afs_callback_arg *aca)
{
- char *path = query->data;
+ char *path = aca->query.data;
struct osl_row *path_brother;
- int ret = aft_get_row_of_path(path, &path_brother);
- if (ret < 0)
- return;
- pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&path_brother,
+ int ret = find_path_brother(path, &path_brother);
+ if (ret <= 0)
+ return ret;
+ return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&path_brother,
sizeof(path_brother));
}
-static void hash_sister_callback(int fd, const struct osl_object *query)
+static int hash_sister_callback(struct afs_callback_arg *aca)
{
- unsigned char *hash = query->data;
+ unsigned char *hash = aca->query.data;
struct osl_row *hash_sister;
+ int ret = find_hash_sister(hash, &hash_sister);
- hash_sister = find_hash_sister(hash);
- if (!hash_sister)
- return;
- pass_buffer_as_shm(fd, SBD_OUTPUT, (char *)&hash_sister,
+ if (ret <= 0)
+ return ret;
+ return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&hash_sister,
sizeof(hash_sister));
}
__a_unused uint8_t band, void *private)
{
struct osl_row **row = private;
- *row = *(struct osl_row **)(result->data);
+
+ if (band == SBD_OUTPUT)
+ *row = *(struct osl_row **)(result->data);
return 1;
}
struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
struct osl_object map, obj = {.data = NULL}, query;
unsigned char hash[HASH_SIZE];
+ bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
+ bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
+ bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
+ bool v_given = SERVER_CMD_OPT_GIVEN(ADD, VERBOSE, pad->lpr);
ret = guess_audio_format(path);
- if (ret < 0 && !(pad->flags & ADD_FLAG_ALL))
+ if (ret < 0 && !a_given) {
+ ret = 0;
goto out_free;
+ }
query.data = (char *)path;
query.size = strlen(path) + 1;
ret = send_callback_request(path_brother_callback, &query,
if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
goto out_free;
ret = 1;
- if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
- if (pad->flags & ADD_FLAG_VERBOSE)
+ if (pb && l_given) { /* lazy is really cheap */
+ if (v_given)
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"lazy-ignore: %s\n", path);
goto out_free;
goto out_unmap;
/* Return success if we already know this file. */
ret = 1;
- if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
- if (pad->flags & ADD_FLAG_VERBOSE)
+ if (pb && hs && hs == pb && !f_given) {
+ if (v_given)
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"%s exists, not forcing update\n", path);
goto out_unmap;
* We won't recalculate the audio format info and the chunk table if
* there is a hash sister and FORCE was not given.
*/
- if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
+ if (!hs || f_given) {
ret = compute_afhi(path, map.data, map.size, fd, &afhi);
if (ret < 0)
goto out_unmap;
}
munmap(map.data, map.size);
close(fd);
- if (pad->flags & ADD_FLAG_VERBOSE) {
+ if (v_given) {
send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
"adding %s\n", path);
if (send_ret < 0)
goto out_free;
}
- save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
+ save_add_callback_buffer(hash, path, afhi_ptr, pad->slpr,
+ pad->slpr_size, format_num, &obj);
/* Ask afs to consider this entry for adding. */
ret = send_callback_request(com_add_callback, &obj,
afs_cb_result_handler, pad->cc);
return send_ret;
}
-int com_add(struct command_context *cc)
+static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
{
int i, ret;
- struct private_add_data pad = {.cc = cc, .flags = 0};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-a")) {
- pad.flags |= ADD_FLAG_ALL;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- pad.flags |= ADD_FLAG_LAZY;
- continue;
- }
- if (!strcmp(arg, "-f")) {
- pad.flags |= ADD_FLAG_FORCE;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- pad.flags |= ADD_FLAG_VERBOSE;
- continue;
- }
+ struct private_add_data pad = {.cc = cc, .lpr = lpr};
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+ unsigned num_inputs;
+ char *errctx;
+
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (cc->argc <= i)
- return -E_AFT_SYNTAX;
- for (; i < cc->argc; i++) {
+ ret = lls_serialize_parse_result(lpr, cmd, &pad.slpr, &pad.slpr_size);
+ assert(ret >= 0);
+ num_inputs = lls_num_inputs(lpr);
+ for (i = 0; i < num_inputs; i++) {
char *path;
- ret = verify_path(cc->argv[i], &path);
+ ret = verify_path(lls_input(i, lpr), &path);
if (ret < 0) {
ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
- cc->argv[i], para_strerror(-ret));
+ lls_input(i, lpr), para_strerror(-ret));
if (ret < 0)
- return ret;
+ goto out;
continue;
}
if (ret == 1) /* directory */
}
free(path);
}
- return 1;
+ ret = 1;
+out:
+ free(pad.slpr);
+ return ret;
}
+EXPORT_SERVER_CMD_HANDLER(add);
/**
* Flags used by the touch command.
TOUCH_FLAG_VERBOSE = 2
};
-/** Options used by com_touch(). */
-struct com_touch_options {
- /** New num_played value. */
- int32_t num_played;
- /** New last played count. */
- int64_t last_played;
- /** New lyrics id. */
- int32_t lyrics_id;
- /** New image id. */
- int32_t image_id;
- /** New amplification value. */
- int32_t amp;
- /** Command line flags (see \ref touch_flags). */
- unsigned flags;
-};
-
-/** Data passed to the action handler of com_touch(). */
-struct touch_action_data {
- /** Command line options (see \ref com_touch_options). */
- struct com_touch_options *cto;
- /** Message buffer. */
- struct para_buffer pb;
-};
-
static int touch_audio_file(__a_unused struct osl_table *table,
struct osl_row *row, const char *name, void *data)
{
- struct touch_action_data *tad = data;
+ struct afs_callback_arg *aca = data;
+ bool v_given = SERVER_CMD_OPT_GIVEN(TOUCH, VERBOSE, aca->lpr);
+ const struct lls_opt_result *r_n, *r_l, *r_i, *r_y, *r_a;
+ int ret;
struct osl_object obj;
struct afs_info old_afsi, new_afsi;
- int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 &&
- tad->cto->lyrics_id < 0 && tad->cto->image_id < 0 && tad->cto->amp < 0;
+ bool no_options;
struct afsi_change_event_data aced;
+ r_n = SERVER_CMD_OPT_RESULT(TOUCH, NUMPLAYED, aca->lpr);
+ r_l = SERVER_CMD_OPT_RESULT(TOUCH, LASTPLAYED, aca->lpr);
+ r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+ r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+ r_a = SERVER_CMD_OPT_RESULT(TOUCH, AMP, aca->lpr);
+ no_options = !lls_opt_given(r_n) && !lls_opt_given(r_l) && !lls_opt_given(r_i)
+ && !lls_opt_given(r_y) && !lls_opt_given(r_a);
+
ret = get_afsi_object_of_row(row, &obj);
- if (ret < 0)
- return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
+ if (ret < 0) {
+ para_printf(&aca->pbout, "cannot touch %s\n", name);
+ return ret;
+ }
ret = load_afsi(&old_afsi, &obj);
- if (ret < 0)
- return para_printf(&tad->pb, "%s: %s\n", name, para_strerror(-ret));
+ if (ret < 0) {
+ para_printf(&aca->pbout, "cannot touch %s\n", name);
+ return ret;
+ }
new_afsi = old_afsi;
if (no_options) {
new_afsi.num_played++;
new_afsi.last_played = time(NULL);
- if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
- ret = para_printf(&tad->pb, "%s: num_played = %u, "
+ if (v_given)
+ para_printf(&aca->pbout, "%s: num_played = %u, "
"last_played = now()\n", name,
new_afsi.num_played);
- if (ret < 0)
- return ret;
- }
} else {
- if (tad->cto->flags & TOUCH_FLAG_VERBOSE) {
- ret = para_printf(&tad->pb, "touching %s\n", name);
- if (ret < 0)
- return ret;
- }
- if (tad->cto->lyrics_id >= 0)
- new_afsi.lyrics_id = tad->cto->lyrics_id;
- if (tad->cto->image_id >= 0)
- new_afsi.image_id = tad->cto->image_id;
- if (tad->cto->num_played >= 0)
- new_afsi.num_played = tad->cto->num_played;
- if (tad->cto->last_played >= 0)
- new_afsi.last_played = tad->cto->last_played;
- if (tad->cto->amp >= 0)
- new_afsi.amp = tad->cto->amp;
+ if (lls_opt_given(r_l))
+ new_afsi.last_played = lls_uint64_val(0, r_l);
+ if (lls_opt_given(r_n))
+ new_afsi.num_played = lls_uint32_val(0, r_n);
+ if (lls_opt_given(r_i))
+ new_afsi.image_id = lls_uint32_val(0, r_i);
+ if (lls_opt_given(r_y))
+ new_afsi.lyrics_id = lls_uint32_val(0, r_y);
+ if (lls_opt_given(r_a))
+ new_afsi.amp = lls_uint32_val(0, r_a);
+ if (v_given)
+ para_printf(&aca->pbout, "touching %s\n", name);
}
save_afsi(&new_afsi, &obj); /* in-place update */
aced.aft_row = row;
aced.old_afsi = &old_afsi;
- afs_event(AFSI_CHANGE, &tad->pb, &aced);
- return 1;
+ return afs_event(AFSI_CHANGE, &aca->pbout, &aced);
}
-static void com_touch_callback(int fd, const struct osl_object *query)
+static int com_touch_callback(struct afs_callback_arg *aca)
{
- struct touch_action_data tad = {.cto = query->data,
- .pb = {
- .max_size = shm_get_shmmax(),
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- },
- .max_size_handler = afs_max_size_handler
- }
- };
- int ret, ret2 = 0;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+ bool p_given;
+ const struct lls_opt_result *r_i, *r_y;
+ int ret;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = (char *)query->data + sizeof(*tad.cto),
- .size = query->size - sizeof(*tad.cto)},
- .data = &tad,
+ .data = aca,
.action = touch_audio_file
};
- if (tad.cto->flags & TOUCH_FLAG_FNM_PATHNAME)
- pmd.fnmatch_flags |= FNM_PATHNAME;
- ret = for_each_matching_row(&pmd);
- if (ret < 0)
- ret2 = para_printf(&tad.pb, "%s\n", para_strerror(-ret));
- else if (pmd.num_matches == 0)
- ret2 = para_printf(&tad.pb, "no matches\n");
- if (ret2 >= 0 && tad.pb.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, tad.pb.buf, tad.pb.offset);
- free(tad.pb.buf);
-}
-
-int com_touch(struct command_context *cc)
-{
- struct com_touch_options cto = {
- .num_played = -1,
- .last_played = -1,
- .lyrics_id = -1,
- .image_id = -1,
- .amp = -1,
- };
- struct osl_object query = {.data = &cto, .size = sizeof(cto)};
- int i, ret;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strncmp(arg, "-n=", 3)) {
- ret = para_atoi32(arg + 3, &cto.num_played);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-l=", 3)) {
- ret = para_atoi64(arg + 3, &cto.last_played);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-y=", 3)) {
- ret = para_atoi32(arg + 3, &cto.lyrics_id);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-i=", 3)) {
- ret = para_atoi32(arg + 3, &cto.image_id);
- if (ret < 0)
- return ret;
- continue;
- }
- if (!strncmp(arg, "-a=", 3)) {
- int32_t val;
- ret = para_atoi32(arg + 3, &val);
- if (ret < 0)
- return ret;
- if (val < 0 || val > 255)
- return -ERRNO_TO_PARA_ERROR(EINVAL);
- cto.amp = val;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- cto.flags |= TOUCH_FLAG_FNM_PATHNAME;
- continue;
+ r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr);
+ if (lls_opt_given(r_i)) {
+ uint32_t id = lls_uint32_val(0, r_i);
+ ret = img_get_name_by_id(id, NULL);
+ if (ret < 0) {
+ para_printf(&aca->pbout, "invalid image ID: %u\n", id);
+ return ret;
}
- if (!strcmp(arg, "-v")) {
- cto.flags |= TOUCH_FLAG_VERBOSE;
- continue;
+ }
+ r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr);
+ if (lls_opt_given(r_y)) {
+ uint32_t id = lls_uint32_val(0, r_y);
+ ret = lyr_get_name_by_id(id, NULL);
+ if (ret < 0) {
+ para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
+ return ret;
}
- break; /* non-option starting with dash */
}
- if (i >= cc->argc)
- return -E_AFT_SYNTAX;
- ret = send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
- if (ret < 0)
- send_strerror(cc, -ret);
+ p_given = SERVER_CMD_OPT_GIVEN(TOUCH, PATHNAME_MATCH, aca->lpr);
+ if (p_given)
+ pmd.fnmatch_flags |= FNM_PATHNAME;
+ ret = for_each_matching_row(&pmd);
+ if (ret >= 0 && pmd.num_matches == 0)
+ ret = -E_NO_MATCH;
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-/** Flags for com_rm(). */
-enum rm_flags {
- /** -v */
- RM_FLAG_VERBOSE = 1,
- /** -f */
- RM_FLAG_FORCE = 2,
- /** -p */
- RM_FLAG_FNM_PATHNAME = 4
-};
+static int com_touch(struct command_context *cc, struct lls_parse_result *lpr)
+{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+ int ret;
+ char *errctx;
-/** Data passed to the action handler of com_rm(). */
-struct com_rm_action_data {
- /** Command line flags ((see \ref rm_flags). */
- uint32_t flags;
- /** Message buffer. */
- struct para_buffer pb;
-};
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_touch_callback, cmd, lpr, cc);
+}
+EXPORT_SERVER_CMD_HANDLER(touch);
static int remove_audio_file(__a_unused struct osl_table *table,
struct osl_row *row, const char *name, void *data)
{
- struct com_rm_action_data *crd = data;
+ struct afs_callback_arg *aca = data;
+ bool v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
int ret;
- if (crd->flags & RM_FLAG_VERBOSE) {
- ret = para_printf(&crd->pb, "removing %s\n", name);
- if (ret < 0)
- return ret;
- }
- afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
+ if (v_given)
+ para_printf(&aca->pbout, "removing %s\n", name);
+ ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, row);
+ if (ret < 0)
+ return ret;
ret = osl(osl_del_row(audio_file_table, row));
if (ret < 0)
- para_printf(&crd->pb, "%s: %s\n", name, para_strerror(-ret));
+ para_printf(&aca->pbout, "cannot remove %s\n", name);
return ret;
}
-static void com_rm_callback(int fd, const struct osl_object *query)
+static int com_rm_callback(struct afs_callback_arg *aca)
{
- struct com_rm_action_data crd = {.flags = *(uint32_t *)query->data,
- .pb = {
- .max_size = shm_get_shmmax(),
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- },
- .max_size_handler = afs_max_size_handler
- }
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
int ret;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = (char *)query->data + sizeof(uint32_t),
- .size = query->size - sizeof(uint32_t)},
- .data = &crd,
+ .data = aca,
.action = remove_audio_file
};
- if (crd.flags & RM_FLAG_FNM_PATHNAME)
+ bool v_given, p_given, f_given;
+
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+ v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
+ p_given = SERVER_CMD_OPT_GIVEN(RM, PATHNAME_MATCH, aca->lpr);
+ f_given = SERVER_CMD_OPT_GIVEN(RM, FORCE, aca->lpr);
+
+ if (p_given)
pmd.fnmatch_flags |= FNM_PATHNAME;
ret = for_each_matching_row(&pmd);
- if (ret < 0) {
- para_printf(&crd.pb, "%s\n", para_strerror(-ret));
- return;
- }
- if ((pmd.num_matches == 0) && !(crd.flags & RM_FLAG_FORCE))
- ret = para_printf(&crd.pb, "no matches -- nothing removed\n");
- else if (crd.flags & RM_FLAG_VERBOSE) {
- ret = para_printf(&crd.pb, "removed %u files\n",
+ if (ret < 0)
+ goto out;
+ if (pmd.num_matches == 0) {
+ if (!f_given)
+ ret = -E_NO_MATCH;
+ } else if (v_given)
+ para_printf(&aca->pbout, "removed %u file(s)\n",
pmd.num_matches);
- }
- if (ret >= 0 && crd.pb.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, crd.pb.buf, crd.pb.offset);
- free(crd.pb.buf);
+out:
+ lls_free_parse_result(aca->lpr, cmd);
+ return ret;
}
/* TODO options: -r (recursive) */
-int com_rm(struct command_context *cc)
+static int com_rm(struct command_context *cc, struct lls_parse_result *lpr)
{
- uint32_t flags = 0;
- struct osl_object query = {.data = &flags, .size = sizeof(flags)};
- int i, ret;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
+ char *errctx;
+ int ret;
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-f")) {
- flags |= RM_FLAG_FORCE;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- flags |= RM_FLAG_FNM_PATHNAME;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- flags |= RM_FLAG_VERBOSE;
- continue;
- }
- break;
+ ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (i >= cc->argc)
- return -E_AFT_SYNTAX;
- ret = send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
- if (ret < 0)
- send_strerror(cc, -ret);
- return ret;
+ return send_lls_callback_request(com_rm_callback, cmd, lpr, cc);
}
-
-/**
- * Flags used by the cpsi command.
- *
- * \sa com_cpsi().
- */
-enum cpsi_flags {
- /** Whether the lyrics id should be copied. */
- CPSI_FLAG_COPY_LYRICS_ID = 1,
- /** Whether the image id should be copied. */
- CPSI_FLAG_COPY_IMAGE_ID = 2,
- /** Whether the lastplayed time should be copied. */
- CPSI_FLAG_COPY_LASTPLAYED = 4,
- /** Whether the numplayed count should be copied. */
- CPSI_FLAG_COPY_NUMPLAYED = 8,
- /** Whether the attributes should be copied. */
- CPSI_FLAG_COPY_ATTRIBUTES = 16,
- /** Activates verbose mode. */
- CPSI_FLAG_VERBOSE = 32,
-};
+EXPORT_SERVER_CMD_HANDLER(rm);
/** Data passed to the action handler of com_cpsi(). */
struct cpsi_action_data {
- /** command line flags (see \ref cpsi_flags). */
- unsigned flags;
- /** Message buffer. */
- struct para_buffer pb;
/** Values are copied from here. */
struct afs_info source_afsi;
+ /** What was passed to com_cpsi_callback(). */
+ struct afs_callback_arg *aca;
+ bool copy_all;
};
static int copy_selector_info(__a_unused struct osl_table *table,
int ret;
struct afs_info old_afsi, target_afsi;
struct afsi_change_event_data aced;
+ bool a_given, y_given, i_given, l_given, n_given, v_given;
+
+ a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, cad->aca->lpr);
+ y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, cad->aca->lpr);
+ i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, cad->aca->lpr);
+ l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, cad->aca->lpr);
+ n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, cad->aca->lpr);
+ v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, cad->aca->lpr);
ret = get_afsi_object_of_row(row, &target_afsi_obj);
if (ret < 0)
return ret;
load_afsi(&target_afsi, &target_afsi_obj);
old_afsi = target_afsi;
- if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID)
+ if (cad->copy_all || y_given)
target_afsi.lyrics_id = cad->source_afsi.lyrics_id;
- if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID)
+ if (cad->copy_all || i_given)
target_afsi.image_id = cad->source_afsi.image_id;
- if (cad->flags & CPSI_FLAG_COPY_LASTPLAYED)
+ if (cad->copy_all || l_given)
target_afsi.last_played = cad->source_afsi.last_played;
- if (cad->flags & CPSI_FLAG_COPY_NUMPLAYED)
+ if (cad->copy_all || n_given)
target_afsi.num_played = cad->source_afsi.num_played;
- if (cad->flags & CPSI_FLAG_COPY_ATTRIBUTES)
+ if (cad->copy_all || a_given)
target_afsi.attributes = cad->source_afsi.attributes;
save_afsi(&target_afsi, &target_afsi_obj); /* in-place update */
- if (cad->flags & CPSI_FLAG_VERBOSE) {
- ret = para_printf(&cad->pb, "copied afsi to %s\n", name);
- if (ret < 0)
- return ret;
- }
+ if (v_given)
+ para_printf(&cad->aca->pbout, "copied afsi to %s\n", name);
aced.aft_row = row;
aced.old_afsi = &old_afsi;
- afs_event(AFSI_CHANGE, &cad->pb, &aced);
- return 1;
+ return afs_event(AFSI_CHANGE, &cad->aca->pbout, &aced);
}
-static void com_cpsi_callback(int fd, const struct osl_object *query)
+static int com_cpsi_callback(struct afs_callback_arg *aca)
{
- struct cpsi_action_data cad = {
- .flags = *(unsigned *)query->data,
- .pb = {
- .max_size = shm_get_shmmax(),
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- },
- .max_size_handler = afs_max_size_handler
- }
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+ bool a_given, y_given, i_given, l_given, n_given, v_given;
+ struct cpsi_action_data cad = {.aca = aca};
int ret;
- char *source_path = (char *)query->data + sizeof(cad.flags);
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = source_path + strlen(source_path) + 1,
- .size = query->size - sizeof(cad.flags)
- - strlen(source_path) - 1},
+ .input_skip = 1, /* skip first argument (source file) */
.data = &cad,
.action = copy_selector_info
};
- ret = get_afsi_of_path(source_path, &cad.source_afsi);
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ a_given = SERVER_CMD_OPT_GIVEN(CPSI, ATTRIBUTE_BITMAP, aca->lpr);
+ y_given = SERVER_CMD_OPT_GIVEN(CPSI, LYRICS_ID, aca->lpr);
+ i_given = SERVER_CMD_OPT_GIVEN(CPSI, IMAGE_ID, aca->lpr);
+ l_given = SERVER_CMD_OPT_GIVEN(CPSI, LASTPLAYED, aca->lpr);
+ n_given = SERVER_CMD_OPT_GIVEN(CPSI, NUMPLAYED, aca->lpr);
+ v_given = SERVER_CMD_OPT_GIVEN(CPSI, VERBOSE, aca->lpr);
+ cad.copy_all = !a_given && !y_given && !i_given && !l_given && !n_given;
+
+ ret = get_afsi_of_path(lls_input(0, aca->lpr), &cad.source_afsi);
if (ret < 0)
goto out;
ret = for_each_matching_row(&pmd);
-out:
if (ret < 0)
- para_printf(&cad.pb, "%s\n", para_strerror(-ret));
- else if (pmd.num_matches > 0) {
- if (cad.flags & CPSI_FLAG_VERBOSE)
- para_printf(&cad.pb, "copied requested afsi from %s "
- "to %u files\n", source_path, pmd.num_matches);
+ goto out;
+ if (pmd.num_matches > 0) {
+ if (v_given)
+ para_printf(&aca->pbout, "updated afsi of %u file(s)\n",
+ pmd.num_matches);
} else
- para_printf(&cad.pb, "no matches - nothing copied\n");
- if (cad.pb.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
- free(cad.pb.buf);
+ ret = -E_NO_MATCH;
+out:
+ lls_free_parse_result(aca->lpr, cmd);
+ return ret;
}
-int com_cpsi(struct command_context *cc)
+static int com_cpsi(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- int i, ret;
- struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-
- for (i = 1; i < cc->argc; i++) {
- const char *arg = cc->argv[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-y")) {
- flags |= CPSI_FLAG_COPY_LYRICS_ID;
- continue;
- }
- if (!strcmp(arg, "-i")) {
- flags |= CPSI_FLAG_COPY_IMAGE_ID;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- flags |= CPSI_FLAG_COPY_LASTPLAYED;
- continue;
- }
- if (!strcmp(arg, "-n")) {
- flags |= CPSI_FLAG_COPY_NUMPLAYED;
- continue;
- }
- if (!strcmp(arg, "-a")) {
- flags |= CPSI_FLAG_COPY_ATTRIBUTES;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- flags |= CPSI_FLAG_VERBOSE;
- continue;
- }
- break;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(CPSI);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
}
- if (i + 1 >= cc->argc) /* need at least source file and pattern */
- return -E_AFT_SYNTAX;
- if (!(flags & ~CPSI_FLAG_VERBOSE)) /* no copy flags given */
- flags = ~(unsigned)CPSI_FLAG_VERBOSE | flags;
- ret = send_option_arg_callback_request(&options, cc->argc - i,
- cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
- if (ret < 0)
- send_strerror(cc, -ret);
- return ret;
+ return send_lls_callback_request(com_cpsi_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(cpsi);
struct change_atts_data {
uint64_t add_mask, del_mask;
- struct para_buffer pb;
+ struct afs_callback_arg *aca;
};
static int change_atts(__a_unused struct osl_table *table,
new_afsi.attributes |= cad->add_mask;
new_afsi.attributes &= ~cad->del_mask;
save_afsi(&new_afsi, &obj); /* in-place update */
- afs_event(AFSI_CHANGE, &cad->pb, &aced);
- return 1;
+ return afs_event(AFSI_CHANGE, &cad->aca->pbout, &aced);
}
-static void com_setatt_callback(int fd, const struct osl_object *query)
+static int com_setatt_callback(struct afs_callback_arg *aca)
{
- char *p;
- int ret;
- size_t len;
- struct change_atts_data cad = {
- .pb = {
- .max_size = shm_get_shmmax(),
- .max_size_handler = afs_max_size_handler,
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- }
- }
- };
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+ int i, ret;
+ struct change_atts_data cad = {.aca = aca};
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.data = &cad,
.action = change_atts
};
+ unsigned num_inputs;
- for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
- char c;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ num_inputs = lls_num_inputs(aca->lpr);
+ for (i = 0; i < num_inputs; i++) {
unsigned char bitnum;
+ uint64_t one = 1;
+ const char *arg = lls_input(i, aca->lpr);
+ char c, *p;
+ size_t len = strlen(arg);
- len = strlen(p);
ret = -E_ATTR_SYNTAX;
if (len == 0)
goto out;
- c = p[len - 1];
- if (c != '+' && c != '-')
- break;
+ c = arg[len - 1];
+ if (c != '+' && c != '-') {
+ if (cad.add_mask == 0 && cad.del_mask == 0)
+ goto out; /* no attribute modifier given */
+ goto set_atts;
+ }
+ p = para_malloc(len);
+ memcpy(p, arg, len - 1);
p[len - 1] = '\0';
ret = get_attribute_bitnum_by_name(p, &bitnum);
- if (ret < 0)
+ free(p);
+ if (ret < 0) {
+ para_printf(&aca->pbout, "invalid argument: %s\n", arg);
goto out;
+ }
if (c == '+')
- cad.add_mask |= (1UL << bitnum);
+ cad.add_mask |= (one << bitnum);
else
- cad.del_mask |= (1UL << bitnum);
+ cad.del_mask |= (one << bitnum);
}
+ /* no pattern given */
ret = -E_ATTR_SYNTAX;
- if (!cad.add_mask && !cad.del_mask)
- goto out;
- pmd.patterns.data = p;
- assert(p < (char *)query->data + query->size);
- pmd.patterns.size = (char *)query->data + query->size - p;
+ goto out;
+set_atts:
+ pmd.input_skip = i;
ret = for_each_matching_row(&pmd);
- if (ret < 0)
- goto out;
- if (pmd.num_matches == 0)
- para_printf(&cad.pb, "no matches\n");
+ if (ret >= 0 && pmd.num_matches == 0)
+ ret = -E_NO_MATCH;
out:
- if (ret < 0)
- para_printf(&cad.pb, "%s\n", para_strerror(-ret));
- if (cad.pb.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
- free(cad.pb.buf);
+ lls_free_parse_result(aca->lpr, cmd);
+ return ret;
}
-int com_setatt(struct command_context *cc)
+static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc < 3)
- return -E_ATTR_SYNTAX;
- return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_setatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_setatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(setatt);
-static void afs_stat_callback(int fd, const struct osl_object *query)
+static int afs_stat_callback(struct afs_callback_arg *aca)
{
- int *parser_friendly = query->data;
+ int *parser_friendly = aca->query.data;
char *buf = *parser_friendly?
parser_friendly_status_items : status_items;
if (!buf)
- return;
- pass_buffer_as_shm(fd, SBD_OUTPUT, buf, strlen(buf));
+ return 0;
+ return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, buf, strlen(buf));
}
/**
afs_cb_result_handler, cc);
}
-/* TODO: optionally fix problems by removing offending rows */
+/* returns success on non-fatal errors to keep the loop going */
static int check_audio_file(struct osl_row *row, void *data)
{
char *path;
struct afs_info afsi;
char *blob_name;
- if (ret < 0)
- return para_printf(pb, "%s\n", para_strerror(-ret));
- if (stat(path, &statbuf) < 0) {
- ret = para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
- if (ret < 0)
- return ret;
- } else {
- if (!S_ISREG(statbuf.st_mode)) {
- ret = para_printf(pb, "%s: not a regular file\n", path);
- if (ret < 0)
- return ret;
- }
+ if (ret < 0) {
+ para_printf(pb, "%s\n", para_strerror(-ret));
+ return ret;
}
+ if (stat(path, &statbuf) < 0)
+ para_printf(pb, "%s: stat error (%s)\n", path, strerror(errno));
+ else if (!S_ISREG(statbuf.st_mode))
+ para_printf(pb, "%s: not a regular file\n", path);
ret = get_afsi_of_row(row, &afsi);
- if (ret < 0)
- return para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
- ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
if (ret < 0) {
- ret = para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
- para_strerror(-ret));
- if (ret < 0)
- return ret;
+ para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+ return 1;
}
+ ret = lyr_get_name_by_id(afsi.lyrics_id, &blob_name);
+ if (ret < 0)
+ para_printf(pb, "%s lyrics id %u: %s\n", path, afsi.lyrics_id,
+ para_strerror(-ret));
ret = img_get_name_by_id(afsi.image_id, &blob_name);
if (ret < 0)
- ret = para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
+ para_printf(pb, "%s image id %u: %s\n", path, afsi.image_id,
para_strerror(-ret));
- return ret;
+ return 0;
}
/**
* Check the audio file table for inconsistencies.
*
- * \param fd The afs socket.
- * \param query Unused.
+ * \param aca Only ->pbout is used for diagnostics.
*
- * This function always succeeds.
+ * \return Standard. Inconsistencies are reported but not regarded as an error.
*
* \sa com_check().
*/
-void aft_check_callback(int fd, __a_unused const struct osl_object *query)
-{
- struct para_buffer pb = {
- .max_size = shm_get_shmmax(),
- .private_data = &(struct afs_max_size_handler_data) {
- .fd = fd,
- .band = SBD_OUTPUT
- },
- .max_size_handler = afs_max_size_handler
- };
- int ret = para_printf(&pb, "checking audio file table...\n");
+int aft_check_callback(struct afs_callback_arg *aca)
+{
+ para_printf(&aca->pbout, "checking audio file table...\n");
+ return audio_file_loop(&aca->pbout, check_audio_file);
+}
- if (ret < 0)
- return;
- audio_file_loop(&pb, check_audio_file);
- if (pb.offset)
- pass_buffer_as_shm(fd, SBD_OUTPUT, pb.buf, pb.offset);
- free(pb.buf);
+struct aft_check_atts_data {
+ uint64_t att_mask;
+ struct para_buffer *pb;
+};
+
+static int check_atts_of_audio_file(struct osl_row *row, void *data)
+{
+ struct aft_check_atts_data *acad = data;
+ int ret;
+ struct afs_info afsi;
+ char *path;
+ uint64_t bad_bits;
+
+ ret = get_afsi_of_row(row, &afsi);
+ if (ret < 0) {
+ para_printf(acad->pb, "cannot get afsi\n");
+ return ret;
+ }
+ bad_bits = afsi.attributes & ~acad->att_mask;
+ if (bad_bits == 0) /* OK */
+ return 0;
+ ret = get_audio_file_path_of_row(row, &path);
+ if (ret < 0) {
+ para_printf(acad->pb, "cannot get path\n");
+ return ret;
+ }
+ para_printf(acad->pb, "invalid attribute bits (%" PRIu64 "): %s\n",
+ bad_bits, path);
+ /* return success to keep looping */
+ return 1;
+}
+
+/**
+ * Iterate over all audio files and check the attribute bit mask.
+ *
+ * \param att_mask The mask of all valid attributes.
+ * \param pb Used for reporting inconsistencies.
+ *
+ * This reads the attribute bit mask of each audio file from the afs info
+ * structure stored in the audio file table and verifies that all set bits are
+ * also turned on in \a att_mask, i.e., correspond to an attribute of the
+ * attribute table. Audio files for which this is not the case are reported via
+ * \a pb.
+ *
+ * \return Standard. Inconsistencies are not regarded as errors.
+ *
+ * \sa \ref attribute_check_callback().
+ */
+int aft_check_attributes(uint64_t att_mask, struct para_buffer *pb)
+{
+ struct aft_check_atts_data acad = {.att_mask = att_mask, .pb = pb};
+
+ para_printf(pb, "checking attributes, mask: %" PRIx64 "\n", att_mask);
+ return audio_file_loop(&acad, check_atts_of_audio_file);
}
/**
if (ret >= 0) {
unsigned num;
osl_get_num_rows(audio_file_table, &num);
- PARA_INFO_LOG("audio file table contains %d files\n", num);
+ PARA_INFO_LOG("audio file table contains %u files\n", num);
return ret;
}
- PARA_INFO_LOG("failed to open audio file table\n");
+ PARA_NOTICE_LOG("failed to open audio file table\n");
audio_file_table = NULL;
- if (ret >= 0 || ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
+ if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_NOENT))
return 1;
return ret;
}
switch (event) {
case ATTRIBUTE_REMOVE: {
const struct rmatt_event_data *red = data;
- ret = para_printf(pb, "clearing attribute %s (bit %u) from all "
+ para_printf(pb, "clearing attribute %s (bit %u) from all "
"entries in the audio file table\n", red->name,
red->bitnum);
- if (ret < 0)
- return ret;
return audio_file_loop(data, clear_attribute);
} case AFSI_CHANGE: {
struct afsi_change_event_data *aced = data;