#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"
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);
}
}
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,
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, "%lu\n", afhi->seconds_total);
+ WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n",
+ afhi->seconds_total);
WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
- WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
+ 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, "%lu\n", afhi->chunks_total);
+ WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
+ afhi->chunks_total);
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);
return -E_AFT_SYNTAX;
}
}
- if (!strcmp(arg, "-p")) {
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-F")) {
flags |= LS_FLAG_FULL_PATH;
continue;
}
+ if (!strcmp(arg, "-b")) {
+ flags &= ~LS_FLAG_FULL_PATH;
+ continue;
+ }
if (!strcmp(arg, "-a")) {
flags |= LS_FLAG_ADMISSIBLE_ONLY;
continue;
CAB_AFHI_OFFSET_POS = 0,
/** Start of the chunk table (if present). */
CAB_CHUNKS_OFFSET_POS = 4,
- /** Flags given to the add command. */
- CAB_FLAGS_OFFSET = 8,
+ /** Start of the (serialized) lopsub parse result. */
+ CAB_LPR_OFFSET = 8,
/** Audio format id. */
CAB_AUDIO_FORMAT_ID_OFFSET = 12,
/** The hash of the audio file being added. */
* 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);
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;
write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
save_afhi(afhi, buf + pos);
pos += afhi_size;
-
write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
- if (afhi)
+ if (afhi) {
save_chunk_table(afhi, buf + pos);
-
- write_u32(buf + CAB_FLAGS_OFFSET, flags);
- 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 += 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 int com_add_callback(struct afs_callback_arg *aca)
{
char *buf = aca->query.data, *path;
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};
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);
ret = find_path_brother(path, &pb);
if (ret < 0)
goto out;
- if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
- if (flags & ADD_FLAG_VERBOSE)
+ 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)
+ 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)
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)
ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
if (ret < 0)
goto out;
- if (!(flags & ADD_FLAG_FORCE))
+ if (!lls_opt_given(r_f))
goto out;
}
/* no hs or force mode, child must have sent afhi */
if (ret < 0)
goto out;
hash_to_asc(old_hash, old_asc);
- if (flags & ADD_FLAG_VERBOSE)
+ if (lls_opt_given(r_v))
para_printf(&aca->pbout, "file change: %s -> %s\n",
old_asc, asc);
- 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)
+ 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,
if (ret < 0)
goto out;
ret = afs_event(AFHI_CHANGE, &aca->pbout, row);
- if (ret < 0)
- goto out;
goto out;
}
/* new entry, use default afsi */
- if (flags & ADD_FLAG_VERBOSE)
+ 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_ID_OFFSET);
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));
+ if (ret < 0)
+ goto out;
ret = afs_event(AUDIO_FILE_ADD, &aca->pbout, aft_row);
out:
if (ret < 0)
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 int path_brother_callback(struct afs_callback_arg *aca)
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;
}
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};
+ struct private_add_data pad = {.cc = cc, .lpr = lpr};
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+ unsigned num_inputs;
+ char *errctx;
- 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;
- }
+ 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.
.data = aca,
.action = touch_audio_file
};
+ if (cto->image_id >= 0) {
+ ret = img_get_name_by_id(cto->image_id, NULL);
+ if (ret < 0) {
+ para_printf(&aca->pbout, "invalid image ID: %d\n",
+ cto->image_id);
+ return ret;
+ }
+ }
+ if (cto->lyrics_id >= 0) {
+ ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+ if (ret < 0) {
+ para_printf(&aca->pbout, "invalid lyrics ID: %d\n",
+ cto->lyrics_id);
+ return ret;
+ }
+ }
if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
pmd.fnmatch_flags |= FNM_PATHNAME;
ret = for_each_matching_row(&pmd);
) {
char c;
unsigned char bitnum;
+ uint64_t one = 1;
len = strlen(p);
ret = -E_ATTR_SYNTAX;
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);
}
ret = -E_ATTR_SYNTAX;
if (!cad.add_mask && !cad.del_mask)
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_NOTICE_LOG("failed to open audio file table\n");