Makefile
TODO
paraslash-*.tar.bz2
+paraslash-*.tar.xz
web/dia/overview.pdf
*.swp
*.rej
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
-EXCLUDE_PATTERNS = *.cmdline.* \
- gcc-compat.h \
- *.command_list.h \
- *.completion.h
+EXCLUDE_PATTERNS = gcc-compat.h
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
-SOURCE_BROWSER = YES
+SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
-USE_HTAGS = YES
+USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# See also: Section \class.
# The default value is: YES.
-VERBATIM_HEADERS = YES
+VERBATIM_HEADERS = NO
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
Any knowledge of how to work with mouse and icons is not required.
+Installing lopsub
+~~~~~~~~~~~~~~~~~
+ git clone git://git.tuebingen.mpg.de/lopsub
+ cd lopsub && make && sudo make install
+ (see http://people.tuebingen.mpg.de/maan/lopsub/)
+
Installing osl
~~~~~~~~~~~~~~
git clone git://git.tuebingen.mpg.de/osl
Example for cross-compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- export CROSS_COMPILE='armv6j-hardfloat-linux-gnueabi-'
+ export CC='armv6j-hardfloat-linux-gnueabi-gcc'
export PATH="/usr/cross/arm/bin:$PATH"
- export CC=${CROSS_COMPILE}gcc
-
export LDFLAGS='
-L/usr/sysroot/arm/lib
-L/usr/sysroot/arm/usr/lib
autoconf
autoheader
./configure --host=arm-linux-gnueabi --prefix /usr/sysroot/arm/usr/local
- make CROSS_COMPILE=$CROSS_COMPILE
+ make
For details see the user manual:
PACKAGE_TARNAME := @PACKAGE_TARNAME@
PACKAGE_VERSION := @PACKAGE_VERSION@
-INSTALL := @INSTALL@
M4 := @M4@
-GENGETOPT := @GENGETOPT@
-HELP2MAN := @HELP2MAN@
-
-ggo_descriptions_declared := @ggo_descriptions_declared@
executables := @executables@
-receivers := @receivers@
-filters := @filters@
-writers := @writers@
recv_objs := @recv_objs@
filter_objs := @filter_objs@
gui_objs := @gui_objs@
audiod_objs := @audiod_objs@
audioc_objs := @audioc_objs@
-fade_objs := @fade_objs@
+mixer_objs := @mixer_objs@
server_objs := @server_objs@
write_objs := @write_objs@
afh_objs := @afh_objs@
id3tag_cppflags := @id3tag_cppflags@
openssl_cppflags := @openssl_cppflags@
gcrypt_cppflags := @gcrypt_cppflags@
+lopsub_cppflags := @lopsub_cppflags@
ogg_cppflags := @ogg_cppflags@
mad_cppflags := @mad_cppflags@
faad_cppflags := @faad_cppflags@
readline_cppflags := @readline_cppflags@
alsa_cppflags := @alsa_cppflags@
oss_cppflags := @oss_cppflags@
-mp4v2_cppflags := @mp4v2_cppflags@
-clock_gettime_ldflags := @clock_gettime_ldflags@
id3tag_ldflags := @id3tag_ldflags@
ogg_ldflags := @ogg_ldflags@
vorbis_ldflags := @vorbis_ldflags@
faad_ldflags := @faad_ldflags@
mad_ldflags := @mad_ldflags@
flac_ldflags := @flac_ldflags@
+lopsub_ldflags := @lopsub_ldflags@
oss_ldflags := @oss_ldflags@
alsa_ldflags := @alsa_ldflags@
pthread_ldflags := @pthread_ldflags@
samplerate_ldflags := @samplerate_ldflags@
osl_ldflags := @osl_ldflags@
curses_ldflags := @curses_ldflags@
-core_audio_ldflags := @core_audio_ldflags@
crypto_ldflags := @crypto_ldflags@
iconv_ldflags := @iconv_ldflags@
-mp4v2_ldflags := @mp4v2_ldflags@
include Makefile.real
CC := cc
endif
+LOGLEVELS := LL_DEBUG,LL_INFO,LL_NOTICE,LL_WARNING,LL_ERROR,LL_CRIT,LL_EMERG
vardir := /var/paraslash
mandir := $(datarootdir)/man/man1
-STRIP := $(CROSS_COMPILE)strip
MKDIR_P := mkdir -p
prefixed_executables := $(addprefix para_, $(executables))
else
build_dir := build
endif
-ggo_dir := $(build_dir)/ggo
object_dir := $(build_dir)/objects
dep_dir := $(build_dir)/deps
man_dir := $(build_dir)/man/man1
-cmdline_dir := $(build_dir)/cmdline
-cmdlist_dir := $(build_dir)/cmdlist
m4depdir := $(build_dir)/m4deps
-help2man_dir := $(build_dir)/help2man
-m4_ggo_dir := m4/gengetopt
+lls_suite_dir := $(build_dir)/lls
+lls_m4_dir := m4/lls
test_dir := t
# sort removes duplicate words, which is all we need here
all_objs := $(sort $(recv_objs) $(filter_objs) $(client_objs) $(gui_objs) \
- $(audiod_objs) $(audioc_objs) $(fade_objs) $(server_objs) \
+ $(audiod_objs) $(audioc_objs) $(mixer_objs) $(server_objs) \
$(write_objs) $(afh_objs) $(play_objs))
-deps := $(addprefix $(dep_dir)/, $(filter-out %.cmdline.d, $(all_objs:.o=.d)))
-m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(executables)))
+deps := $(addprefix $(dep_dir)/, $(all_objs:.o=.d))
+
+afh_objs += afh.lsg.o
+audioc_objs += audioc.lsg.o
+audiod_objs += $(addsuffix _cmd.lsg.o, recv filter audiod write) \
+ client.lsg.o audiod.lsg.o
+client_objs += client.lsg.o
+mixer_objs += mixer.lsg.o
+filter_objs += filter_cmd.lsg.o filter.lsg.o
+gui_objs += gui.lsg.o
+play_objs += $(addsuffix _cmd.lsg.o, recv filter play write) play.lsg.o
+recv_objs += recv_cmd.lsg.o recv.lsg.o
+server_objs += server_cmd.lsg.o server.lsg.o
+write_objs += write_cmd.lsg.o write.lsg.o
+
+cmd_suites := $(addsuffix _cmd, audiod server play recv filter write)
+suites := $(addprefix $(lls_suite_dir)/, $(cmd_suites) $(executables))
+m4_lls_deps := $(addsuffix .m4d, $(suites))
+lsg_h := $(addsuffix .lsg.h, $(suites))
# now prefix all objects with object dir
recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
gui_objs := $(addprefix $(object_dir)/, $(gui_objs))
audiod_objs := $(addprefix $(object_dir)/, $(audiod_objs))
audioc_objs := $(addprefix $(object_dir)/, $(audioc_objs))
-fade_objs := $(addprefix $(object_dir)/, $(fade_objs))
+mixer_objs := $(addprefix $(object_dir)/, $(mixer_objs))
server_objs := $(addprefix $(object_dir)/, $(server_objs))
write_objs := $(addprefix $(object_dir)/, $(write_objs))
afh_objs := $(addprefix $(object_dir)/, $(afh_objs))
autocrap := config.h.in configure
tarball_pfx := $(PACKAGE_TARNAME)-$(GIT_VERSION)
tarball_delete := $(addprefix $(tarball_pfx)/, web .gitignore)
-tarball := $(tarball_pfx).tar.bz2
+tarball := $(tarball_pfx).tar.xz
-.PHONY: all clean clean2 distclean maintainer-clean install man tarball
all: $(prefixed_executables) $(man_pages)
+.PHONY: all mostlyclean clean distclean maintainer-clean install \
+ install-strip man dist tarball
+
man: $(man_pages)
-tarball: $(tarball)
-include $(m4_ggo_dir)/makefile
+include $(lls_m4_dir)/makefile
include $(test_dir)/makefile.test
ifeq ($(findstring clean, $(MAKECMDGOALS)),)
-include $(deps)
--include $(m4_deps)
+-include $(m4_lls_deps)
endif
-$(object_dir) $(man_dir) $(ggo_dir) $(cmdline_dir) $(dep_dir) $(m4depdir) \
- $(help2man_dir) $(cmdlist_dir):
+$(object_dir) $(man_dir) $(dep_dir) $(m4depdir) $(lls_suite_dir):
$(Q) $(MKDIR_P) $@
-# When in doubt, use brute force (Ken Thompson)
-TOUPPER = \
-$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,\
-$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,\
-$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,\
-$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,\
-$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,\
-$(subst z,Z,$1))))))))))))))))))))))))))
-
CPPFLAGS += -DBINDIR='"$(bindir)"'
CPPFLAGS += -DCOPYRIGHT_YEAR='"$(COPYRIGHT_YEAR)"'
CPPFLAGS += -DBUILD_DATE='"$(build_date)"'
+CPPFLAGS += -DLOGLEVELS='$(LOGLEVELS)'
CPPFLAGS += -DUNAME_RS='"$(uname_rs)"'
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -I/usr/local/include
-CPPFLAGS += -I$(cmdline_dir)
-CPPFLAGS += -I$(cmdlist_dir)
-
-CFLAGS += -Os
-CFLAGS += -Wuninitialized
-CFLAGS += -Wchar-subscripts
-CFLAGS += -Werror-implicit-function-declaration
-CFLAGS += -Wmissing-noreturn
-CFLAGS += -Wbad-function-cast
-CFLAGS += -fno-strict-aliasing
-
-STRICT_CFLAGS = $(CFLAGS)
-STRICT_CFLAGS += -g -Wundef -W
+CPPFLAGS += -I$(lls_suite_dir)
+CPPFLAGS += $(lopsub_cppflags)
+
+STRICT_CFLAGS += -fno-strict-aliasing
+STRICT_CFLAGS += -g
+STRICT_CFLAGS += -Os
+STRICT_CFLAGS += -Wundef -W -Wuninitialized
+STRICT_CFLAGS += -Wchar-subscripts
+STRICT_CFLAGS += -Werror-implicit-function-declaration
+STRICT_CFLAGS += -Wmissing-noreturn
+STRICT_CFLAGS += -Wbad-function-cast
STRICT_CFLAGS += -Wredundant-decls
STRICT_CFLAGS += -Wno-sign-compare -Wno-unknown-pragmas
-STRICT_CFLAGS += -Wformat -Wformat-security
-STRICT_CFLAGS += -Wmissing-format-attribute
STRICT_CFLAGS += -Wdeclaration-after-statement
-
-LDFLAGS += $(clock_gettime_ldflags)
+STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
ifeq ($(uname_s),Linux)
# these cause warnings on *BSD
Q := @
endif
-$(cmdlist_dir)/%.command_list.h: %.cmd %.c | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash h < $< >$@
-$(cmdlist_dir)/%.command_list.man: %.cmd %.c | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash man < $< > $@
-$(cmdlist_dir)/%.completion.h: %.cmd | $(cmdlist_dir)
- @[ -z "$(Q)" ] || echo 'GEN $@'
- $(Q) ./command_util.bash compl $(strip $(call TOUPPER,$(*F)))_COMPLETERS \
- $(strip $(call TOUPPER,$(*F)))_COMMANDS < $< > $@
-
-$(cmdlist_dir)/server.command_list.h \
-$(cmdlist_dir)/server.command_list.man \
-$(cmdlist_dir)/server.completion.h \
-: command.c
-
-$(cmdlist_dir)/afs.command_list.h \
-$(cmdlist_dir)/afs.command_list.man \
-$(cmdlist_dir)/afs.completion.h \
-: afs.c aft.c attribute.c
-
-$(cmdlist_dir)/audiod.command_list.h \
-$(cmdlist_dir)/audiod.command_list.man \
-$(cmdlist_dir)/audiod.completion.h \
-: audiod_command.c
-
-server_command_lists := $(cmdlist_dir)/server.command_list.man \
- $(cmdlist_dir)/afs.command_list.man
-audiod_command_lists := $(cmdlist_dir)/audiod.command_list.man
-play_command_lists := $(cmdlist_dir)/play.command_list.man
-
-$(man_dir)/para_server.1: $(server_command_lists)
-$(man_dir)/para_audiod.1: $(audiod_command_lists)
-$(man_dir)/para_play.1: $(play_command_lists)
-
-$(man_dir)/para_server.1: man_util_command_lists := $(server_command_lists)
-$(man_dir)/para_audiod.1: man_util_command_lists := $(audiod_command_lists)
-$(man_dir)/para_play.1: man_util_command_lists := $(play_command_lists)
-
-$(man_dir)/para_%.1: $(ggo_dir)/%.ggo man_util.bash \
- git-version.h | $(man_dir) $(help2man_dir)
- @[ -z "$(Q)" ] || echo 'MAN $<'
- $(Q) \
- COMMAND_LISTS="$(man_util_command_lists)" \
- FILTERS="$(filters)" \
- GENGETOPT=$(GENGETOPT) \
- GGO_DIR=$(ggo_dir) \
- HELP2MAN=$(HELP2MAN) \
- HELP2MAN_DIR=$(help2man_dir) \
- RECEIVERS="$(receivers)" \
- VERSION="$(GIT_VERSION)" \
- WRITERS="$(writers)" \
- ./man_util.bash $@
+audiod_commands := $(addprefix $(lls_suite_dir)/, \
+ $(addsuffix _cmd.lsg.man, audiod recv filter write))
+filter_commands := $(lls_suite_dir)/filter_cmd.lsg.man
+play_commands := $(lls_suite_dir)/play_cmd.lsg.man
+recv_commands := $(lls_suite_dir)/recv_cmd.lsg.man
+server_commands := $(lls_suite_dir)/server_cmd.lsg.man
+write_commands := $(lls_suite_dir)/write_cmd.lsg.man
+
+$(man_dir)/para_audiod.1: $(audiod_commands)
+$(man_dir)/para_filter.1: $(filter_commands)
+$(man_dir)/para_play.1: $(play_commands)
+$(man_dir)/para_recv.1: $(recv_commands)
+$(man_dir)/para_server.1: $(server_commands)
+$(man_dir)/para_write.1: $(write_commands)
+
+$(man_dir)/para_audiod.1: all_commands := $(audiod_commands)
+$(man_dir)/para_filter.1: all_commands := $(filter_commands)
+$(man_dir)/para_play.1: all_commands := $(play_commands)
+$(man_dir)/para_recv.1: all_commands := $(recv_commands)
+$(man_dir)/para_server.1: all_commands := $(server_commands)
+$(man_dir)/para_write.1: all_commands := $(write_commands)
+
+$(man_dir)/para_%.1: $(lls_suite_dir)/%.lsg.man \
+ $(lls_m4_dir)/copyright.m4 | $(man_dir)
+ @[ -z "$(Q)" ] || echo 'LLSMAN $<'
+ $(Q) cat $< $(all_commands) > $@
+ $(Q) $(M4) -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) $(lls_m4_dir)/copyright.m4 >> $@
$(object_dir)/%.o: %.c | $(object_dir)
-$(object_dir)/opus%.o $(dep_dir)/opus%.d: CPPFLAGS += $(opus_cppflags)
-$(object_dir)/gui.o $(object_dir)/gui%.o $(dep_dir)/gui%.d \
+$(object_dir)/opus%.o: CPPFLAGS += $(opus_cppflags)
+$(object_dir)/gui.o $(object_dir)/gui%.o \
: CPPFLAGS += $(curses_cppflags)
-$(object_dir)/spx%.o $(dep_dir)/spx%.d: CPPFLAGS += $(speex_cppflags)
-$(object_dir)/flac%.o $(dep_dir)/flac%.d: CPPFLAGS += $(flac_cppflags)
+$(object_dir)/spx%.o: CPPFLAGS += $(speex_cppflags)
+$(object_dir)/flac%.o: CPPFLAGS += $(flac_cppflags)
-$(object_dir)/mp3_afh.o $(dep_dir)/mp3_afh.d: CPPFLAGS += $(id3tag_cppflags)
-$(object_dir)/crypt.o $(dep_dir)/crypt.d: CPPFLAGS += $(openssl_cppflags)
-$(object_dir)/gcrypt.o $(dep_dir)/gcrypt.d: CPPFLAGS += $(gcrypt_cppflags)
-$(object_dir)/ao_write.o $(dep_dir)/ao_write.d: CPPFLAGS += $(ao_cppflags)
-$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d: CPPFLAGS += $(mp4v2_cppflags)
-$(object_dir)/alsa%.o $(dep_dir)/alsa%.d: CPPFLAGS += $(alsa_cppflags)
+$(object_dir)/mp3_afh.o: CPPFLAGS += $(id3tag_cppflags)
+$(object_dir)/crypt.o: CPPFLAGS += $(openssl_cppflags)
+$(object_dir)/gcrypt.o: CPPFLAGS += $(gcrypt_cppflags)
+$(object_dir)/ao_write.o: CPPFLAGS += $(ao_cppflags)
+$(object_dir)/alsa%.o: CPPFLAGS += $(alsa_cppflags)
-$(object_dir)/interactive.o $(dep_dir)/interactive.d \
+$(object_dir)/interactive.o \
: CPPFLAGS += $(readline_cppflags)
-$(object_dir)/resample_filter.o $(dep_dir)/resample_filter.d \
+$(object_dir)/resample_filter.o \
: CPPFLAGS += $(samplerate_cppflags)
-$(object_dir)/oss_write.o $(dep_dir)/oss_write.d \
+$(object_dir)/oss_write.o \
: CPPFLAGS += $(oss_cppflags)
-$(object_dir)/ao_write.o $(dep_dir)/ao_write.d \
+$(object_dir)/ao_write.o \
: CPPFLAGS += $(ao_cppflags) $(pthread_cppflags)
-$(object_dir)/mp3dec_filter.o $(dep_dir)/mp3dec_filter.d \
+$(object_dir)/mp3dec_filter.o \
: CPPFLAGS += $(mad_cppflags)
-$(object_dir)/aacdec_filter.o $(dep_dir)/aacdec_filter.d \
-$(object_dir)/aac_common.o $(dep_dir)/aac_common.d \
-$(object_dir)/aac_afh.o $(dep_dir)/aac_afh.d \
+$(object_dir)/aacdec_filter.o \
+$(object_dir)/aac_afh.o \
: CPPFLAGS += $(faad_cppflags)
-$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
-$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/oggdec_filter.o \
: CPPFLAGS += $(vorbis_cppflags)
-$(object_dir)/spx_common.o $(dep_dir)/spx_common.d \
-$(object_dir)/spxdec_filter.o $(dep_dir)/spxdec_filter.d \
-$(object_dir)/spx_afh.o $(dep_dir)/spx_afh.d \
-$(object_dir)/oggdec_filter.o $(dep_dir)/oggdec_filter.d \
-$(object_dir)/ogg_afh.o $(dep_dir)/ogg_afh.d \
-$(object_dir)/ogg_afh_common.o $(dep_dir)/ogg_afh_common.d \
-$(object_dir)/opus%.o $(dep_dir)/opus%.d \
+$(object_dir)/spx_common.o \
+$(object_dir)/spxdec_filter.o \
+$(object_dir)/spx_afh.o \
+$(object_dir)/oggdec_filter.o \
+$(object_dir)/ogg_afh.o \
+$(object_dir)/ogg_afh_common.o \
+$(object_dir)/opus%.o \
: CPPFLAGS += $(ogg_cppflags)
-$(object_dir)/afs.o $(dep_dir)/afs.d \
-$(object_dir)/aft.o $(dep_dir)/aft.d \
-$(object_dir)/attribute.o $(dep_dir)/attribute.d \
-$(object_dir)/blob.o $(dep_dir)/blob.d \
-$(object_dir)/mood.o $(dep_dir)/mood.d \
-$(object_dir)/playlist.o $(dep_dir)/playlist.d \
-$(object_dir)/score.o $(dep_dir)/score.d \
-$(object_dir)/server.o $(dep_dir)/server.d \
-$(object_dir)/vss.o $(dep_dir)/vss.d \
-$(object_dir)/command.o $(dep_dir)/command.d \
-$(object_dir)/http_send.o $(dep_dir)/http_send.d \
-$(object_dir)/dccp_send.o $(dep_dir)/dccp_send.d \
-$(object_dir)/udp_send.o $(dep_dir)/udp_send.d \
-$(object_dir)/send_common.o $(dep_dir)/send_common.d \
-$(object_dir)/mm.o $(dep_dir)/mm.d \
+$(object_dir)/afs.o \
+$(object_dir)/aft.o \
+$(object_dir)/attribute.o \
+$(object_dir)/blob.o \
+$(object_dir)/mood.o \
+$(object_dir)/playlist.o \
+$(object_dir)/score.o \
+$(object_dir)/server.o \
+$(object_dir)/vss.o \
+$(object_dir)/command.o \
+$(object_dir)/http_send.o \
+$(object_dir)/dccp_send.o \
+$(object_dir)/udp_send.o \
+$(object_dir)/send_common.o \
+$(object_dir)/mm.o \
: CPPFLAGS += $(osl_cppflags)
-$(object_dir)/%.cmdline.o: CFLAGS += -Wno-unused-function
$(object_dir)/compress_filter.o: CFLAGS += -O3
-$(object_dir)/%.o: %.c | $(object_dir)
- @[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
-
-$(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir)
+$(object_dir)/%.o: %.c | $(object_dir) $(dep_dir) $(lsg_h)
@[ -z "$(Q)" ] || echo 'CC $<'
- $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
-
-# The compiler outputs dependencies either as foo.h or as some_directory/foo.h,
-# depending on whether the latter file exists. Since make needs the directory
-# part we prefix the dependency as appropriate.
-$(dep_dir)/%.d: %.c | $(dep_dir)
- @[ -z "$(Q)" ] || echo 'DEP $<'
- $(Q) $(CC) $(CPPFLAGS) -MM -MG -MP -MT $@ -MT $(object_dir)/$(*F).o $< \
- | sed -e "s@ \([a-zA-Z0-9_]\{1,\}\.cmdline.h\)@ $(cmdline_dir)/\1@g" \
- -e "s@ \([a-zA-Z0-9_]\{1,\}\.command_list.h\)@ $(cmdlist_dir)/\1@g" \
- -e "s@ \([a-zA-Z0-9_]\{1,\}\.completion.h\)@ $(cmdlist_dir)/\1@g" > $@
+ $(Q) $(CC) -c -o $@ -MMD -MF $(dep_dir)/$(*F).d -MT $@ $(CPPFLAGS) \
+ $(STRICT_CFLAGS) $(CFLAGS) $<
para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
para_write para_play para_audiod \
-: LDFLAGS += $(ao_ldflags) $(pthread_ldflags) $(core_audio_ldflags)
+: LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
para_server: LDFLAGS += $(osl_ldflags)
para_gui: LDFLAGS += $(curses_ldflags)
$(samplerate_ldflags) \
-lm
+para_mixer: LDFLAGS += -lm
+
para_write \
para_play \
para_audiod \
-para_fade \
+para_mixer \
: LDFLAGS += \
$(oss_ldflags) \
$(alsa_ldflags)
+para_afh \
+para_audioc \
+para_audiod \
+para_client \
+para_mixer \
+para_filter \
+para_gui \
+para_play \
+para_recv \
+para_server \
+para_write \
+: LDFLAGS += $(lopsub_ldflags)
+
para_server \
para_filter \
para_audiod \
$(faad_ldflags) \
$(flac_ldflags)
-para_server \
-para_play \
-para_afh \
-para_recv \
-: LDFLAGS += \
- $(mp4v2_ldflags)
-
para_afh para_recv para_server para_play: LDFLAGS += $(iconv_ldflags)
$(foreach exe,$(executables),$(eval para_$(exe): $$($(exe)_objs)))
@[ -z "$(Q)" ] || echo 'LD $@'
$(Q) $(CC) $^ -o $@ $(LDFLAGS)
-clean:
- @[ -z "$(Q)" ] || echo 'CLEAN'
+mostlyclean:
+ @[ -z "$(Q)" ] || echo 'MOSTLYCLEAN'
$(Q) rm -f para_*
$(Q) rm -rf $(object_dir)
-
-clean2: clean
- @[ -z "$(Q)" ] || echo 'CLEAN2'
+clean: mostlyclean
+ @[ -z "$(Q)" ] || echo 'CLEAN'
$(Q) rm -rf $(build_dir)
-distclean: clean2 test-clean
+distclean: clean
@[ -z "$(Q)" ] || echo 'DISTCLEAN'
$(Q) rm -f Makefile autoscan.log config.status config.log
- $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
-
+ $(Q) rm -f config.h configure config.h.in
maintainer-clean: distclean
@[ -z "$(Q)" ] || echo 'MAINTAINER-CLEAN'
- $(Q) rm -f *.tar.bz2 config.h configure config.h.in
+ $(Q) rm -f *.tar.bz2 *.tar.xz
+ $(Q) rm -f GPATH GRTAGS GSYMS GTAGS
+
+INSTALL ?= install
+INSTALL_PROGRAM ?= $(INSTALL)
+INSTALL_DATA ?= $(INSTALL) -m 644
+ifneq ($(findstring strip, $(MAKECMDGOALS)),)
+ strip_option := -s
+endif
-install: all man
- $(MKDIR_P) $(bindir) $(mandir)
- $(INSTALL) -s --strip-program $(STRIP) -m 755 \
- $(prefixed_executables) $(bindir)
- $(INSTALL) -m 644 $(man_pages) $(mandir)
- $(MKDIR_P) $(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
+install install-strip: all man
+ $(MKDIR_P) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)
+ $(INSTALL) $(strip_option) $(prefixed_executables) $(DESTDIR)$(bindir)
+ $(INSTALL_DATA) $(man_pages) $(DESTDIR)$(mandir)
+ $(MKDIR_P) $(DESTDIR)$(vardir) >/dev/null 2>&1 || true # not fatal, so don't complain
-$(tarball):
+$(tarball) dist tarball:
$(Q) rm -rf $(tarball) $(tarball_pfx)
$(Q) git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
| tar --delete $(tarball_delete) > $(tarball_pfx).tar
$(Q) ./GIT-VERSION-GEN > $(tarball_pfx)/VERSION
$(Q) cp $(autocrap) $(tarball_pfx)
$(Q) tar rf $(tarball_pfx).tar $(tarball_pfx)/*
- $(Q) bzip2 -9 $(tarball_pfx).tar
+ $(Q) xz -9 $(tarball_pfx).tar
$(Q) ls -l $(tarball)
- $(Q) ln -sf $(tarball) paraslash-git.tar.bz2
+ $(Q) ln -sf $(tarball) paraslash-git.tar.xz
$(Q) rm -rf $(tarball_pfx)
NEWS
====
-------------------------------------------
-0.5.7 (to be announced) "semantic density"
-------------------------------------------
+-------------------------------
+0.6.0 (2017-04-28) "fuzzy flux"
+-------------------------------
+
+The highlights of this release are the conversion of all option parsers
+to the lopsub library and the improved AAC support. But there are
+many other user-visible changes all over the place. Also a lot of
+old cruft has been removed, leading to incompatible changes. Hence
+the new major version number.
+
+- Support for Mac OS X has been removed.
+- On Linux systems, glibc-2.17 or newer is required to build the
+ source tree.
+- Support for RSA public keys in ASN format (as generated by openssl
+ genrsa) has been removed. These keys have been deprecated since
+ 2011, so users should have long switched to keys generated with
+ ssh-keygen(1).
+- If libgcrypt is used as the crypto library, we now require version
+ 1.5.0 (released in 2011) or later.
+- The insecure RC4 stream cipher has been removed. It was superseded
+ by aes_ctr128 three years ago but the RC4 code had been kept for
+ backwards compatibility.
+- On Linux, abstract unix domain sockets are used unconditionally.
+- The "install" target no longer strips executables, the new
+ install-strip target can be used to get the old behaviour.
+- The clean targets have been renamed: clean2 is gone, and the new
+ mostlyclean removes only the executables and object files.
+- New target: check (identical to test).
+- The DESTDIR make variable is honored to prepend a path to the
+ installation directory. This feature is orthogonal to the --prefix
+ option to configure.
+- Minor WMA cleanups.
+- The aac audio format handler has been rewritten to use the mp4ff library.
+ See the manual for how to install the library on your system.
+- New status item: max_chunk_size. The value is stored in a previously
+ unused field of the afhi object of the aft table. Although backwards
+ compatible, users are encouraged to re-add m4a files to populate
+ the new field.
+- No more chunk tables for aac. Chunk boundaries are determined
+ dynamically at stream time.
+- Release and master branch tarballs are now compressed with xz rather
+ than bzip2.
+- The lopsub package is required to install the paraslash package.
+ Gengetopt is no longer needed.
+- make dep is gone. Dependencies have been created automatically for
+ a long time and it was never necessary to run make dep manually.
+- para_gui lost its --timeout option.
+- Most manual pages have been extended to include an overall
+ description of the command.
+- The --stream-delay option of para_audiod has been removed. It had
+ been a no-op for many years.
+- The deprecated --path option of the server ls command has been
+ removed. The command now prints full paths by default, making
+ --full-path a no-op. Hence --full-path has been depreacted and is
+ scheduled for removal in v0.6.1.
+- It is now possible to use 'CFLAGS' to override the default compiler
+ options.
+- para_fade has been renamed to para_mixer. The four modes of operation
+ (set, fade, snooze, sleep) are implemented as subcommands of the
+ new program.
+- The sleep subcommand of para_mixer (former sleep mode of para_fade)
+ lost its --wake-hour and --wake-min options in favor of the new
+ --wake-time option which also accepts relative wakeup times like
+ "+9:30".
+- The new --fade-exponent option of para_mixer allows for non-linear
+ channel fading.
+- The new logo.
+
+Downloads:
+[tarball](./releases/paraslash-0.6.0.tar.xz),
+[signature](./releases/paraslash-0.6.0.tar.xz.asc)
+
+-------------------------------------
+0.5.7 (2016-12-31) "semantic density"
+-------------------------------------
Mostly a bug fix release, and a bunch of internal improvements.
The only user-visible changes are the sanity checks for the touch
- New section on contributing for the user manual.
- Major simplification of the error subsystem.
-Download: [tarball](./releases/paraslash-git.tar.bz2)
+Downloads:
+[tarball](./releases/paraslash-0.5.7.tar.bz2),
+[signature](./releases/paraslash-0.5.7.tar.bz2.asc)
-------------------------------------------
0.4.14 (2016-12-31) "branching oscillation"
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file aac.h Exported symbols from aac_common.c. */
-
-#include <neaacdec.h>
-
-NeAACDecHandle aac_open(void);
-int aac_find_esds(char *buf, size_t buflen, size_t *skip,
- unsigned long *decoder_length);
-ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip);
/** \file aac_afh.c para_server's aac audio format handler. */
#include <regex.h>
-#include <mp4v2/mp4v2.h>
+#include <neaacdec.h>
#include "para.h"
+
+/* To get the mp4ff_tag_t and mp4ff_metadata_t typedefs. */
+#define USE_TAGGING
+#include <mp4ff.h>
+
#include "error.h"
#include "portable_io.h"
#include "afh.h"
#include "string.h"
-#include "aac.h"
#include "fd.h"
-static int aac_find_stsz(char *buf, size_t buflen, off_t *skip)
+
+struct aac_afh_context {
+ const void *map;
+ size_t mapsize;
+ size_t fpos;
+ int32_t track;
+ mp4ff_t *mp4ff;
+ mp4AudioSpecificConfig masc;
+ mp4ff_callback_t cb;
+};
+
+static uint32_t aac_afh_read_cb(void *user_data, void *dest, uint32_t want)
{
- int i;
-
- for (i = 0; i + 16 < buflen; i++) {
- char *p = buf + i;
- unsigned sample_count, sample_size;
-
- if (p[0] != 's' || p[1] != 't' || p[2] != 's' || p[3] != 'z')
- continue;
- PARA_DEBUG_LOG("found stsz@%d\n", i);
- i += 8;
- sample_size = read_u32_be(buf + i);
- PARA_DEBUG_LOG("sample size: %u\n", sample_size);
- i += 4;
- sample_count = read_u32_be(buf + i);
- i += 4;
- PARA_DEBUG_LOG("sample count: %u\n", sample_count);
- *skip = i;
- return sample_count;
+ struct aac_afh_context *c = user_data;
+ uint32_t have, rv;
+
+ if (want == 0 || c->fpos >= c->mapsize) {
+ PARA_INFO_LOG("failed attempt to read %u bytes @%zu\n", want,
+ c->fpos);
+ errno = EAGAIN;
+ return -1;
}
- return -E_STSZ;
+ have = c->mapsize - c->fpos;
+ rv = PARA_MIN(have, want);
+ PARA_DEBUG_LOG("reading %u bytes @%zu\n", rv, c->fpos);
+ memcpy(dest, c->map + c->fpos, rv);
+ c->fpos += rv;
+ return rv;
}
-static int atom_cmp(const char *buf1, const char *buf2)
+static uint32_t aac_afh_seek_cb(void *user_data, uint64_t pos)
{
- return memcmp(buf1, buf2, 4)? 1 : 0;
+ struct aac_afh_context *c = user_data;
+ c->fpos = pos;
+ return 0;
}
-static int read_atom_header(char *buf, uint64_t *subsize, char type[5])
+static int32_t aac_afh_get_track(mp4ff_t *mp4ff, mp4AudioSpecificConfig *masc)
{
- uint64_t size = read_u32_be(buf);
-
- memcpy(type, buf + 4, 4);
- type[4] = '\0';
-
- PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
- if (size != 1) {
- *subsize = size;
- return 8;
+ int32_t i, rc, num_tracks = mp4ff_total_tracks(mp4ff);
+
+ assert(num_tracks >= 0);
+ for (i = 0; i < num_tracks; i++) {
+ unsigned char *buf = NULL;
+ unsigned buf_size = 0;
+
+ mp4ff_get_decoder_config(mp4ff, i, &buf, &buf_size);
+ if (buf) {
+ rc = NeAACDecAudioSpecificConfig(buf, buf_size, masc);
+ free(buf);
+ if (rc < 0)
+ continue;
+ return i;
+ }
}
- buf += 4;
- size = 0;
- size = read_u64_be(buf);
- *subsize = size;
- return 16;
+ return -1; /* no audio track */
}
-static char *get_tag(char *p, int size)
+static int aac_afh_open(const void *map, size_t mapsize, void **afh_context)
{
- char *buf;
-
- assert(size > 0);
- buf = para_malloc(size + 1);
-
- memcpy(buf, p, size);
- buf[size] = '\0';
- PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
- return buf;
+ int ret;
+ struct aac_afh_context *c = para_malloc(sizeof(*c));
+
+ c->map = map;
+ c->mapsize = mapsize;
+ c->fpos = 0;
+ c->cb.read = aac_afh_read_cb;
+ c->cb.seek = aac_afh_seek_cb;
+ c->cb.user_data = c;
+
+ ret = -E_MP4FF_OPEN;
+ c->mp4ff = mp4ff_open_read(&c->cb);
+ if (!c->mp4ff)
+ goto free_ctx;
+ c->track = aac_afh_get_track(c->mp4ff, &c->masc);
+ ret = -E_MP4FF_TRACK;
+ if (c->track < 0)
+ goto close_mp4ff;
+ *afh_context = c;
+ return 0;
+close_mp4ff:
+ mp4ff_close(c->mp4ff);
+free_ctx:
+ free(c);
+ *afh_context = NULL;
+ return ret;
}
-static void read_tags(char *buf, size_t buflen, struct afh_info *afhi)
+static void aac_afh_close(void *afh_context)
{
- char *p = buf;
+ struct aac_afh_context *c = afh_context;
+ mp4ff_close(c->mp4ff);
+ free(c);
+}
- while (p + 32 < buf + buflen) {
- char *q, type1[5], type2[5];
- uint64_t size1, size2;
- int ret, ret2;
+/**
+ * Libmp4ff function to reposition the file to the given sample.
+ *
+ * \param f The opaque handle returned by mp4ff_open_read().
+ * \param track The number of the (audio) track.
+ * \param sample Destination.
+ *
+ * We need this function to obtain the offset of the sample within the audio
+ * file. Unfortunately, it is not exposed in the mp4ff header.
+ *
+ * \return This function always returns 0.
+ */
+int32_t mp4ff_set_sample_position(mp4ff_t *f, const int32_t track, const int32_t sample);
- ret = read_atom_header(p, &size1, type1);
- ret2 = read_atom_header(p + ret, &size2, type2);
+static int aac_afh_get_chunk(long unsigned chunk_num, void *afh_context,
+ const char **buf, size_t *len)
+{
+ struct aac_afh_context *c = afh_context;
+ int32_t ss;
+ size_t offset;
+
+ assert(chunk_num <= INT_MAX);
+ /* this function always returns zero */
+ mp4ff_set_sample_position(c->mp4ff, c->track, chunk_num);
+ offset = c->fpos;
+ ss = mp4ff_read_sample_getsize(c->mp4ff, c->track, chunk_num);
+ if (ss <= 0)
+ return -E_MP4FF_BAD_SAMPLE;
+ assert(ss + offset <= c->mapsize);
+ *buf = c->map + offset;
+ *len = ss;
+ return 1;
+}
- if (size2 <= 16 || atom_cmp(type2, "data")) {
- p += size1;
- continue;
- }
- size2 -= 16;
- q = p + ret + ret2 + 8;
- if (q + size2 > buf + buflen)
- break;
- if (!atom_cmp(type1, "\xa9" "ART"))
- afhi->tags.artist = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "alb"))
- afhi->tags.album = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "nam"))
- afhi->tags.title = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "cmt"))
- afhi->tags.comment = get_tag(q, size2);
- else if (!atom_cmp(type1, "\xa9" "day"))
- afhi->tags.year = get_tag(q, size2);
- p += size1;
- }
+static void _aac_afh_get_taginfo(const mp4ff_t *mp4ff, struct taginfo *tags)
+{
+ mp4ff_meta_get_artist(mp4ff, &tags->artist);
+ mp4ff_meta_get_title(mp4ff, &tags->title);
+ mp4ff_meta_get_date(mp4ff, &tags->year);
+ mp4ff_meta_get_album(mp4ff, &tags->album);
+ mp4ff_meta_get_comment(mp4ff, &tags->comment);
}
-static void read_meta(char *buf, size_t buflen, struct afh_info *afhi)
+/*
+ * Init m4a file and write some tech data to given pointers.
+ */
+static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+ struct afh_info *afhi)
{
- char *p = buf;
+ int ret;
+ int32_t rv;
+ struct aac_afh_context *c;
+ int64_t tmp;
+ const char *buf;
+ size_t sz;
+ uint32_t n;
+
+ ret = aac_afh_open(map, numbytes, (void **)&c);
+ if (ret < 0)
+ return ret;
- while (p + 4 < buf + buflen) {
+ ret = -E_MP4FF_BAD_SAMPLERATE;
+ rv = mp4ff_get_sample_rate(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->frequency = rv;
- if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
- p++;
- continue;
- }
- p += 4;
- return read_tags(p, buflen - (p - buf), afhi);
+ ret = -E_MP4FF_BAD_CHANNEL_COUNT;
+ rv = mp4ff_get_channel_count(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->channels = rv;
+
+ ret = -E_MP4FF_BAD_SAMPLE_COUNT;
+ rv = mp4ff_num_samples(c->mp4ff, c->track);
+ if (rv <= 0)
+ goto close;
+ afhi->chunks_total = rv;
+ afhi->max_chunk_size = 0;
+ for (n = 0; n < afhi->chunks_total; n++) {
+ if (aac_afh_get_chunk(n, c, &buf, &sz) < 0)
+ break;
+ afhi->max_chunk_size = PARA_MAX((size_t)afhi->max_chunk_size, sz);
}
+
+ tmp = c->masc.sbr_present_flag == 1? 2048 : 1024;
+ afhi->seconds_total = tmp * afhi->chunks_total / afhi->frequency;
+ ms2tv(1000 * tmp / afhi->frequency, &afhi->chunk_tv);
+
+ if (aac_afh_get_chunk(0, c, &buf, &sz) >= 0)
+ numbytes -= buf - map;
+ afhi->bitrate = 8 * numbytes / afhi->seconds_total / 1000;
+ _aac_afh_get_taginfo(c->mp4ff, &afhi->tags);
+ ret = 1;
+close:
+ aac_afh_close(c);
+ return ret;
}
-static void aac_get_taginfo(char *buf, size_t buflen, struct afh_info *afhi)
+static uint32_t aac_afh_meta_read_cb(void *user_data, void *dest, uint32_t want)
{
- int i;
- uint64_t subsize;
- char type[5];
-
- for (i = 0; i + 24 < buflen; i++) {
- char *p = buf + i;
- if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
- continue;
- PARA_INFO_LOG("found metadata at offset %d\n", i);
- i += 8;
- p = buf + i;
- i += read_atom_header(p, &subsize, type);
- p = buf + i;
- return read_meta(p, buflen - i, afhi);
- }
- PARA_INFO_LOG("no meta data\n");
+ int fd = *(int *)user_data;
+ return read(fd, dest, want);
}
-static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
- char *map, size_t numbytes)
+static uint32_t aac_afh_meta_seek_cb(void *user_data, uint64_t pos)
{
- int ret, i;
- size_t sum = 0;
- off_t skip;
+ int fd = *(int *)user_data;
+ return lseek(fd, pos, SEEK_SET);
+}
- ret = aac_find_stsz(map, numbytes, &skip);
- if (ret < 0)
- return ret;
- afhi->chunks_total = ret;
- PARA_DEBUG_LOG("sz table has %" PRIu32 " entries\n", afhi->chunks_total);
- afhi->chunk_table = para_malloc((afhi->chunks_total + 1) * sizeof(size_t));
- for (i = 1; i <= afhi->chunks_total; i++) {
- if (skip + 4 > numbytes)
- break;
- sum += read_u32_be(map + skip);
- afhi->chunk_table[i] = sum;
- skip += 4;
-// if (i < 10 || i + 10 > afhi->chunks_total)
-// PARA_DEBUG_LOG("offset #%d: %zu\n", i, afhi->chunk_table[i]);
- }
- return skip;
+static uint32_t aac_afh_meta_write_cb(void *user_data, void *dest, uint32_t want)
+{
+ int fd = *(int *)user_data;
+ return write(fd, dest, want);
}
-static int aac_set_chunk_tv(struct afh_info *afhi,
- mp4AudioSpecificConfig *mp4ASC, uint32_t *seconds)
+static uint32_t aac_afh_meta_truncate_cb(void *user_data)
{
- float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023;
- struct timeval total;
- long unsigned ms;
-
- if (!mp4ASC->samplingFrequency)
- return -E_MP4ASC;
- ms = 1000.0 * afhi->chunks_total * tmp / mp4ASC->samplingFrequency;
- ms2tv(ms, &total);
- tv_divide(afhi->chunks_total, &total, &afhi->chunk_tv);
- PARA_INFO_LOG("%luHz, %lus (%" PRIu32 " x %lums)\n",
- mp4ASC->samplingFrequency, ms / 1000,
- afhi->chunks_total, tv2ms(&afhi->chunk_tv));
- if (ms < 1000)
- return -E_MP4ASC;
- *seconds = ms / 1000;
- return 1;
+ int fd = *(int *)user_data;
+ off_t offset = lseek(fd, 0, SEEK_CUR);
+ return ftruncate(fd, offset);
}
-/*
- * Init m4a file and write some tech data to given pointers.
- */
-static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
- struct afh_info *afhi)
+static void replace_tag(mp4ff_tag_t *tag, const char *new_val, bool *found)
{
- int i;
- size_t skip;
- ssize_t ret;
- unsigned long rate = 0, decoder_len;
- unsigned char channels = 0;
- mp4AudioSpecificConfig mp4ASC;
- NeAACDecHandle handle = NULL;
-
- ret = aac_find_esds(map, numbytes, &skip, &decoder_len);
- if (ret < 0)
- goto out;
- aac_get_taginfo(map, numbytes, afhi);
- handle = aac_open();
- ret = -E_AAC_AFH_INIT;
- if (NeAACDecInit(handle, (unsigned char *)map + skip, decoder_len,
- &rate, &channels))
- goto out;
- if (!channels)
- goto out;
- PARA_DEBUG_LOG("rate: %lu, channels: %d\n", rate, channels);
- ret = -E_MP4ASC;
- if (NeAACDecAudioSpecificConfig((unsigned char *)map + skip,
- numbytes - skip, &mp4ASC))
- goto out;
- if (!mp4ASC.samplingFrequency)
- goto out;
- ret = aac_compute_chunk_table(afhi, map, numbytes);
- if (ret < 0)
- goto out;
- skip = ret;
- ret = aac_set_chunk_tv(afhi, &mp4ASC, &afhi->seconds_total);
- if (ret < 0)
- goto out;
- ret = aac_find_entry_point(map + skip, numbytes - skip, &skip);
- if (ret < 0)
- goto out;
- afhi->chunk_table[0] = ret;
- for (i = 1; i<= afhi->chunks_total; i++)
- afhi->chunk_table[i] += ret;
- afhi->channels = channels;
- afhi->frequency = rate;
- ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
- ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
- afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
- ret = 1;
-out:
- if (handle)
- NeAACDecClose(handle);
- return ret;
+ free(tag->value);
+ tag->value = para_strdup(new_val);
+ *found = true;
}
-static int aac_rewrite_tags(const char *map, size_t mapsize,
- struct taginfo *tags, int fd, const char *filename)
+static void add_tag(mp4ff_metadata_t *md, const char *item, const char *value)
{
- MP4FileHandle h;
- const MP4Tags *mdata;
- int ret = write_all(fd, map, mapsize);
+ md->tags[md->count].item = para_strdup(item);
+ md->tags[md->count].value = para_strdup(value);
+ md->count++;
+}
+static int aac_afh_rewrite_tags(const char *map, size_t mapsize,
+ struct taginfo *tags, int fd, __a_unused const char *filename)
+{
+ int ret, i;
+ int32_t rv;
+ mp4ff_metadata_t metadata;
+ mp4ff_t *mp4ff;
+ mp4ff_callback_t cb = {
+ .read = aac_afh_meta_read_cb,
+ .seek = aac_afh_meta_seek_cb,
+ .write = aac_afh_meta_write_cb,
+ .truncate = aac_afh_meta_truncate_cb,
+ .user_data = &fd
+ };
+ bool found_artist = false, found_title = false, found_album = false,
+ found_year = false, found_comment = false;
+
+ ret = write_all(fd, map, mapsize);
if (ret < 0)
return ret;
lseek(fd, 0, SEEK_SET);
- h = MP4Modify(filename, 0);
- if (!h) {
- PARA_ERROR_LOG("MP4Modify() failed, fd = %d\n", fd);
- return -E_MP4V2;
- }
- mdata = MP4TagsAlloc();
- assert(mdata);
- if (!MP4TagsFetch(mdata, h)) {
- PARA_ERROR_LOG("MP4Tags_Fetch() failed\n");
- ret = -E_MP4V2;
- goto close;
- }
- if (!MP4TagsSetAlbum(mdata, tags->album)) {
- PARA_ERROR_LOG("Could not set album\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetArtist(mdata, tags->artist)) {
- PARA_ERROR_LOG("Could not set album\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetComments(mdata, tags->comment)) {
- PARA_ERROR_LOG("Could not set comment\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetName(mdata, tags->title)) {
- PARA_ERROR_LOG("Could not set title\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
- if (!MP4TagsSetReleaseDate(mdata, tags->year)) {
- PARA_ERROR_LOG("Could not set release date\n");
- ret = -E_MP4V2;
- goto tags_free;
- }
+ mp4ff = mp4ff_open_read_metaonly(&cb);
+ if (!mp4ff)
+ return -E_MP4FF_OPEN;
- if (!MP4TagsStore(mdata, h)) {
- PARA_ERROR_LOG("Could not store tags\n");
- ret = -E_MP4V2;
- goto tags_free;
+ ret = -E_MP4FF_META_READ;
+ rv = mp4ff_meta_get_num_items(mp4ff);
+ if (rv < 0)
+ goto close;
+ metadata.count = rv;
+ PARA_NOTICE_LOG("%d metadata item(s) found\n", rv);
+
+ metadata.tags = para_malloc((metadata.count + 5) * sizeof(mp4ff_tag_t));
+ for (i = 0; i < metadata.count; i++) {
+ mp4ff_tag_t *tag = metadata.tags + i;
+
+ ret = -E_MP4FF_META_READ;
+ if (mp4ff_meta_get_by_index(mp4ff, i,
+ &tag->item, &tag->value) < 0)
+ goto free_tags;
+ PARA_INFO_LOG("found: %s: %s\n", tag->item, tag->value);
+ if (!strcmp(tag->item, "artist"))
+ replace_tag(tag, tags->artist, &found_artist);
+ else if (!strcmp(tag->item, "title"))
+ replace_tag(tag, tags->title, &found_title);
+ else if (!strcmp(tag->item, "album"))
+ replace_tag(tag, tags->album, &found_album);
+ else if (!strcmp(tag->item, "date"))
+ replace_tag(tag, tags->year, &found_year);
+ else if (!strcmp(tag->item, "comment"))
+ replace_tag(tag, tags->comment, &found_comment);
}
+ if (!found_artist)
+ add_tag(&metadata, "artist", tags->artist);
+ if (!found_title)
+ add_tag(&metadata, "title", tags->title);
+ if (!found_album)
+ add_tag(&metadata, "album", tags->album);
+ if (!found_year)
+ add_tag(&metadata, "date", tags->year);
+ if (!found_comment)
+ add_tag(&metadata, "comment", tags->comment);
+ ret = -E_MP4FF_META_WRITE;
+ if (mp4ff_meta_update(&cb, &metadata) < 0)
+ goto free_tags;
ret = 1;
-tags_free:
- MP4TagsFree(mdata);
+free_tags:
+ for (; i > 0; i--) {
+ free(metadata.tags[i - 1].item);
+ free(metadata.tags[i - 1].value);
+ }
+ free(metadata.tags);
close:
- MP4Close(h, 0);
+ mp4ff_close(mp4ff);
return ret;
}
{
afh->get_file_info = aac_get_file_info,
afh->suffixes = aac_suffixes;
- afh->rewrite_tags = aac_rewrite_tags;
+ afh->rewrite_tags = aac_afh_rewrite_tags;
+ afh->open = aac_afh_open;
+ afh->get_chunk = aac_afh_get_chunk;
+ afh->close = aac_afh_close;
}
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/*
- * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
- * Ahead Software AG
- */
-
-/** \file aac_common.c Common functions of aac_afh and aadcec. */
-
-#include "para.h"
-#include "aac.h"
-#include "error.h"
-#include "portable_io.h"
-
-/**
- * Get a new libfaad decoder handle.
- *
- * \return The handle returned by NeAACDecOpen().
- */
-NeAACDecHandle aac_open(void)
-{
- NeAACDecHandle h = NeAACDecOpen();
- NeAACDecConfigurationPtr c = NeAACDecGetCurrentConfiguration(h);
-
- c->defObjectType = LC;
- c->outputFormat = FAAD_FMT_16BIT;
- c->downMatrix = 0;
- NeAACDecSetConfiguration(h, c);
- return h;
-}
-
-static unsigned long aac_read_decoder_length(char *buf, int *description_len)
-{
- uint8_t b;
- uint8_t numBytes = 0;
- unsigned long length = 0;
-
- do {
- b = buf[numBytes];
- numBytes++;
- length = (length << 7) | (b & 0x7F);
- } while
- ((b & 0x80) && numBytes < 4);
- *description_len = numBytes;
- return length;
-}
-
-/**
- * search for the position and the length of the decoder configuration
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the offset in \a buf where
- * the decoder config starts.
- * \param decoder_length result pointer that is filled in with the length of
- * the decoder configuration on success.
- *
- * \return positive on success, negative on errors
- */
-int aac_find_esds(char *buf, size_t buflen, size_t *skip,
- unsigned long *decoder_length)
-{
- size_t i;
-
- for (i = 0; i + 4 < buflen; i++) {
- char *p = buf + i;
- int description_len;
-
- if (p[0] != 'e' || p[1] != 's' || p[2] != 'd' || p[3] != 's')
- continue;
- i += 8;
- p = buf + i;
- PARA_INFO_LOG("found esds@%zu, next: %x\n", i, (unsigned)*p);
- if (*p == 3)
- i += 8;
- else
- i += 6;
- p = buf + i;
- PARA_INFO_LOG("next: %x\n", (unsigned)*p);
- if (*p != 4)
- continue;
- i += 18;
- p = buf + i;
- PARA_INFO_LOG("next: %x\n", (unsigned)*p);
- if (*p != 5)
- continue;
- i++;
- p = buf + i;
- *decoder_length = aac_read_decoder_length(p, &description_len);
- PARA_INFO_LOG("decoder length: %lu\n", *decoder_length);
- i += description_len;
- *skip = i;
- return 1;
- }
- return -E_ESDS;
-}
-
-/**
- * search for the first entry in the stco table
- *
- * \param buf buffer to seach
- * \param buflen length of \a buf
- * \param skip Upon succesful return, this contains the number
- * of bytes to skip from the input buffer.
- *
- * \return the position of the first entry in the table on success,
- * -E_STCO on errors.
- */
-ssize_t aac_find_entry_point(char *buf, size_t buflen, size_t *skip)
-{
- ssize_t ret;
- size_t i;
-
- for (i = 0; i + 20 < buflen; i++) {
- char *p = buf + i;
-
- if (p[0] != 's' || p[1] != 't' || p[2] != 'c' || p[3] != 'o')
- continue;
- PARA_INFO_LOG("found stco@%zu\n", i);
- i += 12;
- ret = read_u32_be(buf + i); /* first offset */
- i += 4;
- PARA_INFO_LOG("entry point: %zd\n", ret);
- *skip = i;
- return ret;
- }
- PARA_WARNING_LOG("stco not found, buflen: %zu\n", buflen);
- return -E_STCO;
-}
/** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
#include <regex.h>
+#include <neaacdec.h>
#include "para.h"
+#include "portable_io.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
-#include "aac.h"
/** Give up decoding after that many errors. */
#define MAX_ERRORS 20
struct private_aacdec_data {
/** the return value of aac_open */
NeAACDecHandle handle;
- /** info about the currently decoded frame */
- NeAACDecFrameInfo frame_info;
/** whether this instance of the aac decoder is already initialized */
- int initialized;
- /**
- * return value of aac_find_esds(). Used to call the right aacdec
- * init function
- */
- unsigned long decoder_length;
+ bool initialized;
/** number of times the decoder returned an error */
unsigned error_count;
/** number of bytes already consumed from the imput stream */
size_t consumed_total;
- /** return value of aac_find_entry_point */
- size_t entry;
/** The number of channels of the current stream. */
unsigned int channels;
/** Current sample rate in Hz. */
static void aacdec_open(struct filter_node *fn)
{
+ NeAACDecConfigurationPtr c;
struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+ padd->handle = NeAACDecOpen();
+ c = NeAACDecGetCurrentConfiguration(padd->handle);
+ c->defObjectType = LC;
+ c->outputFormat = FAAD_FMT_16BIT;
+ c->downMatrix = 0;
+ NeAACDecSetConfiguration(padd->handle, c);
+
fn->private_data = padd;
fn->min_iqs = 2048;
- padd->handle = aac_open();
}
static void aacdec_close(struct filter_node *fn)
struct btr_node *btrn = fn->btrn;
struct private_aacdec_data *padd = fn->private_data;
int i, ret;
- char *p, *inbuf, *outbuffer;
- char *btr_buf;
- size_t len, skip, consumed, loaded;
+ char *inbuf, *outbuf, *btrbuf;
+ size_t len, consumed, loaded = 0;
+ NeAACDecFrameInfo frame_info;
next_buffer:
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
if (!padd->initialized) {
unsigned long rate = 0;
unsigned char channels = 0;
- ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
+ ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
+ len, &rate, &channels);
+ PARA_INFO_LOG("decoder init: %d\n", ret);
if (ret < 0) {
- PARA_INFO_LOG("%s\n", para_strerror(-ret));
- ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf,
- len, &rate, &channels);
- PARA_INFO_LOG("decoder init: %d\n", ret);
- if (ret < 0) {
- ret = -E_AACDEC_INIT;
- goto out;
- }
- consumed = ret;
- } else {
- PARA_INFO_LOG("decoder len: %lu\n",
- padd->decoder_length);
- consumed += skip;
- p = inbuf + consumed;
ret = -E_AACDEC_INIT;
- if (NeAACDecInit2(padd->handle, (unsigned char *)p,
- padd->decoder_length, &rate,
- &channels) != 0)
- goto out;
+ goto err;
}
+ consumed = ret;
padd->sample_rate = rate;
padd->channels = channels;
PARA_INFO_LOG("rate: %u, channels: %u\n",
padd->sample_rate, padd->channels);
- padd->initialized = 1;
+ padd->initialized = true;
}
- if (padd->decoder_length > 0) {
- consumed = 0;
- if (!padd->entry) {
- ret = aac_find_entry_point(inbuf + consumed,
- len - consumed, &skip);
- if (ret < 0) {
- ret = len;
- goto out;
- }
- consumed += skip;
- padd->entry = ret;
- PARA_INFO_LOG("entry: %zu\n", padd->entry);
- }
- ret = len;
- if (padd->consumed_total + len < padd->entry)
- goto out;
- if (padd->consumed_total < padd->entry)
- consumed = padd->entry - padd->consumed_total;
- }
- for (; consumed < len; consumed++)
- if ((inbuf[consumed] & 0xfe) == 0x20)
- break;
if (consumed >= len)
goto success;
- p = inbuf + consumed;
//PARA_CRIT_LOG("consumed: %zu (%zu + %zu), have: %zu\n", padd->consumed_total + consumed,
// padd->consumed_total, consumed, len - consumed);
- outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info,
- (unsigned char *)p, len - consumed);
- if (padd->frame_info.error) {
- int err = padd->frame_info.error;
+ outbuf = NeAACDecDecode(padd->handle, &frame_info,
+ (unsigned char *)inbuf + consumed, len - consumed);
+ if (frame_info.error) {
+ int err = frame_info.error;
ret = -E_AAC_DECODE;
if (padd->error_count++ > MAX_ERRORS)
goto err;
- /* Suppress non-fatal bitstream error message at BOF/EOF */
- if (len < fn->min_iqs || padd->consumed_total == 0) {
- consumed = len;
- goto success;
- }
- PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(err));
- PARA_ERROR_LOG("consumed: %zu + %zu + %lu\n",
+ PARA_NOTICE_LOG("error #%u: (%s)\n", padd->error_count,
+ NeAACDecGetErrorMessage(err));
+ PARA_NOTICE_LOG("consumed (total, buffer, frame): "
+ "%zu, %zu, %lu\n",
padd->consumed_total, consumed,
- padd->frame_info.bytesconsumed);
- if (consumed < len)
- consumed++; /* catch 21 */
+ frame_info.bytesconsumed);
+ consumed++; /* just eat one byte and hope for the best */
goto success;
}
padd->error_count = 0;
- //PARA_CRIT_LOG("decoder ate %lu\n", padd->frame_info.bytesconsumed);
- consumed += padd->frame_info.bytesconsumed;
- ret = consumed;
- if (!padd->frame_info.samples)
- goto out;
- btr_buf = para_malloc(2 * padd->frame_info.samples);
- loaded = 0;
- for (i = 0; i < padd->frame_info.samples; i++) {
- short sh = ((short *)outbuffer)[i];
- write_int16_host_endian(btr_buf + loaded, sh);
+ //PARA_CRIT_LOG("decoder ate %lu\n", frame_info.bytesconsumed);
+ consumed += frame_info.bytesconsumed;
+ if (!frame_info.samples)
+ goto success;
+ btrbuf = para_malloc(2 * frame_info.samples);
+ for (i = 0; i < frame_info.samples; i++) {
+ short sh = ((short *)outbuf)[i];
+ write_int16_host_endian(btrbuf + loaded, sh);
loaded += 2;
}
- btr_add_output(btr_buf, loaded, btrn);
+ btr_add_output(btrbuf, loaded, btrn);
success:
- ret = consumed;
-out:
- if (ret >= 0) {
- padd->consumed_total += ret;
- btr_consume(btrn, ret);
+ btr_consume(btrn, consumed);
+ padd->consumed_total += consumed;
+ if (loaded == 0)
goto next_buffer;
- }
+ return 1;
err:
assert(ret < 0);
btr_remove_node(&fn->btrn);
return ret;
}
-/**
- * The init function of the aacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init
- */
-void aacdec_filter_init(struct filter *f)
-{
- f->open = aacdec_open;
- f->close = aacdec_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = aacdec_post_select;
- f->execute = aacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_aacdec_user_data = {
+ .open = aacdec_open,
+ .close = aacdec_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = aacdec_post_select,
+ .execute = aacdec_execute
+};
* \param addr The address to add.
* \param netmask The netmask to use for this entry.
*/
-static void acl_add_entry(struct list_head *acl, char *addr, int netmask)
+void acl_add_entry(struct list_head *acl, char *addr, int netmask)
{
struct access_info *ai = para_malloc(sizeof(struct access_info));
return ret;
}
-/**
- * Initialize an access control list.
- *
- * \param acl The list to initialize.
- * \param acl_info An array of strings of the form ip/netmask.
- * \param num The number of strings in \a acl_info.
- */
-void acl_init(struct list_head *acl, char * const *acl_info, int num)
-{
- char addr[16];
- int mask, i;
-
- INIT_LIST_HEAD(acl);
- for (i = 0; i < num; i++)
- if (parse_cidr(acl_info[i], addr, sizeof(addr), &mask) == NULL)
- PARA_CRIT_LOG("ACL syntax error: %s, ignoring\n",
- acl_info[i]);
- else
- acl_add_entry(acl, addr, mask);
-}
-
/**
* Check whether the peer name of a given fd is allowed by an acl.
*
/** \file acl.h Exported functions of acl.c. */
-void acl_init(struct list_head *acl, char * const *acl_info, int num);
+void acl_add_entry(struct list_head *acl, char *addr, int netmask);
char *acl_get_contents(struct list_head *acl);
int acl_check_access(int fd, struct list_head *acl, int default_deny);
void acl_allow(char *addr, int mask, struct list_head *acl, int default_deny);
/** \file afh.c Paraslash's standalone audio format handler tool. */
#include <regex.h>
+#include <lopsub.h>
+#include "afh.lsg.h"
#include "para.h"
#include "string.h"
-#include "afh.cmdline.h"
#include "fd.h"
#include "afh.h"
#include "error.h"
#include "version.h"
-#include "ggo.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-static struct afh_args_info conf;
+static struct lls_parse_result *lpr;
+
+#define CMD_PTR (lls_cmd(0, afh_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AFH_PARA_AFH_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
static int loglevel;
INIT_STDERR_LOGGING(loglevel)
struct taginfo *tags = &afhi->tags;
bool modified = false;
char *tmp_name;
+ const char *arg;
int output_fd = -1, ret;
struct stat sb;
- if (tag_needs_update(conf.year_given, tags->year, conf.year_arg)) {
+ arg = OPT_STRING_VAL(YEAR);
+ if (tag_needs_update(OPT_GIVEN(YEAR), tags->year, arg)) {
free(tags->year);
- tags->year = para_strdup(conf.year_arg);
+ tags->year = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.title_given, tags->title, conf.title_arg)) {
+ arg = OPT_STRING_VAL(TITLE);
+ if (tag_needs_update(OPT_GIVEN(TITLE), tags->title, arg)) {
free(tags->title);
- tags->title = para_strdup(conf.title_arg);
+ tags->title = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.artist_given, tags->artist,
- conf.artist_arg)) {
+ arg = OPT_STRING_VAL(ARTIST);
+ if (tag_needs_update(OPT_GIVEN(ARTIST), tags->artist, arg)) {
free(tags->artist);
- tags->artist = para_strdup(conf.artist_arg);
+ tags->artist = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.album_given, tags->album, conf.album_arg)) {
+ arg = OPT_STRING_VAL(ALBUM);
+ if (tag_needs_update(OPT_GIVEN(ALBUM), tags->album, arg)) {
free(tags->album);
- tags->album = para_strdup(conf.album_arg);
+ tags->album = para_strdup(arg);
modified = true;
}
- if (tag_needs_update(conf.comment_given, tags->comment,
- conf.comment_arg)) {
+ arg = OPT_STRING_VAL(COMMENT);
+ if (tag_needs_update(OPT_GIVEN(COMMENT), tags->comment, arg)) {
free(tags->comment);
- tags->comment = para_strdup(conf.comment_arg);
+ tags->comment = para_strdup(arg);
modified = true;
}
if (!modified) {
tmp_name);
if (ret < 0)
goto out;
- if (conf.backup_given) {
+ if (OPT_GIVEN(BACKUP)) {
char *backup_name = make_message("%s~", name);
ret = xrename(name, backup_name);
free(backup_name);
free(msg);
}
-static void print_chunk_table(struct afh_info *afhi)
+static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
+ const void *map, size_t mapsize)
{
- int i;
+ int i, ret;
+ void *ctx = NULL;
- if (conf.parser_friendly_given) {
- printf("chunk_table: ");
- for (i = 0; i <= afhi->chunks_total; i++)
- printf("%u ", afhi->chunk_table[i]);
- printf("\n");
- return;
- }
- for (i = 1; i <= afhi->chunks_total; i++) {
+ for (i = 0; i < afhi->chunks_total; i++) {
struct timeval tv;
long unsigned from, to;
- tv_scale(i - 1, &afhi->chunk_tv, &tv);
- from = tv2ms(&tv);
+ const char *buf;
+ size_t len;
tv_scale(i, &afhi->chunk_tv, &tv);
+ from = tv2ms(&tv);
+ tv_scale(i + 1, &afhi->chunk_tv, &tv);
to = tv2ms(&tv);
- printf("%d [%lu.%03lu - %lu.%03lu] %u - %u (%u)\n", i - 1,
- from / 1000, from % 1000, to / 1000, to % 1000,
- afhi->chunk_table[i - 1], afhi->chunk_table[i],
- afhi->chunk_table[i] - afhi->chunk_table[i - 1]);
+ ret = afh_get_chunk(i, afhi, audio_format_id, map, mapsize,
+ &buf, &len, &ctx);
+ if (ret < 0) {
+ PARA_ERROR_LOG("fatal: chunk %d: %s\n", i,
+ para_strerror(-ret));
+ return;
+ }
+ if (!OPT_GIVEN(PARSER_FRIENDLY))
+ printf("%d [%lu.%03lu - %lu.%03lu] ", i, from / 1000,
+ from % 1000, to / 1000, to % 1000);
+ printf("%td - %td", buf - (const char *)map,
+ buf + len - (const char *)map);
+ if (!OPT_GIVEN(PARSER_FRIENDLY))
+ printf(" (%zu)", len);
+ printf("\n");
}
+ afh_close(ctx, audio_format_id);
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
{
- struct ggo_help h = DEFINE_GGO_HELP(afh);
- int d = conf.detailed_help_given;
- unsigned flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+ char *help;
- ggo_print_help(&h, flags);
- printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP) || lls_num_inputs(lpr) == 0)
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s", help);
+ free(help);
+ printf("Supported audio formats\n %s\n", AUDIO_FORMAT_HANDLERS);
exit(EXIT_SUCCESS);
}
void *audio_file_data;
size_t audio_file_size;
struct afh_info afhi;
+ char *errctx;
- afh_cmdline_parser(argc, argv, &conf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("afh", conf.version_given);
- if (conf.help_given || conf.detailed_help_given || conf.inputs_num == 0)
- print_help_and_die();
+ ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("afh", OPT_GIVEN(VERSION));
+ handle_help_flags();
afh_init();
- for (i = 0; i < conf.inputs_num; i++) {
+ for (i = 0; i < lls_num_inputs(lpr); i++) {
int ret2;
- ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
+ const char *path = lls_input(i, lpr);
+ ret = mmap_full_file(path, O_RDONLY, &audio_file_data,
&audio_file_size, &fd);
if (ret < 0) {
- PARA_ERROR_LOG("failed to mmap \"%s\"\n", conf.inputs[i]);
+ PARA_ERROR_LOG("failed to mmap \"%s\"\n", path);
goto out;
}
- ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
+ ret = compute_afhi(path, audio_file_data, audio_file_size,
fd, &afhi);
if (ret >= 0) {
audio_format_num = ret;
- if (conf.modify_given) {
- ret = rewrite_tags(conf.inputs[i], fd, audio_file_data,
+ if (OPT_GIVEN(MODIFY)) {
+ ret = rewrite_tags(path, fd, audio_file_data,
audio_file_size, audio_format_num, &afhi);
} else {
- printf("File %d: %s\n", i + 1, conf.inputs[i]);
+ printf("File %d: %s\n", i + 1, path);
print_info(audio_format_num, &afhi);
- if (conf.chunk_table_given)
- print_chunk_table(&afhi);
- printf("\n");
+ if (OPT_GIVEN(CHUNK_TABLE))
+ print_chunk_table(&afhi, audio_format_num,
+ audio_file_data, audio_file_size);
}
clear_afhi(&afhi);
}
break;
}
out:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
if (ret < 0)
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
}
* the current audio file.
*/
uint32_t *chunk_table;
+ /** Size of the largest chunk, introduced in v0.6.0. */
+ uint32_t max_chunk_size;
/** Period of time between sending data chunks. */
struct timeval chunk_tv;
/**
int fd;
/** Vss needs this for streaming. */
struct afh_info afhi;
- /** Size of the largest chunk. */
+ /**
+ * Size of the largest chunk. Superseded by afhi->max_chunk_size. May
+ * be removed after v0.6.1.
+ */
uint32_t max_chunk_size;
/** Needed to get the audio file header. */
uint8_t audio_format_id;
* \sa struct afh_info
*/
int (*get_file_info)(char *map, size_t numbytes, int fd,
- struct afh_info *afi);
+ struct afh_info *afhi);
/** Optional, used for header-rewriting. See \ref afh_get_header(). */
void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
+ /**
+ * An audio format handler may signify support for dynamic chunks by
+ * defining ->get_chunk below. In this case the vss calls ->open() at
+ * BOS, ->get_chunk() for each chunk while streaming, and ->close() at
+ * EOS. The chunk table is not accessed at all.
+ *
+ * The function may return its (opaque) context through the last
+ * argument. The returned pointer is passed to subsequent calls to
+ * ->get_chunk() and ->close().
+ */
+ int (*open)(const void *map, size_t mapsize, void **afh_context);
+ /**
+ * Return a reference to one chunk. The returned pointer points to a
+ * portion of the memory mapped audio file. The caller must not call
+ * free() on it.
+ */
+ int (*get_chunk)(long unsigned chunk_num, void *afh_context,
+ const char **buf, size_t *len);
+ /** Deallocate the resources occupied by ->open(). */
+ void (*close)(void *afh_context);
/**
* Write audio file with altered tags, optional.
*
int compute_afhi(const char *path, char *data, size_t size,
int fd, struct afh_info *afhi);
const char *audio_format_name(int);
-void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
- void *map, const char **buf, size_t *len);
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ uint8_t audio_format_id, const void *map, size_t mapsize,
+ const char **buf, size_t *len, void **afh_context);
+void afh_close(void *afh_context, uint8_t audio_format_id);
int32_t afh_get_start_chunk(int32_t approx_chunk_num,
- const struct afh_info *afhi);
+ const struct afh_info *afhi, uint8_t audio_format_id);
void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
void *map, size_t mapsize, char **buf, size_t *len);
void afh_free_header(char *header_buf, uint8_t audio_format_id);
unsigned afh_get_afhi_txt(int audio_format_num, struct afh_info *afhi, char **result);
int afh_rewrite_tags(int audio_format_id, void *map, size_t mapsize,
struct taginfo *tags, int output_fd, const char *filename);
+void set_max_chunk_size(struct afh_info *afhi);
+bool afh_supports_dynamic_chunks(int audio_format_id);
},
{
.name = "aac",
-#if defined(HAVE_MP4V2)
+#if defined(HAVE_FAAD)
.init = aac_afh_init,
#endif
},
if (afl[format].init)
return format;
}
-
}
/** Iterate over each supported audio format. */
}
}
+/**
+ * Tell whether an audio format handler provides chunk tables.
+ *
+ * Each audio format handler either provides a chunk table or supports dynamic
+ * chunks.
+ *
+ * \param audio_format_id Offset in the afl array.
+ *
+ * \return True if dynamic chunks are supported, false if the audio format
+ * handler provides chunk tables.
+ */
+bool afh_supports_dynamic_chunks(int audio_format_id)
+{
+ return afl[audio_format_id].get_chunk;
+}
+
/**
* Guess the audio format judging from filename.
*
/**
* Get one chunk of audio data.
*
+ * This implicitly calls the ->open method of the audio format handler at the
+ * first call.
+ *
* \param chunk_num The number of the chunk to get.
* \param afhi Describes the audio file.
+ * \param audio_format_id Determines the afh.
* \param map The memory mapped audio file.
+ * \param mapsize Passed to the afh's ->open() method.
* \param buf Result pointer.
* \param len The length of the chunk in bytes.
+ * \param afh_context Value/result, determines whether ->open() is called.
*
* Upon return, \a buf will point so memory inside \a map. The returned buffer
* must therefore not be freed by the caller.
+ *
+ * \return Standard.
+ */
+__must_check int afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ uint8_t audio_format_id, const void *map, size_t mapsize,
+ const char **buf, size_t *len, void **afh_context)
+{
+ struct audio_format_handler *afh = afl + audio_format_id;
+
+ if (afh_supports_dynamic_chunks(audio_format_id)) {
+ int ret;
+
+ if (!*afh_context) {
+ ret = afh->open(map, mapsize, afh_context);
+ if (ret < 0)
+ return ret;
+ }
+ ret = afl[audio_format_id].get_chunk(chunk_num, *afh_context,
+ buf, len);
+ if (ret < 0) {
+ afh->close(*afh_context);
+ *afh_context = NULL;
+ }
+ return ret;
+ } else {
+ size_t pos = afhi->chunk_table[chunk_num];
+ *buf = map + pos;
+ *len = get_chunk_len(chunk_num, afhi);
+ return 0;
+ }
+}
+
+/**
+ * Deallocate resources allocated due to dynamic chunk handling.
+ *
+ * This function should be called if afh_get_chunk() was called at least once.
+ * It is OK to call it even for audio formats which do not support dynamic
+ * chunks, in which case the function does nothing.
+ *
+ * \param afh_context As returned from the ->open method of the afh.
+ * \param audio_format_id Determines the afh.
*/
-void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
- void *map, const char **buf, size_t *len)
+void afh_close(void *afh_context, uint8_t audio_format_id)
{
- size_t pos = afhi->chunk_table[chunk_num];
- *buf = map + pos;
- *len = get_chunk_len(chunk_num, afhi);
+ struct audio_format_handler *afh = afl + audio_format_id;
+
+ if (!afh_supports_dynamic_chunks(audio_format_id))
+ return;
+ if (!afh->close)
+ return;
+ if (!afh_context)
+ return;
+ afh->close(afh_context);
}
/**
*
* \param approx_chunk_num Upper bound for the chunk number to return.
* \param afhi Needed for the chunk table.
+ * \param audio_format_id Determines the afh.
*
- * \return The first non-empty chunk <= \a approx_chunk_num.
+ * \return For audio format handlers which support dynamic chunks, the function
+ * returns the given chunk number. Otherwise it returns the first non-empty
+ * chunk <= \a approx_chunk_num.
*
* \sa \ref afh_get_chunk().
*/
int32_t afh_get_start_chunk(int32_t approx_chunk_num,
- const struct afh_info *afhi)
+ const struct afh_info *afhi, uint8_t audio_format_id)
{
int32_t k;
+ if (afh_supports_dynamic_chunks(audio_format_id))
+ return approx_chunk_num;
+
for (k = PARA_MAX(0, approx_chunk_num); k >= 0; k--)
if (get_chunk_len(k, afhi) > 0)
return k;
"%s: %" PRIu32 "\n" /* seconds total */
"%s: %lu: %lu\n" /* chunk time */
"%s: %" PRIu32 "\n" /* num chunks */
+ "%s: %" PRIu32 "\n" /* max chunk size */
"%s: %s\n" /* techinfo */
"%s: %s\n" /* artist */
"%s: %s\n" /* title */
status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
(long unsigned)afhi->chunk_tv.tv_usec,
status_item_list[SI_NUM_CHUNKS], afhi->chunks_total,
+ status_item_list[SI_MAX_CHUNK_SIZE], afhi->max_chunk_size,
status_item_list[SI_TECHINFO], afhi->techinfo? afhi->techinfo : "",
status_item_list[SI_ARTIST], afhi->tags.artist? afhi->tags.artist : "",
status_item_list[SI_TITLE], afhi->tags.title? afhi->tags.title : "",
);
}
+/**
+ * Determine the maximal chunk size by investigating the chunk table.
+ *
+ * \param afhi Value/result.
+ *
+ * This function iterates over the chunk table and sets ->max_chunk_size
+ * accordingly. The function exists only for backward compatibility since as of
+ * version 0.6.0, para_server stores the maximal chunk size in its database.
+ * This function is only called if the database value is zero, indicating that
+ * the file was added by an older server version.
+ */
+void set_max_chunk_size(struct afh_info *afhi)
+{
+ uint32_t n, max = 0, old = 0;
+
+ for (n = 0; n <= afhi->chunks_total; n++) {
+ uint32_t val = afhi->chunk_table[n];
+ /*
+ * If the first chunk is the header, do not consider it for the
+ * calculation of the largest chunk size.
+ */
+ if (n == 0 || (n == 1 && afhi->header_len > 0)) {
+ old = val;
+ continue;
+ }
+ max = PARA_MAX(max, val - old);
+ old = val;
+ }
+ afhi->max_chunk_size = max;
+}
+
/**
* Create a copy of the given file with altered meta tags.
*
#include <regex.h>
#include <sys/types.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
-#include "afh_recv.cmdline.h"
#include "string.h"
#include "fd.h"
#include "afh.h"
long unsigned last_chunk;
struct timeval stream_start;
uint32_t current_chunk;
+ void *afh_context;
};
static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
return ret;
if (x >= pard->afhi.chunks_total)
return -ERRNO_TO_PARA_ERROR(EINVAL);
- pard->first_chunk = afh_get_start_chunk(x, &pard->afhi);
+ pard->first_chunk = afh_get_start_chunk(x, &pard->afhi,
+ pard->audio_format_num);
pard->current_chunk = pard->first_chunk;
return 1;
}
return -E_BTR_NAVAIL;
}
-static void *afh_recv_parse_config(int argc, char **argv)
-{
- struct afh_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
- afh_recv_cmdline_parser(argc, argv, tmp);
- return tmp;
-}
-
-static void afh_recv_free_config(void *conf)
-{
- if (!conf)
- return;
- afh_recv_cmdline_parser_free(conf);
- free(conf);
-}
-
static int afh_recv_open(struct receiver_node *rn)
{
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct private_afh_recv_data *pard;
struct afh_info *afhi;
- char *filename = conf->filename_arg;
-
+ const char *fn = RECV_CMD_OPT_STRING_VAL(AFH, FILENAME, lpr);
+ int32_t bc = RECV_CMD_OPT_INT32_VAL(AFH, BEGIN_CHUNK, lpr);
+ const struct lls_opt_result *r_e = RECV_CMD_OPT_RESULT(AFH, END_CHUNK, lpr);
int ret;
- if (!filename || *filename == '\0')
+ if (!fn || *fn == '\0')
return -E_AFH_RECV_BAD_FILENAME;
rn->private_data = pard = para_calloc(sizeof(*pard));
afhi = &pard->afhi;
- ret = mmap_full_file(filename, O_RDONLY, &pard->map,
+ ret = mmap_full_file(fn, O_RDONLY, &pard->map,
&pard->map_size, &pard->fd);
if (ret < 0)
goto out;
- ret = compute_afhi(filename, pard->map, pard->map_size,
+ ret = compute_afhi(fn, pard->map, pard->map_size,
pard->fd, afhi);
if (ret < 0)
goto out_unmap;
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
if (afhi->chunks_total == 0)
goto out_clear_afhi;
- if (PARA_ABS(conf->begin_chunk_arg) >= afhi->chunks_total)
+ if (PARA_ABS(bc) >= afhi->chunks_total)
goto out_clear_afhi;
- if (conf->begin_chunk_arg >= 0)
- pard->first_chunk = afh_get_start_chunk(
- conf->begin_chunk_arg, &pard->afhi);
+ if (bc >= 0)
+ pard->first_chunk = afh_get_start_chunk(bc, &pard->afhi,
+ pard->audio_format_num);
else
- pard->first_chunk = afh_get_start_chunk(
- afhi->chunks_total + conf->begin_chunk_arg,
- &pard->afhi);
- if (conf->end_chunk_given) {
+ pard->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
+ &pard->afhi, pard->audio_format_num);
+ if (lls_opt_given(r_e)) {
+ int32_t ec = lls_int32_val(0, r_e);
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- if (PARA_ABS(conf->end_chunk_arg) > afhi->chunks_total)
+ if (PARA_ABS(ec) > afhi->chunks_total)
goto out_clear_afhi;
- if (conf->end_chunk_arg >= 0)
- pard->last_chunk = conf->end_chunk_arg;
+ if (ec >= 0)
+ pard->last_chunk = ec;
else
- pard->last_chunk = afhi->chunks_total + conf->end_chunk_arg;
+ pard->last_chunk = afhi->chunks_total + ec;
} else
pard->last_chunk = afhi->chunks_total - 1;
ret = -ERRNO_TO_PARA_ERROR(EINVAL);
clear_afhi(&pard->afhi);
para_munmap(pard->map, pard->map_size);
close(pard->fd);
+ afh_close(pard->afh_context, pard->audio_format_num);
freep(&rn->private_data);
}
struct receiver_node *rn = context;
struct private_afh_recv_data *pard = rn->private_data;
struct afh_info *afhi = &pard->afhi;
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct timeval chunk_time;
int state = generic_recv_pre_select(s, rn);
+ unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
if (state <= 0)
return;
- if (!conf->just_in_time_given) {
+ if (!j_given) {
sched_min_delay(s);
return;
}
static int afh_recv_post_select(__a_unused struct sched *s, void *context)
{
struct receiver_node *rn = context;
- struct afh_recv_args_info *conf = rn->conf;
+ struct lls_parse_result *lpr = rn->lpr;
struct private_afh_recv_data *pard = rn->private_data;
struct btr_node *btrn = rn->btrn;
struct afh_info *afhi = &pard->afhi;
int ret;
char *buf;
- const char *start, *end;
+ const char *start;
size_t size;
struct timeval chunk_time;
+ unsigned j_given = RECV_CMD_OPT_GIVEN(AFH, JUST_IN_TIME, lpr);
+ unsigned H_given = RECV_CMD_OPT_GIVEN(AFH, NO_HEADER, lpr);
ret = btr_node_status(btrn, 0, BTR_NT_ROOT);
if (ret <= 0)
goto out;
- if (pard->first_chunk > 0 && !conf->no_header_given) {
+ if (pard->first_chunk > 0 && !H_given) {
char *header;
afh_get_header(afhi, pard->audio_format_num, pard->map,
pard->map_size, &header, &size);
afh_free_header(header, pard->audio_format_num);
}
}
- if (!conf->just_in_time_given) {
- afh_get_chunk(pard->first_chunk, afhi, pard->map, &start, &size);
- afh_get_chunk(pard->last_chunk, afhi, pard->map, &end, &size);
- end += size;
- PARA_INFO_LOG("adding %td bytes\n", end - start);
- btr_add_output_dont_free(start, end - start, btrn);
+ if (!j_given) {
+ long unsigned n;
+ for (n = pard->first_chunk; n < pard->last_chunk; n++) {
+ ret = afh_get_chunk(n, afhi, pard->audio_format_num,
+ pard->map, pard->map_size, &start, &size,
+ &pard->afh_context);
+ if (ret < 0)
+ goto out;
+ PARA_INFO_LOG("adding %zu bytes\n", size);
+ btr_add_output_dont_free(start, size, btrn);
+ }
ret = -E_RECV_EOF;
goto out;
}
if (ret > 0)
goto out;
}
- afh_get_chunk(pard->current_chunk, afhi, pard->map, &start, &size);
+ ret = afh_get_chunk(pard->current_chunk, afhi,
+ pard->audio_format_num, pard->map,
+ pard->map_size, &start, &size,
+ &pard->afh_context);
+ if (ret < 0)
+ goto out;
PARA_DEBUG_LOG("adding chunk %u\n", pard->current_chunk);
btr_add_output_dont_free(start, size, btrn);
if (pard->current_chunk >= pard->last_chunk) {
return ret;
}
-/**
- * The init function of the afh receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void afh_recv_init(struct receiver *r)
-{
- struct afh_recv_args_info dummy;
-
- afh_init();
- afh_recv_cmdline_parser_init(&dummy);
- r->open = afh_recv_open;
- r->close = afh_recv_close;
- r->pre_select = afh_recv_pre_select;
- r->post_select = afh_recv_post_select;
- r->parse_config = afh_recv_parse_config;
- r->free_config = afh_recv_free_config;
- r->execute = afh_execute;
- r->help = (struct ggo_help)DEFINE_GGO_HELP(afh_recv);
- afh_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_afh_user_data = {
+ .init = afh_init,
+ .open = afh_recv_open,
+ .close = afh_recv_close,
+ .pre_select = afh_recv_pre_select,
+ .post_select = afh_recv_post_select,
+ .execute = afh_execute,
+};
#include <signal.h>
#include <fnmatch.h>
#include <osl.h>
+#include <lopsub.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
+#include <lopsub.h>
-#include "server.cmdline.h"
+#include "server.lsg.h"
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
*(uint32_t *)buf = afs_socket_cookie;
*(int *)(buf + sizeof(afs_socket_cookie)) = query_shmid;
- ret = connect_local_socket(conf.afs_socket_arg);
+ ret = connect_local_socket(OPT_STRING_VAL(AFS_SOCKET));
if (ret < 0)
goto out;
fd = ret;
}
/**
- * Send a callback request passing an options structure and an argument vector.
+ * Wrapper for send_callback_request() which passes a lopsub parse result.
*
- * \param options pointer to an arbitrary data structure.
- * \param argc Argument count.
- * \param argv Standard argument vector.
* \param f The callback function.
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
+ * \param cmd Needed for (de-)serialization.
+ * \param lpr Must match cmd.
+ * \param private_result_data Passed to send_callback_request().
*
- * Some command handlers pass command-specific options to a callback, together
- * with a list of further arguments (often a list of audio files). This
- * function allows to pass an arbitrary structure (given as an osl object) and
- * a usual argument vector to the specified callback.
+ * This function serializes the parse result given by the lpr pointer into a
+ * buffer. The buffer is sent as the query to the afs process with the callback
+ * mechanism.
*
- * \return The return value of the underlying call to \ref
- * send_callback_request().
- *
- * \sa send_standard_callback_request(), send_callback_request().
+ * \return The return value of the underlying call to send_callback_request().
*/
-int send_option_arg_callback_request(struct osl_object *options,
- int argc, char * const * const argv, afs_callback *f,
- callback_result_handler *result_handler,
- void *private_result_data)
+int send_lls_callback_request(afs_callback *f,
+ const struct lls_command * const cmd,
+ struct lls_parse_result *lpr, void *private_result_data)
{
- char *p;
- int i, ret;
- struct osl_object query = {.size = options? options->size : 0};
-
- for (i = 0; i < argc; i++)
- query.size += strlen(argv[i]) + 1;
- query.data = para_malloc(query.size);
- p = query.data;
- if (options) {
- memcpy(query.data, options->data, options->size);
- p += options->size;
- }
- for (i = 0; i < argc; i++) {
- strcpy(p, argv[i]); /* OK */
- p += strlen(argv[i]) + 1;
- }
- ret = send_callback_request(f, &query, result_handler,
- private_result_data);
- free(query.data);
- return ret;
-}
+ struct osl_object query;
+ char *buf = NULL;
+ int ret = lls_serialize_parse_result(lpr, cmd, &buf, &query.size);
-/**
- * Send a callback request with an argument vector only.
- *
- * \param argc The same meaning as in send_option_arg_callback_request().
- * \param argv The same meaning as in send_option_arg_callback_request().
- * \param f The same meaning as in send_option_arg_callback_request().
- * \param result_handler See \ref send_callback_request.
- * \param private_result_data See \ref send_callback_request.
- *
- * This is similar to send_option_arg_callback_request(), but no options buffer
- * is passed to the parent process.
- *
- * \return The return value of the underlying call to
- * send_option_arg_callback_request().
- */
-int send_standard_callback_request(int argc, char * const * const argv,
- afs_callback *f, callback_result_handler *result_handler,
- void *private_result_data)
-{
- return send_option_arg_callback_request(NULL, argc, argv, f, result_handler,
+ assert(ret >= 0);
+ query.data = buf;
+ ret = send_callback_request(f, &query, afs_cb_result_handler,
private_result_data);
+ free(buf);
+ return ret;
}
static int action_if_pattern_matches(struct osl_row *row, void *data)
struct pattern_match_data *pmd = data;
struct osl_object name_obj;
const char *p, *name;
- int ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num, &name_obj));
- const char *pattern_txt = (const char *)pmd->patterns.data;
+ int i, ret;
+ ret = osl(osl_get_object(pmd->table, row, pmd->match_col_num,
+ &name_obj));
if (ret < 0)
return ret;
name = (char *)name_obj.data;
if ((!name || !*name) && (pmd->pm_flags & PM_SKIP_EMPTY_NAME))
return 1;
- if (pmd->patterns.size == 0 &&
- (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING)) {
- pmd->num_matches++;
- return pmd->action(pmd->table, row, name, pmd->data);
+ if (lls_num_inputs(pmd->lpr) == 0) {
+ if (pmd->pm_flags & PM_NO_PATTERN_MATCHES_EVERYTHING) {
+ pmd->num_matches++;
+ return pmd->action(pmd->table, row, name, pmd->data);
+ }
}
- for (p = pattern_txt; p < pattern_txt + pmd->patterns.size;
- p += strlen(p) + 1) {
+ i = pmd->input_skip;
+ for (;;) {
+ if (i >= lls_num_inputs(pmd->lpr))
+ break;
+ p = lls_input(i, pmd->lpr);
ret = fnmatch(p, name, pmd->fnmatch_flags);
- if (ret == FNM_NOMATCH)
- continue;
- if (ret)
- return -E_FNMATCH;
- ret = pmd->action(pmd->table, row, name, pmd->data);
- if (ret >= 0)
- pmd->num_matches++;
- return ret;
+ if (ret != FNM_NOMATCH) {
+ if (ret != 0)
+ return -E_FNMATCH;
+ ret = pmd->action(pmd->table, row, name, pmd->data);
+ if (ret >= 0)
+ pmd->num_matches++;
+ return ret;
+
+ }
+ i++;
}
return 1;
}
static int com_select_callback(struct afs_callback_arg *aca)
{
- const char *arg = aca->query.data;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+ const char *arg;
int num_admissible, ret;
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ arg = lls_input(0, aca->lpr);
ret = clear_score_table();
if (ret < 0) {
- para_printf(&aca->pbout, "could not clear score table: %s\n",
- para_strerror(-ret));
- return ret;
+ para_printf(&aca->pbout, "could not clear score table\n");
+ goto free_lpr;
}
if (current_play_mode == PLAY_MODE_MOOD)
close_current_mood();
out:
para_printf(&aca->pbout, "activated %s (%d admissible files)\n",
current_mop? current_mop : "dummy mood", num_admissible);
+free_lpr:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_select(struct command_context *cc)
+static int com_select(struct command_context *cc, struct lls_parse_result *lpr)
{
- struct osl_object query;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SELECT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
- if (cc->argc != 2)
- return -E_AFS_SYNTAX;
- query.data = cc->argv[1];
- query.size = strlen(cc->argv[1]) + 1;
- return send_callback_request(com_select_callback, &query,
- &afs_cb_result_handler, cc);
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_select_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(select);
-static void init_admissible_files(char *arg)
+static void init_admissible_files(const char *arg)
{
if (activate_mood_or_playlist(arg, NULL) < 0)
activate_mood_or_playlist(NULL, NULL); /* always successful */
static int setup_command_socket_or_die(void)
{
int ret, socket_fd;
- char *socket_name = conf.afs_socket_arg;
+ const char *socket_name = OPT_STRING_VAL(AFS_SOCKET);
unlink(socket_name);
- ret = create_local_socket(socket_name, 0);
+ ret = create_local_socket(socket_name);
if (ret < 0) {
- ret = create_local_socket(socket_name,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IROTH);
- if (ret < 0) {
- PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret),
- socket_name);
- exit(EXIT_FAILURE);
- }
+ PARA_EMERG_LOG("%s: %s\n", para_strerror(-ret), socket_name);
+ exit(EXIT_FAILURE);
}
socket_fd = ret;
PARA_INFO_LOG("listening on socket %s (fd %d)\n", socket_name,
static void get_database_dir(void)
{
if (!database_dir) {
- if (conf.afs_database_dir_given)
- database_dir = para_strdup(conf.afs_database_dir_arg);
+ if (OPT_GIVEN(AFS_DATABASE_DIR))
+ database_dir = para_strdup(OPT_STRING_VAL(AFS_DATABASE_DIR));
else {
char *home = para_homedir();
database_dir = make_message(
return ret;
buf[n] = '\0';
if (strcmp(buf, "new"))
- return -E_BAD_CMD;
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
return open_next_audio_file();
}
goto out_close;
PARA_INFO_LOG("server_socket: %d, afs_socket_cookie: %u\n",
server_socket, (unsigned) cookie);
- init_admissible_files(conf.afs_initial_mode_arg);
+ init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
register_command_task(cookie, &s);
s.default_timeout.tv_sec = 0;
s.default_timeout.tv_usec = 999 * 1000;
return ret;
}
-int com_init(struct command_context *cc)
+static int com_init(struct command_context *cc, struct lls_parse_result *lpr)
{
int i, j, ret;
uint32_t table_mask = (1 << (NUM_AFS_TABLES + 1)) - 1;
struct osl_object query = {.data = &table_mask,
.size = sizeof(table_mask)};
+ unsigned num_inputs = lls_num_inputs(lpr);
ret = make_database_dir();
if (ret < 0)
return ret;
- if (cc->argc != 1) {
+ if (num_inputs > 0) {
table_mask = 0;
- for (i = 1; i < cc->argc; i++) {
+ for (i = 0; i < num_inputs; i++) {
for (j = 0; j < NUM_AFS_TABLES; j++) {
struct afs_table *t = &afs_tables[j];
- if (strcmp(cc->argv[i], t->name))
+ if (strcmp(lls_input(i, lpr), t->name))
continue;
table_mask |= (1 << j);
break;
return send_callback_request(com_init_callback, &query,
afs_cb_result_handler, cc);
}
+EXPORT_SERVER_CMD_HANDLER(init);
-/**
- * Flags for the check command.
- *
- * \sa com_check().
- */
-enum com_check_flags {
- /** Check the audio file table. */
- CHECK_AFT = 1,
- /** Check the mood table. */
- CHECK_MOODS = 2,
- /** Check the playlist table. */
- CHECK_PLAYLISTS = 4,
- /** Check the attribute table against the audio file table. */
- CHECK_ATTS = 8
-};
-
-int com_check(struct command_context *cc)
+static int com_check(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- int i, ret;
+ const struct lls_opt_result *r_a = SERVER_CMD_OPT_RESULT(CHECK, AFT, lpr);
+ const struct lls_opt_result *r_A = SERVER_CMD_OPT_RESULT(CHECK, ATTRIBUTE, lpr);
+ const struct lls_opt_result *r_m = SERVER_CMD_OPT_RESULT(CHECK, MOOD, lpr);
+ const struct lls_opt_result *r_p = SERVER_CMD_OPT_RESULT(CHECK, PLAYLIST, lpr);
+ bool noopt = !lls_opt_given(r_a) && !lls_opt_given(r_A)
+ && !lls_opt_given(r_m) && !lls_opt_given(r_p);
+ 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, "-a")) {
- flags |= CHECK_AFT;
- continue;
- }
- if (!strcmp(arg, "-A")) {
- flags |= CHECK_ATTS;
- continue;
- }
- if (!strcmp(arg, "-p")) {
- flags |= CHECK_PLAYLISTS;
- continue;
- }
- if (!strcmp(arg, "-m")) {
- flags |= CHECK_MOODS;
- continue;
- }
- return -E_AFS_SYNTAX;
- }
- if (i < cc->argc)
- return -E_AFS_SYNTAX;
- if (!flags)
- flags = ~0U;
- if (flags & CHECK_AFT) {
+ if (noopt || lls_opt_given(r_a)) {
ret = send_callback_request(aft_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_ATTS) {
+ if (noopt || lls_opt_given(r_A)) {
ret = send_callback_request(attribute_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_PLAYLISTS) {
+ if (noopt || lls_opt_given(r_p)) {
ret = send_callback_request(playlist_check_callback,
NULL, afs_cb_result_handler, cc);
if (ret < 0)
return ret;
}
- if (flags & CHECK_MOODS) {
+ if (noopt || lls_opt_given(r_m)) {
ret = send_callback_request(mood_check_callback, NULL,
afs_cb_result_handler, cc);
if (ret < 0)
}
return 1;
}
+EXPORT_SERVER_CMD_HANDLER(check);
/**
* The afs event dispatcher.
+++ /dev/null
-BN: afs
-SF: afs.c aft.c attribute.c
-SN: list of afs commands
-TM: mood lyr img pl
----
-N: add
-P: AFS_READ | AFS_WRITE
-D: Add or update audio files.
-U: add [-a] [-l] [-f] [-v] path...
-H: Each path must be absolute and refer to either an audio file, or a
-H: directory. In case of a directory, all audio files in that directory
-H: are added recursively. Only absolute paths are accepted.
-H:
-H: Options:
-H:
-H: -a Add all files. The default is to add only files ending in a
-H: known suffix for a supported audio format.
-H:
-H: -l Add files lazily. If the path already exists in the
-H: database, skip this file. This operation is really cheap. Useful
-H: to update large directories after some files have been added or
-H: deleted.
-H:
-H: -f Force adding/updating. Recompute the audio format handler data
-H: even if a file with the same path and the same hash value exists.
-H:
-H: -v Verbose mode. Print what is being done.
----
-N: init
-P: AFS_READ | AFS_WRITE
-D: Initialize the osl tables for the audio file selector.
-U: init [table_name ...]
-H: When invoked without arguments, this command creates all tables. Otherwise
-H: only the tables given by table_name... are created.
----
-N: ls
-P: AFS_READ
-D: List audio files.
-U: ls [-l=mode] [-p] [-a] [-r] [-d] [-s=order] [pattern...]
-H: Print a list of all audio files matching pattern.
-H:
-H: Options:
-H:
-H: -l=mode Change listing mode. Defaults to short listing if not given.
-H:
-H: Available modes:
-H: s: short listing mode
-H: l: long listing mode (equivalent to -l)
-H: v: verbose listing mode
-H: p: parser-friendly mode
-H: m: mbox listing mode
-H: c: chunk-table listing mode
-H:
-H: -F List full paths. If this option is not specified, only the basename
-H: of each file is printed.
-H: -p Synonym for -F. Deprecated.
-H:
-H: -b Print only the basename of each matching file. This is the default, so
-H: the option is currently a no-op. It is recommended to specify this option,
-H: though, as the default might change in a future release.
-H:
-H: -a List only files that are admissible with respect to the current mood or
-H: playlist.
-H:
-H: -r Reverse sort order.
-H:
-H: -d Print dates as seconds after the epoch.
-H:
-H: -s=order
-H: Change sort order. Defaults to alphabetical path sort if not given.
-H:
-H: Possible values for order:
-H: p: by path
-H: l: by last played time
-H: s: by score (implies -a)
-H: n: by num played count
-H: f: by frequency
-H: c: by number of channels
-H: i: by image id
-H: y: by lyrics id
-H: b: by bit rate
-H: d: by duration
-H: a: by audio format
----
-N: lsatt
-P: AFS_READ
-D: List attributes.
-U: lsatt [-i] [-l] [-r] [pattern]
-H: Print the list of all defined attributes which match the given
-H: pattern. If no pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i Sort attributes by id. The default is to sort alphabetically by name.
-H:
-H: -l Print a long listing containing both identifier and attribute name. The
-H: default is to print only the name.
-H:
-H: -r Reverse sort order.
----
-N: setatt
-P: AFS_READ | AFS_WRITE
-D: Set attribute(s) for all files matching a pattern.
-U: setatt attribute{+|-}... pattern
-H: Set ('+') or unset ('-') the given attributes for all audio files matching
-H: pattern. Example:
-H:
-H: setatt rock+ punk+ pop- '*foo.mp3'
-H:
-H: sets the 'rock' and the 'punk' attribute and unsets the 'pop'
-H: attribute of all files ending with 'foo.mp3'.
----
-N: addatt
-P: AFS_READ | AFS_WRITE
-D: Add new attribute(s).
-U: addatt attribute1...
-H: This adds new attributes to the attribute table. At most 64
-H: attributes may be defined.
----
-N: mvatt
-P: AFS_READ | AFS_WRITE
-D: Rename an attribute.
-U: mvatt old new
-H: Rename attribute old to new.
----
-N: check
-P: AFS_READ
-D: Run integrity checks against osl tables.
-U: check [-a] [-A] [-m] [-p]
-H: Check the audio file table, the attribute table, the mood definitions
-H: and all defined playlists. Report any inconsistencies.
-H:
-H: Options:
-H:
-H: -a Run audio file table checks. Checks for entries in the audio file
-H: table which are not present in the file system. Moreover, it checks
-H: whether the lyrics id and all entries in the audio file table are
-H: valid.
-H:
-H: -A Check the attribute table against the afs attribute bitmask of
-H: each audio file in the audio file table. Reports audio files
-H: whose attribute bitmask is invalid, i.e., has a bit set which
-H: does not correspond to any attribute of the attribute table.
-H:
-H: -m Run syntax checks on all defined moods in the mood table.
-H:
-H: -p Check all playlists for lines that correspond to files not contained
-H: in the audio file table.
-H:
-H: If called without arguments, all checks are run.
----
-N: rmatt
-P: AFS_READ | AFS_WRITE
-D: Remove attribute(s).
-U: rmatt pattern...
-H: Remove all attributes matching any given pattern. All information
-H: about this attribute in the audio file table is lost.
----
-N: rm
-P: AFS_READ | AFS_WRITE
-D: Remove entries from the audio file table.
-U: rm [-v] [-f] [-p] pattern...
-H: Delete all entries in the audio file table that match any given pattern. Note
-H: that this affects the table entries only; the command won't touch your audio
-H: files on disk.
-H:
-H: Options:
-H:
-H: -v Verbose mode. Explain what is being done.
-H:
-H: -f Force mode. Ignore nonexistent files. Don't complain if nothing
-H: was removed.
-H:
-H: -p Pathname match. Match a slash in the path only with a slash
-H: in pattern and not by an asterisk (*) or a question mark
-H: (?) metacharacter, nor by a bracket expression ([]) containing
-H: a slash (see fnmatch(3)).
----
-N: touch
-P: AFS_READ | AFS_WRITE
-D: Manipulate the afs entry of audio files.
-U: touch [-n=numplayed] [-l=lastplayed] [-y=lyrics_id] [-i=image_id] [-a=amp] [-v] [-p] pattern
-H: If no option is given, the lastplayed field is set to the current time
-H: and the value of the numplayed field is increased by one. Otherwise,
-H: only the given options are taken into account.
-H:
-H: Options:
-H:
-H: -n Set the numplayed count, i.e. the number of times this audio
-H: file was selected for streaming so far.
-H:
-H: -l Set the lastplayed time, i.e. the last time this audio file was
-H: selected for streaming. The argument must be a number of seconds
-H: since the epoch. Example:
-H:
-H: touch -l=$(date +%s) file
-H:
-H: sets the lastplayed time of 'file' to the current time.
-H:
-H: -y Set the lyrics ID which specifies the lyrics data file associated
-H: with the audio file.
-H:
-H: -i Like -y, but sets the image ID.
-H:
-H: -a Set the amplification value (0-255). This determines a scaling
-H: factor by which the amplitude should be multiplied in order to
-H: normalize the volume of the audio file. A value of zero means
-H: no amplification, 64 means the amplitude should be multiplied
-H: by a factor of two, 128 by three and so on.
-H:
-H: This value is used by the amp filter.
-H:
-H: -v Verbose mode. Explain what is being done.
-H:
-H: -p Pathname match. Match a slash in the path only with a slash
-H: in pattern and not by an asterisk (*) or a question mark
-H: (?) metacharacter, nor by a bracket expression ([]) containing
-H: a slash (see fnmatch(3)).
----
-N: cpsi
-P: AFS_READ | AFS_WRITE
-D: Copy audio file selector info.
-U: cpsi [-a] [-y] [-i] [-l] [-n] [-v] source pattern...
-H: If no option, or only the -v option is given, all fields of the
-H: audio file selector info are copied to all files matching pattern.
-H: Otherwise, only the given options are taken into account.
-H:
-H: Options:
-H:
-H: -a Copy attributes.
-H:
-H: -y Copy the lyrics id.
-H:
-H: -i Copy the image id.
-H:
-H: -l Copy the lastplayed time.
-H:
-H: -n Copy the numplayed count.
-H:
-H: -v Verbose mode.
----
-N: select
-P: AFS_READ | AFS_WRITE
-D: Activate a mood or a playlist.
-U: select specifier/name
-H: The specifier is either 'm' or 'p' to indicate whether a playlist or
-H: a mood should be activated. Example:
-H:
-H: select m/foo
-H:
-H: loads the mood named 'foo'.
----
-T: add
-N: add@member@
-O: int com_add@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Add stdin as a blob to the @member@ table.
-U: add@member@ @member@_name
-H: Read from stdin and ask the audio file selector to create a blob in the
-H: corresponding osl table. If the named blob already exists, it gets replaced
-H: with the new data.
----
-T: cat
-N: cat@member@
-O: int com_cat@member@(struct command_context *cc);
-P: AFS_READ
-D: Dump the contents of a blob of type @member@ to stdout.
-U: cat@member@ @member@_name
-H: Retrieve the named blob and write it to stdout.
----
-T: ls
-N: ls@member@
-O: int com_ls@member@(struct command_context *cc);
-P: AFS_READ
-D: List blobs of type @member@ which match a pattern.
-U: ls@member@ [-i] [-l] [-r] [pattern]
-H: Print the list of all blobs which match the given pattern. If no
-H: pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i Sort by identifier. The default is to sort alphabetically by name.
-H:
-H: -l Print identifier and name. The default is to print only the name.
-H:
-H: -r Reverse sort order.
----
-T: rm
-N: rm@member@
-O: int com_rm@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Remove blob(s) of type @member@ from the @member@ table.
-U: rm@member@ pattern...
-H: Remove all blobs whose name matches any of the given patterns.
----
-T: mv
-N: mv@member@
-O: int com_mv@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Rename a blob of type @member@.
-U: mv@member@ source_@member@_name dest_@member@_name
-H: Rename the blob identified by the source blob name to the destination blob
-H: name. The command fails if the source does not exist, or if the destination
-H: already exists.
unsigned pm_flags;
/** This value is passed verbatim to fnmatch(). */
int fnmatch_flags;
- /** Null-terminated array of patterns. */
- struct osl_object patterns;
+ /** Obtained by deserializing the query buffer in the callback. */
+ struct lls_parse_result *lpr;
+ /** Do not try to match the first inputs of lpr */
+ unsigned input_skip;
/** Data pointer passed to the action function. */
void *data;
/** Gets increased by one for each match. */
struct osl_object query;
/** Will be written on band SBD_OUTPUT, fully buffered. */
struct para_buffer pbout;
+ struct lls_parse_result *lpr;
};
/**
int send_callback_request(afs_callback *f, struct osl_object *query,
callback_result_handler *result_handler,
void *private_result_data);
-int send_option_arg_callback_request(struct osl_object *options,
- int argc, char * const * const argv, afs_callback *f,
- callback_result_handler *result_handler,
- void *private_result_data);
-int send_standard_callback_request(int argc, char * const * const argv,
- afs_callback *f, callback_result_handler *result_handler,
- void *private_result_data);
+int send_lls_callback_request(afs_callback *f,
+ const struct lls_command * const cmd,
+ struct lls_parse_result *lpr, void *private_result_data);
int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
int for_each_matching_row(struct pattern_match_data *pmd);
#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. */
CHUNKS_TOTAL_OFFSET = 20,
/** The length of the audio file header (4 bytes). */
HEADER_LEN_OFFSET = 24,
- /** Was: The start of the audio file header (4 bytes). */
- AFHI_UNUSED2_OFFSET = 28,
+ /** Size of the largest chunk in bytes. (4 bytes). */
+ AFHI_MAX_CHUNK_SIZE_OFFSET = 28,
/** The seconds part of the chunk time (4 bytes). */
CHUNK_TV_TV_SEC_OFFSET = 32,
/** The microseconds part of the chunk time (4 bytes). */
write_u8(buf + AFHI_CHANNELS_OFFSET, afhi->channels);
write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total);
write_u32(buf + HEADER_LEN_OFFSET, afhi->header_len);
- write_u32(buf + AFHI_UNUSED2_OFFSET, 0);
+ write_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET, afhi->max_chunk_size);
write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec);
write_u32(buf + CHUNK_TV_TV_USEC_OFFSET, afhi->chunk_tv.tv_usec);
p = buf + AFHI_INFO_STRING_OFFSET;
afhi->channels = read_u8(buf + AFHI_CHANNELS_OFFSET);
afhi->chunks_total = read_u32(buf + CHUNKS_TOTAL_OFFSET);
afhi->header_len = read_u32(buf + HEADER_LEN_OFFSET);
+ afhi->max_chunk_size = read_u32(buf + AFHI_MAX_CHUNK_SIZE_OFFSET);
afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET);
afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC_OFFSET);
afhi->techinfo = (char *)buf + AFHI_INFO_STRING_OFFSET;
afhi->tags.comment = afhi->tags.album + strlen(afhi->tags.album) + 1;
}
+/* Only used for saving the chunk table, but not for loading. */
static unsigned sizeof_chunk_table(struct afh_info *afhi)
{
- if (!afhi)
+ if (!afhi || !afhi->chunk_table)
return 0;
return 4 * (afhi->chunks_total + 1);
}
-static uint32_t save_chunk_table(struct afh_info *afhi, char *buf)
+static void save_chunk_table(struct afh_info *afhi, char *buf)
{
- int i;
- uint32_t max = 0, old = 0;
-
- for (i = 0; i <= afhi->chunks_total; i++) {
- uint32_t val = afhi->chunk_table[i];
- write_u32(buf + 4 * i, val);
- /*
- * If the first chunk is the header, do not consider it for the
- * calculation of the largest chunk size.
- */
- if (i == 0 || (i == 1 && afhi->header_len > 0)) {
- old = val;
- continue;
- }
- max = PARA_MAX(max, val - old);
- old = val;
- }
- return max;
+ uint32_t n;
+
+ if (!afhi->chunk_table)
+ return;
+ for (n = 0; n <= afhi->chunks_total; n++)
+ write_u32(buf + 4 * n, afhi->chunk_table[n]);
}
-static void load_chunk_table(struct afh_info *afhi, char *buf)
+static void load_chunk_table(struct afh_info *afhi, const struct osl_object *ct)
{
int i;
+ size_t sz;
- afhi->chunk_table = para_malloc(sizeof_chunk_table(afhi));
- for (i = 0; i <= afhi->chunks_total; i++)
- afhi->chunk_table[i] = read_u32(buf + 4 * i);
+ if (!ct->data || ct->size < 4) {
+ afhi->chunk_table = NULL;
+ return;
+ }
+ sz = PARA_MIN(((size_t)afhi->chunks_total + 1) * 4, ct->size) + 1;
+ afhi->chunk_table = para_malloc(sz);
+ for (i = 0; i <= afhi->chunks_total && i * 4 + 3 < ct->size; i++)
+ afhi->chunk_table[i] = read_u32(ct->data + 4 * i);
}
/**
goto err;
buf = shm_afd;
buf += sizeof(*afd);
- afd->max_chunk_size = save_chunk_table(&afd->afhi, buf);
+ save_chunk_table(&afd->afhi, buf);
+ if (afd->afhi.max_chunk_size == 0) { /* v0.5.x on-disk afhi */
+ set_max_chunk_size(&afd->afhi);
+ PARA_NOTICE_LOG("max chunk size unset, re-add required\n");
+ } else
+ PARA_INFO_LOG("using max chunk size from afhi\n");
+ afd->max_chunk_size = afd->afhi.max_chunk_size;
*(struct audio_file_data *)shm_afd = *afd;
shm_detach(shm_afd);
return shmid;
{
void *shm_afd;
int ret;
+ struct osl_object obj;
ret = shm_attach(shmid, ATTACH_RO, &shm_afd);
if (ret < 0)
return ret;
+ ret = shm_size(shmid, &obj.size);
+ if (ret < 0)
+ goto detach;
*afd = *(struct audio_file_data *)shm_afd;
- load_chunk_table(&afd->afhi, shm_afd + sizeof(*afd));
+ obj.data = shm_afd + sizeof(*afd);
+ obj.size -= sizeof(*afd);
+ load_chunk_table(&afd->afhi, &obj);
+ ret = 1;
+detach:
shm_detach(shm_afd);
- return 1;
+ return ret;
}
static int get_local_time(uint64_t *seconds, char *buf, size_t size,
}
static void write_filename_items(struct para_buffer *b, const char *path,
- unsigned flags)
+ bool basename)
{
char *val;
- if (!(flags & LS_FLAG_FULL_PATH)) {
+ if (basename) {
WRITE_STATUS_ITEM(b, SI_BASENAME, "%s\n", path);
return;
}
(long unsigned) d->afhi.chunk_tv.tv_usec
);
buf = chunk_table_obj.data;
- for (i = 0; i <= d->afhi.chunks_total; i++)
+ for (
+ i = 0;
+ i <= d->afhi.chunks_total && 4 * i + 3 < chunk_table_obj.size;
+ i++
+ )
para_printf(b, "%u ", (unsigned) read_u32(buf + 4 * i));
para_printf(b, "\n");
ret = 1;
return ret;
}
-static void write_score(struct para_buffer *b, struct ls_data *d,
- struct ls_options *opts)
-{
- if (!(opts->flags & LS_FLAG_ADMISSIBLE_ONLY)) /* no score*/
- 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];
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) {
+ if (lls_opt_given(r_a))
para_printf(b, "%*li ", opts->widths.score_width,
d->score);
- }
para_printf(b,
"%s " /* attributes */
"%*u " /* amp */
last_played_time,
bn? bn : "?");
}
- write_filename_items(b, d->path, opts->flags);
- write_score(b, d, opts);
+ 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;
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);
+ WRITE_STATUS_ITEM(b, SI_MAX_CHUNK_SIZE, "%" PRIu32 "\n",
+ afhi->max_chunk_size);
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);
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;
}
/**
d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
AFTCOL_CHUNKS, &chunk_table_obj));
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ if (!afh_supports_dynamic_chunks(d->afsi.audio_format_id))
+ return ret;
+ PARA_INFO_LOG("no chunk table for %s\n", d->path);
+ chunk_table_obj.data = NULL;
+ chunk_table_obj.size = 0;
+ } else {
+ PARA_INFO_LOG("chunk table: %zu bytes\n", chunk_table_obj.size);
+ }
ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
if (ret < 0)
goto out;
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
afd->audio_format_id = d->afsi.audio_format_id;
- load_chunk_table(&afd->afhi, chunk_table_obj.data);
+ load_chunk_table(&afd->afhi, &chunk_table_obj);
aced.aft_row = current_aft_row;
aced.old_afsi = &d->afsi;
/*
ret = save_afd(afd);
out:
free(afd->afhi.chunk_table);
- osl_close_disk_object(&chunk_table_obj);
+ if (chunk_table_obj.data)
+ osl_close_disk_object(&chunk_table_obj);
if (ret < 0) {
PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
ret = score_delete(current_aft_row);
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);
static int com_ls_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LS);
struct ls_options *opts = aca->query.data;
- char *p, *pattern_start = (char *)aca->query.data + sizeof(*opts);
int i = 0, ret;
time_t current_time;
+ const struct lls_opt_result *r_r;
+
+ 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 (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)
+ 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) {
- ret = opts->num_patterns > 0? -E_NO_MATCH : 0;
+ 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,
&aca->pbout, current_time);
goto out;
}
out:
+ 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;
- 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;
- }
- /*
- * Compatibility: Prior to 0.5.5 it was necessary to specify
- * the listing mode without the '=' character as in -lv, for
- * example. Now the variant with '=' is preferred and
- * documented but we still accept the old way to specify the
- * listing mode.
- *
- * Support for the legacy syntax can be dropped at 0.6.0
- * or later.
- */
- if (!strncmp(arg, "-l", 2)) {
- arg += 2;
- if (*arg == '=')
- arg++;
- switch (*arg) {
- case 's':
- mode = LS_MODE_SHORT;
- continue;
- case 'l':
- case '\0':
- 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") || !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;
- }
- 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;
}
- /* The compatibility remark above applies also to -s. */
- if (!strncmp(arg, "-s", 2)) {
- arg += 2;
- if (*arg == '=')
- arg++;
- switch (*arg) {
- 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;
- return 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.
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,
&objs[AFTCOL_AFHI]));
if (ret < 0)
goto out;
+ /* truncate the file to size zero if there is no chunk table */
ret = osl(osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
&objs[AFTCOL_CHUNKS]));
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);
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};
-
- 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;
-};
-
static int touch_audio_file(__a_unused struct osl_table *table,
struct osl_row *row, const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- struct com_touch_options *cto = aca->query.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 = cto->num_played < 0 && cto->last_played < 0 &&
- cto->lyrics_id < 0 && cto->image_id < 0 && 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) {
para_printf(&aca->pbout, "cannot touch %s\n", name);
if (no_options) {
new_afsi.num_played++;
new_afsi.last_played = time(NULL);
- if (cto->flags & TOUCH_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "%s: num_played = %u, "
"last_played = now()\n", name,
new_afsi.num_played);
} else {
- if (cto->flags & TOUCH_FLAG_VERBOSE)
+ 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);
- if (cto->lyrics_id >= 0)
- new_afsi.lyrics_id = cto->lyrics_id;
- if (cto->image_id >= 0)
- new_afsi.image_id = cto->image_id;
- if (cto->num_played >= 0)
- new_afsi.num_played = cto->num_played;
- if (cto->last_played >= 0)
- new_afsi.last_played = cto->last_played;
- if (cto->amp >= 0)
- new_afsi.amp = cto->amp;
}
save_afsi(&new_afsi, &obj); /* in-place update */
aced.aft_row = row;
static int com_touch_callback(struct afs_callback_arg *aca)
{
+ 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 com_touch_options *cto = aca->query.data;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {
- .data = (char *)aca->query.data
- + sizeof(struct com_touch_options),
- .size = aca->query.size
- - sizeof(struct com_touch_options)
- },
.data = aca,
.action = touch_audio_file
};
- if (cto->image_id >= 0) {
- ret = img_get_name_by_id(cto->image_id, NULL);
+
+ ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+
+ 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: %d\n",
- cto->image_id);
+ para_printf(&aca->pbout, "invalid image ID: %u\n", id);
return ret;
}
}
- if (cto->lyrics_id >= 0) {
- ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+ 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: %d\n",
- cto->lyrics_id);
+ para_printf(&aca->pbout, "invalid lyrics ID: %u\n", id);
return ret;
}
}
- if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
+ 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;
}
-int com_touch(struct command_context *cc)
+static int com_touch(struct command_context *cc, struct lls_parse_result *lpr)
{
- 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;
-
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH);
+ int ret;
+ 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 (!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;
- }
- if (!strcmp(arg, "-v")) {
- cto.flags |= TOUCH_FLAG_VERBOSE;
- continue;
- }
- break; /* non-option starting with dash */
+ 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;
- return send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_touch_callback, afs_cb_result_handler, cc);
-}
-
-/** Flags for com_rm(). */
-enum rm_flags {
- /** -v */
- RM_FLAG_VERBOSE = 1,
- /** -f */
- RM_FLAG_FORCE = 2,
- /** -p */
- RM_FLAG_FNM_PATHNAME = 4
-};
+ 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 afs_callback_arg *aca = data;
- uint32_t flags =*(uint32_t *)aca->query.data;
+ bool v_given = SERVER_CMD_OPT_GIVEN(RM, VERBOSE, aca->lpr);
int ret;
- if (flags & RM_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "removing %s\n", name);
ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, row);
if (ret < 0)
static int com_rm_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RM);
int ret;
- uint32_t flags = *(uint32_t *)aca->query.data;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.match_col_num = AFTCOL_PATH,
- .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
- .size = aca->query.size - sizeof(uint32_t)},
.data = aca,
.action = remove_audio_file
};
- if (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)
goto out;
if (pmd.num_matches == 0) {
- if (!(flags & RM_FLAG_FORCE))
+ if (!f_given)
ret = -E_NO_MATCH;
- } else if (flags & RM_FLAG_VERBOSE)
+ } else if (v_given)
para_printf(&aca->pbout, "removed %u file(s)\n",
pmd.num_matches);
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;
+ 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;
- return send_option_arg_callback_request(&query, cc->argc - i,
- cc->argv + i, com_rm_callback, afs_cb_result_handler, cc);
+ 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;
/** 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)
if (ret < 0)
return ret;
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)
+ if (v_given)
para_printf(&cad->aca->pbout, "copied afsi to %s\n", name);
aced.aft_row = row;
aced.old_afsi = &old_afsi;
static int com_cpsi_callback(struct afs_callback_arg *aca)
{
- struct cpsi_action_data cad = {
- .flags = *(unsigned *)aca->query.data,
- .aca = aca
- };
+ 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 *)aca->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 = aca->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);
if (ret < 0)
goto out;
if (pmd.num_matches > 0) {
- if (cad.flags & CPSI_FLAG_VERBOSE)
+ if (v_given)
para_printf(&aca->pbout, "updated afsi of %u file(s)\n",
pmd.num_matches);
} else
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;
- 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;
- return send_option_arg_callback_request(&options, cc->argc - i,
- cc->argv + i, com_cpsi_callback, afs_cb_result_handler, cc);
+ 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;
static int com_setatt_callback(struct afs_callback_arg *aca)
{
- char *p;
- int ret;
- size_t len;
+ 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,
.data = &cad,
.action = change_atts
};
+ unsigned num_inputs;
- for (
- p = aca->query.data;
- p < (char *)aca->query.data + aca->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);
+ free(p);
if (ret < 0) {
- para_printf(&aca->pbout, "attribute not found: %s\n", p);
+ para_printf(&aca->pbout, "invalid argument: %s\n", arg);
goto out;
}
if (c == '+')
else
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;
- if (p >= (char *)aca->query.data + aca->query.size)
- goto out;
- pmd.patterns.size = (char *)aca->query.data + aca->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)
+ if (ret >= 0 && pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ 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 int afs_stat_callback(struct afs_callback_arg *aca)
{
return 1;
}
-/**
- * The init function of the ALSA mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref oss_mix_init().
- */
-void alsa_mix_init(struct mixer *self)
-{
- self->open = alsa_mix_open;
- self->get_channels = alsa_mix_get_channels;
- self->set_channel = alsa_mix_set_channel;
- self->close = alsa_mix_close;
- self->get = alsa_mix_get;
- self->set = alsa_mix_set;
-}
+/** The mixer operations for the ALSA mixer. */
+const struct mixer alsa_mixer = {
+ .name = "alsa",
+ .open = alsa_mix_open,
+ .get_channels = alsa_mix_get_channels,
+ .set_channel = alsa_mix_set_channel,
+ .close = alsa_mix_close,
+ .get = alsa_mix_get,
+ .set = alsa_mix_set
+};
#include <regex.h>
#include <sys/types.h>
#include <alsa/asoundlib.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "alsa_write.cmdline.h"
#include "error.h"
/** Data specific to the alsa writer. */
}
/* Install PCM software and hardware configuration. */
-static int alsa_init(struct private_alsa_write_data *pad,
- struct alsa_write_args_info *conf)
+static int alsa_init(struct writer_node *wn)
{
+ struct private_alsa_write_data *pad = wn->private_data;
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_sw_params_t *swparams = NULL;
snd_pcm_uframes_t start_threshold, stop_threshold;
snd_pcm_uframes_t buffer_size, period_size;
snd_output_t *output_log;
int ret;
- const char *msg;
+ const char *msg, *dev = WRITE_CMD_OPT_STRING_VAL(ALSA, DEVICE, wn->lpr);
unsigned period_time;
- PARA_INFO_LOG("opening %s\n", conf->device_arg);
+ PARA_INFO_LOG("opening %s\n", dev);
msg = "unable to open pcm";
- ret = snd_pcm_open(&pad->handle, conf->device_arg,
- SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ ret = snd_pcm_open(&pad->handle, dev, SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK);
if (ret < 0)
goto fail;
ret = snd_pcm_hw_params_malloc(&hwparams);
if (ret < 0)
goto fail;
/* alsa wants microseconds */
- pad->buffer_time = conf->buffer_time_arg * 1000;
+ pad->buffer_time = 1000U * WRITE_CMD_OPT_UINT32_VAL(ALSA, BUFFER_TIME,
+ wn->lpr);
msg = "could not set buffer time";
ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
&pad->buffer_time, NULL);
if (bytes == 0) /* no data available */
return 0;
- pad = para_calloc(sizeof(*pad));
+ pad = wn->private_data = para_calloc(sizeof(*pad));
get_btr_sample_rate(btrn, &val);
pad->sample_rate = val;
get_btr_channels(btrn, &val);
PARA_INFO_LOG("%u channel(s), %uHz\n", pad->channels,
pad->sample_rate);
- ret = alsa_init(pad, wn->conf);
+ ret = alsa_init(wn);
if (ret < 0) {
- free(pad);
+ free(wn->private_data);
+ wn->private_data = NULL;
goto err;
}
- wn->private_data = pad;
wn->min_iqs = pad->bytes_per_frame;
goto again;
}
return ret;
}
-__malloc static void *alsa_parse_config_or_die(int argc, char **argv)
-{
- struct alsa_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- alsa_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void alsa_free_config(void *conf)
-{
- alsa_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the alsa writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct \ref writer.
- */
-void alsa_write_init(struct writer *w)
-{
- struct alsa_write_args_info dummy;
+struct writer lsg_write_cmd_com_alsa_user_data = {
- alsa_write_cmdline_parser_init(&dummy);
- w->close = alsa_close;
- w->pre_select = alsa_write_pre_select;
- w->post_select = alsa_write_post_select;
- w->parse_config_or_die = alsa_parse_config_or_die;
- w->free_config = alsa_free_config;
- w->help = (struct ggo_help)DEFINE_GGO_HELP(alsa_write);
- alsa_write_cmdline_parser_free(&dummy);
-}
+ .pre_select = alsa_write_pre_select,
+ .post_select = alsa_write_post_select,
+ .close = alsa_close,
+};
/** \file amp_filter.c Paraslash's amplify filter. */
#include <regex.h>
+#include <lopsub.h>
+#include "filter_cmd.lsg.h"
#include "para.h"
-#include "amp_filter.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
free(fn->private_data);
}
-static int amp_parse_config(int argc, char **argv, void **config)
-{
- struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
- int ret;
-
- amp_filter_cmdline_parser(argc, argv, conf);
- ret = -ERRNO_TO_PARA_ERROR(EINVAL);
- if (conf->amp_arg < 0)
- goto err;
- *config = conf;
- return 1;
-err:
- free(conf);
- return ret;
-}
-
static void amp_open(struct filter_node *fn)
{
struct private_amp_data *pad = para_calloc(sizeof(*pad));
- struct amp_filter_args_info *conf = fn->conf;
+ unsigned given = FILTER_CMD_OPT_GIVEN(AMP, AMP, fn->lpr);
+ uint32_t amp_arg = FILTER_CMD_OPT_UINT32_VAL(AMP, AMP, fn->lpr);
fn->private_data = pad;
fn->min_iqs = 2;
- if (!conf->amp_given && stat_item_values[SI_AMPLIFICATION])
+ if (!given && stat_item_values[SI_AMPLIFICATION])
sscanf(stat_item_values[SI_AMPLIFICATION], "%u", &pad->amp);
else
- pad->amp = conf->amp_arg;
+ pad->amp = amp_arg;
PARA_INFO_LOG("amplification: %u (scaling factor: %1.2f)\n",
pad->amp, pad->amp / 64.0 + 1.0);
}
return ret;
}
-static void amp_free_config(void *conf)
-{
- amp_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the amplify filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void amp_filter_init(struct filter *f)
-{
- struct amp_filter_args_info dummy;
-
- amp_filter_cmdline_parser_init(&dummy);
- f->open = amp_open;
- f->close = amp_close;
- f->pre_select = generic_filter_pre_select;
- f->post_select = amp_post_select;
- f->parse_config = amp_parse_config;
- f->free_config = amp_free_config;
- f->help = (struct ggo_help)DEFINE_GGO_HELP(amp_filter);
-}
+const struct filter lsg_filter_cmd_com_amp_user_data = {
+ .open = amp_open,
+ .close = amp_close,
+ .pre_select = generic_filter_pre_select,
+ .post_select = amp_post_select,
+};
#include <pthread.h>
#include <ao/ao.h>
#include <regex.h>
+#include <lopsub.h>
+#include "write_cmd.lsg.h"
#include "para.h"
#include "fd.h"
#include "string.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "write.h"
-#include "write_common.h"
-#include "ao_write.cmdline.h"
#include "error.h"
struct private_aow_data {
ao_close(pawd->dev);
free(pawd);
wn->private_data = NULL;
+ ao_shutdown();
}
static void aow_pre_select(struct sched *s, void *context)
return -E_AO_OPEN_LIVE;
}
+static void aow_show_drivers(void)
+{
+ int i, j, num_drivers;
+ ao_info **driver_list;
+
+ PARA_DEBUG_LOG("libao drivers available on this host:\n");
+ PARA_DEBUG_LOG("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+
+ driver_list = ao_driver_info_list(&num_drivers);
+
+ for (i = 0; i < num_drivers; i++) {
+ ao_info *info = driver_list[i];
+ char *keys = NULL, *tmp = NULL;
+
+ if (info->type == AO_TYPE_FILE)
+ continue;
+ PARA_DEBUG_LOG("%s: %s", info->short_name, info->name);
+ PARA_DEBUG_LOG("priority: %d", info->priority);
+ for (j = 0; j < info->option_count; j++) {
+ tmp = make_message("%s%s%s", keys? keys : "",
+ keys? ", " : "",
+ info->options[j]);
+ free(keys);
+ keys = tmp;
+ }
+ PARA_DEBUG_LOG("keys: %s", keys? keys : "[none]");
+ free(keys);
+ PARA_DEBUG_LOG("comment: %s", info->comment?
+ info->comment : "[none]");
+ }
+}
+
static int aow_init(struct writer_node *wn, unsigned sample_rate,
unsigned channels, int sample_format)
{
ao_option *aoo = NULL;
ao_sample_format asf;
ao_info *info;
+ const struct lls_opt_result *r;
+ unsigned n;
struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
- struct ao_write_args_info *conf = wn->conf;
- if (conf->driver_given) {
+ ao_initialize();
+ aow_show_drivers();
+ if (WRITE_CMD_OPT_GIVEN(AO, DRIVER, wn->lpr)) {
ret = -E_AO_BAD_DRIVER;
- id = ao_driver_id(conf->driver_arg);
+ id = ao_driver_id(WRITE_CMD_OPT_STRING_VAL(AO, DRIVER, wn->lpr));
} else {
ret = -E_AO_DEFAULT_DRIVER;
id = ao_default_driver_id();
goto fail;
}
PARA_INFO_LOG("using %s driver\n", info->short_name);
- for (i = 0; i < conf->ao_option_given; i++) {
- char *o = para_strdup(conf->ao_option_arg[i]), *value;
+ r = WRITE_CMD_OPT_RESULT(AO, AO_OPTION, wn->lpr);
+ n = lls_opt_given(r);
+ for (i = 0; i < n; i++) {
+ char *o = para_strdup(lls_string_val(i, r));
+ char *value;
ret = -E_AO_BAD_OPTION;
value = strchr(o, ':');
return ret;
}
-__malloc static void *aow_parse_config_or_die(int argc, char **argv)
-{
- struct ao_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- ao_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void aow_free_config(void *conf)
-{
- ao_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the ao writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void ao_write_init(struct writer *w)
-{
- struct ao_write_args_info dummy;
- int i, j, num_drivers, num_lines;
- ao_info **driver_list;
- char **dh; /* detailed help */
-
- ao_write_cmdline_parser_init(&dummy);
- w->close = aow_close;
- w->pre_select = aow_pre_select;
- w->post_select = aow_post_select;
- w->parse_config_or_die = aow_parse_config_or_die;
- w->free_config = aow_free_config;
- w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write);
- /* create detailed help containing all supported drivers/options */
- for (i = 0; ao_write_args_info_detailed_help[i]; i++)
- ; /* nothing */
- num_lines = i;
- dh = para_malloc((num_lines + 3) * sizeof(char *));
- for (i = 0; i < num_lines; i++)
- dh[i] = para_strdup(ao_write_args_info_detailed_help[i]);
- dh[num_lines++] = para_strdup("libao drivers available on this host:");
- dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
-
- ao_initialize();
- driver_list = ao_driver_info_list(&num_drivers);
-
- for (i = 0; i < num_drivers; i++) {
- ao_info *info = driver_list[i];
- char *keys = NULL, *tmp = NULL;
-
- if (info->type == AO_TYPE_FILE)
- continue;
- for (j = 0; j < info->option_count; j++) {
- tmp = make_message("%s%s%s", keys? keys : "",
- keys? ", " : "",
- info->options[j]);
- free(keys);
- keys = tmp;
- }
- dh = para_realloc(dh, (num_lines + 6) * sizeof(char *));
- dh[num_lines++] = make_message("%s: %s", info->short_name, info->name);
- dh[num_lines++] = make_message("priority: %d", info->priority);
- dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]");
- dh[num_lines++] = make_message("comment: %s", info->comment?
- info->comment : "[none]");
- dh[num_lines++] = para_strdup(NULL);
- free(keys);
- }
- dh[num_lines] = NULL;
- w->help.detailed_help = (const char **)dh;
- ao_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_ao_user_data = {
+ .close = aow_close,
+ .pre_select = aow_pre_select,
+ .post_select = aow_post_select,
+};
#include <regex.h>
#include <osl.h>
+#include <lopsub.h>
+#include "server_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
return 1;
}
-/**
- * Flags used by the lsatt command.
- *
- * \param \sa com_lsatt().
- */
-enum lsatt_flags {
- /** Whether "-a" was given for the lsatt command. */
- LSATT_FLAG_SORT_BY_ID = 1,
- /** Whether "-l" was given for the lsatt command. */
- LSATT_FLAG_LONG = 2,
- /** Reverse sort order. */
- LSATT_FLAG_REVERSE = 4
-};
-
/** Data passed to the action function of lsatt */
static int print_attribute(struct osl_table *table, struct osl_row *row,
const char *name, void *data)
{
struct afs_callback_arg *aca = data;
- unsigned flags = *(unsigned *)aca->query.data;
+ bool l_given = SERVER_CMD_OPT_GIVEN(LSATT, LONG, aca->lpr);
struct osl_object bitnum_obj;
int ret;
- if (!(flags & LSATT_FLAG_LONG)) {
+ if (!l_given) {
para_printf(&aca->pbout, "%s\n", name);
return 1;
}
static int com_lsatt_callback(struct afs_callback_arg *aca)
{
- unsigned flags = *(unsigned *)aca->query.data;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+ bool i_given, r_given;
int ret;
struct pattern_match_data pmd = {
.table = attribute_table,
.loop_col_num = ATTCOL_NAME,
.match_col_num = ATTCOL_NAME,
- .patterns = {.data = (char *)aca->query.data + sizeof(flags),
- .size = aca->query.size - sizeof(flags)},
.pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING,
.data = aca,
.action = print_attribute
};
- if (flags & LSATT_FLAG_SORT_BY_ID)
+
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
+ i_given = SERVER_CMD_OPT_GIVEN(LSATT, ID_SORT, aca->lpr);
+ r_given = SERVER_CMD_OPT_GIVEN(LSATT, REVERSE, aca->lpr);
+
+ if (i_given)
pmd.loop_col_num = ATTCOL_BITNUM;
- if (flags & LSATT_FLAG_REVERSE)
+ if (r_given)
pmd.pm_flags |= PM_REVERSE_LOOP;
ret = for_each_matching_row(&pmd);
if (ret < 0)
if (pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_lsatt(struct command_context *cc)
+static int com_lsatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- unsigned flags = 0;
- struct osl_object options = {.data = &flags, .size = sizeof(flags)};
- int i;
-
- 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, "-i")) {
- flags |= LSATT_FLAG_SORT_BY_ID;
- continue;
- }
- if (!strcmp(arg, "-l")) {
- flags |= LSATT_FLAG_LONG;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- flags |= LSATT_FLAG_REVERSE;
- continue;
- }
- }
- return send_option_arg_callback_request(&options, cc->argc - i, cc->argv + i,
- com_lsatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(LSATT);
+ return send_lls_callback_request(com_lsatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(lsatt);
struct addatt_event_data {
const char *name;
unsigned char bitnum;
};
-
static int com_addatt_callback(struct afs_callback_arg *aca)
{
- char *p;
- int ret = 1;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+ int i, ret = 1;
size_t len;
+ unsigned num_inputs;
- for (
- p = aca->query.data;
- p < (char *)aca->query.data + aca->query.size;
- p += len + 1
- ) {
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ num_inputs = lls_num_inputs(aca->lpr);
+ for (i = 0; i < num_inputs; i++) {
+ const char *name = lls_input(i, aca->lpr);
struct osl_object objs[NUM_ATT_COLUMNS];
struct osl_row *row;
unsigned char bitnum;
struct addatt_event_data aed;
- len = strlen(p);
- if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
- para_printf(&aca->pbout, "invalid attribute name: %s\n", p);
+ len = strlen(name);
+ if (len == 0 || name[len - 1] == '-' || name[len - 1] == '+') {
+ para_printf(&aca->pbout,
+ "invalid attribute name: %s\n", name);
continue;
}
- ret = get_attribute_bitnum_by_name(p, &bitnum);
+ ret = get_attribute_bitnum_by_name(name, &bitnum);
if (ret >= 0) {
- para_printf(&aca->pbout, "attribute \"%s\" already exists\n", p);
+ para_printf(&aca->pbout,
+ "attribute \"%s\" already exists\n", name);
continue;
}
if (ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND)) /* error */
ret = -E_ATT_TABLE_FULL;
goto out;
}
- objs[ATTCOL_NAME].data = p;
+ objs[ATTCOL_NAME].data = (char *)name;
objs[ATTCOL_NAME].size = len + 1;
ret = osl(osl_add_row(attribute_table, objs));
if (ret < 0)
goto out;
- aed.name = p;
+ aed.name = name;
aed.bitnum = bitnum;
ret = afs_event(ATTRIBUTE_ADD, &aca->pbout, &aed);
if (ret < 0)
}
out:
if (ret < 0)
- para_printf(&aca->pbout, "%s: %s\n", p, para_strerror(-ret));
+ para_printf(&aca->pbout, "error while adding %s\n",
+ lls_input(i, aca->lpr));
return ret;
}
-int com_addatt(struct command_context *cc)
+static int com_addatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- int ret;
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADDATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 1, 64, &errctx));
- if (cc->argc < 2)
- return -E_ATTR_SYNTAX;
- ret = send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_addatt_callback, afs_cb_result_handler, cc);
- if (ret < 0)
- send_strerror(cc, -ret);
- return ret;
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_addatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(addatt);
static int com_mvatt_callback(struct afs_callback_arg *aca)
{
- char *old = aca->query.data;
- size_t size = strlen(old) + 1;
- char *new = old + size;
- struct osl_object obj = {.data = old, .size = size};
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+ const char *old, *new;
+ struct osl_object obj;
struct osl_row *row;
int ret;
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ old = lls_input(0, aca->lpr);
+ new = lls_input(1, aca->lpr);
+ obj.data = (char *)old;
+ obj.size = strlen(old) + 1;
ret = osl(osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row));
if (ret < 0)
goto out;
- obj.data = new;
+ obj.data = (char *)new;
obj.size = strlen(new) + 1;
+ /* The update fails if the destination attribute exists. */
ret = osl(osl_update_object(attribute_table, row, ATTCOL_NAME, &obj));
out:
if (ret < 0)
para_printf(&aca->pbout, "cannot rename %s to %s\n", old, new);
else
ret = afs_event(ATTRIBUTE_RENAME, &aca->pbout, NULL);
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_mvatt(struct command_context *cc)
+static int com_mvatt(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_mvatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(MVATT);
+ char *errctx;
+ int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ return send_lls_callback_request(com_mvatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(mvatt);
static int remove_attribute(struct osl_table *table, struct osl_row *row,
const char *name, void *data)
static int com_rmatt_callback(struct afs_callback_arg *aca)
{
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
int ret;
struct pattern_match_data pmd = {
.table = attribute_table,
- .patterns = aca->query,
.loop_col_num = ATTCOL_BITNUM,
.match_col_num = ATTCOL_NAME,
.data = aca,
.action = remove_attribute
};
+ ret = lls(lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr));
+ assert(ret >= 0);
+ pmd.lpr = aca->lpr;
ret = for_each_matching_row(&pmd);
if (ret < 0)
goto out;
if (pmd.num_matches == 0)
ret = -E_NO_MATCH;
out:
+ lls_free_parse_result(aca->lpr, cmd);
return ret;
}
-int com_rmatt(struct command_context *cc)
+static int com_rmatt(struct command_context *cc, struct lls_parse_result *lpr)
{
- if (cc->argc < 2)
- return -E_ATTR_SYNTAX;
- return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
- com_rmatt_callback, afs_cb_result_handler, cc);
+ const struct lls_command *cmd = SERVER_CMD_CMD_PTR(RMATT);
+ char *errctx;
+ int 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_rmatt_callback, cmd, lpr, cc);
}
+EXPORT_SERVER_CMD_HANDLER(rmatt);
/**
* Return a binary representation of the given attribute value.
#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
+#include "audioc.lsg.h"
-#include "audioc.cmdline.h"
#include "para.h"
#include "error.h"
#include "net.h"
#include "string.h"
#include "fd.h"
-#include "ggo.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
-/** The gengetopt structure containing command line args. */
-static struct audioc_args_info conf;
static char *socket_name;
+static struct lls_parse_result *lpr;
+#define CMD_PTR (lls_cmd(0, audioc_suite))
+#define OPT_RESULT(_name) \
+ (lls_opt_result(LSG_AUDIOC_PARA_AUDIOC_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
static int loglevel;
INIT_STDERR_LOGGING(loglevel);
char *buf = NULL;
for (i = 0; i < argc; i++) {
- buf = para_strcat(buf, argv[i]);
+ const char *arg = argv? argv[i] : lls_input(i, lpr);
+ buf = para_strcat(buf, arg);
if (i != argc - 1)
buf = para_strcat(buf, "\n");
}
#include "sched.h"
#include "buffer_tree.h"
#include "interactive.h"
-#include "audiod.completion.h"
static struct sched sched;
static void version_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-v", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_VERSION_OPTS, NULL};
if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
struct i9e_completion_result *cr)
{
char *sia[] = {STATUS_ITEM_ARRAY NULL};
- char *opts[] = {"-p", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_STAT_OPTS, NULL};
if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
i9e_complete_option(opts, ci, cr);
static void grab_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
- char *opts[] = {"-ms", "-ms", "-ma", "-p=", "-n=", "-o", NULL};
+ char *opts[] = {LSG_AUDIOD_CMD_GRAB_OPTS, NULL};
i9e_complete_option(opts, ci, cr);
}
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
static struct i9e_completer audiod_completers[] = {
- AUDIOD_COMPLETERS
+#define LSG_AUDIOD_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_AUDIOD_CMD_SUBCOMMANDS
+#undef LSG_AUDIOD_CMD_CMD
{.name = NULL}
};
char *buf = NULL;
struct audioc_task *at = context;
int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
+ size_t bufsize;
if (ret < 0)
goto out;
if (!FD_ISSET(at->fd, &s->rfds))
return 0;
- buf = para_malloc(conf.bufsize_arg);
- ret = recv_bin_buffer(at->fd, buf, conf.bufsize_arg);
+ bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
+ buf = para_malloc(bufsize);
+ ret = recv_bin_buffer(at->fd, buf, bufsize);
PARA_DEBUG_LOG("recv: %d\n", ret);
if (ret == 0)
ret = -E_AUDIOC_EOF;
static int audioc_i9e_line_handler(char *line)
{
- char *args = NULL;
- int ret;
+ int argc, ret;
+ char *args, **argv;
PARA_DEBUG_LOG("line: %s\n", line);
- ret = create_argv(line, " ", &conf.inputs);
+ ret = create_argv(line, " ", &argv);
if (ret < 0)
return ret;
- conf.inputs_num = ret;
- args = concat_args(conf.inputs_num, conf.inputs);
- free_argv(conf.inputs);
+ argc = ret;
+ args = concat_args(argc, argv);
+ free_argv(argv);
if (!args)
return 0;
- conf.inputs_num = 0; /* required for audioc_cmdline_parser_free() */
ret = connect_audiod(socket_name, args);
+ free(args);
if (ret < 0)
- goto out;
+ return ret;
at->fd = ret;
ret = mark_fd_nonblocking(at->fd);
if (ret < 0)
goto close;
- free(args);
- args = NULL;
at->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = "audioc line handler"));
at->task = task_register(&(struct task_info) {
return 1;
close:
close(at->fd);
-out:
- free(args);
return ret;
}
.loglevel = loglevel,
.completers = audiod_completers,
};
+
PARA_NOTICE_LOG("\n%s\n", version_text("audioc"));
- if (conf.history_file_given)
- history_file = para_strdup(conf.history_file_arg);
+ if (OPT_GIVEN(HISTORY_FILE))
+ history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
else {
char *home = para_homedir();
history_file = make_message("%s/.paraslash/audioc.history",
para_log = stderr_log;
out:
free(history_file);
- audioc_cmdline_parser_free(&conf);
+ free(socket_name);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
return NULL;
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
{
- struct ggo_help h = DEFINE_GGO_HELP(audioc);
- bool d = conf.detailed_help_given;
+ char *help;
- ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
- exit(0);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
/**
*/
int main(int argc, char *argv[])
{
+ const struct lls_command *cmd = CMD_PTR;
int ret, fd;
- char *cf, *buf = NULL, *args = NULL;
+ char *cf = NULL, *buf, *args, *errctx = NULL;
size_t bufsize;
+ struct lls_parse_result *lpr1, *lpr2, *lpr3;
+ unsigned num_inputs;
- audioc_cmdline_parser(argc, argv, &conf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("audioc", conf.version_given);
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
+ ret = lls(lls_parse(argc, argv, cmd, &lpr1, &errctx));
+ if (ret < 0)
+ goto fail;
+ lpr = lpr1;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("audioc", OPT_GIVEN(VERSION));
+ handle_help_flag();
cf = configfile_exists();
if (cf) {
- struct audioc_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1,
-
- };
- audioc_cmdline_parser_config_file(cf, &conf, ¶ms);
- free(cf);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char **cf_argv;
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret != -E_EMPTY) {
+ if (ret < 0)
+ goto out;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv,
+ &errctx));
+ para_munmap(map, sz);
+ if (ret < 0) {
+ PARA_ERROR_LOG("syntax error in %s\n", cf);
+ goto out;
+ }
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &lpr2,
+ &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0) {
+ PARA_ERROR_LOG("parse error in %s\n", cf);
+ goto out;
+ }
+ ret = lls(lls_merge(lpr1, lpr2, cmd, &lpr3, &errctx));
+ lls_free_parse_result(lpr2, cmd);
+ if (ret < 0)
+ goto out;
+ lls_free_parse_result(lpr1, cmd);
+ lpr = lpr3;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ }
}
- if (conf.socket_given)
- socket_name = para_strdup(conf.socket_arg);
+ if (OPT_GIVEN(COMPLETE))
+ print_completions();
+ if (OPT_GIVEN(SOCKET))
+ socket_name = para_strdup(OPT_STRING_VAL(SOCKET));
else {
char *hn = para_hostname();
socket_name = make_message("/var/paraslash/audiod_socket.%s",
hn);
free(hn);
}
-
- if (conf.complete_given)
- print_completions();
-
- if (conf.inputs_num == 0)
+ num_inputs = lls_num_inputs(lpr);
+ if (num_inputs == 0)
interactive_session();
- args = concat_args(conf.inputs_num, conf.inputs);
+ args = concat_args(num_inputs, NULL);
ret = connect_audiod(socket_name, args);
free(socket_name);
+ free(args);
if (ret < 0)
goto out;
fd = ret;
ret = mark_fd_blocking(STDOUT_FILENO);
if (ret < 0)
goto out;
- bufsize = conf.bufsize_arg;
+ bufsize = PARA_MAX(1024U, OPT_UINT32_VAL(BUFSIZE));
buf = para_malloc(bufsize);
do {
size_t n = ret = recv_bin_buffer(fd, buf, bufsize);
break;
ret = write_all(STDOUT_FILENO, buf, n);
} while (ret >= 0);
-out:
free(buf);
- free(args);
+out:
+ lls_free_parse_result(lpr, cmd);
+ free(cf);
+fail:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
#include <netdb.h>
#include <signal.h>
#include <pwd.h>
+#include <lopsub.h>
+#include "audiod.lsg.h"
+#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
#include "crypt.h"
-#include "audiod.cmdline.h"
#include "list.h"
#include "sched.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "recv.h"
#include "filter.h"
#include "grab_client.h"
-#include "client.cmdline.h"
#include "client.h"
#include "audiod.h"
#include "net.h"
#include "string.h"
#include "fd.h"
#include "write.h"
-#include "write_common.h"
#include "signal.h"
#include "version.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *lpr;
+#define CMD_PTR (lls_cmd(0, audiod_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+ lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
+
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
/** define the array containing all supported audio formats */
const char *audio_formats[] = {AUDIOD_AUDIO_FORMAT_ARRAY NULL};
-DEFINE_RECEIVER_ARRAY;
-
/** Defines how audiod handles one supported audio format. */
struct audio_format_info {
- /** pointer to the receiver for this audio format */
- struct receiver *receiver;
- /** the receiver configuration */
- void *receiver_conf;
+ /** the receiver for this audio format */
+ int receiver_num;
+ /** Parsed receiver command line. */
+ struct lls_parse_result *receiver_lpr;
/** the number of filters that should be activated for this audio format */
unsigned int num_filters;
/** Array of filter numbers to be activated. */
unsigned *filter_nums;
/** Pointer to the array of filter configurations. */
void **filter_conf;
+ /** Parsed filter command line, one parse result per filter. */
+ struct lls_parse_result **filter_lpr;
/** the number of filters that should be activated for this audio format */
unsigned int num_writers;
- /** Array of writer numbers to be activated. */
- int *writer_nums;
- /** pointer to the array of writer configurations */
- void **writer_conf;
+ /** Array of writer IDs to be activated. */
+ int *wids;
+ /** Parsed writer command line(s) */
+ struct lls_parse_result **writer_lpr;
/** do not start receiver/filters/writer before this time */
struct timeval restart_barrier;
};
struct writer_node *wns;
};
+#define RECEIVER_CMD(_a) lls_cmd((_a)->receiver_num, recv_cmd_suite)
+#define RECEIVER(_a) ((const struct receiver *)lls_user_data(RECEIVER_CMD(_a)))
+
/** Maximal number of simultaneous instances. */
#define MAX_STREAM_SLOTS 5
*/
int audiod_status = AUDIOD_ON;
-/**
- * the gengetopt args_info struct that holds information on all command line
- * arguments
- */
-static struct audiod_args_info conf;
-
static char *socket_name;
static struct audio_format_info afi[NUM_AUDIO_FORMATS];
-
static struct signal_task *signal_task;
-
static struct status_task status_task_struct;
-
static uid_t *uid_whitelist;
/**
*/
static struct status_task *stat_task = &status_task_struct;
-/*
- * The task for handling audiod commands.
- *
- * We need two listening sockets for backward compability: on Linux systems
- * fd[0] is an abstract socket (more precisely, a socket bound to an address in
- * the abstract namespace), and fd[1] is the usual pathname socket. On other
- * systems, fd[0] is negative, and only the pathname socket is used.
- *
- * For 0.5.x we accept connections on both sockets to make sure that old
- * para_audioc versions can still connect. New versions use only the abstract
- * socket. Hence after v0.6.0 we can go back to a single socket, either an
- * abstract one (Linux) or a pathname socket (all other systems).
- */
struct command_task {
- /** The local listening sockets. */
- int fd[2];
+ /** The local listening socket. */
+ int fd;
/** the associated task structure */
struct task *task;
};
static void parse_config_or_die(void)
{
- int ret, i;
- char *config_file;
- struct audiod_cmdline_parser_params params = {
- .override = 0,
- .initialize = 0,
- .check_required = 1,
- .check_ambiguity = 0,
- .print_errors = 1
- };
-
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ int ret;
+ char *cf, *errctx = NULL;
+ void *map;
+ size_t sz;
+
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/audiod.conf", home);
+ cf = make_message("%s/.paraslash/audiod.conf", home);
free(home);
}
- ret = file_exists(config_file);
- if (conf.config_file_given && !ret) {
- PARA_EMERG_LOG("can not read config file %s\n", config_file);
- free(config_file);
- goto err;
- }
- if (ret) {
- audiod_cmdline_parser_config_file(config_file, &conf, ¶ms);
- daemon_set_loglevel(conf.loglevel_arg);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ } else {
+ int cf_argc;
+ char **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr,
+ &errctx));
+ lls_free_parse_result(cf_lpr, CMD_PTR);
+ if (ret < 0)
+ goto free_cf;
+ lls_free_parse_result(lpr, CMD_PTR);
+ lpr = merged_lpr;
}
- free(config_file);
- if (conf.user_allow_given > 0) {
- uid_whitelist = para_malloc(conf.user_allow_given
- * sizeof(uid_t));
- for (i = 0; i < conf.user_allow_given; i++) {
+ daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ if (OPT_GIVEN(USER_ALLOW)) {
+ uint32_t n = OPT_GIVEN(USER_ALLOW);
+ int i;
+
+ uid_whitelist = para_malloc(n * sizeof(uid_t));
+ for (i = 0; i < n; i++) {
+ const char *arg = lls_string_val(i,
+ OPT_RESULT(USER_ALLOW));
int32_t val;
struct passwd *pw;
- ret = para_atoi32(conf.user_allow_arg[i], &val);
+ ret = para_atoi32(arg, &val);
if (ret >= 0) {
uid_whitelist[i] = val;
continue;
}
errno = 0; /* see getpwnam(3) */
- pw = getpwnam(conf.user_allow_arg[i]);
+ pw = getpwnam(arg);
if (!pw) {
- PARA_EMERG_LOG("invalid username: %s\n",
- conf.user_allow_arg[i]);
- goto err;
+ PARA_EMERG_LOG("invalid username: %s\n", arg);
+ free(uid_whitelist);
+ goto free_cf;
}
uid_whitelist[i] = pw->pw_uid;
}
}
- return;
-err:
- exit(EXIT_FAILURE);
+ ret = 0;
+free_cf:
+ free(cf);
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ lls_free_parse_result(lpr, CMD_PTR);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(EXIT_FAILURE);
+ }
}
static void setup_signal_handling(void)
a = &afi[s->format];
PARA_NOTICE_LOG("closing %s receiver in slot %d\n",
audio_formats[s->format], slot_num);
- a->receiver->close(s->receiver_node);
+ RECEIVER(a)->close(s->receiver_node);
btr_remove_node(&s->receiver_node->btrn);
task_reap(&s->receiver_node->task);
free(s->receiver_node);
static void writer_cleanup(struct writer_node *wn)
{
- struct writer *w;
-
if (!wn)
return;
- w = writers + wn->writer_num;
- PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]);
- w->close(wn);
+ PARA_INFO_LOG("closing %s\n", writer_name(wn->wid));
+ writer_get(wn->wid)->close(wn);
btr_remove_node(&wn->btrn);
task_reap(&wn->task);
}
parent = s->receiver_node->btrn;
for (i = 0; i < nf; i++) {
char buf[20];
+ const char *name;
const struct filter *f = filter_get(a->filter_nums[i]);
fn = s->fns + i;
fn->filter_num = a->filter_nums[i];
fn->conf = a->filter_conf[i];
+ fn->lpr = a->filter_lpr[i];
+ name = filter_name(fn->filter_num);
fn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = f->name, .parent = parent,
+ EMBRACE(.name = name, .parent = parent,
.handler = f->execute, .context = fn));
if (f->open)
f->open(fn);
- sprintf(buf, "%s (slot %d)", f->name, (int)(s - slot));
+ sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
fn->task = task_register(&(struct task_info) {
.name = buf,
.pre_select = f->pre_select,
}, &sched);
parent = fn->btrn;
PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n",
- audio_formats[s->format], i, nf, f->name, (int)(s - slot));
+ audio_formats[s->format], i, nf, name, (int)(s - slot));
}
}
* sizeof(struct writer_node));
for (i = 0; i < a->num_writers; i++) {
wn = s->wns + i;
- wn->conf = a->writer_conf[i];
- wn->writer_num = a->writer_nums[i];
+ wn->wid = a->wids[i];
+ wn->lpr = a->writer_lpr[i];
register_writer_node(wn, parent, &sched);
PARA_NOTICE_LOG("%s writer started in slot %d\n",
- writer_names[a->writer_nums[i]], (int)(s - slot));
+ writer_name(a->wids[i]), (int)(s - slot));
}
}
struct audio_format_info *a = &afi[format];
struct slot_info *s;
int ret, slot_num;
- struct receiver *r = a->receiver;
+ const struct receiver *r = RECEIVER(a);
+ const char *name = lls_command_name(RECEIVER_CMD(a));
struct receiver_node *rn;
tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier);
slot_num = ret;
rn = para_calloc(sizeof(*rn));
rn->receiver = r;
- rn->conf = a->receiver_conf;
+ rn->lpr = a->receiver_lpr;
rn->btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = r->name, .context = rn));
+ EMBRACE(.name = name, .context = rn));
ret = r->open(rn);
if (ret < 0) {
btr_remove_node(&rn->btrn);
s->format = format;
s->receiver_node = rn;
PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n",
- audio_formats[format], r->name, slot_num);
+ audio_formats[format], name, slot_num);
rn->task = task_register(&(struct task_info) {
- .name = r->name,
+ .name = name,
.pre_select = r->pre_select,
.post_select = r->post_select,
.context = rn,
return 1;
}
-static int parse_stream_command(const char *txt, char **cmd)
+static int parse_stream_command(const char *txt, const char **cmd)
{
int ret, len;
char *re, *p = strchr(txt, ':');
return ret;
}
-static int add_filter(int format, char *cmdline)
+static int add_filter(int format, const char *cmdline)
{
struct audio_format_info *a = &afi[format];
int filter_num, nf = a->num_filters;
void *cfg;
+ struct lls_parse_result *flpr;
- filter_num = check_filter_arg(cmdline, &cfg);
- if (filter_num < 0)
- return filter_num;
+ filter_num = filter_setup(cmdline, &cfg, &flpr);
+ a->filter_lpr = para_realloc(a->filter_lpr,
+ (nf + 1) * sizeof(flpr));
a->filter_conf = para_realloc(a->filter_conf,
(nf + 1) * sizeof(void *));
a->filter_nums = para_realloc(a->filter_nums,
(nf + 1) * sizeof(unsigned));
+
a->filter_nums[nf] = filter_num;
a->filter_conf[nf] = cfg;
+ a->filter_lpr[nf] = flpr;
a->num_filters++;
PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf,
- filter_get(filter_num)->name);
+ filter_name(filter_num));
return filter_num;
}
static int parse_writer_args(void)
{
int i, ret;
- char *cmd;
+ const char *cmd;
struct audio_format_info *a;
- for (i = 0; i < conf.writer_given; i++) {
- void *wconf;
- int j, nw, writer_num, af_mask;
+ for (i = 0; i < OPT_GIVEN(WRITER); i++) {
+ int j, nw, af_mask;
- ret = parse_stream_command(conf.writer_arg[i], &cmd);
+ ret = parse_stream_command(lls_string_val(i,
+ OPT_RESULT(WRITER)), &cmd);
if (ret < 0)
return ret;
af_mask = ret;
a = afi + j;
if ((af_mask & (1 << j)) == 0) /* no match */
continue;
- wconf = check_writer_arg_or_die(cmd, &writer_num);
nw = a->num_writers;
- a->writer_nums = para_realloc(a->writer_nums, (nw + 1) * sizeof(int));
- a->writer_conf = para_realloc(a->writer_conf, (nw + 1) * sizeof(void *));
- a->writer_nums[nw] = writer_num;
- a->writer_conf[nw] = wconf;
+ a->wids = para_realloc(a->wids, (nw + 1) * sizeof(int));
+ a->writer_lpr = para_realloc(a->writer_lpr,
+ (nw + 1) * sizeof(struct lls_parse_result *));
+ a->wids[nw] = check_writer_arg_or_die(cmd,
+ a->writer_lpr + nw);
PARA_INFO_LOG("%s writer #%d: %s\n", audio_formats[j],
- nw, writer_names[writer_num]);
+ nw, writer_name(a->wids[nw]));
a->num_writers++;
}
}
/* Use default writer for audio formats which are not yet set up. */
FOR_EACH_AUDIO_FORMAT(i) {
- void *writer_conf;
- int writer_num;
a = afi + i;
if (a->num_writers > 0)
continue; /* already set up */
- writer_conf = check_writer_arg_or_die(NULL, &writer_num);
- a->writer_nums = para_malloc(sizeof(int));
- a->writer_nums[0] = writer_num;
- a->writer_conf = para_malloc(sizeof(void *));
- a->writer_conf[0] = writer_conf;
a->num_writers = 1;
+ a->wids = para_malloc(sizeof(int));
+ a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+ a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
- writer_names[writer_num]);
+ writer_name(a->wids[0]));
}
return 1;
}
static int parse_receiver_args(void)
{
- int i, ret, receiver_num;
- char *cmd = NULL;
+ int i, ret;
+ const char *arg;
struct audio_format_info *a;
- for (i = conf.receiver_given - 1; i >= 0; i--) {
- char *arg;
+ FOR_EACH_AUDIO_FORMAT(i)
+ afi[i].receiver_num = -1;
+ for (i = OPT_GIVEN(RECEIVER) - 1; i >= 0; i--) {
int j, af_mask;
- ret = parse_stream_command(conf.receiver_arg[i], &arg);
+ ret = parse_stream_command(lls_string_val(i,
+ OPT_RESULT(RECEIVER)), &arg);
if (ret < 0)
goto out;
af_mask = ret;
* config here. Since we are iterating backwards, the winning
* receiver arg is in fact the first one given.
*/
- if (a->receiver_conf)
- a->receiver->free_config(a->receiver_conf);
- a->receiver_conf = check_receiver_arg(arg, &receiver_num);
- ret = -E_RECV_SYNTAX;
- if (!a->receiver_conf)
- goto out;
- a->receiver = receivers + receiver_num;
+ lls_free_parse_result(a->receiver_lpr, RECEIVER_CMD(a));
+ a->receiver_num = check_receiver_arg(arg, &a->receiver_lpr);
}
}
/*
- * Use the first available receiver with no arguments for those audio
- * formats for which no receiver was specified.
+ * Use the default receiver for those audio formats for which no
+ * receiver was specified.
*/
- cmd = para_strdup(receivers[0].name);
FOR_EACH_AUDIO_FORMAT(i) {
- a = &afi[i];
- if (a->receiver_conf)
+ a = afi + i;
+ if (a->receiver_num >= 0)
continue;
- a->receiver_conf = check_receiver_arg(cmd, &receiver_num);
- if (!a->receiver_conf)
- return -E_RECV_SYNTAX;
- a->receiver = &receivers[receiver_num];
+ a->receiver_num = check_receiver_arg(NULL, &a->receiver_lpr);
}