]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'refs/heads/t/lopsub'
authorAndre Noll <maan@tuebingen.mpg.de>
Thu, 27 Apr 2017 18:39:32 +0000 (20:39 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Thu, 27 Apr 2017 18:47:05 +0000 (20:47 +0200)
The bulk of the changes in this release is the conversion of all
command line parsers from gengetopt to lopsub.

The series also contains a few cleanups that have become possible
due to the switch from gengetopt to lopsub.

The patches towards the end of the series rename para_fade to
para_mixer.

Naturally, the merge conflicted rather heavily against the other
topic branches that have been merged since the lopsub branch was
started. Conflicting files:

Makefile.real afh.c afh_recv.c configure.ac osx_write.c write.c

The resolutions for these conflicts were recorded with git rerere
and have been tested for quite some time.

Cooking for three weeks.

* refs/heads/t/lopsub: (74 commits)
  audioc: Avoid double free in audioc_i9e_line_handler().
  audiod: Avoid uninitialized memory access.
  Simplify mixer setup.
  mixer: Implement non-linear time scale for fading.
  mixer: Allow arbitrary relative time for sleep subcommand.
  Convert para_fade to subcommands, rename it to para_mixer.
  build: Create .dep files only during compilation.
  build: Simplify definition of $m4_lls_deps.
  build: Rename command list variables.
  build: Combine $(CFLAGS) and $(STRICT_CFLAGS).
  build: Let .d files depend only on .c.
  build: Don't create phony targets for dependencies.
  build: Remove duplicate dependency.
  build: Remove cmdline_dir and friends.
  build: Remove some unused variables from Makefile.real.
  build: Remove m4/gengetopt.
  Remove gengetopt and help2man checks from configure.ac.
  Remove man_util.bash.
  Remove ggo.c and ggo.h.
  manual: Do not mention gengetopt and help2man any more.
  ...

174 files changed:
INSTALL
Makefile.in
Makefile.real
NEWS.md
aacdec_filter.c
acl.c
acl.h
afh.c
afh_recv.c
afs.c
afs.cmd [deleted file]
afs.h
aft.c
alsa_mix.c
alsa_write.c
amp_filter.c
ao_write.c
attribute.c
audioc.c
audiod.c
audiod.cmd [deleted file]
audiod.h
audiod_command.c
blob.c
check_wav.h
client.c
client.h
client_common.c
command.c
command.h
command_util.bash [deleted file]
compress_filter.c
configure.ac
dccp_recv.c
dccp_send.c
error.h
fade.c [deleted file]
fecdec_filter.c
file_write.c
filter.c
filter.h
filter_common.c
flacdec_filter.c
ggo.c [deleted file]
ggo.h [deleted file]
grab_client.c
grab_client.h
gui.c
http_recv.c
http_send.c
m4/gengetopt/afh.m4 [deleted file]
m4/gengetopt/afh_recv.m4 [deleted file]
m4/gengetopt/alsa_write.m4 [deleted file]
m4/gengetopt/amp_filter.m4 [deleted file]
m4/gengetopt/ao_write.m4 [deleted file]
m4/gengetopt/audioc.m4 [deleted file]
m4/gengetopt/audiod.m4 [deleted file]
m4/gengetopt/channels.m4 [deleted file]
m4/gengetopt/client.m4 [deleted file]
m4/gengetopt/color.m4 [deleted file]
m4/gengetopt/complete.m4 [deleted file]
m4/gengetopt/compress_filter.m4 [deleted file]
m4/gengetopt/config_file.m4 [deleted file]
m4/gengetopt/daemon.m4 [deleted file]
m4/gengetopt/dccp_recv.m4 [deleted file]
m4/gengetopt/fade.m4 [deleted file]
m4/gengetopt/file_write.m4 [deleted file]
m4/gengetopt/filter.m4 [deleted file]
m4/gengetopt/group.m4 [deleted file]
m4/gengetopt/gui.m4 [deleted file]
m4/gengetopt/header.m4 [deleted file]
m4/gengetopt/history_file.m4 [deleted file]
m4/gengetopt/http_recv.m4 [deleted file]
m4/gengetopt/log_timing.m4 [deleted file]
m4/gengetopt/logfile.m4 [deleted file]
m4/gengetopt/loglevel.m4 [deleted file]
m4/gengetopt/makefile [deleted file]
m4/gengetopt/mp3dec_filter.m4 [deleted file]
m4/gengetopt/oss_write.m4 [deleted file]
m4/gengetopt/play.m4 [deleted file]
m4/gengetopt/prebuffer_filter.m4 [deleted file]
m4/gengetopt/priority.m4 [deleted file]
m4/gengetopt/recv.m4 [deleted file]
m4/gengetopt/resample_filter.m4 [deleted file]
m4/gengetopt/sample_format.m4 [deleted file]
m4/gengetopt/sample_rate.m4 [deleted file]
m4/gengetopt/server.m4 [deleted file]
m4/gengetopt/sync_filter.m4 [deleted file]
m4/gengetopt/udp_recv.m4 [deleted file]
m4/gengetopt/user.m4 [deleted file]
m4/gengetopt/write.m4 [deleted file]
m4/lls/afh.suite.m4 [new file with mode: 0644]
m4/lls/audioc.suite.m4 [new file with mode: 0644]
m4/lls/audiod.suite.m4 [new file with mode: 0644]
m4/lls/audiod_cmd.suite.m4 [new file with mode: 0644]
m4/lls/client.suite.m4 [new file with mode: 0644]
m4/lls/copyright.m4 [new file with mode: 0644]
m4/lls/filter.suite.m4 [new file with mode: 0644]
m4/lls/filter_cmd.suite.m4 [new file with mode: 0644]
m4/lls/gui.suite.m4 [new file with mode: 0644]
m4/lls/include/channels.m4 [new file with mode: 0644]
m4/lls/include/color.m4 [new file with mode: 0644]
m4/lls/include/common-option-section.m4 [new file with mode: 0644]
m4/lls/include/complete.m4 [new file with mode: 0644]
m4/lls/include/config-file.m4 [new file with mode: 0644]
m4/lls/include/daemon.m4 [new file with mode: 0644]
m4/lls/include/detailed-help.m4 [new file with mode: 0644]
m4/lls/include/group.m4 [new file with mode: 0644]
m4/lls/include/help.m4 [new file with mode: 0644]
m4/lls/include/history-file.m4 [new file with mode: 0644]
m4/lls/include/host.m4 [new file with mode: 0644]
m4/lls/include/log-timing.m4 [new file with mode: 0644]
m4/lls/include/logfile.m4 [new file with mode: 0644]
m4/lls/include/loglevel.m4 [new file with mode: 0644]
m4/lls/include/per-command-options-section.m4 [new file with mode: 0644]
m4/lls/include/port.m4 [new file with mode: 0644]
m4/lls/include/priority.m4 [new file with mode: 0644]
m4/lls/include/sample-format.m4 [new file with mode: 0644]
m4/lls/include/sample-rate.m4 [new file with mode: 0644]
m4/lls/include/user.m4 [new file with mode: 0644]
m4/lls/include/version.m4 [new file with mode: 0644]
m4/lls/makefile [new file with mode: 0644]
m4/lls/mixer.suite.m4 [new file with mode: 0644]
m4/lls/play.suite.m4 [new file with mode: 0644]
m4/lls/play_cmd.suite.m4 [new file with mode: 0644]
m4/lls/recv.suite.m4 [new file with mode: 0644]
m4/lls/recv_cmd.suite.m4 [new file with mode: 0644]
m4/lls/server.suite.m4 [new file with mode: 0644]
m4/lls/server_cmd.suite.m4 [new file with mode: 0644]
m4/lls/write.suite.m4 [new file with mode: 0644]
m4/lls/write_cmd.suite.m4 [new file with mode: 0644]
man_util.bash [deleted file]
mix.h
mixer.c [new file with mode: 0644]
mm.c
mood.c
mp3dec_filter.c
oggdec_filter.c
opusdec_filter.c
oss_mix.c
oss_write.c
osx_write.c [new file with mode: 0644]
para.h
play.c
play.cmd [deleted file]
playlist.c
prebuffer_filter.c
recv.c
recv.h
recv_common.c
resample_filter.c
score.c
send.h
send_common.c
server.c
server.cmd [deleted file]
server.h
spxdec_filter.c
sync_filter.c
t/t0004-server.sh
t/t0005-man.sh
t/test-lib.sh
udp_recv.c
udp_send.c
user_list.h
vss.c
wav_filter.c
web/documentation.in.html
web/manual.md
wmadec_filter.c
write.c
write.h
write_common.c
write_common.h [deleted file]

diff --git a/INSTALL b/INSTALL
index 547036b104d03759d79626e6ac3b686b165b88c0..4a86e967ee72955a32ed37e2fd88b849f808053e 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,5 +1,11 @@
 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
index aff321fd95d8d5f5114ccd7abe513451e3b09061..b3e3be5cc82deb414f85404af830759ccd0e0963 100644 (file)
@@ -9,15 +9,8 @@ PACKAGE_TARNAME := @PACKAGE_TARNAME@
 PACKAGE_VERSION := @PACKAGE_VERSION@
 
 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@
@@ -25,7 +18,7 @@ client_objs := @client_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@
@@ -38,6 +31,7 @@ osl_cppflags := @osl_cppflags@
 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@
@@ -58,6 +52,7 @@ opus_ldflags := @opus_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@
index 29c4bae7e8e593debbe45afd62e143b66c75c218..3631a5c9f47569c48d38df3877cea286a12e468b 100644 (file)
@@ -7,6 +7,7 @@ ifeq ("$(origin CC)", "default")
         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
 MKDIR_P := mkdir -p
@@ -24,23 +25,37 @@ ifeq ("$(origin O)", "command line")
 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))
@@ -49,7 +64,7 @@ client_objs := $(addprefix $(object_dir)/, $(client_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))
@@ -68,50 +83,38 @@ all: $(prefixed_executables) $(man_pages)
 
 man: $(man_pages)
 
-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
+STRICT_CFLAGS += -Wformat -Wformat-security -Wmissing-format-attribute
 
 ifeq ($(uname_s),Linux)
        # these cause warnings on *BSD
@@ -138,143 +141,103 @@ else
        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)/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_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 \
@@ -297,14 +260,29 @@ para_play \
        $(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 \
diff --git a/NEWS.md b/NEWS.md
index 39022ad955426c12f68126a875c217b217f90c81..8a9e49802ef60714723cd5cdb533a1853ce2b56a 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -36,6 +36,30 @@ NEWS
   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.
 
 Downloads:
 [tarball](./releases/paraslash-git.tar.bz2),
index e1cf802cb0e4c807e8684957424c7237eee787e4..26d5f652c5d8fbd21933290d55cf0296118b9a17 100644 (file)
@@ -17,7 +17,6 @@
 #include "portable_io.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -160,18 +159,10 @@ err:
        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
+};
diff --git a/acl.c b/acl.c
index 560ff9999d191f418c35695f4a6629b45436f49f..62677711e34f7c00e732bb049c705d08a9a3b8e1 100644 (file)
--- a/acl.c
+++ b/acl.c
@@ -81,7 +81,7 @@ no_match:
  * \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));
 
@@ -139,27 +139,6 @@ char *acl_get_contents(struct list_head *acl)
        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.
  *
diff --git a/acl.h b/acl.h
index 5bfa39f23b2c087456471e06ea3d55b816fffbaf..91efa600cdd32be1ab40cbfea3e68f55cf034498 100644 (file)
--- a/acl.h
+++ b/acl.h
@@ -6,7 +6,7 @@
 
 /** \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);
diff --git a/afh.c b/afh.c
index e6c46c3f9d68471ad31529205a46d4590a92b985..4955b3cfea12e6e7abf93b672d4501cb1e46ca85 100644 (file)
--- a/afh.c
+++ b/afh.c
@@ -7,20 +7,26 @@
 /** \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;
+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)
@@ -37,34 +43,38 @@ static int rewrite_tags(const char *name, int input_fd, void *map,
        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) {
@@ -99,7 +109,7 @@ static int rewrite_tags(const char *name, int input_fd, void *map,
                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);
@@ -147,26 +157,31 @@ static void print_chunk_table(struct afh_info *afhi, int audio_format_id,
                                para_strerror(-ret));
                        return;
                }
-               if (!conf.parser_friendly_given)
+               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 (!conf.parser_friendly_given)
+               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;
-
-       ggo_print_help(&h, flags);
-       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
+       char *help;
+
+       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);
 }
 
@@ -184,32 +199,35 @@ int main(int argc, char **argv)
        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)
+                               if (OPT_GIVEN(CHUNK_TABLE))
                                        print_chunk_table(&afhi, audio_format_num,
                                                audio_file_data, audio_file_size);
                        }
@@ -223,7 +241,9 @@ int main(int argc, char **argv)
                        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;
 }
index 1eac9a5b38c212ae845ce823574ac3d6718cdba6..9d6effe1e25f6cc40ad0ccf5386ee997bce94862 100644 (file)
@@ -8,15 +8,15 @@
 
 #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"
@@ -67,40 +67,25 @@ static int afh_execute(struct btr_node *btrn, const char *cmd, char **result)
        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;
@@ -108,24 +93,23 @@ static int afh_recv_open(struct receiver_node *rn)
        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->first_chunk = afh_get_start_chunk(afhi->chunks_total + bc,
                        &pard->afhi, pard->audio_format_num);
-       if (conf->end_chunk_given) {
+       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);
@@ -162,13 +146,14 @@ static void afh_recv_pre_select(struct sched *s, void *context)
        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;
        }
@@ -180,7 +165,7 @@ static void afh_recv_pre_select(struct sched *s, void *context)
 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;
@@ -189,11 +174,13 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
        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);
@@ -205,7 +192,7 @@ static int afh_recv_post_select(__a_unused struct sched *s, void *context)
                        afh_free_header(header, pard->audio_format_num);
                }
        }
-       if (!conf->just_in_time_given) {
+       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,
@@ -250,26 +237,11 @@ out:
        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,
+};
diff --git a/afs.c b/afs.c
index 7f27b7dd2653d79a0a7679da3e80d2d9c55e37ac..75b82c21317a718fbf42ee4c5abaaed61192fbf7 100644 (file)
--- a/afs.c
+++ b/afs.c
 #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"
@@ -235,7 +238,7 @@ int send_callback_request(afs_callback *f, struct osl_object *query,
        *(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;
@@ -280,73 +283,33 @@ out:
 }
 
 /**
- * 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)
@@ -354,30 +317,37 @@ 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;
 }
@@ -590,14 +560,17 @@ static void flush_and_free_pb(struct para_buffer *pb)
 
 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();
@@ -622,22 +595,26 @@ static int com_select_callback(struct afs_callback_arg *aca)
 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 */
@@ -646,7 +623,7 @@ static void init_admissible_files(char *arg)
 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);
@@ -673,8 +650,8 @@ static char *database_dir;
 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(
@@ -883,7 +860,7 @@ static int execute_server_command(fd_set *rfds)
                return ret;
        buf[n] = '\0';
        if (strcmp(buf, "new"))
-               return -E_BAD_CMD;
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        return open_next_audio_file();
 }
 
@@ -1015,7 +992,7 @@ __noreturn void afs_init(uint32_t cookie, int socket_fd)
                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;
@@ -1065,23 +1042,24 @@ out:
        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;
@@ -1093,77 +1071,37 @@ int com_init(struct command_context *cc)
        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)
@@ -1171,6 +1109,7 @@ int com_check(struct command_context *cc)
        }
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(check);
 
 /**
  * The afs event dispatcher.
diff --git a/afs.cmd b/afs.cmd
deleted file mode 100644 (file)
index 76b5f4d..0000000
--- a/afs.cmd
+++ /dev/null
@@ -1,304 +0,0 @@
-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.
diff --git a/afs.h b/afs.h
index 2f9d7e5fc40bdc9c1661336ffce20d7a7df76148..879331ec85b244a31fb56287ce15e2192701d911 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -145,8 +145,10 @@ struct pattern_match_data {
        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. */
@@ -163,6 +165,7 @@ struct afs_callback_arg {
        struct osl_object query;
        /** Will be written on band SBD_OUTPUT, fully buffered. */
        struct para_buffer pbout;
+       struct lls_parse_result *lpr;
 };
 
 /**
@@ -221,13 +224,9 @@ __must_check int afs_event(enum afs_events event, struct para_buffer *pb,
 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);
 
diff --git a/aft.c b/aft.c
index 22890e569c0fdc73561644d1a346f7792853b38e..4a8124481b551b71c584a9483400420f270949d0 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -11,7 +11,9 @@
 #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"
@@ -86,18 +88,6 @@ struct ls_data {
        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.
  *
@@ -128,16 +118,11 @@ struct ls_widths {
 
 /** 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. */
@@ -793,11 +778,11 @@ static void write_image_items(struct para_buffer *b, struct afs_info *afsi)
 }
 
 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;
        }
@@ -842,17 +827,12 @@ static int print_chunk_table(struct ls_data *d, struct para_buffer *b)
        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];
@@ -871,7 +851,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                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 {
@@ -883,10 +863,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        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 */
@@ -926,8 +905,9 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        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;
@@ -996,21 +976,23 @@ out:
 
 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(&current_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;
@@ -1023,7 +1005,10 @@ static int make_status_items(void)
        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;
 }
 
 /**
@@ -1185,8 +1170,16 @@ static int ls_path_compare(const void *a, const void *b)
        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 *);
@@ -1197,13 +1190,13 @@ static int sort_matching_paths(struct ls_options *options)
                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:
@@ -1241,15 +1234,16 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
 {
        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;
@@ -1260,21 +1254,22 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        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++;
@@ -1313,7 +1308,7 @@ static int prepare_ls_row(struct osl_row *row, void *ls_opts)
        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);
@@ -1326,21 +1321,19 @@ err:
 
 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,
@@ -1348,14 +1341,14 @@ static int com_ls_callback(struct afs_callback_arg *aca)
        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(&current_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);
@@ -1370,143 +1363,90 @@ static int com_ls_callback(struct afs_callback_arg *aca)
                                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.
@@ -1546,8 +1486,8 @@ enum com_add_buffer_offsets {
        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. */
@@ -1564,31 +1504,32 @@ enum com_add_buffer_offsets {
  * 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;
 }
@@ -1644,18 +1585,6 @@ Notes:
 
 */
 
-/** 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;
@@ -1666,9 +1595,16 @@ static int com_add_callback(struct afs_callback_arg *aca)
        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);
@@ -1686,8 +1622,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
        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;
@@ -1695,7 +1631,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        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)
@@ -1706,7 +1642,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                        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)
@@ -1721,7 +1657,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                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 */
@@ -1742,18 +1678,18 @@ static int com_add_callback(struct afs_callback_arg *aca)
                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,
@@ -1766,12 +1702,10 @@ static int com_add_callback(struct afs_callback_arg *aca)
                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);
@@ -1786,15 +1720,20 @@ static int com_add_callback(struct afs_callback_arg *aca)
 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)
@@ -1839,9 +1778,13 @@ static int add_one_audio_file(const char *path, void *private_data)
        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;
        }
@@ -1852,8 +1795,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        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;
@@ -1873,8 +1816,8 @@ static int add_one_audio_file(const char *path, void *private_data)
                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;
@@ -1883,7 +1826,7 @@ static int add_one_audio_file(const char *path, void *private_data)
         * 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;
@@ -1892,13 +1835,14 @@ static int add_one_audio_file(const char *path, void *private_data)
        }
        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);
@@ -1917,46 +1861,30 @@ out_free:
        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 */
@@ -1972,8 +1900,12 @@ int com_add(struct command_context *cc)
                }
                free(path);
        }
-       return 1;
+       ret = 1;
+out:
+       free(pad.slpr);
+       return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(add);
 
 /**
  * Flags used by the touch command.
@@ -1987,33 +1919,26 @@ enum touch_flags {
        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);
@@ -2028,23 +1953,23 @@ static int touch_audio_file(__a_unused struct osl_table *table,
        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;
@@ -2054,134 +1979,73 @@ static int touch_audio_file(__a_unused struct osl_table *table,
 
 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)
@@ -2194,95 +2058,63 @@ static int remove_audio_file(__a_unused struct osl_table *table,
 
 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,
@@ -2293,6 +2125,14 @@ 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)
@@ -2301,18 +2141,18 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        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;
@@ -2321,86 +2161,60 @@ static int copy_selector_info(__a_unused struct osl_table *table,
 
 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;
@@ -2434,9 +2248,8 @@ static int change_atts(__a_unused struct osl_table *table,
 
 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,
@@ -2446,27 +2259,36 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                .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 == '+')
@@ -2474,29 +2296,32 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                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)
 {
index 3adee929a804434fcea1999a439819dc85c66936..6520416fb83931ede15a299118a972f5893b2f34 100644 (file)
@@ -216,19 +216,13 @@ static int alsa_mix_set(struct mixer_handle *h, int val)
        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
+};
index fd3b404c0359c1a571255b1ac7265b0ee0faef41..3935c8c687be53e25791abbb93697a8d61186527 100644 (file)
 #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. */
@@ -71,22 +70,22 @@ static snd_pcm_format_t get_alsa_pcm_format(enum sample_format sf)
 }
 
 /* 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);
@@ -116,7 +115,8 @@ static int alsa_init(struct private_alsa_write_data *pad,
        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);
@@ -295,7 +295,7 @@ again:
 
                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);
@@ -305,12 +305,12 @@ again:
 
                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;
        }
@@ -340,37 +340,9 @@ err:
        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,
+};
index 5193d7c166d9345ced27899e38714ddd5d393fcf..f3d0d87d45f077d901d5f4cb98c20dc9a3099547 100644 (file)
@@ -7,12 +7,12 @@
 /** \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"
@@ -31,33 +31,18 @@ static void amp_close(struct filter_node *fn)
        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);
 }
@@ -116,26 +101,9 @@ err:
        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,
+};
index 82d98f3ee8cf6c0491e1820ee32b63c8216abac2..e469a394fa6ee8df698a633274cf16394891e102 100644 (file)
@@ -9,17 +9,16 @@
 #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 {
@@ -44,6 +43,7 @@ static void aow_close(struct writer_node *wn)
        ao_close(pawd->dev);
        free(pawd);
        wn->private_data = NULL;
+       ao_shutdown();
 }
 
 static void aow_pre_select(struct sched *s, void *context)
@@ -147,6 +147,38 @@ static int aow_open_device(int id, ao_sample_format *asf, ao_option *options,
        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)
 {
@@ -154,12 +186,15 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
        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();
@@ -173,8 +208,11 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate,
                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, ':');
@@ -379,78 +417,9 @@ out:
        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,
+};
 
index 4cb2982860de78dd26b9fc55ad552c73a7ca1b9f..637e1f519b863ed346b9bfba01471eab36bb6b3c 100644 (file)
@@ -8,7 +8,9 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -106,30 +108,16 @@ int get_attribute_bitnum_by_name(const char *att_name, unsigned char *bitnum)
        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;
        }
@@ -145,21 +133,27 @@ static int print_attribute(struct osl_table *table, struct osl_row *row,
 
 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)
@@ -167,70 +161,49 @@ static int com_lsatt_callback(struct afs_callback_arg *aca)
        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 */
@@ -251,12 +224,12 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
                        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)
@@ -265,53 +238,67 @@ static int com_addatt_callback(struct afs_callback_arg *aca)
        }
 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)
@@ -336,31 +323,41 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
 
 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.
index f8fd80faa77bdea4a4f1139bb8168268ce59ddc3..38a8db5dfb075e839b69042195fbdc6afb003455 100644 (file)
--- a/audioc.c
+++ b/audioc.c
 #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);
@@ -41,7 +48,8 @@ static char *concat_args(unsigned argc, char * const *argv)
        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");
        }
@@ -72,7 +80,6 @@ fail:
 #include "sched.h"
 #include "buffer_tree.h"
 #include "interactive.h"
-#include "audiod.completion.h"
 
 static struct sched sched;
 
@@ -100,7 +107,7 @@ static void help_completer(struct i9e_completion_info *ci,
 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);
@@ -110,7 +117,7 @@ static void stat_completer(struct i9e_completion_info *ci,
                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);
@@ -121,12 +128,16 @@ static void stat_completer(struct i9e_completion_info *ci,
 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}
 };
 
@@ -145,13 +156,15 @@ static int audioc_post_select(struct sched *s, void *context)
        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;
@@ -172,28 +185,26 @@ static struct audioc_task audioc_task, *at = &audioc_task;
 
 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) {
@@ -206,8 +217,6 @@ static int audioc_i9e_line_handler(char *line)
        return 1;
 close:
        close(at->fd);
-out:
-       free(args);
        return ret;
 }
 
@@ -223,9 +232,10 @@ __noreturn static void interactive_session(void)
                .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",
@@ -251,7 +261,7 @@ __noreturn static void interactive_session(void)
        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);
@@ -293,13 +303,19 @@ static char *configfile_exists(void)
        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);
 }
 
 /**
@@ -320,54 +336,79 @@ __noreturn static void print_help_and_die(void)
  */
 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, &params);
-               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);
@@ -375,9 +416,14 @@ int main(int argc, char *argv[])
                        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;
index 517958474f39077ebbbf3b6557d4bab6a6ebf82c..ead29e86b96dae01921ab4167f7231e308e7b76e 100644 (file)
--- a/audiod.c
+++ b/audiod.c
 #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;
 };
@@ -88,6 +96,9 @@ struct slot_info {
        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
 
@@ -161,19 +172,10 @@ char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
  */
 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;
 
 /**
@@ -382,58 +384,82 @@ empty:
 
 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, &params);
-               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)
@@ -464,7 +490,7 @@ static void close_receiver(int slot_num)
        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);
@@ -476,13 +502,10 @@ static void close_receiver(int slot_num)
 
 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);
 }
@@ -577,17 +600,20 @@ static void open_filters(struct slot_info *s)
        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,
@@ -596,7 +622,7 @@ static void open_filters(struct slot_info *s)
                }, &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));
        }
 }
 
@@ -612,11 +638,11 @@ static void open_writers(struct slot_info *s)
                * 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));
        }
 }
 
@@ -626,7 +652,8 @@ static int open_receiver(int format)
        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);
@@ -636,9 +663,9 @@ static int open_receiver(int format)
        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);
@@ -649,9 +676,9 @@ static int open_receiver(int format)
        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,
@@ -814,7 +841,7 @@ static int update_item(int itemnum, char *buf)
        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, ':');
@@ -831,38 +858,41 @@ static int parse_stream_command(const char *txt, char **cmd)
        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;
@@ -870,47 +900,45 @@ static int parse_writer_args(void)
                        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;
@@ -924,37 +952,27 @@ static int parse_receiver_args(void)
                         * 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);
        }
        FOR_EACH_AUDIO_FORMAT(i) {
                a = afi + i;
                PARA_INFO_LOG("receiving %s streams via %s receiver\n",
-                       audio_formats[i], a->receiver->name);
+                       audio_formats[i], lls_command_name(RECEIVER_CMD(a)));
        }
        ret = 1;
 out:
-       free(cmd);
        return ret;
 }
 
@@ -964,6 +982,7 @@ static int init_default_filters(void)
 
        FOR_EACH_AUDIO_FORMAT(i) {
                struct audio_format_info *a = &afi[i];
+               const char *name = lls_command_name(RECEIVER_CMD(a));
                char *tmp;
                int j;
 
@@ -973,8 +992,7 @@ static int init_default_filters(void)
                 * udp and dccp streams are fec-encoded, so add fecdec as the
                 * first filter.
                 */
-               if (strcmp(afi[i].receiver->name, "udp") == 0 ||
-                               strcmp(afi[i].receiver->name, "dccp") == 0) {
+               if (strcmp(name, "udp") == 0 || strcmp(name, "dccp") == 0) {
                        tmp = para_strdup("fecdec");
                        add_filter(i, tmp);
                        free(tmp);
@@ -984,19 +1002,19 @@ static int init_default_filters(void)
                /* add "dec" to audio format name */
                tmp = make_message("%sdec", audio_formats[i]);
                for (j = 0; filter_get(j); j++)
-                       if (!strcmp(tmp, filter_get(j)->name))
+                       if (!strcmp(tmp, filter_name(j)))
                                break;
                free(tmp);
                ret = -E_UNSUPPORTED_FILTER;
                if (!filter_get(j))
                        goto out;
-               tmp = para_strdup(filter_get(j)->name);
+               tmp = para_strdup(filter_name(j));
                ret = add_filter(i, tmp);
                free(tmp);
                if (ret < 0)
                        goto out;
                PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i],
-                       filter_get(j)->name);
+                       filter_name(j));
        }
 out:
        return ret;
@@ -1006,9 +1024,10 @@ static int parse_filter_args(void)
 {
        int i, j, ret, af_mask, num_matches;
 
-       for (i = 0; i < conf.filter_given; i++) {
-               char *arg;
-               ret = parse_stream_command(conf.filter_arg[i], &arg);
+       for (i = 0; i < OPT_GIVEN(FILTER); i++) {
+               const char *arg;
+               ret = parse_stream_command(lls_string_val(i,
+                       OPT_RESULT(FILTER)), &arg);
                if (ret < 0)
                        goto out;
                af_mask = ret;
@@ -1023,7 +1042,7 @@ static int parse_filter_args(void)
                }
                if (num_matches == 0)
                        PARA_WARNING_LOG("ignoring filter spec: %s\n",
-                               conf.filter_arg[i]);
+                               lls_string_val(i, OPT_RESULT(FILTER)));
        }
        ret = init_default_filters(); /* use default values for the rest */
 out:
@@ -1049,8 +1068,8 @@ static int parse_stream_args(void)
 /* does not unlink socket on errors */
 static void init_local_socket(struct command_task *ct)
 {
-       if (conf.socket_given)
-               socket_name = para_strdup(conf.socket_arg);
+       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",
@@ -1058,7 +1077,7 @@ static void init_local_socket(struct command_task *ct)
                free(hn);
        }
        PARA_NOTICE_LOG("local socket: %s\n", socket_name);
-       if (conf.force_given)
+       if (OPT_GIVEN(FORCE))
                unlink(socket_name);
        ct->fd = create_local_socket(socket_name);
        if (ct->fd >= 0)
@@ -1233,7 +1252,6 @@ static void audiod_cleanup(void)
                unlink(socket_name);
        close_stat_pipe();
        close_unused_slots();
-       audiod_cmdline_parser_free(&conf);
        close_stat_clients();
        free(uid_whitelist);
 }
@@ -1314,7 +1332,7 @@ static int status_post_select(struct sched *s, void *context)
                        goto out;
                }
                close_stat_pipe();
-               st->clock_diff_count = conf.clock_diff_count_arg;
+               st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
                goto out;
        }
        if (st->ct) {
@@ -1382,7 +1400,7 @@ static void init_status_task(struct status_task *st)
 {
        memset(st, 0, sizeof(struct status_task));
        st->sa_time_diff_sign = 1;
-       st->clock_diff_count = conf.clock_diff_count_arg;
+       st->clock_diff_count = OPT_UINT32_VAL(CLOCK_DIFF_COUNT);
        st->current_audio_format_num = -1;
        st->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stat"));
@@ -1398,36 +1416,20 @@ static void init_status_task(struct status_task *st)
 static void set_initial_status(void)
 {
        audiod_status = AUDIOD_ON;
-       if (!conf.mode_given)
+       if (!OPT_GIVEN(MODE))
                return;
-       if (!strcmp(conf.mode_arg, "sb")) {
+       if (!strcmp(OPT_STRING_VAL(MODE), "sb")) {
                audiod_status = AUDIOD_STANDBY;
                return;
        }
-       if (!strcmp(conf.mode_arg, "off")) {
+       if (!strcmp(OPT_STRING_VAL(MODE), "off")) {
                audiod_status = AUDIOD_OFF;
                return;
        }
-       if (strcmp(conf.mode_arg, "on"))
+       if (strcmp(OPT_STRING_VAL(MODE), "on"))
                PARA_WARNING_LOG("invalid mode\n");
 }
 
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(audiod);
-       bool d = conf.detailed_help_given;
-       unsigned flags;
-
-       flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
-       ggo_print_help(&h, flags);
-
-       flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
-       print_receiver_helps(flags);
-       print_filter_helps(flags);
-       print_writer_helps(flags);
-       exit(0);
-}
-
 /**
  * Lookup the given UID in the whitelist.
  *
@@ -1443,14 +1445,33 @@ bool uid_is_whitelisted(uid_t uid)
 {
        int i;
 
-       if (!conf.user_allow_given)
+       if (!OPT_GIVEN(USER_ALLOW))
                return true;
-       for (i = 0; i < conf.user_allow_given; i++)
+       for (i = 0; i < OPT_GIVEN(USER_ALLOW); i++)
                if (uid == uid_whitelist[i])
                        return true;
        return false;
 }
 
+static void handle_help_flags(void)
+{
+       char *help;
+       bool d = OPT_GIVEN(DETAILED_HELP);
+
+       if (d)
+               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);
+       print_receiver_helps(d);
+       print_filter_helps(d);
+       print_writer_helps(d);
+       exit(EXIT_SUCCESS);
+}
+
 /**
  * the main function of para_audiod
  *
@@ -1465,40 +1486,34 @@ int main(int argc, char *argv[])
 {
        int ret, i;
        struct command_task command_task_struct, *cmd_task = &command_task_struct;
-       struct audiod_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
+       char *errctx;
 
        valid_fd_012();
-       audiod_cmdline_parser_ext(argc, argv, &conf, &params);
-       daemon_set_loglevel(conf.loglevel_arg);
-       version_handle_flag("audiod", conf.version_given);
-       /* init receivers/filters/writers early to make help work */
-       recv_init();
-       filter_init();
-       writer_init();
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       daemon_set_priority(conf.priority_arg);
-       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+               OPT_STRING_VAL(GROUP));
+       version_handle_flag("audiod", OPT_GIVEN(VERSION));
+       handle_help_flags();
        parse_config_or_die();
-       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
-               conf.logfile_given)) {
-                       for (i = 0; i < conf.log_color_given; i++)
-                               daemon_set_log_color_or_die(conf.log_color_arg[i]);
-       }
        init_random_seed_or_die();
+       daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+       recv_init();
+       if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+                       COLOR_NO, OPT_GIVEN(LOGFILE))) {
+               for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+                       daemon_set_log_color_or_die(lls_string_val(i,
+                               OPT_RESULT(LOG_COLOR)));
+       }
        daemon_set_flag(DF_LOG_TIME);
        daemon_set_flag(DF_LOG_HOSTNAME);
        daemon_set_flag(DF_LOG_LL);
-       if (conf.log_timing_given)
+       if (OPT_GIVEN(LOG_TIMING))
                daemon_set_flag(DF_LOG_TIMING);
-       if (conf.logfile_given) {
-               daemon_set_logfile(conf.logfile_arg);
+       if (OPT_GIVEN(LOGFILE)) {
+               daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        }
        ret = parse_stream_args();
@@ -1516,7 +1531,7 @@ int main(int argc, char *argv[])
        init_status_task(stat_task);
        init_command_task(cmd_task);
 
-       if (conf.daemon_given)
+       if (OPT_GIVEN(DAEMON))
                daemonize(false /* parent exits immediately */);
 
        signal_task->task = task_register(&(struct task_info) {
@@ -1533,6 +1548,10 @@ int main(int argc, char *argv[])
        sched_shutdown(&sched);
        signal_shutdown(signal_task);
 
+out:
+       lls_free_parse_result(lpr, CMD_PTR);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
        if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/audiod.cmd b/audiod.cmd
deleted file mode 100644 (file)
index 18c802d..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-BN: audiod
-SF: audiod_command.c
-SN: list of audiod commands
----
-N: cycle
-D: switch to next mode
-U: cycle
-H: on -> standby -> off -> on
----
-N: grab
-D: grab the audio stream
-L:
-U: -- grab [-m[{s|p|a}]] [-p=PARENT] [-n=NAME] [-o]
-H:
-H: grab ('splice') the audio stream at any position in the buffer
-H: tree and send that data back to the client.
-H:
-H: Options:
-H:
-H: -m  Change grab mode. Defaults to sloppy grab if not given.
-H:
-H:    -ms: sloppy grab
-H:    -mp: pedantic grab
-H:    -ma: aggressive grab
-H:
-H: The various grab modes only differ in what happens if an attempt to
-H: write the grabbed audio data would block. Sloppy mode ignores the
-H: write, pedantic mode aborts and aggressive mode tries to write anyway.
-H:
-H: -p  Grab output of node PARENT of the buffer tree.
-H:
-H: -n  Name of the new buffer tree node. Defaults to 'grab'.
-H:
-H: -o  One-shot mode: Stop grabbing if audio file changes.
----
-N: help
-D: display command list or help for given command
-U: help [command]
-H: When I was younger, so much younger than today, I never needed anybody's help
-H: in any way. But now these days are gone, I'm not so self assured. Now I find
-H: I've changed my mind and opened up the doors.
-H:                                                              -- Beatles: Help
----
-N: off
-D: deactivate para_audiod
-U: off
-H: Close connection to para_server and stop all decoders.
----
-N: on
-D: activate para_audiod
-U: on
-H: Establish connection to para_server, retrieve para_server's current status. If
-H: playing, start corresponding decoder. Otherwise stop all decoders.
----
-N: sb
-D: enter standby mode
-U: sb
-H: Stop all decoders but leave connection to para_server open.
----
-N: stat
-D: print status information
-U: stat [-p] [item1 ...]
-H: Dump given status items (all if none given) to stdout. If -p is given, use
-H: parser-friendly mode.
----
-N: tasks
-D: list current tasks
-U: tasks
-H: Print the list of task ids together with the status of each task.
----
-N: term
-D: terminate audiod
-U: term
-H: Stop all decoders, shut down connection to para_server and exit.
----
-N: version
-D: print the version of para_audiod
-U: version [-v]
-H: If the -v option is given, a more detailed version text is printed.
index 7073c6dd2108fb31b915724fdbed431d76d51e63..295a02bc44ddb339932539d276f570f3ad785b10 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -13,15 +13,6 @@ enum {AUDIOD_AUDIO_FORMATS_ENUM};
 /** array of audio format names supported by para_audiod */
 extern const char *audio_formats[];
 
-/**
- * the possible modes of operation
- *
- * - off: disconnect from para_server
- * - on: receive status information from para_server and play the audio stream
- * - sb: only receive status information but not the audio stream
- */
-enum audiod_status_info {AUDIOD_OFF, AUDIOD_ON, AUDIOD_STANDBY};
-
 extern int audiod_status;
 
 /* defined in audiod.c */
index 3a39027523a72c686d997d1b358912dc5dfd2363..9623c4f51453c81deb6f11d97a218bcb8696c987 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "audiod.lsg.h"
 #include "para.h"
-#include "audiod.cmdline.h"
-#include "audiod.command_list.h"
+#include "audiod_cmd.lsg.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
-typedef int audiod_command_handler_t(int, int, char **);
-static audiod_command_handler_t AUDIOD_COMMAND_HANDLERS;
-
-/* Defines one command of para_audiod. */
-struct audiod_command {
-       const char *name;
-       /* Pointer to the function that handles the command. */
-       /*
-        * Command handlers must never never close their file descriptor. A
-        * positive return value tells audiod that the status items have
-        * changed. In this case audiod will send an updated version of all
-        * status items to to each connected stat client.
-        */
-       audiod_command_handler_t *handler;
-       /* One-line description. */
-       const char *description;
-       /* Summary of the command line options. */
-       const char *usage;
-       /* The long help text. */
-       const char *help;
-};
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
 
-static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
 
-/** Iterate over the array of all audiod commands. */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+       audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
 
-/** The maximal number of simultaneous connections. */
-#define MAX_STAT_CLIENTS 50
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+       /** Implementation of _cmd. */ \
+       const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 /** Flags used for the stat command of para_audiod. */
 enum stat_client_flags {
@@ -245,10 +233,11 @@ static int dump_commands(int fd)
        char *buf = para_strdup(""), *tmp = NULL;
        int i;
        ssize_t ret;
+       const struct lls_command *cmd;
 
-       FOR_EACH_COMMAND(i) {
-               tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
-                       audiod_cmds[i].description);
+       for (i = 1; (cmd = lls_cmd(i, audiod_cmd_suite)); i++) {
+               tmp = make_message("%s%s\t%s\n", buf, lls_command_name(cmd),
+                       lls_purpose(cmd));
                free(buf);
                buf = tmp;
        }
@@ -257,76 +246,80 @@ static int dump_commands(int fd)
        return ret;
 }
 
-static int com_help(int fd, int argc, char **argv)
+static int com_help(int fd, struct lls_parse_result *lpr)
 {
-       int i, ret;
-       char *buf;
-
-       if (argc < 2) {
-               ret = dump_commands(fd);
-               goto out;
+       int ret;
+       char *buf, *errctx;
+       const struct lls_command *cmd;
+
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx) {
+                       buf = make_message("%s\n", errctx);
+                       client_write(fd, buf);
+                       free(buf);
+                       free(errctx);
+               }
+               return ret;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[1]))
-                       continue;
-               buf = make_message(
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\tpara_audioc %s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       audiod_cmds[i].description,
-                       audiod_cmds[i].usage,
-                       audiod_cmds[i].help
-               );
-               ret = client_write(fd, buf);
+       if (lls_num_inputs(lpr) == 0)
+               return dump_commands(fd);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), audiod_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               buf = make_message("%s: %s\nAvailable commands:\n", errctx?
+                       errctx : lls_input(0, lpr), para_strerror(-ret));
+               if (client_write(fd, buf) >= 0)
+                       dump_commands(fd);
+               free(errctx);
                free(buf);
                goto out;
        }
-       ret = client_write(fd, "No such command. Available commands:\n");
-       if (ret > 0)
-               ret = dump_commands(fd);
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       ret = client_write(fd, buf);
+       free(buf);
 out:
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(help)
 
-static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
+       int ret;
        char *tl = get_task_list(&sched);
-       int ret = 1;
 
-       if (tl)
-               ret = client_write(fd, tl);
+       if (!tl) /* no tasks registered yet */
+               return 0;
+       ret = client_write(fd, tl);
        free(tl);
-       return ret < 0? ret : 0;
+       return ret;
 }
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
-static int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
+       const struct lls_opt_result *r;
+       unsigned num_inputs;
 
        ret = mark_fd_nonblocking(fd);
        if (ret < 0)
                return ret;
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-p", 2)) {
-                       parser_friendly = 1;
-                       b.flags = PBF_SIZE_PREFIX;
-               }
+       r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+       if (lls_opt_given(r) > 0) {
+               parser_friendly = 1;
+               b.flags = PBF_SIZE_PREFIX;
        }
-       if (i >= argc)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                mask--; /* set all bits */
-       for (; i < argc; i++) {
-               ret = stat_item_valid(argv[i]);
+       for (i = 0; i < num_inputs; i++) {
+               ret = stat_item_valid(lls_input(i, lpr));
                if (ret < 0)
                        return ret;
                mask |= (one << ret);
@@ -344,58 +337,61 @@ static int com_stat(int fd, int argc, char **argv)
        free(b.buf);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(stat)
 
-static int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
 {
-       int ret = grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, lpr, &sched);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(grab)
 
-static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        return -E_AUDIOD_TERM;
 }
+EXPORT_AUDIOD_CMD_HANDLER(term)
 
-static int com_on(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_ON;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(on)
 
-static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_OFF;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(off)
 
-static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_STANDBY;
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(sb)
 
-static int com_cycle(__a_unused int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        switch (audiod_status) {
-               case  AUDIOD_ON:
-                       return com_sb(fd, argc, argv);
-                       break;
-               case  AUDIOD_OFF:
-                       return com_on(fd, argc, argv);
-                       break;
-               case  AUDIOD_STANDBY:
-                       return com_off(fd, argc, argv);
-                       break;
+               case  AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+               case  AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+               case  AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
        }
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
 
-static int com_version(int fd, int argc, char **argv)
+static int com_version(int fd, struct lls_parse_result *lpr)
 {
        int ret;
        char *msg;
+       const struct lls_opt_result *r_v;
 
-       if (argc > 1 && strcmp(argv[1], "-v") == 0)
+       r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+       if (lls_opt_given(r_v))
                msg = make_message("%s", version_text("audiod"));
        else
                msg = make_message("%s\n", version_single_line("audiod"));
@@ -403,6 +399,7 @@ static int com_version(int fd, int argc, char **argv)
        free(msg);
        return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(version)
 
 /**
  * Handle arriving connections on the local socket.
@@ -423,10 +420,14 @@ static int com_version(int fd, int argc, char **argv)
  * */
 int handle_connect(int accept_fd, fd_set *rfds)
 {
-       int i, argc, ret, clifd;
+       int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr;
+       char *errctx = NULL;
+       const struct audiod_command_info *aci;
 
        ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
@@ -443,18 +444,27 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret <= 0)
                goto out;
        argc = ret;
-       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[0]))
-                       continue;
-               ret = audiod_cmds[i].handler(clifd, argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       ret = -E_INVALID_AUDIOD_CMD;
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       aci = lls_user_data(cmd);
+       ret = aci->handler(clifd, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
        free_argv(argv);
        if (ret < 0 && ret != -E_CLIENT_WRITE) {
-               char *tmp = make_message("%s\n", para_strerror(-ret));
+               char *tmp;
+               if (errctx) {
+                       tmp = make_message("%s\n", errctx);
+                       free(errctx);
+                       client_write(clifd, tmp);
+                       free(tmp);
+               }
+               tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
        }
diff --git a/blob.c b/blob.c
index ed684428aba55ae848c58fe8c96af2c900f4d6b9..8abecf6aa7301318426de7f44ca9bb96f3bd734b 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -9,7 +9,9 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -90,26 +92,16 @@ INIT_BLOB_TABLE(moods);
 INIT_BLOB_TABLE(playlists);
 /** \endcond blob_table */
 
-/** Flags that may be passed to the \p ls functions of each blob  type. */
-enum blob_ls_flags {
-       /** List both id and name. */
-       BLOB_LS_FLAG_LONG = 1,
-       /** Reverse sort order. */
-       BLOB_LS_FLAG_REVERSE = 2,
-       /** Sort by id instead of name. */
-       BLOB_LS_FLAG_SORT_BY_ID = 4,
-};
-
 static int print_blob(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 l_given = SERVER_CMD_OPT_GIVEN(LSMOOD, LONG, aca->lpr);
        struct osl_object obj;
        uint32_t id;
        int ret;
 
-       if (!(flags & BLOB_LS_FLAG_LONG)) {
+       if (!l_given) {
                para_printf(&aca->pbout, "%s\n", name);
                return 0;
        }
@@ -123,68 +115,44 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_lsblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_lsblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       uint32_t flags = *(uint32_t *)aca->query.data;
+       bool i_given, r_given;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
-                       .size = aca->query.size - sizeof(uint32_t)},
                .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING | PM_SKIP_EMPTY_NAME,
                .match_col_num = BLOBCOL_NAME,
                .data = aca,
                .action = print_blob,
        };
        int ret;
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       pmd.lpr = aca->lpr;
+       assert(ret >= 0);
+       i_given = SERVER_CMD_OPT_GIVEN(LSMOOD, ID_SORT, aca->lpr);
+       r_given = SERVER_CMD_OPT_GIVEN(LSMOOD, REVERSE, aca->lpr);
 
-       if (flags & BLOB_LS_FLAG_REVERSE)
+       if (r_given)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       if (!(flags & BLOB_LS_FLAG_SORT_BY_ID))
-               pmd.loop_col_num = BLOBCOL_NAME;
-       else
+       if (i_given)
                pmd.loop_col_num = BLOBCOL_ID;
+       else
+               pmd.loop_col_num = BLOBCOL_NAME;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
-       if (pmd.num_matches == 0 && pmd.patterns.size > 0)
+       if (pmd.num_matches == 0 && lls_num_inputs(aca->lpr) > 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_lsblob(afs_callback *f, struct command_context *cc)
+static int com_lsblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       uint32_t 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, "-l")) {
-                       flags |= BLOB_LS_FLAG_LONG;
-                       continue;
-               }
-               if (!strcmp(arg, "-i")) {
-                       flags |= BLOB_LS_FLAG_SORT_BY_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-r")) {
-                       flags |= BLOB_LS_FLAG_REVERSE;
-                       continue;
-               }
-               break;
-       }
-//     if (argc > i)
-//             return -E_BLOB_SYNTAX;
-       return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, f, afs_cb_result_handler, cc);
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
 static int cat_blob(struct osl_table *table, struct osl_row *row,
@@ -202,33 +170,42 @@ static int cat_blob(struct osl_table *table, struct osl_row *row,
        return (ret < 0)? ret : ret2;
 }
 
-static int com_catblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_catblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = &aca->fd,
                .action = cat_blob
        };
+       ret = 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)
-               return ret;
+               goto out;
        if (pmd.num_matches == 0)
                ret = -E_NO_MATCH;
+out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_catblob(afs_callback *f, struct command_context *cc)
+static int com_catblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_BLOB_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       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(f, cmd, lpr, cc);
 }
 
 static int remove_blob(struct osl_table *table, struct osl_row *row,
@@ -243,19 +220,21 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_rmblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_rmblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = aca,
                .action = remove_blob
        };
+       ret = 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;
@@ -267,19 +246,25 @@ static int com_rmblob_callback(struct osl_table *table,
                ret = afs_event(BLOB_REMOVE, NULL, table);
        }
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_rmblob(afs_callback *f, struct command_context *cc)
+static int com_rmblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       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(f, cmd, lpr, cc);
 }
 
-static int com_addblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = aca->query.data;
@@ -399,11 +384,12 @@ again:
  * the name of the blob to create. The combined buffer is made available to the
  * afs process via the callback method.
  */
-static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
-               afs_callback *f)
+static int stdin_command(struct command_context *cc,
+               struct lls_parse_result *lpr, afs_callback *f)
 {
        struct osl_object query, stdin_obj;
        int ret;
+       size_t len = strlen(lls_input(0, lpr));
 
        ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
        if (ret < 0)
@@ -411,11 +397,11 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        ret = fd2buf(&cc->scc, &stdin_obj);
        if (ret < 0)
                return ret;
-       query.size = arg_obj->size + stdin_obj.size;
+       query.size = len + 1 + stdin_obj.size;
        query.data = para_malloc(query.size);
-       memcpy(query.data, arg_obj->data, arg_obj->size);
+       memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
-               memcpy((char *)query.data + arg_obj->size, stdin_obj.data,
+               memcpy((char *)query.data + len + 1, stdin_obj.data,
                        stdin_obj.size);
        free(stdin_obj.data);
        ret = send_callback_request(f, &query, afs_cb_result_handler, cc);
@@ -423,33 +409,42 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        return ret;
 }
 
-static int com_addblob(afs_callback *f, struct command_context *cc)
+static int com_addblob(afs_callback *f, __a_unused const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct osl_object arg_obj;
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
 
-       if (cc->argc != 2)
-               return -E_BLOB_SYNTAX;
-       if (!*cc->argv[1]) /* empty name is reserved for the dummy row */
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (!lls_input(0, lpr)[0]) /* empty name is reserved for the dummy row */
                return -E_BLOB_SYNTAX;
-       arg_obj.size = strlen(cc->argv[1]) + 1;
-       arg_obj.data = (char *)cc->argv[1];
-       return stdin_command(cc, &arg_obj, f);
+       return stdin_command(cc, lpr, f);
 }
 
-static int com_mvblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_mvblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       char *src = (char *)aca->query.data;
-       struct osl_object obj = {.data = src, .size = strlen(src) + 1};
-       char *dest = src + obj.size;
+       const char *src, *dest;
+       struct osl_object obj;
        struct osl_row *row;
-       int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
+       int ret;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       src = lls_input(0, aca->lpr);
+       dest = lls_input(1, aca->lpr);
+       obj.data = (char *)src;
+       obj.size = strlen(src) + 1;
+       ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot find source blob %s\n", src);
                goto out;
        }
-       obj.data = dest;
+       obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
@@ -459,26 +454,35 @@ static int com_mvblob_callback(struct osl_table *table,
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_mvblob(afs_callback *f, struct command_context *cc)
+static int com_mvblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc != 3)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1,
-               cc->argv + 1, f, afs_cb_result_handler, cc);
+       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(f, cmd, lpr, cc);
 }
 
-#define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
-       static int com_ ## cmd_name ## cmd_prefix ## _callback(struct afs_callback_arg *aca) \
+#define DEFINE_BLOB_COMMAND(cmd_name, c_cmd_name, table_name, short_name, c_short_name) \
+       static int com_ ## cmd_name ## short_name ## _callback(struct afs_callback_arg *aca) \
        { \
-               return com_ ## cmd_name ## blob_callback(table_name ## _table, aca); \
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob_callback(cmd, table_name ## _table, aca); \
        } \
-       int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
+       static int com_ ## cmd_name ## short_name(struct command_context *cc, struct lls_parse_result *lpr) \
        { \
-               return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, cc); \
-       }
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob(com_ ## cmd_name ## short_name ## _callback, cmd, cc, lpr); \
+       } \
+       EXPORT_SERVER_CMD_HANDLER(cmd_name ## short_name);
 
 static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                char **name)
@@ -629,25 +633,25 @@ static int blob_open(struct osl_table **table,
 
 
 /** Define all functions for this blob type. */
-#define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
+#define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
        DEFINE_BLOB_INIT(table_name) \
-       DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
-       DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
-       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
+       DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(rm, RM, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(mv, MV, table_name, short_name, c_short_name) \
+       DEFINE_GET_NAME_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_NAME(table_name, short_name); \
+       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, short_name); \
 
 /* doxygen isn't smart enough to recognize these */
 /** \cond blob_function */
-DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
-DEFINE_BLOB_FUNCTIONS(images, img);
-DEFINE_BLOB_FUNCTIONS(moods, mood);
-DEFINE_BLOB_FUNCTIONS(playlists, pl);
+DEFINE_BLOB_FUNCTIONS(lyrics, lyr, LYR);
+DEFINE_BLOB_FUNCTIONS(images, img, IMG);
+DEFINE_BLOB_FUNCTIONS(moods, mood, MOOD);
+DEFINE_BLOB_FUNCTIONS(playlists, pl, PL);
 /** \endcond blob_function */
index 0957fe0364c914b7f50c3c1148ef16cbb2350f31..045158ab2d2d32dd97e000896dce450a2b854496 100644 (file)
@@ -29,22 +29,19 @@ struct wav_params {
        int sample_format_given;
 };
 
-/**
- * Copy the wav parameters.
- *
- * \param dst Usually a pointer to struct wav_params.
- * \param src Usually a pointer to some args_info struct.
- *
- * This can not be implemented as a function since the type of the structure
- * pointed to by \a src depends on the application.
- */
-#define COPY_WAV_PARMS(dst, src) \
-       (dst)->channels_arg = (src)->channels_arg; \
-       (dst)->channels_given = (src)->channels_given; \
-       (dst)->sample_rate_arg = (src)->sample_rate_arg; \
-       (dst)->sample_rate_given = (src)->sample_rate_given; \
-       (dst)->sample_format_arg = (src)->sample_format_arg; \
-       (dst)->sample_format_given = (src)->sample_format_given;
+#define LLS_COPY_WAV_PARMS(_dst, _pfx, _lpr) \
+       (_dst)->channels_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_CHANNELS, (_lpr))); \
+       (_dst)->sample_rate_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+       (_dst)->sample_format_given = lls_opt_given(lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_FORMAT, (_lpr))); \
+       (_dst)->channels_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_CHANNELS, (_lpr))); \
+       (_dst)->sample_rate_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_RATE, (_lpr))); \
+       (_dst)->sample_format_arg = lls_uint32_val(0, lls_opt_result( \
+               _pfx ## _OPT_SAMPLE_FORMAT, (_lpr)));
 
 struct check_wav_context *check_wav_init(struct btr_node *parent,
                struct btr_node *child, struct wav_params *params,
index f1100df49e144b873779ca84d226c1829cb1c1a3..68d8a7efed285891653adfd5d68faaae06d41c1a 100644 (file)
--- a/client.c
+++ b/client.c
@@ -8,12 +8,13 @@
 
 #include <regex.h>
 #include <signal.h>
+#include <lopsub.h>
 
+#include "client.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "crypt.h"
-#include "client.cmdline.h"
 #include "string.h"
 #include "stdin.h"
 #include "stdout.h"
@@ -36,8 +37,7 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = stderr_log;
 
 #ifdef HAVE_READLINE
 #include "interactive.h"
-#include "server.completion.h"
-#include "afs.completion.h"
+#include "server_cmd.lsg.h"
 
 struct exec_task {
        struct task *task;
@@ -78,17 +78,52 @@ out:
        return 0;
 }
 
-static int make_client_argv(const char *line)
+/* Called from the line handler and the completers. This overwrites ct->lpr. */
+static int create_merged_lpr(const char *line)
 {
-       int ret;
+       const struct lls_command *cmd = CLIENT_CMD_PTR;
+       int argc, ret;
+       char *cmdline, **argv, *errctx;
+       struct lls_parse_result *argv_lpr;
+       static struct lls_parse_result *orig_lpr;
 
-       free_argv(ct->conf.inputs);
-       ret = create_argv(line, " ", &ct->conf.inputs);
-       if (ret >= 0)
-               ct->conf.inputs_num = ret;
+       if (!orig_lpr)
+               orig_lpr = ct->lpr;
+       ct->lpr = NULL;
+       cmdline = make_message("-- %s", line);
+       ret = create_shifted_argv(cmdline, " ", &argv);
+       free(cmdline);
+       if (ret < 0)
+               return ret;
+       argc = ret;
+       if (argc == 2) { /* no words (only blanks in line) */
+               free_argv(argv);
+               return 0;
+       }
+       argv[0] = para_strdup("--");
+       /*
+        * The original lpr for the interactive session has no non-option
+        * arguments. We create a fresh lpr from the words in "line" and merge
+        * it with the orignal lpr.
+        */
+       ret = lls(lls_parse(argc, argv, cmd, &argv_lpr, &errctx));
+       free_argv(argv);
+       if (ret < 0)
+               goto fail;
+       ret = lls(lls_merge(orig_lpr, argv_lpr, cmd, &ct->lpr, &errctx));
+       lls_free_parse_result(argv_lpr, cmd);
+       if (ret < 0)
+               goto fail;
+       return 1;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       free(errctx);
+       assert(ret < 0);
        return ret;
 }
 
+/* called from completers */
 static int execute_client_command(const char *cmd, char **result)
 {
        int ret;
@@ -97,9 +132,11 @@ static int execute_client_command(const char *cmd, char **result)
                .result_buf = para_strdup(""),
                .result_size = 1,
        };
+       struct lls_parse_result *old_lpr = ct->lpr;
+
        *result = NULL;
-       ret = make_client_argv(cmd);
-       if (ret < 0)
+       ret = create_merged_lpr(cmd);
+       if (ret <= 0)
                goto out;
        exec_task.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "exec_collect"));
@@ -114,6 +151,8 @@ static int execute_client_command(const char *cmd, char **result)
                goto out;
        schedule(&command_sched);
        sched_shutdown(&command_sched);
+       lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+       ct->lpr = old_lpr;
        *result = exec_task.result_buf;
        btr_remove_node(&exec_task.btrn);
        ret = 1;
@@ -174,7 +213,7 @@ static void complete_lsblob(const char *blob_type,
                struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-i", "-l", "-r", NULL};
+       char *opts[] = {LSG_SERVER_CMD_LSIMG_OPTS, NULL};
 
        if (ci->word[0] == '-')
                return i9e_complete_option(opts, ci, cr);
@@ -218,18 +257,17 @@ static void help_completer(struct i9e_completion_info *ci,
        result->matches = i9e_complete_commands(ci->word, completers);
 }
 
-static void version_completer(struct i9e_completion_info *ci,
+static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", NULL};
+       char *opts[] = {LSG_SERVER_CMD_STAT_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
-static void stat_completer(struct i9e_completion_info *ci,
+static void version_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-n=", "-p", NULL};
-       //PARA_CRIT_LOG("word: %s\n", ci->word);
+       char *opts[] = {LSG_SERVER_CMD_VERSION_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
@@ -266,7 +304,7 @@ static void sender_completer(struct i9e_completion_info *ci,
 static void add_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-l", "-f", "-v", "--", NULL};
+       char *opts[] = {LSG_SERVER_CMD_ADD_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -276,11 +314,7 @@ static void add_completer(struct i9e_completion_info *ci,
 static void ls_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {
-               "--", "-l", "-l=s", "-l=l", "-l=v", "-l=p", "-l=m", "-l=c",
-               "-p", "-a", "-r", "-d", "-s=p", "-s=l", "-s=s", "-s=n", "-s=f",
-               "-s=c", "-s=i", "-s=y", "-s=b", "-s=d", "-s=a", "-F", "-b", NULL
-       };
+       char *opts[] = {LSG_SERVER_CMD_LS_OPTS, NULL};
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
        cr->filename_completion_desired = true;
@@ -323,7 +357,7 @@ out:
 static void lsatt_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-i", "-l", "-r", NULL};
+       char *opts[] = {LSG_SERVER_CMD_LSATT_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
@@ -342,14 +376,14 @@ static void rmatt_completer(struct i9e_completion_info *ci,
 static void check_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-m", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_CHECK_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
 static void rm_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-v", "-f", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_RM_OPTS, NULL};
 
        if (ci->word[0] == '-') {
                i9e_complete_option(opts, ci, cr);
@@ -361,7 +395,7 @@ static void rm_completer(struct i9e_completion_info *ci,
 static void touch_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-n=", "-l=", "-y=", "-i=", "-a=", "-v", "-p", NULL};
+       char *opts[] = {LSG_SERVER_CMD_TOUCH_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -371,7 +405,7 @@ static void touch_completer(struct i9e_completion_info *ci,
 static void cpsi_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-a", "-y", "-i", "-l", "-n", "-v", NULL};
+       char *opts[] = {LSG_SERVER_CMD_CPSI_OPTS, NULL};
 
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
@@ -447,9 +481,13 @@ DEFINE_BLOB_COMPLETER(mv, pl)
 static int client_i9e_line_handler(char *line)
 {
        int ret;
+       static bool first = true;
 
-       PARA_DEBUG_LOG("line: %s\n", line);
-       ret = make_client_argv(line);
+       if (first)
+               first = false;
+       else
+               lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
+       ret = create_merged_lpr(line);
        if (ret <= 0)
                return ret;
        ret = client_connect(ct, &sched, NULL, NULL);
@@ -459,16 +497,18 @@ static int client_i9e_line_handler(char *line)
        return 1;
 }
 
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
 static struct i9e_completer completers[] = {
-       SERVER_COMPLETERS
-       AFS_COMPLETERS
+#define LSG_SERVER_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_SERVER_CMD_SUBCOMMANDS
+#undef LSG_SERVER_CMD_CMD
        {.name = NULL}
 };
 
 __noreturn static void interactive_session(void)
 {
        int ret;
-       char *history_file;
        struct sigaction act;
        struct i9e_client_info ici = {
                .fds = {0, 1, 2},
@@ -479,16 +519,15 @@ __noreturn static void interactive_session(void)
        };
 
        PARA_NOTICE_LOG("\n%s\n", version_text("client"));
-       if (ct->conf.history_file_given)
-               history_file = para_strdup(ct->conf.history_file_arg);
+       if (CLIENT_OPT_GIVEN(HISTORY_FILE, ct->lpr))
+               ici.history_file = para_strdup(CLIENT_OPT_STRING_VAL(
+                       HISTORY_FILE, ct->lpr));
        else {
                char *home = para_homedir();
-               history_file = make_message("%s/.paraslash/client.history",
+               ici.history_file = make_message("%s/.paraslash/client.history",
                        home);
                free(home);
        }
-       ici.history_file = history_file;
-
        act.sa_handler = i9e_signal_dispatch;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
@@ -512,7 +551,9 @@ out:
 
 __noreturn static void print_completions(void)
 {
-       int ret = i9e_print_completions(completers);
+       int ret;
+
+       ret = i9e_print_completions(completers);
        exit(ret <= 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
@@ -587,7 +628,7 @@ int main(int argc, char *argv[])
        ret = client_parse_config(argc, argv, &ct, &client_loglevel);
        if (ret < 0)
                goto out;
-       if (ct->conf.complete_given)
+       if (CLIENT_OPT_GIVEN(COMPLETE, ct->lpr))
                print_completions();
        if (ret == 0)
                interactive_session(); /* does not return */
index 2f257221510b1cb0b0848eb615d1dfb894f0cc81..87cfc618d64825684d0d607a07cf4605d4476ae2 100644 (file)
--- a/client.h
+++ b/client.h
@@ -38,8 +38,8 @@ struct client_task {
        struct btr_node *btrn[2];
        /** The hash value of the decrypted challenge. */
        unsigned char *challenge_hash;
-       /** The configuration (including the command). */
-       struct client_args_info conf;
+       /** The parsed command line (including the command). */
+       struct lls_parse_result *lpr;
        /** The config file for client options. */
        char *config_file;
        /** The RSA private key. */
@@ -52,6 +52,16 @@ struct client_task {
        char **features;
 };
 
+#define CLIENT_CMD_PTR (lls_cmd(0, client_suite))
+#define CLIENT_OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_CLIENT_PARA_CLIENT_OPT_ ## _name, _lpr))
+#define CLIENT_OPT_GIVEN(_name, _lpr) \
+       (lls_opt_given(CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_UINT32_VAL(_name, _lpr) \
+       (lls_uint32_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+#define CLIENT_OPT_STRING_VAL(_name, _lpr) \
+       (lls_string_val(0, CLIENT_OPT_RESULT(_name, _lpr)))
+
 void client_close(struct client_task *ct);
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel);
index ac53b9b547642ceb3e99c4ac47c91068405dec15..a06aaa000b1df567914a00c65ec7cd86b00e6e46 100644 (file)
@@ -13,7 +13,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "client.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "fd.h"
 #include "sideband.h"
 #include "string.h"
-#include "client.cmdline.h"
 #include "client.h"
 #include "buffer_tree.h"
 #include "version.h"
-#include "ggo.h"
 
 /** The size of the receiving buffer. */
 #define CLIENT_BUFSIZE 4000
@@ -46,7 +46,7 @@ void client_close(struct client_task *ct)
        free(ct->user);
        free(ct->config_file);
        free(ct->key_file);
-       client_cmdline_parser_free(&ct->conf);
+       lls_free_parse_result(ct->lpr, CLIENT_CMD_PTR);
        free(ct->challenge_hash);
        sb_free(ct->sbc[0]);
        sb_free(ct->sbc[1]);
@@ -246,16 +246,18 @@ static int send_sb_command(struct client_task *ct)
        int i;
        char *command, *p;
        size_t len = 0;
+       unsigned num_inputs = lls_num_inputs(ct->lpr);
 
        if (ct->sbc[1])
                return send_sb(ct, 0, NULL, 0, 0, false);
 
-       for (i = 0; i < ct->conf.inputs_num; i++)
-               len += strlen(ct->conf.inputs[i]) + 1;
+       for (i = 0; i < num_inputs; i++)
+               len += strlen(lls_input(i, ct->lpr)) + 1;
        p = command = para_malloc(len);
-       for (i = 0; i < ct->conf.inputs_num; i++) {
-               strcpy(p, ct->conf.inputs[i]);
-               p += strlen(ct->conf.inputs[i]) + 1;
+       for (i = 0; i < num_inputs; i++) {
+               const char *str = lls_input(i, ct->lpr);
+               strcpy(p, str);
+               p += strlen(str) + 1;
        }
        PARA_DEBUG_LOG("--> %s\n", command);
        return send_sb(ct, 0, command, len, SBD_COMMAND, false);
@@ -460,12 +462,12 @@ int client_connect(struct client_task *ct, struct sched *s,
                struct btr_node *parent, struct btr_node *child)
 {
        int ret;
+       const char *host = CLIENT_OPT_STRING_VAL(HOSTNAME, ct->lpr);
+       uint32_t port = CLIENT_OPT_UINT32_VAL(SERVER_PORT, ct->lpr);
 
-       PARA_NOTICE_LOG("connecting %s:%d\n", ct->conf.hostname_arg,
-               ct->conf.server_port_arg);
+       PARA_NOTICE_LOG("connecting %s:%u\n", host, port);
        ct->scc.fd = -1;
-       ret = para_connect_simple(IPPROTO_TCP, ct->conf.hostname_arg,
-                                              ct->conf.server_port_arg);
+       ret = para_connect_simple(IPPROTO_TCP, host, port);
        if (ret < 0)
                return ret;
        ct->scc.fd = ret;
@@ -491,13 +493,19 @@ err_out:
        return ret;
 }
 
-__noreturn static void print_help_and_die(struct client_task *ct)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(client);
-       bool d = ct->conf.detailed_help_given;
+       char *help;
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (CLIENT_OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CLIENT_CMD_PTR);
+       else if (CLIENT_OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CLIENT_CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       exit(EXIT_SUCCESS);
 }
 
 /**
@@ -521,65 +529,90 @@ __noreturn static void print_help_and_die(struct client_task *ct)
 int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr,
                int *loglevel)
 {
-       char *home = para_homedir();
-       int ret;
-       struct client_task *ct = para_calloc(sizeof(struct client_task));
-
-       *ct_ptr = ct;
-       ct->scc.fd = -1;
-       ret = -E_CLIENT_SYNTAX;
-       if (client_cmdline_parser(argc, argv, &ct->conf))
-               goto out;
-       version_handle_flag("client", ct->conf.version_given);
-       if (ct->conf.help_given || ct->conf.detailed_help_given)
-               print_help_and_die(ct);
-
-       ct->config_file = ct->conf.config_file_given?
-               para_strdup(ct->conf.config_file_arg) :
-               make_message("%s/.paraslash/client.conf", home);
-       ret = file_exists(ct->config_file);
-       if (!ret && ct->conf.config_file_given) {
-               ret = -E_NO_CONFIG;
+       const struct lls_command *cmd = CLIENT_CMD_PTR;
+       void *map;
+       size_t sz;
+       struct lls_parse_result *lpr;
+       int ret, ll;
+       struct client_task *ct;
+       char *cf = NULL, *kf = NULL, *user, *errctx, *home = para_homedir();
+
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       if (ret) {
-               struct client_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 0
-               };
-               ret = -E_BAD_CONFIG;
-               if (client_cmdline_parser_config_file(ct->config_file,
-                       &ct->conf, &params))
+       ll = CLIENT_OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("client", CLIENT_OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+
+       if (CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
+               cf = para_strdup(CLIENT_OPT_STRING_VAL(CONFIG_FILE, lpr));
+       else
+               cf = make_message("%s/.paraslash/client.conf", home);
+       ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+       if (ret < 0) {
+               if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+                       goto out;
+               if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) &&
+                               CLIENT_OPT_GIVEN(CONFIG_FILE, lpr))
                        goto out;
+       } 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 out;
+               cf_argc = ret;
+               ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+               lls_free_argv(cf_argv);
+               if (ret < 0)
+                       goto out;
+               ret = lls(lls_merge(lpr, cf_lpr, cmd, &merged_lpr,
+                       &errctx));
+               lls_free_parse_result(cf_lpr, cmd);
+               if (ret < 0)
+                       goto out;
+               lls_free_parse_result(lpr, cmd);
+               lpr = merged_lpr;
        }
-       ct->user = ct->conf.user_given?
-               para_strdup(ct->conf.user_arg) : para_logname();
+       /* success */
+       user = CLIENT_OPT_GIVEN(USER, lpr)?
+               para_strdup(CLIENT_OPT_STRING_VAL(USER, lpr)) : para_logname();
 
-       if (ct->conf.key_file_given)
-               ct->key_file = para_strdup(ct->conf.key_file_arg);
+       if (CLIENT_OPT_GIVEN(KEY_FILE, lpr))
+               kf = para_strdup(CLIENT_OPT_STRING_VAL(KEY_FILE, lpr));
        else {
-               ct->key_file = make_message("%s/.paraslash/key.%s",
-                       home, ct->user);
-               if (!file_exists(ct->key_file)) {
-                       free(ct->key_file);
-                       ct->key_file = make_message("%s/.ssh/id_rsa", home);
+               kf = make_message("%s/.paraslash/key.%s", home, user);
+               if (!file_exists(kf)) {
+                       free(kf);
+                       kf = make_message("%s/.ssh/id_rsa", home);
                }
        }
-
+       PARA_INFO_LOG("user: %s\n", user);
+       PARA_INFO_LOG("config file: %s\n", cf);
+       PARA_INFO_LOG("key file: %s\n", kf);
+       PARA_INFO_LOG("loglevel: %d\n", ll);
+       ct = para_calloc(sizeof(*ct));
+       ct->scc.fd = -1;
+       ct->lpr = lpr;
+       ct->key_file = kf;
+       ct->config_file = cf;
+       ct->user = user;
+       *ct_ptr = ct;
        if (loglevel)
-               *loglevel = get_loglevel_by_name(ct->conf.loglevel_arg);
-       PARA_INFO_LOG("loglevel: %s\n", ct->conf.loglevel_arg);
-       PARA_INFO_LOG("config_file: %s\n", ct->config_file);
-       PARA_INFO_LOG("key_file: %s\n", ct->key_file);
-       ret = ct->conf.inputs_num;
+               *loglevel = ll;
+       ret = lls_num_inputs(lpr);
 out:
        free(home);
        if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               client_close(ct);
+               lls_free_parse_result(lpr, cmd);
+               free(cf);
+               free(kf);
                *ct_ptr = NULL;
        }
        return ret;
index 58441b02495cef4465da6c675541b9feb64fce65..d0aeea3dc895bf60749a212b1076ef5be8bc18bb 100644 (file)
--- a/command.c
+++ b/command.c
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
 #include "sideband.h"
 #include "command.h"
-#include "server.cmdline.h"
 #include "string.h"
 #include "afh.h"
 #include "afs.h"
 #include "daemon.h"
 #include "fd.h"
 #include "ipc.h"
+#include "server_cmd.lsg.h"
 #include "user_list.h"
-#include "server.command_list.h"
-#include "afs.command_list.h"
 #include "signal.h"
 #include "version.h"
 
-typedef int server_command_handler_t(struct command_context *);
-static server_command_handler_t SERVER_COMMAND_HANDLERS;
-server_command_handler_t AFS_COMMAND_HANDLERS;
-
-/* Defines one command of para_server. */
-struct server_command {
-       /* The name of the command. */
-       const char *name;
-       /* Pointer to the function that handles the command. */
-       server_command_handler_t *handler;
-       /* The privileges a user must have to execute this command. */
-       unsigned int perms;
-       /* One-line description of the command. */
-       const char *description;
-       /* Summary of the command line options. */
-       const char *usage;
-       /* The long help text. */
-       const char *help;
-};
-
-static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
-static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
+#define SERVER_CMD_AUX_INFO(_arg) _arg,
+static const unsigned server_command_perms[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
+#define SERVER_CMD_AUX_INFO(_arg) #_arg,
+static const char * const server_command_perms_txt[] = {LSG_SERVER_CMD_AUX_INFOS};
+#undef SERVER_CMD_AUX_INFO
 
 /** Commands including options must be shorter than this. */
 #define MAX_COMMAND_LEN 32768
@@ -93,21 +77,6 @@ static char *vss_status_tohuman(unsigned int flags)
        return para_strdup("paused");
 }
 
-/*
- * return human readable permission string. Never returns NULL.
- */
-static char *cmd_perms_itohuman(unsigned int perms)
-{
-       char *msg = para_malloc(5 * sizeof(char));
-
-       msg[0] = perms & AFS_READ? 'a' : '-';
-       msg[1] = perms & AFS_WRITE? 'A' : '-';
-       msg[2] = perms & VSS_READ? 'v' : '-';
-       msg[3] = perms & VSS_WRITE? 'V' : '-';
-       msg[4] = '\0';
-       return msg;
-}
-
 /*
  * Never returns NULL.
  */
@@ -123,7 +92,7 @@ static char *vss_get_status_flags(unsigned int flags)
        return msg;
 }
 
-static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
+static unsigned get_status(struct misc_meta_data *nmmd, bool parser_friendly,
                char **result)
 {
        char *status, *flags; /* vss status info */
@@ -158,52 +127,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
        return b.offset;
 }
 
-static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
-{
-       int i;
-
-       const char *subcmds[] = {SENDER_SUBCOMMANDS NULL};
-       scd->sender_num = -1;
-       if (argc < 3)
-               return -E_COMMAND_SYNTAX;
-       for (i = 0; senders[i].name; i++)
-               if (!strcmp(senders[i].name, argv[1]))
-                       break;
-       PARA_DEBUG_LOG("%d:%s\n", argc, argv[1]);
-       if (!senders[i].name)
-               return -E_COMMAND_SYNTAX;
-       scd->sender_num = i;
-       for (i = 0; subcmds[i]; i++)
-               if (!strcmp(subcmds[i], argv[2]))
-                       break;
-       if (!subcmds[i])
-               return -E_COMMAND_SYNTAX;
-       scd->cmd_num = i;
-       if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
-               return -E_SENDER_CMD;
-       switch (scd->cmd_num) {
-       case SENDER_on:
-       case SENDER_off:
-               if (argc != 3)
-                       return -E_COMMAND_SYNTAX;
-               break;
-       case SENDER_deny:
-       case SENDER_allow:
-               if (argc != 4 || parse_cidr(argv[3], scd->host,
-                               sizeof(scd->host), &scd->netmask) == NULL)
-                       return -E_COMMAND_SYNTAX;
-               break;
-       case SENDER_add:
-       case SENDER_delete:
-               if (argc != 4)
-                       return -E_COMMAND_SYNTAX;
-               return parse_fec_url(argv[3], scd);
-       default:
-               return -E_COMMAND_SYNTAX;
-       }
-       return 1;
-}
-
 /**
  * Send a sideband packet through a blocking file descriptor.
  *
@@ -279,6 +202,81 @@ int send_strerror(struct command_context *cc, int err)
        return send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", para_strerror(err));
 }
 
+/**
+ * Send an error context to a client,
+ *
+ * \param cc Client info.
+ * \param errctx The error context string.
+ *
+ * \return The return value of the underlying call to send_sb_va().
+ *
+ * This function frees the error context string after it was sent.
+ */
+int send_errctx(struct command_context *cc, char *errctx)
+{
+       int ret;
+
+       if (!errctx)
+               return 0;
+       ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s\n", errctx);
+       free(errctx);
+       return ret;
+}
+
+static int check_sender_args(struct command_context *cc,
+               struct lls_parse_result *lpr, struct sender_command_data *scd)
+{
+       int i, ret;
+       const char *subcmds[] = {SENDER_SUBCOMMANDS};
+       const char *arg;
+       char *errctx;
+       unsigned num_inputs = lls_num_inputs(lpr);
+
+       scd->sender_num = -1;
+       ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       arg = lls_input(0, lpr);
+       for (i = 0; senders[i].name; i++)
+               if (!strcmp(senders[i].name, arg))
+                       break;
+       if (!senders[i].name)
+               return -E_COMMAND_SYNTAX;
+       scd->sender_num = i;
+       arg = lls_input(1, lpr);
+       for (i = 0; subcmds[i]; i++)
+               if (!strcmp(subcmds[i], arg))
+                       break;
+       if (!subcmds[i])
+               return -E_COMMAND_SYNTAX;
+       scd->cmd_num = i;
+       if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
+               return -E_SENDER_CMD;
+       switch (scd->cmd_num) {
+       case SENDER_on:
+       case SENDER_off:
+               if (num_inputs != 2)
+                       return -E_COMMAND_SYNTAX;
+               break;
+       case SENDER_deny:
+       case SENDER_allow:
+               if (num_inputs != 3 || parse_cidr(lls_input(2, lpr), scd->host,
+                               sizeof(scd->host), &scd->netmask) == NULL)
+                       return -E_COMMAND_SYNTAX;
+               break;
+       case SENDER_add:
+       case SENDER_delete:
+               if (num_inputs != 3)
+                       return -E_COMMAND_SYNTAX;
+               return parse_fec_url(lls_input(2, lpr), scd);
+       default:
+               return -E_COMMAND_SYNTAX;
+       }
+       return 1;
+}
+
 /**
  * Send a sideband packet through a blocking file descriptor.
  *
@@ -329,13 +327,13 @@ fail:
        return ret;
 }
 
-static int com_sender(struct command_context *cc)
+static int com_sender(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret = 0;
        char *msg = NULL;
        struct sender_command_data scd;
 
-       if (cc->argc < 2) {
+       if (lls_num_inputs(lpr) == 0) {
                for (i = 0; senders[i].name; i++) {
                        char *tmp;
                        ret = xasprintf(&tmp, "%s%s\n", msg? msg : "",
@@ -345,11 +343,11 @@ static int com_sender(struct command_context *cc)
                }
                return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
        }
-       ret = check_sender_args(cc->argc, cc->argv, &scd);
+       ret = check_sender_args(cc, lpr, &scd);
        if (ret < 0) {
                if (scd.sender_num < 0)
                        return ret;
-               if (strcmp(cc->argv[2], "status") == 0)
+               if (strcmp(lls_input(1, lpr), "status") == 0)
                        msg = senders[scd.sender_num].status();
                else
                        msg = senders[scd.sender_num].help();
@@ -360,7 +358,8 @@ static int com_sender(struct command_context *cc)
        case SENDER_add:
        case SENDER_delete:
                assert(senders[scd.sender_num].resolve_target);
-               ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
+               ret = senders[scd.sender_num].resolve_target(lls_input(2, lpr),
+                       &scd);
                if (ret < 0)
                        return ret;
        }
@@ -380,17 +379,16 @@ static int com_sender(struct command_context *cc)
        }
        return (i < 10)? 1 : -E_LOCK;
 }
+EXPORT_SERVER_CMD_HANDLER(sender);
 
-/* server info */
-static int com_si(struct command_context *cc)
+static int com_si(struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       int ret;
        char *msg, *ut;
+       int ret;
 
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
-       mutex_lock(mmd_mutex);
        ut = daemon_get_uptime_str(now);
+       mutex_lock(mmd_mutex);
        ret = xasprintf(&msg,
                "up: %s\nplayed: %u\n"
                "server_pid: %d\n"
@@ -404,26 +402,27 @@ static int com_si(struct command_context *cc)
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
-               conf.loglevel_arg,
+               ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
        free(ut);
        return send_sb(&cc->scc, msg, ret, SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(si);
 
-/* version */
-static int com_version(struct command_context *cc)
+static int com_version(struct command_context *cc, struct lls_parse_result *lpr)
 {
        char *msg;
        size_t len;
 
-       if (cc->argc > 1 && strcmp(cc->argv[1], "-v") == 0)
+       if (SERVER_CMD_OPT_GIVEN(VERSION, VERBOSE, lpr))
                len = xasprintf(&msg, "%s", version_text("server"));
        else
                len = xasprintf(&msg, "%s\n", version_single_line("server"));
        return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(version);
 
 /** These status items are cleared if no audio file is currently open. */
 #define EMPTY_STATUS_ITEMS \
@@ -463,7 +462,7 @@ static int com_version(struct command_context *cc)
  *
  * This is used by vss when currently no audio file is open.
  */
-static unsigned empty_status_items(int parser_friendly, char **result)
+static unsigned empty_status_items(bool parser_friendly, char **result)
 {
        char *esi;
        unsigned len;
@@ -491,39 +490,16 @@ static unsigned empty_status_items(int parser_friendly, char **result)
 }
 #undef EMPTY_STATUS_ITEMS
 
-/* stat */
-static int com_stat(struct command_context *cc)
+static int com_stat(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       int i, ret;
+       int ret;
        struct misc_meta_data tmp, *nmmd = &tmp;
        char *s;
-       int32_t num = 0;
-       int parser_friendly = 0;
+       bool parser_friendly = SERVER_CMD_OPT_GIVEN(STAT, PARSER_FRIENDLY,
+               lpr) > 0;
+       uint32_t num = SERVER_CMD_UINT32_VAL(STAT, NUM, lpr);
 
        para_sigaction(SIGUSR1, dummy);
-
-       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, &num);
-                       if (ret < 0)
-                               return ret;
-                       continue;
-               }
-               if (!strcmp(arg, "-p")) {
-                       parser_friendly = 1;
-                       continue;
-               }
-               return -E_COMMAND_SYNTAX;
-       }
-       if (i != cc->argc)
-               return -E_COMMAND_SYNTAX;
        for (;;) {
                /*
                 * Copy the mmd structure to minimize the time we hold the mmd
@@ -555,114 +531,98 @@ static int com_stat(struct command_context *cc)
 out:
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(stat);
 
-static int send_list_of_commands(struct command_context *cc, struct server_command *cmd,
-               const char *handler)
+/* fixed-length, human readable permission string */
+const char *server_cmd_perms_str(unsigned int perms)
 {
-       char *msg = NULL;
+       static char result[5];
 
-       for (; cmd->name; cmd++) {
-               char *tmp, *perms = cmd_perms_itohuman(cmd->perms);
-               tmp = make_message("%s\t%s\t%s\t%s\n", cmd->name, handler,
-                       perms, cmd->description);
-               free(perms);
-               msg = para_strcat(msg, tmp);
-               free(tmp);
-       }
-       assert(msg);
-       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
+       result[0] = perms & AFS_READ? 'a' : '-';
+       result[1] = perms & AFS_WRITE? 'A' : '-';
+       result[2] = perms & VSS_READ? 'v' : '-';
+       result[3] = perms & VSS_WRITE? 'V' : '-';
+       result[4] = '\0';
+       return result;
 }
 
-/* returns string that must be freed by the caller */
-static struct server_command *get_cmd_ptr(const char *name, char **handler)
+static int send_list_of_commands(struct command_context *cc)
 {
-       struct server_command *cmd;
-
-       for (cmd = server_cmds; cmd->name; cmd++)
-               if (!strcmp(cmd->name, name)) {
-                       if (handler)
-                               *handler = para_strdup("server"); /* server commands */
-                       return cmd;
-               }
-       /* not found, look for commands supported by afs */
-       for (cmd = afs_cmds; cmd->name; cmd++)
-               if (!strcmp(cmd->name, name)) {
-                       if (handler)
-                               *handler = para_strdup("afs");
-                       return cmd;
-               }
-       return NULL;
+       int i;
+       const struct lls_command *cmd;
+       char *msg = para_strdup("");
+
+       for (i = 1; (cmd = lls_cmd(i, server_cmd_suite)); i++) {
+               const char *perms = server_cmd_perms_str(server_command_perms[i]);
+               char *tmp = make_message("%s%s\t%s\t%s\n", msg,
+                       lls_command_name(cmd), perms, lls_purpose(cmd));
+               free(msg);
+               msg = tmp;
+       }
+       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
 }
 
-/* help */
-static int com_help(struct command_context *cc)
+static int com_help(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct server_command *cmd;
-       char *perms, *handler, *buf;
+       const char *perms;
+       char *long_help, *buf, *errctx;
        int ret;
+       const struct lls_command *cmd;
 
-       if (cc->argc < 2) {
-               /* no argument given, print list of commands */
-               if ((ret = send_list_of_commands(cc, server_cmds, "server")) < 0)
-                       return ret;
-               return send_list_of_commands(cc, afs_cmds, "afs");
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
+       if (lls_num_inputs(lpr) == 0)
+               return send_list_of_commands(cc);
        /* argument given for help */
-       cmd = get_cmd_ptr(cc->argv[1], &handler);
-       if (!cmd)
-               return -E_BAD_CMD;
-       perms = cmd_perms_itohuman(cmd->perms);
-       ret = xasprintf(&buf, "%s - %s\n\n"
-               "handler: %s\n"
-               "permissions: %s\n"
-               "usage: %s\n\n"
-               "%s\n",
-               cc->argv[1],
-               cmd->description,
-               handler,
-               perms,
-               cmd->usage,
-               cmd->help
-       );
-       free(perms);
-       free(handler);
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), server_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       cmd = lls_cmd(ret, server_cmd_suite);
+       perms = server_command_perms_txt[ret];
+       long_help = lls_long_help(cmd);
+       assert(long_help);
+       ret = xasprintf(&buf, "%spermissions: %s\n", long_help, perms);
+       free(long_help);
        return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(help);
 
-/* hup */
-static int com_hup(struct command_context *cc)
+static int com_hup(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        kill(getppid(), SIGHUP);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(hup);
 
-/* term */
-static int com_term(struct command_context *cc)
+static int com_term(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        kill(getppid(), SIGTERM);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(term);
 
-static int com_play(struct command_context *cc)
+static int com_play(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->new_vss_status_flags |= VSS_PLAYING;
        mmd->new_vss_status_flags &= ~VSS_NOMORE;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(play);
 
-/* stop */
-static int com_stop(struct command_context *cc)
+static int com_stop(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->new_vss_status_flags &= ~VSS_PLAYING;
        mmd->new_vss_status_flags &= ~VSS_REPOS;
@@ -670,12 +630,11 @@ static int com_stop(struct command_context *cc)
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(stop);
 
-/* pause */
-static int com_pause(struct command_context *cc)
+static int com_pause(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        if (!vss_paused() && !vss_stopped()) {
                mmd->events++;
@@ -685,42 +644,44 @@ static int com_pause(struct command_context *cc)
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(pause);
 
-/* next */
-static int com_next(struct command_context *cc)
+static int com_next(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        mmd->events++;
        mmd->new_vss_status_flags |= VSS_NEXT;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(next);
 
-/* nomore */
-static int com_nomore(struct command_context *cc)
+static int com_nomore(__a_unused struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
        mutex_lock(mmd_mutex);
        if (vss_playing() || vss_paused())
                mmd->new_vss_status_flags |= VSS_NOMORE;
        mutex_unlock(mmd_mutex);
        return 1;
 }
+EXPORT_SERVER_CMD_HANDLER(nomore);
 
-/* ff */
-static int com_ff(struct command_context *cc)
+static int com_ff(__a_unused struct command_context *cc,
+               struct lls_parse_result *lpr)
 {
        long promille;
        int ret, backwards = 0;
        unsigned i;
-       char c;
+       char c, *errctx;
 
-       if (cc->argc != 2)
-               return -E_COMMAND_SYNTAX;
-       if (!(ret = sscanf(cc->argv[1], "%u%c", &i, &c)))
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (!(ret = sscanf(lls_input(0, lpr), "%u%c", &i, &c)))
                return -E_COMMAND_SYNTAX;
        if (ret > 1 && c == '-')
                backwards = 1; /* jmp backwards */
@@ -748,17 +709,22 @@ out:
        mutex_unlock(mmd_mutex);
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(ff);
 
-/* jmp */
-static int com_jmp(struct command_context *cc)
+static int com_jmp(__a_unused struct command_context *cc,
+               struct lls_parse_result *lpr)
 {
        long unsigned int i;
        int ret;
+       char *errctx;
 
-       if (cc->argc != 2)
-               return -E_COMMAND_SYNTAX;
-       if (sscanf(cc->argv[1], "%lu", &i) <= 0)
-               return -E_COMMAND_SYNTAX;
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (sscanf(lls_input(0, lpr), "%lu", &i) <= 0)
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        mutex_lock(mmd_mutex);
        ret = -E_NO_AUDIO_FILE;
        if (!mmd->afd.afhi.chunks_total)
@@ -777,26 +743,16 @@ out:
        mutex_unlock(mmd_mutex);
        return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(jmp);
 
-static int com_tasks(struct command_context *cc)
+static int com_tasks(struct command_context *cc,
+               __a_unused struct lls_parse_result *lpr)
 {
        char *tl = server_get_tasks();
-       int ret = 1;
-
-       if (tl)
-               ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
-       return ret;
-}
-
-/*
- * check if perms are sufficient to exec a command having perms cmd_perms.
- * Returns 0 if perms are sufficient, -E_PERM otherwise.
- */
-static int check_perms(unsigned int perms, const struct server_command *cmd_ptr)
-{
-       PARA_DEBUG_LOG("checking permissions\n");
-       return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
+       assert(tl);
+       return send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
 }
+EXPORT_SERVER_CMD_HANDLER(tasks);
 
 static void reset_signals(void)
 {
@@ -856,37 +812,50 @@ out:
 static int run_command(struct command_context *cc, struct iovec *iov,
                const char *peername)
 {
-       int ret, i;
-       char *p, *end;
-       struct server_command *cmd;
+       int ret, i, argc;
+       char *p, *end, **argv;
+       const struct lls_command *lcmd = NULL;
+       unsigned perms;
+       struct lls_parse_result *lpr;
+       char *errctx;
 
        if (iov->iov_base == NULL || iov->iov_len == 0)
-               return -E_BAD_CMD;
+               return -ERRNO_TO_PARA_ERROR(EINVAL);
        p = iov->iov_base;
        p[iov->iov_len - 1] = '\0'; /* just to be sure */
-       cmd = get_cmd_ptr(p, NULL);
-       if (!cmd)
-               return -E_BAD_CMD;
-       ret = check_perms(cc->u->perms, cmd);
-       if (ret < 0)
+
+       ret = lls(lls_lookup_subcmd(p, server_cmd_suite, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
                return ret;
+       }
+       perms = server_command_perms[ret];
+       if ((perms & cc->u->perms) != perms)
+               return -E_PERM;
+       lcmd = lls_cmd(ret, server_cmd_suite);
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
-       cc->argc = i;
-       cc->argv = para_malloc((cc->argc + 1) * sizeof(char *));
+       argc = i;
+       argv = para_malloc((argc + 1) * sizeof(char *));
        for (i = 0, p = iov->iov_base; p < end; i++) {
-               cc->argv[i] = para_strdup(p);
+               argv[i] = para_strdup(p);
                p += strlen(p) + 1;
        }
-       cc->argv[cc->argc] = NULL;
-       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
+       argv[argc] = NULL;
+       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", lls_command_name(lcmd),
                cc->u->name, peername);
-       ret = cmd->handler(cc);
-       free_argv(cc->argv);
+       ret = lls(lls_parse(argc, argv, lcmd, &lpr, &errctx));
+       if (ret >= 0) {
+               const struct server_cmd_user_data *ud = lls_user_data(lcmd);
+               ret = ud->handler(cc, lpr);
+               lls_free_parse_result(lpr, lcmd);
+       } else
+               send_errctx(cc, errctx);
+       free_argv(argv);
        mutex_lock(mmd_mutex);
        mmd->num_commands++;
-       if (ret >= 0 && (cmd->perms & AFS_WRITE))
+       if (ret >= 0 && (perms & AFS_WRITE))
                mmd->events++;
        mutex_unlock(mmd_mutex);
        return ret;
index 5e1a4ce3edc1a8812948ada45eecd2f76df4f7be..5b01eb79e3500f34d135d1bdc8db6ac8ec4ad59c 100644 (file)
--- a/command.h
+++ b/command.h
@@ -6,19 +6,55 @@ struct command_context {
        const char *peer;
        /** The paraslash user that executes this command. */
        struct user *u;
-       /** Argument count. */
-       int argc;
-       /** Argument vector. */
-       char **argv;
        /** File descriptor and crypto keys. */
        struct stream_cipher_context scc;
 };
 
+/** Prototype of a server command handler. */
+typedef int (*server_cmd_handler_t)(struct command_context *,
+               struct lls_parse_result *);
+
+/**
+ * The lopsub user data structure for server commands.
+ *
+ * One such structure exists for each server command. Lopsub maintains
+ * references to the user data structures and provides lls_user_data() for
+ * applications to fetch the user data pointer of a given command. This
+ * mechanism is used by the dispatcher of command.c to run the command handler.
+ */
+struct server_cmd_user_data {
+       /** Pointer to the command handler. */
+       server_cmd_handler_t handler;
+};
+
+/** Define the user data structure for one command. */
+#define EXPORT_SERVER_CMD_HANDLER(_cmd) \
+       const struct server_cmd_user_data lsg_server_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
+
+/** Get the lopsub command pointer by command name. */
+#define SERVER_CMD_CMD_PTR(_cmd) \
+       (lls_cmd(LSG_SERVER_CMD_CMD_ ## _cmd, server_cmd_suite))
+
+/** Get the lopsub parse result of an option. */
+#define SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+               (lls_opt_result(LSG_SERVER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+
+/** How many times an option was given. */
+#define SERVER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
+/** Fetch the (first) argument given to an option of type uint32. */
+#define SERVER_CMD_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, SERVER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
 int send_sb(struct stream_cipher_context *scc, void *buf, size_t numbytes,
                int band, bool dont_free);
 __printf_3_4 int send_sb_va(struct stream_cipher_context *scc, int band,
                const char *fmt, ...);
 int send_strerror(struct command_context *cc, int err);
+int send_errctx(struct command_context *cc, char *errctx);
 int recv_sb(struct stream_cipher_context *scc,
                enum sb_designator expected_band,
                size_t max_size, struct iovec *result);
diff --git a/command_util.bash b/command_util.bash
deleted file mode 100755 (executable)
index 5e9dbcc..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-#!/usr/bin/env bash
-
-read_header()
-{
-       local key value i
-
-       while read key value; do
-               case "$key" in
-               ---)
-                       break
-                       ;;
-               BN:)
-                       base_name="$value"
-                       ;;
-               SF:)
-                       source_files="$value"
-                       ;;
-               SN:)
-                       section_name="$value"
-                       ;;
-               TM:)
-                       template_members="$value"
-               esac
-       done
-}
-
-read_one_command()
-{
-       local line
-
-       name_txt=""
-       desc_txt=""
-       usage_txt=""
-       help_txt=""
-       perms_txt=""
-       template=0
-       template_name=""
-       template_prototype=""
-       while read key value; do
-               case "$key" in
-               ---)
-                       break
-                       ;;
-               N:)
-                       name_txt="$value"
-                       ;;
-               T:)
-                       template_name="$value"
-                       template=1
-                       ;;
-               O:)
-                       template_prototype="$value"
-                       template=1
-                       ;;
-               P:)
-                       perms_txt="$value"
-                       ;;
-               D:)
-                       desc_txt="$value"
-                       ;;
-               U:)
-                       usage_txt="$value"
-                       ;;
-               H:)
-                       help_txt="${value}"
-                       while read line; do
-                               if test "$line" = "---"; then
-                                       break;
-                               fi
-                               line=${line#H:}
-                               help_txt="$help_txt
-${line# }"
-                       done
-                       break
-                       ;;
-               esac
-       done
-       if test $template -eq 0; then
-               if test -n "$name_txt" -a -n "$desc_txt" -a -n "$usage_txt" \
-                               -a -n "$help_txt"; then
-                       ret=1
-                       return
-               fi
-       else
-               if test -n "$template_name" -a -n "$template_prototype" \
-                               -a -n "$name_txt " -a -n "$template_members" \
-                               -a -n "$desc_txt" -a -n "$usage_txt" \
-                               -a -n "$help_txt"; then
-                       ret=1
-                       return
-               fi
-       fi
-       if test -z "$name_txt" -a -z "$desc_txt" -a -z "$usage_txt" \
-                       -a -z "$help_txt"; then
-               ret=0
-               return
-       fi
-       ret=-1
-       #return
-       echo "!ERROR!"
-       echo "N: $name_txt"
-       echo "D: $desc_txt"
-       echo "S: $usage_txt"
-       echo "P: $perms_txt"
-       echo "H: $help_txt"
-       echo "O: $template_prototype"
-}
-
-dump_man()
-{
-       if test $template -eq 0; then
-               echo ".SS \"$name_txt\""
-               echo "$desc_txt"
-               echo
-               echo "\\fBUsage: \\fP$usage_txt"
-       else
-               for member in $template_members; do
-                       local sed_cmd="sed -e s/@member@/$member/g"
-                       local t_name_txt=$(echo $name_txt | $sed_cmd)
-                       echo ".SS \"$t_name_txt\""
-               done
-               echo "$desc_txt" | sed -e "s/@member@/{$(echo $template_members | sed -e 's/ / | /g')}/g"
-               echo
-               echo "\\fBUsage: \\fP"
-               echo
-               echo ".nf"
-               for member in $template_members; do
-                       local sed_cmd="sed -e s/@member@/$member/g"
-                       local t_usage_txt=$(echo $usage_txt | $sed_cmd)
-                       printf "\t$t_usage_txt\n"
-               done
-               echo ".fi"
-       fi
-       echo
-       echo "$help_txt" | sed -e 's/^  //'
-       echo
-       if test -n "$perms_txt"; then
-               echo -n "\\fBpermissions:\\fP "
-               if test "$perms_txt" = "0"; then
-                       echo "(none)"
-               else
-                       echo "$perms_txt"
-               fi
-       fi
-       echo
-}
-
-
-com_man()
-{
-       echo "[$section_name]"
-       echo
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               dump_man
-       done
-}
-
-cmd_handler_name()
-{
-       result="com_$name_txt,"
-}
-
-make_array_member()
-{
-       local TAB='     ' CR='
-'
-       local tmp
-
-       result="{.name = \"$name_txt\", .handler = com_$name_txt, "
-       if test -n "$perms_txt"; then
-               result="$result .perms = $perms_txt,"
-       fi
-       result="$result.description = \"$desc_txt\", .usage = \"$usage_txt\", \\$CR .help = "
-       tmp="$(printf "%s\n" "$help_txt" | sed -e 's/^/\"/g' -e 's/$/\\n\"/g' \
-               -e "s/$TAB/\\\t/g" -e's/$/\\/g')"
-       result="$result$tmp$CR}, \\$CR"
-}
-
-make_completion()
-{
-       local CR='
-'
-       result="  {.name = \"$name_txt\", .completer = ${name_txt}_completer}, \\$CR"
-}
-
-template_loop()
-{
-       local loop_result=
-
-       local t_name="$name_txt"
-       local t_perms="$perms_txt"
-       local t_desc="$desc_txt"
-       local t_usage="$usage_txt"
-       local t_help="$help_txt"
-       local t_source_files="$source_files"
-       local member
-       for member in $template_members; do
-               name_txt="${t_name//@member@/$member}"
-               perms_txt="${t_perms//@member@/$member}"
-               desc_txt="${t_desc//@member@/$member}"
-               usage_txt="${t_usage//@member@/$member}"
-               help_txt="${t_help//@member@/$member}"
-               prototype="${template_prototype//@member@/$member}"
-               result=
-               $1
-               loop_result="$loop_result$result"
-       done
-       result="$loop_result"
-       # reset global variables
-       name_txt="$t_name"
-       perms_txt="$t_perms"
-       desc_txt="$t_desc"
-       usage_txt="$t_usage"
-       help_txt="$t_help"
-       source_files="$t_source_files"
-}
-
-com_header()
-{
-       local array_members handlers= CR='
-'
-
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               if test $template -eq 0; then
-                       cmd_handler_name
-                       handlers+="$result"
-                       make_array_member
-                       array_members="$array_members$result"
-                       continue
-               fi
-               template_loop cmd_handler_name
-               handlers+="$result"
-               template_loop make_array_member
-               array_members="$array_members$result"
-       done
-       array_members="$array_members{.name = NULL} \\$CR"
-       echo "#define DEFINE_${base_name^^}_CMD_ARRAY $array_members"
-       echo "#define ${base_name^^}_COMMAND_HANDLERS ${handlers%,}"
-}
-
-com_completion()
-{
-
-       echo "#define $1 \\"
-       while : ; do
-               read_one_command
-               if test $ret -lt 0; then
-                       exit 1
-               fi
-               if test $ret -eq 0; then
-                       break
-               fi
-               if test $template -eq 0; then
-                       make_completion
-                       printf "%s" "$result"
-                       continue
-               fi
-               template_loop make_completion
-               printf "%s" "$result"
-       done
-       echo
-}
-
-read_header
-arg="$1"
-shift
-case "$arg" in
-       "h")
-               com_header
-               ;;
-       "man")
-               com_man $*
-               ;;
-       "compl")
-               com_completion $*
-               ;;
-esac
index d0d96fd960955bcccedc731eb33221d5369e778f..658976423faa1115b710102633beff6f3093151c 100644 (file)
  */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "compress_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
 
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(COMPRESS, _opt, _lpr))
+
 /** Data specific to the compress filter. */
 struct private_compress_data {
        /** The current multiplier. */
        unsigned current_gain;
-       /** Points to the configuration data for this instance of the compress filter. */
-       struct compress_filter_args_info *conf;
        /** Maximal admissible gain. */
        unsigned max_gain;
        /** Number of samples already seen. */
@@ -51,9 +51,9 @@ static int compress_post_select(__a_unused struct sched *s, void *context)
        char *inbuf;
        size_t length, i;
        int16_t *ip, *op;
-       unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
-               mask = (1 << pcd->conf->blocksize_arg) - 1;
-
+       uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+       unsigned gain_shift = inertia + U32_OPTVAL(DAMP, fn->lpr),
+               mask = (1 << U32_OPTVAL(BLOCKSIZE, fn->lpr)) - 1;
        //inplace = false;
 next_buffer:
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
@@ -86,7 +86,7 @@ next_buffer:
                if (sample > 32767) { /* clip */
                        sample = 32767;
                        pcd->current_gain = (3 * pcd->current_gain +
-                               (1 << pcd->conf->inertia_arg)) / 4;
+                               (1 << inertia)) / 4;
                        pcd->peak = 0;
                } else if (sample > pcd->peak)
                        pcd->peak = sample;
@@ -95,12 +95,12 @@ next_buffer:
                        continue;
 //             PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
 //                     pcd->peak);
-               if (pcd->peak < pcd->conf->target_level_arg) {
+               if (pcd->peak < U32_OPTVAL(TARGET_LEVEL, fn->lpr)) {
                        if (pcd->current_gain < pcd->max_gain)
                                pcd->current_gain++;
                } else
                        pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
-                               1U << pcd->conf->inertia_arg);
+                               1U << inertia);
                pcd->peak = 0;
        }
        if (inplace)
@@ -116,47 +116,21 @@ err:
        return ret;
 }
 
-/** TODO: Add sanity checks */
-static int compress_parse_config(int argc, char **argv, void **config)
-{
-       struct compress_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       compress_filter_cmdline_parser(argc, argv, conf);
-       *config = conf;
-       return 1;
-}
-
 static void compress_open(struct filter_node *fn)
 {
-       struct private_compress_data *pcd = para_calloc(
-               sizeof(struct private_compress_data));
-       pcd->conf = fn->conf;
+       struct private_compress_data *pcd = para_calloc(sizeof(*pcd));
+       uint32_t inertia = U32_OPTVAL(INERTIA, fn->lpr);
+       uint32_t aggressiveness = U32_OPTVAL(AGGRESSIVENESS, fn->lpr);
+
        fn->private_data = pcd;
        fn->min_iqs = 2; /* 16 bit audio */
-       pcd->current_gain = 1 << pcd->conf->inertia_arg;
-       pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
-}
-
-static void compress_free_config(void *conf)
-{
-       compress_filter_cmdline_parser_free(conf);
+       pcd->current_gain = 1U << inertia;
+       pcd->max_gain = 1U << (inertia + aggressiveness);
 }
 
-/**
- * The init function of the compress filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void compress_filter_init(struct filter *f)
-{
-       struct compress_filter_args_info dummy;
-
-       compress_filter_cmdline_parser_init(&dummy);
-       f->open = compress_open;
-       f->close = compress_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = compress_post_select;
-       f->parse_config = compress_parse_config;
-       f->free_config = compress_free_config;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(compress_filter);
-}
+const struct filter lsg_filter_cmd_com_compress_user_data = {
+       .open = compress_open,
+       .close = compress_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = compress_post_select,
+};
index 929d44f35fd482a83d3713d5ab24ffe9b0edd8db..a589613b5ad5f000edb65f77e6f73101f1d34ae2 100644 (file)
@@ -51,17 +51,13 @@ AC_DEFUN([LIB_SUBST_FLAGS], [
 AC_USE_SYSTEM_EXTENSIONS
 AC_C_BIGENDIAN()
 
-AC_PATH_PROG([GENGETOPT], [gengetopt])
-test -z "$GENGETOPT" && AC_MSG_ERROR(
-       [gengetopt is required to build this package])
-
 AC_PATH_PROG([M4], [m4])
 test -z "$M4" && AC_MSG_ERROR(
        [The m4 macro processor is required to build this package])
 
-AC_PATH_PROG([HELP2MAN], [help2man])
-test -z "$HELP2MAN" && AC_MSG_ERROR(
-       [help2man is required to build this package])
+AC_PATH_PROG([lopsubgen], [lopsubgen])
+test -z "$lopsubgen" && AC_MSG_ERROR(
+       [lopsubgen is required to build this package])
 
 AC_PROG_CC
 AC_PROG_CPP
@@ -75,6 +71,22 @@ AC_CHECK_HEADER(osl.h, [], [HAVE_OSL=no])
 AC_CHECK_LIB([osl], [osl_open_table], [], [HAVE_OSL=no])
 LIB_SUBST_FLAGS(osl)
 UNSTASH_FLAGS
+######################################################################## lopsub
+STASH_FLAGS
+LIB_ARG_WITH([lopsub], [-llopsub])
+HAVE_LOPSUB=yes
+AC_CHECK_HEADER(lopsub.h, [], [HAVE_LOPSUB=no])
+AC_CHECK_LIB([lopsub], [lls_merge], [], [HAVE_LOPSUB=yes])
+if test $HAVE_LOPSUB = no; then AC_MSG_ERROR([
+       The lopsub library is required to build this software, but
+       the above checks indicate it is not installed on your system.
+       Run the following command to download a copy.
+               git clone git://git.tuebingen.mpg.de/lopsub.git
+       Install the library, then run this configure script again.
+])
+fi
+LIB_SUBST_FLAGS([lopsub])
+UNSTASH_FLAGS
 ######################################################################## openssl
 STASH_FLAGS
 HAVE_OPENSSL=yes
@@ -165,15 +177,6 @@ AC_MSG_RESULT($have_ucred)
 if test ${have_ucred} = yes; then
        AC_DEFINE(HAVE_UCRED, 1, define to 1 you have struct ucred)
 fi
-########################################################################### gengetopt
-echo 'option "z" z "" flag off' | $GENGETOPT --file-name conftest-ggo &&
-AC_CHECK_DECL(
-       [gengetopt_args_info_description],
-       [ggo_descriptions_declared=yes],
-       [ggo_descriptions_declared=no],
-       [#include "conftest-ggo.h"]
-)
-AC_SUBST(ggo_descriptions_declared)
 ########################################################################### curses
 STASH_FLAGS
 LIB_ARG_WITH([curses], [])
@@ -348,7 +351,6 @@ UNSTASH_FLAGS
 if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
        build_server="yes"
        executables="$executables server"
-       server_cmdline_objs="server"
        server_errlist_objs="
                server
                afh_common
@@ -387,7 +389,6 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
                wma_common
                sideband
                version
-               ggo
        "
        if test "$CRYPTOLIB" = openssl; then
                server_errlist_objs="$server_errlist_objs crypt"
@@ -402,7 +403,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes; then
        if test $HAVE_FAAD = yes; then
                server_errlist_objs="$server_errlist_objs aac_afh"
        fi
-       server_objs="add_cmdline($server_cmdline_objs) $server_errlist_objs"
+       server_objs="$server_errlist_objs"
        AC_SUBST(server_objs, add_dot_o($server_objs))
 else
        build_server="no"
@@ -411,7 +412,6 @@ fi
 if test -n "$CRYPTOLIB"; then
        build_client="yes"
        executables="$executables client"
-       client_cmdline_objs="client"
        client_errlist_objs="
                client
                net
@@ -427,7 +427,6 @@ if test -n "$CRYPTOLIB"; then
                crypt_common
                base64
                version
-               ggo
        "
        if test "$CRYPTOLIB" = openssl; then
                client_errlist_objs="$client_errlist_objs crypt"
@@ -437,8 +436,8 @@ if test -n "$CRYPTOLIB"; then
        if test $HAVE_READLINE = yes; then
                client_errlist_objs="$client_errlist_objs interactive"
        fi
-       client_objs="add_cmdline($client_cmdline_objs) $client_errlist_objs"
-       AC_SUBST(client_objs, add_dot_o($client_objs))
+       client_objs="$client_errlist_objs"
+       AC_SUBST(client_objs, add_dot_o($client_errlist_objs))
 else
        build_client="no"
 fi
@@ -447,18 +446,6 @@ if test -n "$CRYPTOLIB"; then
        build_audiod="yes"
        executables="$executables audiod"
        audiod_audio_formats="wma"
-       audiod_cmdline_objs="$audiod_cmdline_objs
-               audiod
-               compress_filter
-               http_recv
-               dccp_recv
-               file_write
-               client
-               amp_filter
-               udp_recv
-               prebuffer_filter
-               sync_filter
-       "
        audiod_errlist_objs="$audiod_errlist_objs
                audiod
                signal
@@ -485,7 +472,6 @@ if test -n "$CRYPTOLIB"; then
                audiod_command
                fecdec_filter
                client_common
-               ggo
                udp_recv
                color
                fec
@@ -525,26 +511,21 @@ if test -n "$CRYPTOLIB"; then
        fi
        if test $HAVE_MAD = yes; then
                audiod_audio_formats="$audiod_audio_formats mp3"
-               audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter"
                audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
        fi
        if test $HAVE_OSS = yes; then
                audiod_errlist_objs="$audiod_errlist_objs oss_write"
-               audiod_cmdline_objs="$audiod_cmdline_objs oss_write"
        fi
        if test $HAVE_ALSA = yes; then
                audiod_errlist_objs="$audiod_errlist_objs alsa_write"
-               audiod_cmdline_objs="$audiod_cmdline_objs alsa_write"
        fi
        NEED_AO_OBJECTS && {
                audiod_errlist_objs="$audiod_errlist_objs ao_write"
-               audiod_cmdline_objs="$audiod_cmdline_objs ao_write"
        }
        if test $HAVE_SAMPLERATE = yes; then
                audiod_errlist_objs="$audiod_errlist_objs resample_filter check_wav"
-               audiod_cmdline_objs="$audiod_cmdline_objs resample_filter"
        fi
-       audiod_objs="add_cmdline($audiod_cmdline_objs) $audiod_errlist_objs"
+       audiod_objs="$audiod_errlist_objs"
        AC_SUBST(audiod_objs, add_dot_o($audiod_objs))
 
        enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
@@ -555,53 +536,27 @@ if test -n "$CRYPTOLIB"; then
 else
        build_audiod="no"
 fi
-########################################################################### fade
+########################################################################### mixer
 if test $HAVE_OSS = yes -o $HAVE_ALSA = yes; then
-       build_fade="yes"
-       executables="$executables fade"
-       fade_cmdline_objs="fade"
-       fade_errlist_objs="fade exec string fd version ggo"
+       build_mixer="yes"
+       executables="$executables mixer"
+       mixer_errlist_objs="mixer exec string fd version"
        if test $HAVE_OSS = yes; then
-               fade_errlist_objs="$fade_errlist_objs oss_mix"
-               mixers="${mixers}oss "
-               default_mixer="OSS_MIX"
+               mixer_errlist_objs="$mixer_errlist_objs oss_mix"
        fi
        if test $HAVE_ALSA = yes; then
-               fade_errlist_objs="$fade_errlist_objs alsa_mix"
-               mixers="${mixers}alsa "
-               default_mixer="ALSA_MIX"
+               mixer_errlist_objs="$mixer_errlist_objs alsa_mix"
        fi
-       fade_objs="add_cmdline($fade_cmdline_objs) $fade_errlist_objs"
-       AC_SUBST(fade_objs, add_dot_o($fade_objs))
-       enum="$(
-               for i in $mixers; do
-                       printf "${i}_MIX, " | tr '[a-z]' '[A-Z]'
-               done
-       )"
-       AC_DEFINE_UNQUOTED(MIXER_ENUM, $enum NUM_SUPPORTED_MIXERS,
-               enum of supported mixers)
-       AC_DEFINE_UNQUOTED(DEFAULT_MIXER, $default_mixer,
-               use this mixer if none was specified)
-       names="$(for i in $mixers; do printf \"$i\",' ' ; done)"
-       AC_DEFINE_UNQUOTED(MIXER_NAMES, $names, supported mixer names)
-       inits="$(
-               for i in $mixers; do
-                       printf 'extern void '$i'_mix_init(struct mixer *); '
-               done
-       )"
-       AC_DEFINE_UNQUOTED(DECLARE_MIXER_INITS, $inits,
-               init functions of the supported mixers)
-       array="$(for i in $mixers; do printf '{.init = '$i'_mix_init},'; done)"
-       AC_DEFINE_UNQUOTED(MIXER_ARRAY, $array, array of supported mixers)
+       mixer_objs="$mixer_errlist_objs"
+       AC_SUBST(mixer_objs, add_dot_o($mixer_objs))
 else
-       build_fade="no"
+       build_mixer="no"
        AC_MSG_WARN([no mixer support])
 fi
 ########################################################################### gui
 if test $HAVE_CURSES = yes; then
        build_gui="yes"
        executables="$executables gui"
-       gui_cmdline_objs="gui"
        gui_errlist_objs="
                exec
                signal
@@ -614,24 +569,14 @@ if test $HAVE_CURSES = yes; then
                time
                sched
                version
-               ggo
        "
-       gui_objs="add_cmdline($gui_cmdline_objs) $gui_errlist_objs"
+       gui_objs="$gui_errlist_objs"
        AC_SUBST(gui_objs, add_dot_o($gui_objs))
 else
        build_gui="no"
        AC_MSG_WARN([no curses lib, cannot build para_gui])
 fi
 ######################################################################## filter
-filters="
-       compress
-       wav
-       amp
-       fecdec
-       wmadec
-       prebuffer
-       sync
-"
 filter_errlist_objs="
        filter_common
        wav_filter
@@ -643,7 +588,6 @@ filter_errlist_objs="
        sched
        fd
        amp_filter
-       ggo
        fecdec_filter
        fec
        version
@@ -657,65 +601,23 @@ filter_errlist_objs="
        net
        sync_filter
 "
-filter_cmdline_objs="
-       filter
-       compress_filter
-       amp_filter
-       prebuffer_filter
-       sync_filter
-"
-NEED_VORBIS_OBJECTS && {
-       filters="$filters oggdec"
-       filter_errlist_objs="$filter_errlist_objs oggdec_filter"
-}
-NEED_SPEEX_OBJECTS && {
-       filters="$filters spxdec"
-       filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
-}
-NEED_OPUS_OBJECTS && {
-       filters="$filters opusdec"
-       filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
-}
-NEED_FLAC_OBJECTS && {
-       filter_errlist_objs="$filter_errlist_objs flacdec_filter"
-       filters="$filters flacdec"
-}
+NEED_VORBIS_OBJECTS && filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+NEED_SPEEX_OBJECTS && filter_errlist_objs="$filter_errlist_objs spxdec_filter spx_common"
+NEED_OPUS_OBJECTS && filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+NEED_FLAC_OBJECTS && filter_errlist_objs="$filter_errlist_objs flacdec_filter"
 if test $HAVE_FAAD = yes; then
        filter_errlist_objs="$filter_errlist_objs aacdec_filter"
-       filters="$filters aacdec"
 fi
 if test $HAVE_MAD = yes; then
-       filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter"
        filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
-       filters="$filters mp3dec"
 fi
 if test $HAVE_SAMPLERATE = yes; then
        filter_errlist_objs="$filter_errlist_objs resample_filter check_wav"
-       filter_cmdline_objs="$filter_cmdline_objs resample_filter"
-       filters="$filters resample"
 fi
-filters="$(echo $filters)"
-AC_SUBST(filters)
-filter_objs="add_cmdline($filter_cmdline_objs) $filter_errlist_objs"
+filter_objs="$filter_errlist_objs"
 
 AC_SUBST(filter_objs, add_dot_o($filter_objs))
-
-enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
-       enum of supported filters)
-inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
-array="$(for i in $filters; do printf '{.name = "'$i'", .init = '$i'_filter_init},'; done)"
-AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
 ########################################################################## recv
-recv_cmdline_objs="
-       recv
-       http_recv
-       dccp_recv
-       udp_recv
-       afh_recv
-"
-
 recv_errlist_objs="
        http_recv
        recv_common
@@ -727,7 +629,6 @@ recv_errlist_objs="
        fd
        sched
        stdout
-       ggo
        udp_recv
        buffer_tree
        afh_recv
@@ -746,12 +647,10 @@ NEED_FLAC_OBJECTS && recv_errlist_objs="$recv_errlist_objs flac_afh"
 if test $HAVE_FAAD = yes; then
        recv_errlist_objs="$recv_errlist_objs aac_afh"
 fi
-recv_objs="add_cmdline($recv_cmdline_objs) $recv_errlist_objs"
-AC_SUBST(receivers, "http dccp udp afh")
+recv_objs="$recv_errlist_objs"
 AC_SUBST(recv_objs, add_dot_o($recv_objs))
 ########################################################################### afh
 audio_format_handlers="mp3 wma"
-afh_cmdline_objs="afh"
 afh_errlist_objs="
        afh
        string
@@ -762,7 +661,6 @@ afh_errlist_objs="
        wma_afh
        wma_common
        version
-       ggo
 "
 NEED_OGG_OBJECTS && afh_errlist_objs="$afh_errlist_objs ogg_afh_common"
 NEED_VORBIS_OBJECTS && {
@@ -786,7 +684,7 @@ if test $HAVE_FAAD = yes; then
        audio_format_handlers="$audio_format_handlers aac"
 fi
 
-afh_objs="add_cmdline($afh_cmdline_objs) $afh_errlist_objs"
+afh_objs="$afh_errlist_objs"
 
 AC_SUBST(afh_objs, add_dot_o($afh_objs))
 ########################################################################## play
@@ -794,7 +692,6 @@ play_errlist_objs="
        play
        fd
        sched
-       ggo
        buffer_tree
        time
        string
@@ -823,18 +720,6 @@ play_errlist_objs="
        version
        sync_filter
 "
-play_cmdline_objs="
-       http_recv
-       dccp_recv
-       udp_recv
-       afh_recv
-       compress_filter
-       amp_filter
-       prebuffer_filter
-       file_write
-       play
-       sync_filter
-"
 NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common"
 NEED_VORBIS_OBJECTS && {
        play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
@@ -855,36 +740,27 @@ if test $HAVE_FAAD = yes; then
        play_errlist_objs="$play_errlist_objs aac_afh aacdec_filter"
 fi
 if test $HAVE_MAD = yes; then
-       play_cmdline_objs="$play_cmdline_objs mp3dec_filter"
        play_errlist_objs="$play_errlist_objs mp3dec_filter"
 fi
 if test $HAVE_OSS = yes; then
        play_errlist_objs="$play_errlist_objs oss_write"
-       play_cmdline_objs="$play_cmdline_objs oss_write"
 fi
 if test $HAVE_ALSA = yes; then
        play_errlist_objs="$play_errlist_objs alsa_write"
-       play_cmdline_objs="$play_cmdline_objs alsa_write"
 fi
 NEED_AO_OBJECTS && {
        play_errlist_objs="$play_errlist_objs ao_write"
-       play_cmdline_objs="$play_cmdline_objs ao_write"
 }
 if test $HAVE_READLINE = yes; then
        play_errlist_objs="$play_errlist_objs interactive"
 fi
 if test $HAVE_SAMPLERATE = yes; then
        play_errlist_objs="$play_errlist_objs resample_filter check_wav"
-       play_cmdline_objs="$play_cmdline_objs resample_filter"
 fi
 
-play_objs="add_cmdline($play_cmdline_objs) $play_errlist_objs"
+play_objs="$play_errlist_objs"
 AC_SUBST(play_objs, add_dot_o($play_objs))
 ######################################################################### write
-write_cmdline_objs="
-       write
-       file_write
-"
 write_errlist_objs="
        write
        write_common
@@ -895,53 +771,28 @@ write_errlist_objs="
        sched
        stdin
        buffer_tree
-       ggo
        check_wav
        version
 "
-writers="file"
-default_writer="FILE_WRITE"
 
 NEED_AO_OBJECTS && {
        write_errlist_objs="$write_errlist_objs ao_write"
-       write_cmdline_objs="$write_cmdline_objs ao_write"
-       writers="$writers ao"
-       default_writer="AO_WRITE"
 }
 if test $HAVE_OSS = yes; then
        write_errlist_objs="$write_errlist_objs oss_write"
-       write_cmdline_objs="$write_cmdline_objs oss_write"
-       writers="$writers oss"
-       default_writer="OSS_WRITE"
 fi
 if test $HAVE_ALSA = yes; then
        write_errlist_objs="$write_errlist_objs alsa_write"
-       write_cmdline_objs="$write_cmdline_objs alsa_write"
-       writers="$writers alsa"
-       default_writer="ALSA_WRITE"
 fi
-AC_SUBST(writers)
-write_objs="add_cmdline($write_cmdline_objs) $write_errlist_objs"
+write_objs="$write_errlist_objs"
 AC_SUBST(write_objs, add_dot_o($write_objs))
-enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
-       enum of supported writers)
-AC_DEFINE_UNQUOTED(DEFAULT_WRITER, $default_writer, use this writer if none was specified)
-names="$(for i in $writers; do printf \"$i\",' ' ; done)"
-AC_DEFINE_UNQUOTED(WRITER_NAMES, $names, supported writer names)
-inits="$(for i in $writers; do printf 'extern void '$i'_write_init(struct writer *); '; done)"
-AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
-array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
-AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
 ######################################################################## audioc
-audioc_cmdline_objs="audioc"
 audioc_errlist_objs="
        audioc
        string
        net
        fd
        version
-       ggo
 "
 if test $HAVE_READLINE = yes; then
        audioc_errlist_objs="$audioc_errlist_objs
@@ -951,7 +802,7 @@ if test $HAVE_READLINE = yes; then
                time
        "
 fi
-audioc_objs="add_cmdline($audioc_cmdline_objs) $audioc_errlist_objs"
+audioc_objs="$audioc_errlist_objs"
 AC_SUBST(audioc_objs, add_dot_o($audioc_objs))
 ################################################################## status items
 
@@ -991,14 +842,11 @@ unix socket credentials: $have_ucred
 readline (interactive CLIs): $HAVE_READLINE
 id3 version 2 support: $HAVE_ID3TAG
 faad: $HAVE_FAAD
-
 audio format handlers: $audio_format_handlers
-filters: $(echo $filters)
-writers: $writers
 
 para_server: $build_server
 para_gui: $build_gui
-para_fade: $build_fade
+para_mixer: $build_mixer
 para_client: $build_client
 para_audiod: $build_audiod
 ])
index 253586e1f7ee4f90867a157757a34a227458047a..318969afe3c123c611638e3954c4556d29c82e14 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.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 "string.h"
 #include "net.h"
 #include "fd.h"
 
-#include "dccp_recv.cmdline.h"
-
 static void dccp_recv_close(struct receiver_node *rn)
 {
        if (rn->fd > 0)
@@ -39,25 +38,56 @@ static void dccp_recv_close(struct receiver_node *rn)
        btr_pool_free(rn->btrp);
 }
 
+/* Check whether the host supports the requested 'ccid' arguments. */
+static int dccp_recv_ccid_support_check(const struct lls_parse_result *lpr)
+{
+       uint8_t *ccids;
+       int i, j, ret, nccids;
+       unsigned given = RECV_CMD_OPT_GIVEN(DCCP, CCID, lpr);
+
+       ret = dccp_available_ccids(&ccids);
+       if (ret < 0)
+               return ret;
+       nccids = ret;
+       for (i = 0; i < given; i++) {
+               uint32_t val = lls_uint32_val(i,
+                       RECV_CMD_OPT_RESULT(DCCP, CCID, lpr));
+               for (j = 0; j < nccids && ccids[j] != val; j++)
+                       ;
+               if (j == nccids) {
+                       PARA_ERROR_LOG("'CCID-%u' not supported on this host\n",
+                               val);
+                       return -ERRNO_TO_PARA_ERROR(EINVAL);
+               }
+       }
+       return 1;
+}
+
 static int dccp_recv_open(struct receiver_node *rn)
 {
-       struct dccp_recv_args_info *conf = rn->conf;
+       struct lls_parse_result *lpr = rn->lpr;
        struct flowopts *fo = NULL;
        uint8_t *ccids = NULL;
        int fd, ret, i;
+       const struct lls_opt_result *r_c = RECV_CMD_OPT_RESULT(DCCP, CCID, lpr);
+       const char *host = RECV_CMD_OPT_STRING_VAL(DCCP, HOST, lpr);
+       uint32_t port = RECV_CMD_OPT_UINT32_VAL(DCCP, PORT, lpr);
+       unsigned given;
 
+       ret = dccp_recv_ccid_support_check(lpr);
+       if (ret < 0)
+               return ret;
        /* Copy CCID preference list (u8 array required) */
-       if (conf->ccid_given) {
-               ccids = para_malloc(conf->ccid_given);
-               fo    = flowopt_new();
-
-               for (i = 0; i < conf->ccid_given; i++)
-                       ccids[i] = conf->ccid_arg[i];
-
+       given = lls_opt_given(r_c);
+       if (given) {
+               ccids = para_malloc(given);
+               fo = flowopt_new();
+               for (i = 0; i < given; i++)
+                       ccids[i] = lls_int32_val(i, r_c);
                OPT_ADD(fo, SOL_DCCP, DCCP_SOCKOPT_CCID, ccids, i);
        }
 
-       fd = makesock(IPPROTO_DCCP, 0, conf->host_arg, conf->port_arg, fo);
+       fd = makesock(IPPROTO_DCCP, 0, host, port, fo);
        flowopt_cleanup(fo);
        free(ccids);
        if (fd < 0)
@@ -83,42 +113,6 @@ err:
        return ret;
 }
 
-/**
- * Check whether the host supports the requested 'ccid' arguments.
- * \param conf DCCP receiver arguments.
- * \return True if all CCIDs requested in \a conf are supported.
- */
-static bool dccp_recv_ccid_support_check(struct dccp_recv_args_info *conf)
-{
-       uint8_t *ccids;
-       int i, j, nccids;
-
-       nccids = dccp_available_ccids(&ccids);
-       if (nccids <= 0)
-               return false;
-
-       for (i = 0; i < conf->ccid_given; i++) {
-               for (j = 0; j < nccids && ccids[j] != conf->ccid_arg[i]; j++)
-                       ;
-               if (j == nccids) {
-                       PARA_ERROR_LOG("'CCID-%d' not supported on this host.\n",
-                                       conf->ccid_arg[i]);
-                       return false;
-               }
-       }
-       return true;
-}
-
-static void *dccp_recv_parse_config(int argc, char **argv)
-{
-       struct dccp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
-       dccp_recv_cmdline_parser(argc, argv, tmp);
-       if (!dccp_recv_ccid_support_check(tmp))
-               exit(EXIT_FAILURE);
-       return tmp;
-}
-
 static void dccp_recv_pre_select(struct sched *s, void *context)
 {
        struct receiver_node *rn = context;
@@ -161,30 +155,9 @@ out:
        return ret;
 }
 
-static void dccp_recv_free_config(void *conf)
-{
-       dccp_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the dccp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void dccp_recv_init(struct receiver *r)
-{
-       struct dccp_recv_args_info dummy;
-
-       dccp_recv_cmdline_parser_init(&dummy);
-       r->open = dccp_recv_open;
-       r->close = dccp_recv_close;
-       r->pre_select = dccp_recv_pre_select;
-       r->post_select = dccp_recv_post_select;
-       r->parse_config = dccp_recv_parse_config;
-       r->free_config = dccp_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(dccp_recv);
-       dccp_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_dccp_user_data = {
+       .open = dccp_recv_open,
+       .close = dccp_recv_close,
+       .pre_select = dccp_recv_pre_select,
+       .post_select = dccp_recv_post_select,
+};
index 92dd933347d2c48d857355da34c13673da54968b..61d42126f95ffc98cf10b33c3c286f7082d44692 100644 (file)
@@ -18,7 +18,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
@@ -32,7 +34,6 @@
 #include "fd.h"
 #include "close_on_fork.h"
 #include "chunk_queue.h"
-#include "server.cmdline.h"
 #include "acl.h"
 
 static struct sender_status dccp_sender_status, *dss = &dccp_sender_status;
@@ -91,6 +92,7 @@ static int dccp_init_fec(struct sender_client *sc)
 {
        int mps, ret;
        socklen_t ml = sizeof(mps);
+       uint32_t mss; /* max slize size */
 
        /* If call fails, return some sensible minimum value */
        ret = getsockopt(sc->fd, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &mps, &ml);
@@ -100,8 +102,9 @@ static int dccp_init_fec(struct sender_client *sc)
        }
        PARA_INFO_LOG("current MPS = %d bytes\n", mps);
        assert(mps > 0);
-       if (conf.dccp_max_slice_size_arg > 0)
-               mps = PARA_MIN(mps, conf.dccp_max_slice_size_arg);
+       mss = OPT_UINT32_VAL(DCCP_MAX_SLICE_SIZE);
+       if (mss > 0 && mss <= INT_MAX)
+               mps = PARA_MIN(mps, (int)mss);
        return mps;
 }
 
@@ -118,6 +121,7 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        struct sender_client *sc;
        struct dccp_fec_client *dfc;
        int tx_ccid;
+       uint32_t k, n;
 
        sc = accept_sender_client(dss, rfds);
        if (!sc)
@@ -143,8 +147,16 @@ static void dccp_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        }
        dfc = para_calloc(sizeof(*dfc));
        sc->private_data = dfc;
-       dfc->fcp.data_slices_per_group  = conf.dccp_data_slices_per_group_arg;
-       dfc->fcp.slices_per_group       = conf.dccp_slices_per_group_arg;
+       k = OPT_UINT32_VAL(DCCP_DATA_SLICES_PER_GROUP);
+       n = OPT_UINT32_VAL(DCCP_SLICES_PER_GROUP);
+       if (k == 0 || n == 0 || k >= n) {
+               PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
+               dfc->fcp.data_slices_per_group = 3;
+               dfc->fcp.slices_per_group = 4;
+       } else {
+               dfc->fcp.data_slices_per_group = k;
+               dfc->fcp.slices_per_group = n;
+       }
        dfc->fcp.init_fec               = dccp_init_fec;
        dfc->fcp.send_fec               = dccp_send_fec;
        dfc->fcp.need_periodic_header   = false;
@@ -216,7 +228,7 @@ static char *dccp_status(void)
  */
 void dccp_send_init(struct sender *s)
 {
-       int ret, k, n;
+       int ret;
 
        s->status = dccp_status;
        s->send = NULL;
@@ -232,18 +244,9 @@ void dccp_send_init(struct sender *s)
        s->client_cmds[SENDER_add] = NULL;
        s->client_cmds[SENDER_delete] = NULL;
 
-       k = conf.dccp_data_slices_per_group_arg;
-       n = conf.dccp_slices_per_group_arg;
-
-       if (k <= 0 || n <= 0 || k >= n) {
-               PARA_WARNING_LOG("invalid FEC parameters, using defaults\n");
-               conf.dccp_data_slices_per_group_arg = 3;
-               conf.dccp_slices_per_group_arg = 4;
-       }
-
-       init_sender_status(dss, conf.dccp_access_arg, conf.dccp_access_given,
-               conf.dccp_port_arg, conf.dccp_max_clients_arg,
-               conf.dccp_default_deny_given);
+       init_sender_status(dss, OPT_RESULT(DCCP_ACCESS),
+               OPT_UINT32_VAL(DCCP_PORT), OPT_UINT32_VAL(DCCP_MAX_CLIENTS),
+               OPT_GIVEN(DCCP_DEFAULT_DENY));
        ret = generic_com_on(dss, IPPROTO_DCCP);
        if (ret < 0)
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
diff --git a/error.h b/error.h
index 32e525cd36e220e28a61359a008889e5b1e2c08b..58fcd3757c64acda473456de39196a7a6766fde3 100644 (file)
--- a/error.h
+++ b/error.h
        PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
        PARA_ERROR(BAD_CHANNEL_COUNT, "channel count not supported"), \
        PARA_ERROR(BAD_CHANNEL, "invalid channel"), \
-       PARA_ERROR(BAD_CMD, "invalid command"), \
-       PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
        PARA_ERROR(BAD_CT, "invalid chunk table or bad FEC configuration"), \
        PARA_ERROR(BAD_FEATURE, "invalid feature request"), \
        PARA_ERROR(BAD_FEC_HEADER, "invalid fec header"), \
-       PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
        PARA_ERROR(BAD_LL, "invalid loglevel"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
-       PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
        PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
        PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
        PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
        PARA_ERROR(INCOHERENT_BLOCK_LEN, "incoherent block length"), \
-       PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
        PARA_ERROR(KEY_MARKER, "invalid/missing key header or footer"), \
        PARA_ERROR(KEY_PERM, "unprotected private key"), \
        PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
        PARA_ERROR(NO_AFHI, "audio format handler info required"), \
        PARA_ERROR(NO_ATTRIBUTES, "no attributes defined yet"), \
        PARA_ERROR(NO_AUDIO_FILE, "no audio file"), \
-       PARA_ERROR(NO_CONFIG, "config file not found"), \
        PARA_ERROR(NOFD, "did not receive open fd from afs"), \
-       PARA_ERROR(NO_FILTERS, "at least one filter must be given"), \
        PARA_ERROR(NO_MATCH, "no matches"), \
        PARA_ERROR(NO_MOOD, "no mood available"), \
        PARA_ERROR(NO_MORE_SLOTS, "no more empty slots"), \
        PARA_ERROR(PERM, "permission denied"), \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
        PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
-       PARA_ERROR(PLAY_SYNTAX, "para_play: syntax error"), \
        PARA_ERROR(PREBUFFER_SUCCESS, "prebuffering complete"), \
        PARA_ERROR(PRIVATE_KEY, "can not read private key"), \
        PARA_ERROR(QUEUE, "packet queue overrun"), \
        PARA_ERROR(READ_PATTERN, "did not read expected pattern"), \
        PARA_ERROR(RECV_EOF, "end of file"), \
        PARA_ERROR(RECVMSG, "recvmsg() failed"), \
-       PARA_ERROR(RECV_SYNTAX, "recv syntax error"), \
        PARA_ERROR(REGEX, "regular expression error"), \
        PARA_ERROR(RESAMPLE_EOF, "resample filter: end of file"), \
        PARA_ERROR(RSA, "RSA error"), \
@@ -282,19 +273,28 @@ extern const char * const para_errlist[];
  */
 #define OSL_ERROR_BIT 29
 
+#define LLS_ERROR_BIT 28
+
 /** Check whether the system error bit is set. */
 #define IS_SYSTEM_ERROR(num) (!!((num) & (1 << SYSTEM_ERROR_BIT)))
 
 /** Check whether the osl error bit is set. */
 #define IS_OSL_ERROR(num) (!!((num) & (1 << OSL_ERROR_BIT)))
 
+/** Check whether the lopsub error bit is set. */
+#define IS_LLS_ERROR(num) (!!((num) & (1 << LLS_ERROR_BIT)))
+
 /** Set the system error bit for the given number. */
 #define ERRNO_TO_PARA_ERROR(num) ((num) | (1 << SYSTEM_ERROR_BIT))
 
 /** Set the osl error bit for the given number. */
 #define OSL_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << OSL_ERROR_BIT))
 
+/** Set the lopsub error bit for the error code. */
+#define LLS_ERRNO_TO_PARA_ERROR(num) ((num) | (1 << LLS_ERROR_BIT))
+
 static const char *weak_osl_strerror(int) __attribute__ ((weakref("osl_strerror")));
+static const char *weak_lls_strerror(int) __attribute__ ((weakref("lls_strerror")));
 /**
  * Paraslash's version of strerror(3).
  *
@@ -309,6 +309,10 @@ _static_inline_ const char *para_strerror(int num)
                assert(weak_osl_strerror);
                return weak_osl_strerror(num & ~(1U << OSL_ERROR_BIT));
        }
+       if (IS_LLS_ERROR(num)) {
+               assert(weak_lls_strerror);
+               return weak_lls_strerror(num & ~(1U << LLS_ERROR_BIT));
+       }
        if (IS_SYSTEM_ERROR(num))
                return strerror(num & ~(1U << SYSTEM_ERROR_BIT));
        return para_errlist[num];
@@ -331,3 +335,16 @@ _static_inline_ int osl(int ret)
                return ret;
        return -OSL_ERRNO_TO_PARA_ERROR(-ret);
 }
+
+/**
+ * Wrapper for lopsub library calls.
+ *
+ * \param ret See osl().
+ * \return See osl().
+ */
+_static_inline_ int lls(int ret)
+{
+       if (ret >= 0)
+               return ret;
+       return -LLS_ERRNO_TO_PARA_ERROR(-ret);
+}
diff --git a/fade.c b/fade.c
deleted file mode 100644 (file)
index 67dc4d5..0000000
--- a/fade.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file fade.c A volume fader and alarm clock for OSS. */
-
-#include <regex.h>
-
-#include "fade.cmdline.h"
-#include "para.h"
-#include "fd.h"
-#include "string.h"
-#include "mix.h"
-#include "error.h"
-#include "ggo.h"
-#include "version.h"
-
-/** Array of error strings. */
-DEFINE_PARA_ERRLIST;
-
-static struct fade_args_info conf;
-
-enum mixer_id {MIXER_ENUM};
-static char *mixer_name[] = {MIXER_NAMES};
-DECLARE_MIXER_INITS;
-static struct mixer supported_mixer[] = {MIXER_ARRAY};
-#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
-
-static int loglevel;
-static __printf_2_3 void date_log(int ll, const char *fmt, ...)
-{
-       va_list argp;
-       time_t t1;
-       struct tm *tm;
-
-       if (ll < loglevel)
-               return;
-       time(&t1);
-       tm = localtime(&t1);
-       fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
-       va_start(argp, fmt);
-       vprintf(fmt, argp);
-       va_end(argp);
-}
-__printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
-
-static int set_channel(struct mixer *m, struct mixer_handle *h, const char *channel)
-{
-
-       PARA_NOTICE_LOG("using %s mixer channel\n", channel?
-               channel : "default");
-       return m->set_channel(h, channel);
-}
-
-/* Fade to new volume in fade_time seconds. */
-static int fade(struct mixer *m, struct mixer_handle *h, int new_vol, int fade_time)
-{
-       int vol, diff, incr, ret;
-       unsigned secs;
-       struct timespec ts;
-       unsigned long long tmp, tmp2; /* Careful with that axe, Eugene! */
-
-       if (fade_time <= 0)
-               return m->set(h, new_vol);
-       secs = fade_time;
-       ret = m->get(h);
-       if (ret < 0)
-               goto out;
-       vol = ret;
-       PARA_NOTICE_LOG("fading %s from %d to %d in %u seconds\n",
-               conf.mixer_channel_arg, vol, new_vol, secs);
-       diff = new_vol - vol;
-       if (!diff) {
-               sleep(secs);
-               goto out;
-       }
-       incr = diff > 0? 1: -1;
-       diff = diff > 0? diff: -diff;
-       tmp = secs * 1000 / diff;
-       tmp2 = tmp % 1000;
-       while ((new_vol - vol) * incr > 0) {
-               ts.tv_nsec = tmp2 * 1000000; /* really nec ?*/
-               ts.tv_sec = tmp / 1000; /* really nec ?*/
-               //printf("ts.tv_sec: %i\n", ts.tv_nsec);
-               vol += incr;
-               ret = m->set(h, vol);
-               if (ret < 0)
-                       goto out;
-               //printf("vol = %i\n", vol);
-               nanosleep(&ts, NULL);
-       }
-out:
-       return ret;
-}
-
-static void client_cmd(const char *cmd)
-{
-       int ret, status, fds[3] = {0, 0, 0};
-       pid_t pid;
-       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
-
-       PARA_NOTICE_LOG("%s\n", cmdline);
-       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
-       free(cmdline);
-       if (ret < 0) {
-               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               goto fail;
-       }
-       do
-               pid = waitpid(pid, &status, 0);
-       while (pid == -1 && errno == EINTR);
-       if (pid < 0) {
-               PARA_ERROR_LOG("%s\n", strerror(errno));
-               goto fail;
-       }
-       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
-               goto fail;
-       return;
-fail:
-       PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
-       exit(EXIT_FAILURE);
-}
-
-static void change_afs_mode(char *afs_mode)
-{
-       char *cmd;
-
-       client_cmd("stop");
-       if (!afs_mode)
-               return;
-       cmd = make_message("select %s", afs_mode);
-       client_cmd(cmd);
-       free(cmd);
-}
-
-static int set_initial_volume(struct mixer *m, struct mixer_handle *h)
-{
-       int i, ret;
-
-       for (i = 0; i < conf.ivol_given; i++) {
-               char *p, *ch, *arg = para_strdup(conf.ivol_arg[i]);
-               int32_t iv;
-               p = strchr(arg, ':');
-               if (p) {
-                       *p = '\0';
-                       p++;
-                       ch = arg;
-               } else {
-                       p = arg;
-                       ch = NULL;
-               }
-               ret = para_atoi32(p, &iv);
-               if (ret < 0) {
-                       free(arg);
-                       return ret;
-               }
-               ret = set_channel(m, h, ch);
-               if (!ch)
-                       ch = "default";
-               if (ret < 0) {
-                       PARA_WARNING_LOG("ignoring channel %s\n", ch);
-                       ret = 0;
-               } else {
-                       PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
-                       ret = m->set(h, iv);
-               }
-               free(arg);
-               if (ret < 0)
-                       return ret;
-       }
-       return 1;
-}
-
-static int sweet_dreams(struct mixer *m, struct mixer_handle *h)
-{
-       time_t t1, wake_time_epoch;
-       unsigned int delay;
-       struct tm *tm;
-       int ret, min = conf.wake_min_arg;
-       char *fo_mood = conf.fo_mood_arg;
-       char *fi_mood = conf.fi_mood_arg;
-       char *sleep_mood = conf.sleep_mood_arg;
-       int fit = conf.fi_time_arg;
-       int fot = conf.fo_time_arg;
-       int fiv = conf.fi_vol_arg;
-       int fov = conf.fo_vol_arg;
-
-       /* calculate wake time */
-       time(&t1);
-       if (conf.wake_hour_given) {
-               int hour = conf.wake_hour_arg;
-               tm = localtime(&t1);
-               if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
-                       t1 += 86400; /* wake time is tomorrow */
-                       tm = localtime(&t1);
-               }
-               tm->tm_hour = hour;
-               tm->tm_min = min;
-               tm->tm_sec = 0;
-       } else {
-               t1 += 9 * 60 * 60; /* nine hours from now */
-               PARA_INFO_LOG("default wake time: %lu\n", (long unsigned)t1);
-               tm = localtime(&t1);
-       }
-       wake_time_epoch = mktime(tm);
-       PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
-       client_cmd("stop");
-       sleep(1);
-       if (fot) {
-               ret = set_initial_volume(m, h);
-               if (ret < 0)
-                       return ret;
-               change_afs_mode(fo_mood);
-               client_cmd("play");
-               ret = set_channel(m, h, conf.mixer_channel_arg);
-               if (ret < 0)
-                       return ret;
-               ret = fade(m, h, fov, fot);
-               if (ret < 0)
-                       return ret;
-       } else {
-               ret = m->set(h, fov);
-               if (ret < 0)
-                       return ret;
-       }
-       if (conf.sleep_mood_given) {
-               change_afs_mode(sleep_mood);
-               client_cmd("play");
-       } else
-               client_cmd("stop");
-       if (!fit)
-               return 1;
-       change_afs_mode(fi_mood);
-       for (;;) {
-               time(&t1);
-               if (wake_time_epoch <= t1 + fit)
-                       break;
-               delay = wake_time_epoch - t1 - fit;
-               PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
-                       delay, delay / 3600,
-                       (delay % 3600) / 60);
-               sleep(delay);
-       }
-       client_cmd("play");
-       ret = fade(m, h, fiv, fit);
-       PARA_INFO_LOG("fade complete, returning\n");
-       return ret;
-}
-
-static int snooze(struct mixer *m, struct mixer_handle *h)
-{
-       int ret, val;
-
-       if (conf.so_time_arg <= 0)
-               return 1;
-       ret = m->get(h);
-       if (ret < 0)
-               return ret;
-       val = ret;
-       if (val < conf.so_vol_arg)
-               ret = m->set(h, conf.so_vol_arg);
-       else
-               ret = fade(m, h, conf.so_vol_arg, conf.so_time_arg);
-       if (ret < 0)
-               return ret;
-       client_cmd("pause");
-       PARA_NOTICE_LOG("%d seconds snooze time...\n", conf.snooze_time_arg);
-       sleep(conf.snooze_time_arg);
-       client_cmd("play");
-       return fade(m, h, conf.si_vol_arg, conf.si_time_arg);
-}
-
-static int configfile_exists(void)
-{
-       static char *config_file;
-
-       if (!conf.config_file_given) {
-               char *home = para_homedir();
-               free(config_file);
-               config_file = make_message("%s/.paraslash/fade.conf", home);
-               free(home);
-               conf.config_file_arg = config_file;
-       }
-       return file_exists(conf.config_file_arg);
-}
-
-static void init_mixers(void)
-{
-       int i;
-
-       FOR_EACH_MIXER(i) {
-               struct mixer *m = &supported_mixer[i];
-               PARA_DEBUG_LOG("initializing mixer API #%d (%s)\n",
-                       i, mixer_name[i]);
-               m->init(m);
-       }
-}
-
-static int set_val(struct mixer *m, struct mixer_handle *h)
-{
-       return m->set(h, conf.val_arg);
-}
-
-static struct mixer *get_mixer_or_die(void)
-{
-       int i;
-
-       if (!conf.mixer_api_given)
-               i = DEFAULT_MIXER;
-       else
-               FOR_EACH_MIXER(i)
-                       if (!strcmp(mixer_name[i], conf.mixer_api_arg))
-                               break;
-       if (i < NUM_SUPPORTED_MIXERS) {
-               PARA_NOTICE_LOG("using %s mixer API\n", mixer_name[i]);
-               return supported_mixer + i;
-       }
-       printf("available mixer APIs: ");
-       FOR_EACH_MIXER(i) {
-               int d = (i == DEFAULT_MIXER);
-               printf("%s%s%s ", d? "[" : "", mixer_name[i], d? "]" : "");
-       }
-       printf("\n");
-       exit(EXIT_FAILURE);
-}
-
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(fade);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
-}
-
-/**
- * The main function of para_fade.
- *
- * The executable is linked with the alsa or the oss mixer API, or both. It has
- * a custom log function which prefixes log messages with the current date.
- *
- * \param argc Argument counter.
- * \param argv Argument vector.
- *
- * \return EXIT_SUCCESS or EXIT_FAILURE.
- */
-int main(int argc, char *argv[])
-{
-       int ret;
-       struct mixer *m;
-       struct mixer_handle *h = NULL;
-
-       fade_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("fade", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       ret = configfile_exists();
-       if (!ret && conf.config_file_given) {
-               PARA_EMERG_LOG("can not read config file %s\n",
-                       conf.config_file_arg);
-               exit(EXIT_FAILURE);
-       }
-       if (ret) {
-               struct fade_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1
-               };
-               fade_cmdline_parser_config_file(conf.config_file_arg,
-                       &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       }
-       init_mixers();
-       m = get_mixer_or_die();
-       ret = m->open(conf.mixer_device_arg, &h);
-       if (ret < 0)
-               goto out;
-       ret = set_channel(m, h, conf.mixer_channel_arg);
-       if (ret == -E_BAD_CHANNEL) {
-               char *channels = m->get_channels(h);
-               printf("Available channels: %s\n", channels);
-               free(channels);
-       }
-       if (ret < 0)
-               goto out;
-       switch (conf.mode_arg) {
-       case mode_arg_fade:
-               ret = fade(m, h, conf.fade_vol_arg, conf.fade_time_arg);
-               break;
-       case mode_arg_snooze:
-               ret = snooze(m, h);
-               break;
-       case mode_arg_set:
-               ret = set_val(m, h);
-               break;
-       default: /* sleep mode */
-               ret = sweet_dreams(m, h);
-               break;
-       }
-out:
-       m->close(&h);
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
-}
index 1c3a37849d1309fbf3b676bf04c513628dc1c657..1b95ea4c5fd76ca926e8caec733243947254c7ca 100644 (file)
@@ -12,7 +12,6 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -481,15 +480,9 @@ static void fecdec_open(struct filter_node *fn)
        fn->min_iqs = FEC_HEADER_SIZE;
 }
 
-/**
- * The init function of the fecdec filter.
- *
- * \param f Struct to initialize.
- */
-void fecdec_filter_init(struct filter *f)
-{
-       f->close = fecdec_close;
-       f->open = fecdec_open;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = fecdec_post_select;
-}
+const struct filter lsg_filter_cmd_com_fecdec_user_data = {
+       .open = fecdec_open,
+       .pre_select = generic_filter_pre_select,
+       .post_select = fecdec_post_select,
+       .close = fecdec_close,
+};
index 5e66607e4dba4a0595da7f8fcfeae0dba0bf3bad..9837e810278ee220fce4a313d695db3b9772f3ad 100644 (file)
@@ -8,17 +8,16 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
 #include "string.h"
 #include "fd.h"
-#include "file_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the file writer. */
@@ -47,31 +46,31 @@ __must_check __malloc static char *random_filename(void)
 
 static int prepare_output_file(struct writer_node *wn)
 {
-       struct file_write_args_info *conf = wn->conf;
-       char *filename;
-       int ret;
-       struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd));
-
-       if (conf->filename_given)
-               filename = conf->filename_arg;
-       else
-               filename = random_filename();
-       ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
-       if (!conf->filename_given)
-               free(filename);
-       if (ret < 0)
-               goto out;
-       pfwd->fd = ret;
-       ret = mark_fd_blocking(pfwd->fd);
+       const unsigned flags = O_WRONLY | O_CREAT, mode = S_IRUSR | S_IWUSR;
+       int ret, fd;
+       struct private_file_write_data *pfwd;
+
+
+       if (WRITE_CMD_OPT_GIVEN(FILE, FILENAME, wn->lpr)) {
+               const char *path = WRITE_CMD_OPT_STRING_VAL(FILE, FILENAME,
+                       wn->lpr);
+               ret = para_open(path, flags, mode);
+       } else {
+               char *path = random_filename();
+               ret = para_open(path, flags, mode);
+               free(path);
+       }
        if (ret < 0)
-               goto out_close;
-       wn->private_data = pfwd;
+               return ret;
+       fd = ret;
+       ret = mark_fd_blocking(fd);
+       if (ret < 0) {
+               close(fd);
+               return ret;
+       }
+       pfwd = wn->private_data = para_calloc(sizeof(*pfwd));
+       pfwd->fd = fd;
        return 1;
-out_close:
-       close(pfwd->fd);
-out:
-       free(pfwd);
-       return ret;
 }
 
 static void file_write_pre_select(struct sched *s, void *context)
@@ -131,31 +130,9 @@ out:
        return ret;
 }
 
-__malloc static void *file_write_parse_config_or_die(int argc, char **argv)
-{
-       struct file_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       file_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void file_write_free_config(void *conf)
-{
-       file_write_cmdline_parser_free(conf);
-}
-
 /** the init function of the file writer */
-void file_write_init(struct writer *w)
-{
-       struct file_write_args_info dummy;
-
-       file_write_cmdline_parser_init(&dummy);
-       w->pre_select = file_write_pre_select;
-       w->post_select = file_write_post_select;
-       w->parse_config_or_die = file_write_parse_config_or_die;
-       w->free_config = file_write_free_config;
-       w->close = file_write_close;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(file_write);
-       file_write_cmdline_parser_free(&dummy);
-}
+struct writer lsg_write_cmd_com_file_user_data = {
+       .pre_select = file_write_pre_select,
+       .post_select = file_write_post_select,
+       .close = file_write_close,
+};
index 81901896aef25294a64599c9d77f63bf541f91e4..d5dac6758cc1a2e2041a97bf8cba4b2abe75e90a 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -7,22 +7,31 @@
 /** \file filter.c The stand-alone filter program. */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter.lsg.h"
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "stdin.h"
 #include "stdout.h"
 #include "error.h"
+#include "fd.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
+static struct lls_parse_result *lpr; /* command line options */
+
+#define CMD_PTR (lls_cmd(0, filter_suite))
+#define OPT_RESULT(_name) \
+       (lls_opt_result(LSG_FILTER_PARA_FILTER_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
 
 /** The list of all status items used by para_{server,audiod,gui}. */
 const char *status_item_list[] = {STATUS_ITEM_ARRAY};
@@ -45,48 +54,75 @@ static struct stdout_task stdout_task_struct;
 /** Pointer to the stdout task. */
 static struct stdout_task *sot = &stdout_task_struct;
 
-/** Gengetopt struct that holds the command line args. */
-static struct filter_args_info conf;
-
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel);
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(filter);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_filter_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
+       char *help;
+
+       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);
+       print_filter_helps(OPT_GIVEN(DETAILED_HELP));
        exit(EXIT_SUCCESS);
 }
 
 static int parse_config(void)
 {
-       static char *cf; /* config file */
+       char *home, *cf; /* config file */
        struct stat statbuf;
+       int ret;
 
-       version_handle_flag("filter", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       if (!cf) {
-               char *home = para_homedir();
-               cf = make_message("%s/.paraslash/filter.conf", home);
-               free(home);
-       }
+       version_handle_flag("filter", OPT_GIVEN(VERSION));
+       handle_help_flag();
+       home = para_homedir();
+       cf = make_message("%s/.paraslash/filter.conf", home);
+       free(home);
        if (!stat(cf, &statbuf)) {
-               struct filter_cmdline_parser_params params = {
-                       .override = 0,
-                       .initialize = 0,
-                       .check_required = 0,
-                       .check_ambiguity = 0,
-                       .print_errors = 1
-               };
-               filter_cmdline_parser_config_file(cf, &conf, &params);
-               loglevel = get_loglevel_by_name(conf.loglevel_arg);
+               void *map;
+               size_t sz;
+               int cf_argc;
+               char **cf_argv, *errctx;
+               struct lls_parse_result *cf_lpr, *merged_lpr;
+
+               ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+               if (ret != -E_EMPTY) {
+                       if (ret < 0)
+                               return ret;
+                       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);
+                               return ret;
+                       }
+                       cf_argc = ret;
+                       ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr,
+                               &errctx));
+                       lls_free_argv(cf_argv);
+                       if (ret < 0) {
+                               PARA_ERROR_LOG("parse error in %s\n", cf);
+                               return ret;
+                       }
+                       ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr, &errctx));
+                       lls_free_parse_result(cf_lpr, CMD_PTR);
+                       if (ret < 0)
+                               return ret;
+                       lls_free_parse_result(lpr, CMD_PTR);
+                       lpr = merged_lpr;
+                       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+               }
+       }
+       if (!OPT_GIVEN(FILTER)) {
+               print_filter_list();
+               exit(EXIT_SUCCESS);
        }
-       if (!conf.filter_given)
-               return -E_NO_FILTERS;
        return 1;
 }
 
@@ -109,36 +145,37 @@ int main(int argc, char *argv[])
        const struct filter *f;
        struct btr_node *parent;
        struct filter_node **fns;
+       struct lls_parse_result *filter_lpr; /* per-filter options */
+       char *errctx;
 
-       filter_cmdline_parser(argc, argv, &conf); /* aborts on errors */
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       filter_init();
-       ret = parse_config();
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
        if (ret < 0)
                goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       ret = parse_config();
+       if (ret < 0)
+               goto free_lpr;
        sit->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "stdin"));
        stdin_task_register(sit, &s);
 
-       fns = para_malloc(conf.filter_given * sizeof(*fns));
-       for (i = 0, parent = sit->btrn; i < conf.filter_given; i++) {
-               char *fa = conf.filter_arg[i];
+       fns = para_malloc(OPT_GIVEN(FILTER) * sizeof(*fns));
+       for (i = 0, parent = sit->btrn; i < OPT_GIVEN(FILTER); i++) {
+               const char *fa = lls_string_val(i, OPT_RESULT(FILTER));
+               const char *name;
                struct filter_node *fn;
                struct task_info ti;
 
                fn = fns[i] = para_calloc(sizeof(*fn));
-               ret = check_filter_arg(fa, &fn->conf);
-               if (ret < 0) {
-                       free(fn);
-                       goto out_cleanup;
-               }
-               fn->filter_num = ret;
+               fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr);
+               name = filter_name(fn->filter_num);
+               fn->lpr = filter_lpr;
+               PARA_DEBUG_LOG("filter #%d: %s\n", i, name);
                f = filter_get(fn->filter_num);
-               PARA_DEBUG_LOG("filter #%d: %s\n", i, f->name);
                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));
-               ti.name = f->name;
+               ti.name = name;
                ti.pre_select = f->pre_select;
                ti.post_select = f->post_select;
                ti.context = fn;
@@ -156,7 +193,6 @@ int main(int argc, char *argv[])
        btr_log_tree(sit->btrn, LL_INFO);
        ret = schedule(&s);
        sched_shutdown(&s);
-out_cleanup:
        for (i--; i >= 0; i--) {
                struct filter_node *fn = fns[i];
 
@@ -164,15 +200,21 @@ out_cleanup:
                if (f->close)
                        f->close(fn);
                btr_remove_node(&fn->btrn);
-               if (f->free_config)
-                       f->free_config(fn->conf);
+               if (f->teardown)
+                       f->teardown(fn->lpr, fn->conf);
                free(fn);
        }
        free(fns);
        btr_remove_node(&sit->btrn);
        btr_remove_node(&sot->btrn);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR);
 out:
-       if (ret < 0)
-               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       }
        exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
index 0bd546903927d412fccef946cf8f43783ed8bc70..4537b2202c3d5cffdf5397553364fc67f23dc287 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -6,9 +6,6 @@
 
 /** \file filter.h Filter-related structures and exported symbols from filter_common.c. */
 
-/** The list of supported filters. */
-enum filter_enum {FILTER_ENUM};
-
 /**
  * Describes one running instance of a filter.
 */
@@ -24,6 +21,8 @@ struct filter_node {
        struct list_head callbacks;
        /** A pointer to the configuration of this instance. */
        void *conf;
+       /** The parsed command line, merged with options given in the config file. */
+       struct lls_parse_result *lpr;
        /** The buffer tree node. */
        struct btr_node *btrn;
        /** The task corresponding to this filter node. */
@@ -35,26 +34,16 @@ struct filter_node {
 /**
  * The structure associated with a paraslash filter.
  *
- * Paraslash filters are "modules" which are used to transform an audio stream.
- * struct filter contains pointers to functions that must be supplied by the
- * filter code in order to be used by the driving application (currently
- * para_audiod and para_filter).
+ * Paraslash filters are "modules" which transform an audio stream. struct
+ * filter contains methods which are implemented by each filter.
  *
  * Note: As several instances of the same filter may be running at the same
  * time, all these filter functions must be reentrant; no static non-constant
  * variables may be used.
+ *
  * \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node
  */
 struct filter {
-       /** The name of the filter. */
-       const char *name;
-       /**
-        * Pointer to the filter init routine.
-        *
-        * This function is only called once at startup. It must initialize the
-        * other non-optional function pointers of this structure.
-        */
-       void (*init)(struct filter *f);
        /**
         * Open one instance of this filter.
         *
@@ -73,27 +62,29 @@ struct filter {
         */
        void (*close)(struct filter_node *fn);
        /**
-        * A pointer to the filter's command line parser.
+        * Prepare the filter according to command line options.
         *
-        * If this optional function pointer is not NULL, any filter options
-        * are passed from the main program to this command line parser once at
-        * application startup. The command line parser should check its
-        * command line options given by \a argc and \a argv and abort on
-        * errors. Success must be indicated by a non-negative return value. In
-        * this case the function should return a pointer to the
-        * filter-specific configuration data determined by \a argc and \a
-        * argv. On failure, a negative paraslash error code must be returned.
+        * In addition to the syntactic checks which are automatically performed
+        * by the lopsub functions, some filters like to also check the command
+        * line arguments semantically. Moreover, since applications may open
+        * the filter many times with the same options, filters need a method
+        * which allows them to precompute once those parts of the setup which
+        * depend only on the command line options.
+        *
+        * If this function pointer is not NULL, the function is called once at
+        * startup. The returned pointer value is made available to the ->open
+        * method via the ->conf pointer of struct filter_node.
+        *
+        * Filters are supposed to abort if the setup fails. If the function
+        * returns, it is assumed to have succeeded.
         */
-       int (*parse_config)(int argc, char **argv, void **config);
+       void *(*setup)(const struct lls_parse_result *lpr);
        /**
-        * Deallocate the memory for the configuration.
+        * Deallocate precomputed resources.
         *
-        * This is called to free whatever ->parse_config() has allocated.
+        * This should free whatever ->setup() has allocated.
         */
-       void (*free_config)(void *conf);
-
-       /** The help texts for this filter. */
-       struct ggo_help help;
+       void (*teardown)(const struct lls_parse_result *lpr, void *conf);
        /**
         * Set scheduler timeout and add file descriptors to fd sets.
         *
@@ -121,9 +112,22 @@ struct filter {
        btr_command_handler execute;
 };
 
-void filter_init(void);
-int check_filter_arg(const char *fa, void **conf);
-void print_filter_helps(unsigned flags);
+void print_filter_helps(bool detailed);
+void print_filter_list(void);
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp);
+#define FILTER_CMD(_num) (lls_cmd(_num, filter_cmd_suite))
+#define FILTER_CMD_OPT(_cmd, _opt) (lls_opt( \
+       LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, \
+       FILTER_CMD(LSG_FILTER_CMD_CMD_ ## _cmd)))
+#define FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+       (lls_opt_result(LSG_FILTER_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define FILTER_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define FILTER_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+       (lls_string_val(0, FILTER_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+
 void generic_filter_pre_select(struct sched *s, void *context);
 int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels,
                char **result);
@@ -139,6 +143,6 @@ static inline void write_int16_host_endian(char *buf, int val)
 #endif
 }
 
-DECLARE_FILTER_INITS
-
+/** Make a filter pointer from the filter number. */
 const struct filter *filter_get(int filter_num);
+const char *filter_name(int filter_num);
index e9b97e54633a8770009c0f746b7daa712d4135fb..991b3a1e5a96dc47d7cc6594922883856aeac4d9 100644 (file)
@@ -8,22 +8,20 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "fd.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
 
-/** Iterate over the array of supported filters. */
-#define FOR_EACH_SUPPORTED_FILTER(j)  for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
-
-/** The array of supported filters. */
-static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+/** Iterate over all filters. */
+#define FOR_EACH_FILTER(j) for (j = 1; lls_cmd(j, filter_cmd_suite); j++)
 
 /**
  * Obtain a reference to a filter structure.
@@ -37,115 +35,130 @@ static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
  */
 const struct filter *filter_get(int filter_num)
 {
-       assert(filter_num >= 0);
-       assert(filter_num < NUM_SUPPORTED_FILTERS);
-       return filters + filter_num;
+       assert(filter_num >= 1);
+       assert(filter_num <= LSG_NUM_FILTER_CMD_SUBCOMMANDS);
+       return lls_user_data(FILTER_CMD(filter_num));
 }
 
-/**
- * Call the init function of each supported filter.
- * \sa filter::init
- */
-void filter_init(void)
+static inline bool filter_supported(int filter_num)
 {
-       int i;
-
-       FOR_EACH_SUPPORTED_FILTER(i)
-               filter_get(i)->init((struct filter *)filter_get(i));
+       return lls_user_data(FILTER_CMD(filter_num));
 }
 
-/*
- * If the filter has a command line parser and options is not NULL, run it.
- * Returns filter_num on success, negative on errors
- */
-static int parse_filter_args(int filter_num, const char *options, void **conf)
+const char *filter_name(int filter_num)
 {
-       const struct filter *f = filter_get(filter_num);
-       int ret, argc;
-       char **argv;
-
-       if (!f->parse_config)
-               return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
-       argc = create_shifted_argv(options, " \t", &argv);
-       if (argc < 0)
-               return -E_BAD_FILTER_OPTIONS;
-       argv[0] = para_strdup(f->name);
-       ret = f->parse_config(argc, argv, conf);
-       free_argv(argv);
-       return ret < 0? ret : filter_num;
+       return lls_command_name(FILTER_CMD(filter_num));
 }
 
 /**
- * Check the filter command line options.
+ * Parse a filter command line and call the corresponding ->setup method.
  *
  * \param fa The filter argument.
- * \param conf Points to the filter configuration upon successful return.
+ * \param conf Points to filter-specific setup upon successful return.
+ * \param lprp Parsed command line options are returned here.
  *
  * Check if the given filter argument starts with the name of a supported
  * filter, optionally followed by options for this filter. If yes, call the
- * command line parser of that filter.
- *
- * \return On success, the number of the filter is returned and \a conf
- * is initialized to point to the filter configuration determined by \a fa.
- * On errors, a negative value is returned.
- *
- * Note: If \a fa specifies a filter that has no command line parser success is
- * returned, and \a conf is initialized to \p NULL.
+ * command line parser of that filter and its ->setup method.
  *
- * \sa filter::parse_config
+ * \return This function either succeeds or does not return. On success, the
+ * number of the filter is returned and conf is initialized to point to the
+ * filter configuration as returned by the filter's ->setup() method, if any.
+ * Moreover, *lprp is initialized to contain the parsed command line options.
+ * On errors, the function calls exit(EXIT_FAILURE).
  */
-int check_filter_arg(const char *fa, void **conf)
+int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp)
 {
-       int j;
-
-       *conf = NULL;
-//     PARA_DEBUG_LOG("arg: %s\n", fa);
-       FOR_EACH_SUPPORTED_FILTER(j) {
-               const char *name = filter_get(j)->name;
-               size_t len = strlen(name);
-               char c;
-               if (strlen(fa) < len)
-                       continue;
-               if (strncmp(name, fa, len))
-                       continue;
-               c = fa[len];
-               if (c && c != ' ')
-                       continue;
-               if (c && !filter_get(j)->parse_config)
-                       return -E_BAD_FILTER_OPTIONS;
-               return parse_filter_args(j, c? fa + len + 1 :
-                       fa + strlen(fa), conf);
+       int ret, filter_num, argc;
+       char *errctx = NULL, **argv;
+       const struct lls_command *cmd;
+       const struct filter *f;
+
+       ret = create_argv(fa, " \t\n", &argv);
+       if (ret < 0)
+               goto fail;
+       argc = ret;
+       ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       filter_num = ret;
+       cmd = FILTER_CMD(filter_num);
+       if (!filter_supported(filter_num)) {
+               ret = -E_UNSUPPORTED_FILTER;
+               errctx = make_message("bad filter name: %s",
+                       lls_command_name(cmd));
+               goto free_argv;
        }
-       return -E_UNSUPPORTED_FILTER;
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       f = filter_get(filter_num);
+       *conf = f->setup? f->setup(*lprp) : NULL;
+       ret = filter_num;
+free_argv:
+       free_argv(argv);
+       if (ret >= 0)
+               return ret;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 /**
  * Print help text of each filter to stdout.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print short or long help.
  */
-void print_filter_helps(unsigned flags)
+void print_filter_helps(bool detailed)
 {
        int i, num = 0;
 
-       printf_or_die("\nAvailable filters: ");
-       FOR_EACH_SUPPORTED_FILTER(i) {
+       printf("\nAvailable filters: ");
+       FOR_EACH_FILTER(i) {
+               if (!filter_supported(i))
+                       continue;
                if (num > 50) {
-                       printf_or_die("\n                  ");
+                       printf("\n                  ");
                        num = 0;
                }
-               num += printf_or_die("%s%s", i? " " : "", filter_get(i)->name);
+               num += printf("%s%s", i? " " : "", filter_name(i));
        }
-       printf_or_die("\n");
+       printf("\n");
 
-       FOR_EACH_SUPPORTED_FILTER(i) {
-               struct filter *f = (struct filter *)filter_get(i);
+       FOR_EACH_FILTER(i) {
+               const struct lls_command *cmd = FILTER_CMD(i);
+               char *help;
+
+               if (!filter_supported(i))
+                       continue;
+               help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
+                       continue;
+               printf("%s\n", help);
+               free(help);
+       }
+}
+
+/**
+ * Print a short summary of all available filters to stdout.
+ *
+ * For each supported filter, the filter name and the purpose text is printed
+ * in a single line. Since no options are shown, the filter list is more
+ * concise than the text obtained from print_filter_helps().
+ */
+void print_filter_list(void)
+{
+       int i;
 
-               if (!f->help.short_help)
+       printf("Available filters:\n");
+       FOR_EACH_FILTER(i) {
+               const struct lls_command *cmd = FILTER_CMD(i);
+               if (!filter_supported(i))
                        continue;
-               printf_or_die("\nOptions for %s (%s):", f->name,
-                       f->help.purpose);
-               ggo_print_help(&f->help, flags);
+               printf("%-9s %s\n", filter_name(i), lls_purpose(cmd));
        }
 }
 
index bbacb3dad87025073b5bf8e99dc1ea8fa6bf5800..16237acbdcce863f37e1ab81988812ce4c53563c 100644 (file)
@@ -12,7 +12,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -296,18 +295,10 @@ static void flacdec_open(struct filter_node *fn)
        fn->min_iqs = 0;
 }
 
-/**
- * The init function of the flacdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void flacdec_filter_init(struct filter *f)
-{
-       f->open = flacdec_open;
-       f->close = flacdec_close;
-       f->pre_select = flacdec_pre_select;
-       f->post_select = flacdec_post_select;
-       f->execute = flacdec_execute;
-}
+const struct filter lsg_filter_cmd_com_flacdec_user_data = {
+       .open = flacdec_open,
+       .close = flacdec_close,
+       .pre_select = flacdec_pre_select,
+       .post_select = flacdec_post_select,
+       .execute = flacdec_execute,
+};
diff --git a/ggo.c b/ggo.c
deleted file mode 100644 (file)
index 81ab464..0000000
--- a/ggo.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.c Function for printing help. */
-
-
-#include "para.h"
-#include "ggo.h"
-#include "version.h"
-
-/**
- * Wrapper for printf() that exits on errors.
- *
- * \param fmt Usual format string.
- *
- * \return The return value of the underlying (successful) call to vprintf(3),
- * i.e. the number of characters printed, excluding the terminating null byte.
- */
-__printf_1_2 int printf_or_die(const char *fmt, ...)
-{
-       va_list argp;
-       int ret;
-
-       va_start(argp, fmt);
-       ret = vprintf(fmt, argp);
-       va_end(argp);
-       if (ret >= 0)
-               return ret;
-       exit(EXIT_FAILURE);
-}
-
-/**
- * Print one of the two given help texts.
- *
- * \param help contains the help texts.
- * \param flags What to print, see \ref ggo_print_help_flags.
- */
-void ggo_print_help(struct ggo_help *help, unsigned flags)
-{
-       const char **p;
-
-       if (help->purpose && (flags & GPH_PRINT_NAME_PURPOSE))
-               printf_or_die("para_%s - %s\n", help->prefix, help->purpose);
-       if (help->usage && (flags & GPH_PRINT_USAGE))
-               printf_or_die("\n%s\n", help->usage);
-       if (help->description && (flags & GPH_PRINT_DESCRIPTION))
-               printf_or_die("\n%s\n", help->description);
-       printf_or_die("\n");
-       if (flags & GPH_DETAILED)
-               p = help->detailed_help;
-       else
-               p = help->short_help;
-       if (!p)
-               return;
-       for (; *p; p++)
-               printf_or_die("%s\n", *p);
-}
diff --git a/ggo.h b/ggo.h
deleted file mode 100644 (file)
index 7e52403..0000000
--- a/ggo.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file ggo.h Functions and structures for help text handling. */
-
-/**
- * Information extracted from the .cmdline.h header files.
- */
-struct ggo_help {
-       /** Program or module (receiver, filter, writer) name. */
-       const char *prefix;
-       /** Generated by gengetopt from the options in the .ggo file. */
-       const char **short_help;
-       /** Like \a short_help, plus the \a details section. */
-       const char **detailed_help;
-       /** The purpose text as specified in the ggo file. */
-       const char *purpose;
-       /** Generated by gengetopt and exported via the *.cmdline.h file. */
-       const char *usage;
-       /** The description text given in the .ggo file. */
-       const char *description;
-};
-
-/**
- * Control the output of \ref ggo_print_help().
- *
- * Any combination of these flags may be passed to ggo_print_help().
- * Note that the list of supported options is always printed.
- */
-enum ggo_print_help_flags {
-       /** Whether to print the short help or the detailed help. */
-       GPH_DETAILED = 1 << 0,
-       /** Print the program or module name and the purpose text. */
-       GPH_PRINT_NAME_PURPOSE = 1 << 1,
-       /** Print the synopsis. */
-       GPH_PRINT_USAGE = 1 << 2,
-       /** Print the description text. */
-       GPH_PRINT_DESCRIPTION = 1 << 3,
-};
-
-/** Used to print the normal help of programs (--help) */
-#define GPH_STANDARD_FLAGS \
-       (GPH_PRINT_NAME_PURPOSE | GPH_PRINT_USAGE)
-
-/** Additional information for --detailed-help. */
-#define GPH_STANDARD_FLAGS_DETAILED \
-       (GPH_STANDARD_FLAGS | GPH_DETAILED | GPH_PRINT_DESCRIPTION)
-
-/** For module help embedded in a program help. */
-#define GPH_MODULE_FLAGS 0
-
-/** Modules help with detailed descriptions. */
-#define GPH_MODULE_FLAGS_DETAILED GPH_DETAILED | GPH_PRINT_DESCRIPTION
-
-/** Make a ggo_help structure using information from the .cmdline.h file. */
-#define DEFINE_GGO_HELP(_prefix) \
-       { \
-               .prefix = #_prefix, \
-               .short_help = _prefix ## _args_info_help, \
-               .detailed_help = _prefix ## _args_info_detailed_help, \
-               .purpose = _prefix ## _args_info_purpose, \
-               .usage = _prefix ## _args_info_usage, \
-               .description = _prefix ## _args_info_description, \
-       }
-
-void ggo_print_help(struct ggo_help *help, unsigned flags);
-__printf_1_2 int printf_or_die(const char *fmt, ...);
index 926f47927c7617f0aa53cae4eeec7f42026ce9f6..11fff4cc985db6716d14ee663f59facf764a2eeb 100644 (file)
@@ -8,11 +8,14 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <inttypes.h>
+#include <lopsub.h>
+
+#include "audiod_cmd.lsg.h"
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "grab_client.h"
 #include "audiod.h"
@@ -210,77 +213,62 @@ err:
        return ret;
 }
 
-static int gc_check_args(int argc, char **argv, struct grab_client *gc)
+static int gc_check_args(struct lls_parse_result *lpr, struct grab_client *gc)
 {
-       int i;
+       const struct lls_opt_result *r;
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-m", 2)) {
-                       if (*(arg + 3))
-                               return -E_GC_SYNTAX;
-                       switch(*(arg + 2)) {
-                       case 's':
-                               gc->mode = GM_SLOPPY;
-                               continue;
-                       case 'a':
-                               gc->mode = GM_AGGRESSIVE;
-                               continue;
-                       case 'p':
-                               gc->mode = GM_PEDANTIC;
-                               continue;
-                       default:
-                               return -E_GC_SYNTAX;
-                       }
-               }
-               if (!strcmp(arg, "-o")) {
-                       gc->flags |= GF_ONE_SHOT;
-                       continue;
-               }
-               if (!strncmp(arg, "-p=", 3)) {
-                       gc->parent = para_strdup(arg + 3);
-                       continue;
-               }
-               if (!strncmp(arg, "-n=", 3)) {
-                       gc->name = para_strdup(arg + 3);
-                       continue;
-               }
-               return -E_GC_SYNTAX;
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_MODE, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               if (strcmp(arg, "s") == 0)
+                       gc->mode = GM_SLOPPY;
+               else if (strcmp(arg, "a") == 0)
+                       gc->mode = GM_AGGRESSIVE;
+               else if (strcmp(arg, "p") == 0)
+                       gc->mode = GM_PEDANTIC;
+               else
+                       return -E_GC_SYNTAX;
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_ONE_SHOT, lpr);
+       if (lls_opt_given(r) > 0)
+               gc->flags |= GF_ONE_SHOT;
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_PARENT, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->parent = para_strdup(arg);
+       }
+
+       r = lls_opt_result(LSG_AUDIOD_CMD_GRAB_OPT_NAME, lpr);
+       if (lls_opt_given(r) > 0) {
+               const char *arg = lls_string_val(0, r);
+               gc->name = para_strdup(arg);
        }
-       if (i != argc)
-               return -E_GC_SYNTAX;
        return 1;
 }
 
 /**
- * Check the command line options and allocate a grab_client structure.
+ * Create and activate a grab client.
  *
  * \param fd The file descriptor of the client.
- * \param argc Argument count.
- * \param argv Argument vector.
+ * \param lpr The parsed command line of the grab command.
  * \param s The scheduler to register the grab client task to.
  *
- * If the command line options given by \a argc and \a argv are valid.
- * allocate a struct grab_client and initialize it with this valid
- * configuration.
- *
- * If the new grab client can be added to an existing buffer tree, activate it.
- * Otherwise, add it to the inactive list for later activation.
+ * This function semantically parses the arguments given as options to the grab
+ * command. On success it allocates a struct grab_client, associates it with
+ * the given file descriptor and activates it. If the new grab client can not
+ * be attached to an existing buffer tree node it is put into the inactive list
+ * for later activation.
  *
  * \return Standard.
  */
-int grab_client_new(int fd, int argc, char **argv, struct sched *s)
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s)
 {
        int ret;
        struct grab_client *gc = para_calloc(sizeof(struct grab_client));
 
-       ret = gc_check_args(argc, argv, gc);
+       ret = gc_check_args(lpr, gc);
        if (ret < 0)
                goto err_out;
        ret = dup(fd);
index 7a752cee96052fcc7516f9e4e58a1abe252e3e9b..3f3a0c036206de61e55cf2ca6c7f4dda23b839eb 100644 (file)
@@ -6,5 +6,5 @@
 
 /** \file grab_client.h exported symbols from grab_client.c */
 
-int grab_client_new(int fd, int argc, char **argv, struct sched *s);
+int grab_client_new(int fd, struct lls_parse_result *lpr, struct sched *s);
 void activate_grab_clients(struct sched *s);
diff --git a/gui.c b/gui.c
index 8e83cc53c6d55f53ccdb0a9dfbb4c89f0bb7525a..24f1c7276d9e551da6dc05a039a2171007e97c25 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -12,8 +12,9 @@
 #include <curses.h>
 #include <locale.h>
 #include <sys/time.h>
+#include <lopsub.h>
 
-#include "gui.cmdline.h"
+#include "gui.lsg.h"
 #include "para.h"
 #include "gui.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
 #include "signal.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *cmdline_lpr, *lpr;
+
+#define CMD_PTR (lls_cmd(0, gui_suite))
+#define OPT_RESULT(_name) (lls_opt_result(LSG_GUI_PARA_GUI_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 FOR_EACH_KEY_MAP(_i) for (_i = 0; _i < OPT_GIVEN(KEY_MAP); _i++)
+
 static char *stat_content[NUM_STAT_ITEMS];
 
 static struct gui_window {
@@ -51,7 +60,6 @@ static unsigned scroll_position;
 static pid_t exec_pid;
 
 static int exec_fds[2] = {-1, -1};
-static struct gui_args_info conf;
 static int loglevel;
 
 /** Type of the process currently being executed. */
@@ -644,7 +652,8 @@ static int status_post_select(struct sched *s, void *context)
                if (tv_diff(&st->next_exec, now, NULL) > 0)
                        return 0;
                st->next_exec.tv_sec = now->tv_sec + 2;
-               ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
+               ret = para_exec_cmdline_pid(&st->pid,
+                       OPT_STRING_VAL(STAT_CMD), fds);
                if (ret < 0)
                        return 0;
                ret = mark_fd_nonblocking(fds[1]);
@@ -831,12 +840,13 @@ static void check_key_map_args_or_die(void)
 {
        int i;
        char *tmp = NULL;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *handler, *arg;
 
                free(tmp);
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg))
                        break;
                if (strlen(handler) != 1)
@@ -849,77 +859,85 @@ static void check_key_map_args_or_die(void)
                if (find_cmd_byname(arg) < 0)
                        break;
        }
-       if (i != conf.key_map_given)
-               die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
+       if (i != OPT_GIVEN(KEY_MAP))
+               die(EXIT_FAILURE, "invalid key map: %s\n",
+                       lls_string_val(i, lor));
        free(tmp);
 }
 
-static void parse_config_file_or_die(bool override)
+static void parse_config_file_or_die(bool reload)
 {
-       bool err;
-       char *config_file;
-       struct gui_cmdline_parser_params params = {
-               .override = override,
-               .initialize = 0,
-               .check_required = !override,
-               .check_ambiguity = 0,
-               .print_errors = 1,
-       };
+       int ret;
+       char *cf = NULL, *errctx = NULL;
+       void *map;
+       size_t sz;
+       int cf_argc;
+       char **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
 
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/gui.conf", home);
+               cf = make_message("%s/.paraslash/gui.conf", home);
                free(home);
        }
-       if (!file_exists(config_file)) {
-               if (!conf.config_file_given)
-                       err = false;
-               else {
-                       PARA_EMERG_LOG("config file %s does not exist\n",
-                               config_file);
-                       err = true;
-               }
-               goto out;
-       }
-       /*
-        * When the gengetopt config file parser is called more than once, any
-        * key map arguments found in the config file are _appended_ to the old
-        * values, even though we turn on ->override. We want the new arguments
-        * to replace the old ones, so we must empty the key_map_arg array
-        * first. Unfortunately, this also clears any key map arguments given
-        * at the command line.
-        */
-       if (override) {
-               int i;
-               for (i = 0; i < conf.key_map_given; i++) {
-                       free(conf.key_map_arg[i]);
-                       conf.key_map_arg[i] = NULL;
-               }
-               conf.key_map_given = 0;
+       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;
+               ret = 0;
+               lpr = cmdline_lpr;
+               goto success;
        }
-
-       gui_cmdline_parser_config_file(config_file, &conf, &params);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
+       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;
+       if (reload) /* config file overrides command line */
+               ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       else /* command line options overrride config file options */
+               ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       lls_free_parse_result(cf_lpr, CMD_PTR);
+       if (ret < 0)
+               goto free_cf;
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(lpr, CMD_PTR);
+       lpr = merged_lpr;
+success:
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
        check_key_map_args_or_die();
-       err = false;
-out:
-       free(config_file);
-       if (err)
+       theme_init(OPT_STRING_VAL(THEME), &theme);
+free_cf:
+       free(cf);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
-       theme_init(conf.theme_arg, &theme);
+       }
 }
 
 /* reread configuration, terminate on errors */
 static void reread_conf(void)
 {
        /*
-        * gengetopt might print to stderr and exit on errors. So we have to
-        * shutdown curses first.
+        * If the reload of the config file fails, we are about to exit. In
+        * this case we print the error message to stderr rather than to the
+        * curses window.  So we have to shutdown curses first.
         */
        shutdown_curses();
-       parse_config_file_or_die(true /* override */);
+       parse_config_file_or_die(true);
        init_curses();
        print_in_bar(COLOR_MSG, "config file reloaded\n");
 }
@@ -1077,12 +1095,13 @@ static void exec_external(char *file_and_args)
 static void handle_command(int c)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
        /* first check user-defined key bindings */
-       for (i = 0; i < conf.key_map_given; ++i) {
+       FOR_EACH_KEY_MAP(i) {
                char *tmp, *handler, *arg;
 
-               tmp = para_strdup(conf.key_map_arg[i]);
+               tmp = para_strdup(lls_string_val(i, lor));
                if (!split_key_map(tmp, &handler, &arg)) {
                        free(tmp);
                        return;
@@ -1329,9 +1348,10 @@ static void com_reread_conf(void)
 static void com_help(void)
 {
        int i;
+       const struct lls_opt_result *lor = OPT_RESULT(KEY_MAP);
 
-       for (i = 0; i < conf.key_map_given; ++i) {
-               char *handler, *arg, *tmp = para_strdup(conf.key_map_arg[i]);
+       FOR_EACH_KEY_MAP(i) {
+               char *handler, *arg, *tmp = para_strdup(lls_string_val(i, lor));
                const char *handler_text = "???", *desc = NULL;
 
                if (!split_key_map(tmp, &handler, &arg)) {
@@ -1415,15 +1435,6 @@ static void com_prev_theme(void)
        com_refresh();
 }
 
-__noreturn static void print_help_and_die(void)
-{
-       struct ggo_help h = DEFINE_GGO_HELP(gui);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
-}
-
 static int setup_tasks_and_schedule(void)
 {
        int ret;
@@ -1431,12 +1442,7 @@ static int setup_tasks_and_schedule(void)
        struct status_task status_task = {.fd = -1};
        struct input_task input_task = {.task = NULL};
        struct signal_task *signal_task;
-       struct sched sched = {
-               .default_timeout = {
-                       .tv_sec = conf.timeout_arg  / 1000,
-                       .tv_usec = (conf.timeout_arg % 1000) * 1000,
-               },
-       };
+       struct sched sched = {.default_timeout = {.tv_sec = 1}};
 
        exec_task.task = task_register(&(struct task_info) {
                .name = "exec",
@@ -1476,6 +1482,21 @@ static int setup_tasks_and_schedule(void)
        return ret;
 }
 
+static void handle_help_flags(void)
+{
+       char *help;
+
+       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);
+}
+
 /**
  * The main function of para_gui.
  *
@@ -1503,15 +1524,31 @@ static int setup_tasks_and_schedule(void)
  */
 int main(int argc, char *argv[])
 {
-       gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("gui", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       parse_config_file_or_die(false /* override */);
+       int ret;
+       char *errctx;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       lpr = cmdline_lpr;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("gui", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_file_or_die(false);
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
        setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
-       return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
+       ret = setup_tasks_and_schedule();
+out:
+       lls_free_parse_result(lpr, CMD_PTR);
+       if (lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       }
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 2f334787200e4a0134df6a108a0c4dc8a7dd0775..d49cf2a853b3043edc09e5e419632b7cce2da6be 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "http.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "http_recv.cmdline.h"
 #include "net.h"
 #include "string.h"
 #include "fd.h"
@@ -144,20 +144,13 @@ static void http_recv_close(struct receiver_node *rn)
        free(rn->private_data);
 }
 
-static void *http_recv_parse_config(int argc, char **argv)
-{
-       struct http_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-
-       http_recv_cmdline_parser(argc, argv, tmp);
-       return tmp;
-}
-
 static int http_recv_open(struct receiver_node *rn)
 {
        struct private_http_recv_data *phd;
-       struct http_recv_args_info *conf = rn->conf;
-       int fd, ret = para_connect_simple(IPPROTO_TCP, conf->host_arg,
-                                                      conf->port_arg);
+       struct lls_parse_result *lpr = rn->lpr;
+       const char *r_i = RECV_CMD_OPT_STRING_VAL(HTTP, HOST, lpr);
+       uint32_t r_p = RECV_CMD_OPT_UINT32_VAL(HTTP, PORT, lpr);
+       int fd, ret = para_connect_simple(IPPROTO_TCP, r_i, r_p);
 
        if (ret < 0)
                return ret;
@@ -174,30 +167,9 @@ static int http_recv_open(struct receiver_node *rn)
        return 1;
 }
 
-static void http_recv_free_config(void *conf)
-{
-       http_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the http receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * This initializes all function pointers of \a r.
- */
-void http_recv_init(struct receiver *r)
-{
-       struct http_recv_args_info dummy;
-
-       http_recv_cmdline_parser_init(&dummy);
-       r->open = http_recv_open;
-       r->close = http_recv_close;
-       r->pre_select = http_recv_pre_select;
-       r->post_select = http_recv_post_select;
-       r->parse_config = http_recv_parse_config;
-       r->free_config = http_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(http_recv);
-       http_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_http_user_data = {
+       .open = http_recv_open,
+       .close = http_recv_close,
+       .pre_select = http_recv_pre_select,
+       .post_select = http_recv_post_select,
+};
index 9d0f49aee03cd6b30d6a73e9eda74dcb9be4cfc0..26536d07eea58350de76196c29c5512a0e0963a5 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
-#include "server.cmdline.h"
 #include "afh.h"
 #include "server.h"
 #include "http.h"
@@ -263,10 +264,10 @@ void http_send_init(struct sender *s)
        s->client_cmds[SENDER_add] = NULL;
        s->client_cmds[SENDER_delete] = NULL;
 
-       init_sender_status(hss, conf.http_access_arg, conf.http_access_given,
-               conf.http_port_arg, conf.http_max_clients_arg,
-               conf.http_default_deny_given);
-       if (conf.http_no_autostart_given)
+       init_sender_status(hss, OPT_RESULT(HTTP_ACCESS),
+               OPT_UINT32_VAL(HTTP_PORT), OPT_UINT32_VAL(HTTP_MAX_CLIENTS),
+               OPT_GIVEN(HTTP_DEFAULT_DENY));
+       if (OPT_GIVEN(HTTP_NO_AUTOSTART))
                return;
        ret = generic_com_on(hss, IPPROTO_TCP);
        if (ret < 0)
diff --git a/m4/gengetopt/afh.m4 b/m4/gengetopt/afh.m4
deleted file mode 100644 (file)
index 9b8a650..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-args "--unamed-opts=audio_file --no-handle-version --no-handle-help"
-
-purpose "Print information about audio file(s)"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-
-###################################
-section "printing meta information"
-###################################
-
-option "chunk-table" c
-#~~~~~~~~~~~~~~~~~~~~~
-"print also the chunk table"
-flag off
-details = "
-       The 'chunk table' of an audio file is an array of offsets
-       within the audio file. Each offset corresponds to chunk
-       of encoded data. The exact meaning of 'chunk' depends on
-       the audio format.
-
-       Programs which are unaware of the particular audio format can
-       read the chunk table to obtain the timing information needed
-       to stream the file.
-"
-
-option "parser-friendly" p
-#~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not use human-readable output format"
-flag off
-details = "
-       Currently this option only affects the format of the chunk table,
-       so it has no effect if --chunk-table is not given.
-
-       The human-readable output (the default) consists of one output
-       line per chunk and the output contains also the chunk number,
-       the duration and the size of each chunk. The parser-friendly
-       output prints only the offsets, in one line.
-"
-
-#############################
-section "modifying meta tags"
-#############################
-
-option "modify" m
-#~~~~~~~~~~~~~~~~
-"modify (rather than print) tags"
-flag off
-details = "
-       When this option is given, para_afh creates the result file
-       as a temporary copy of the given file(s), but with meta
-       tags changed according to the options below. On errors,
-       the temporary file is removed, leaving the original file
-       unchanged. On success, if --backup is given, the original
-       file is moved away. Finally the temporary file is renamed to
-       the name of the original file.
-"
-
-option "backup" b
-"create backup of the original file"
-flag off
-details = "
-       The backup suffix is '~', i.e. a single tilde character is appended
-       to the given file name.
-"
-
-option "year" y
-#~~~~~~~~~~~~~~
-"set the year tag"
-string typestr="year"
-optional
-
-option "title" t
-#~~~~~~~~~~~~~~~
-"set the title tag"
-string typestr="title"
-optional
-
-option "artist" a
-#~~~~~~~~~~~~~~~~
-"set the artist/author tag"
-string typestr="artist"
-optional
-
-option "album" A
-#~~~~~~~~~~~~~~~
-"set the album tag"
-string typestr="album"
-optional
-
-option "comment" C
-#~~~~~~~~~~~~~~~~~
-"set the comment tag"
-string typestr="comment"
-optional
-
-</qu>
diff --git a/m4/gengetopt/afh_recv.m4 b/m4/gengetopt/afh_recv.m4
deleted file mode 100644 (file)
index 28a0a9e..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Make an audio stream from a local file"
-
-description "
-       The afh (audio format handler) receiver can be used to write
-       selected parts of the given audio file without decoding
-       the data.
-
-       The selected parts of the content of the audio file are passed
-       to the child nodes of the buffer tree. Only complete chunks
-       with respect of the underlying audio format are passed.
-"
-
-include(header.m4)
-<qu>
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"file to open"
-string typestr = "filename"
-required
-
-option "begin-chunk" b
-#~~~~~~~~~~~~~~~~~~~~~
-"skip the beginning of the file"
-int typestr = "chunk_num"
-default = "0"
-optional
-details = "
-       The chunk_num argument must be between -num_chunks and
-       num_chunks - 1, inclusively, where num_chunks is the total
-       number of chunks of the audio file given by the argument to
-       --filename. If chunk_num is negative, the given number of
-       chunks are counted backwards from the end of the file. For
-       example --begin-chunk -100 instructs the afh receiver to
-       start output at chunk num_chunks - 100. This is useful for
-       selecting the last part of an audio file.
-"
-
-option "end-chunk" e
-#~~~~~~~~~~~~~~~~~~~
-"only write up to chunk chunk_num"
-int typestr = "chunk_num"
-optional
-details = "
-       For the chunk_num argument the same rules as for --begin-chunk
-       apply. The default is to write up to the last chunk.
-"
-
-option "just-in-time" j
-#~~~~~~~~~~~~~~~~~~~~~~
-"use timed writes"
-flag off
-details = "
-       Write the specified chunks of data 'just in time', i.e. the
-       write of each chunk is delayed until the time it is needed
-       by the decoder/player in order to guarantee an uninterrupted
-       audio stream. This may be useful for third-party software
-       that is capable of reading from stdin.
-"
-
-option "no-header" H
-#~~~~~~~~~~~~~~~~~~~
-"do not write an audio file header"
-flag off
-details = "
-       If an audio format needs information about the audio file
-       in a format-specific header in order to be understood by
-       the decoding software, a suitable header is automatically
-       send. This option changes the default behaviour, i.e. no
-       header is written.
-"
-
-</qu>
diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4
deleted file mode 100644 (file)
index b2c5621..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Native ALSA output plugin"
-
-include(header.m4)
-
-<qu>
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr = "device"
-default = "default"
-optional
-details = "
-       Check for the presence of a /proc/asound/ directory to see if
-       ALSA is present in your kernel. The file /proc/asound/devices
-       contains all devices ALSA knows about.
-"
-
-option "buffer-time" B
-#~~~~~~~~~~~~~~~~~~~~~
-"duration of the ALSA buffer"
-int typestr = "milliseconds"
-default = "170"
-optional
-details = "
-       This is only a hint as ALSA might pick a slightly different
-       time, depending on the sound hardware. The chosen value is
-       shown in debug output as BUFFER_TIME.
-
-       If synchronization between multiple clients is desired,
-       the same buffer time should be configured for all clients.
-"
-
-</qu>
-
diff --git a/m4/gengetopt/amp_filter.m4 b/m4/gengetopt/amp_filter.m4
deleted file mode 100644 (file)
index a02cc5b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Amplify the decoded audio stream"
-
-option "amp" a
-#~~~~~~~~~~~~~
-"amplification value"
-int typestr="number"
-default="32"
-optional
-details="
-       The amplification value determines the scaling factor by
-       which the amplitude of the audio stream is multiplied. The
-       formula for the scaling factor is
-
-               factor = 1 + amp / 64.
-
-       For example, an amplifiction value of zero results in a
-       scaling factor of one while an amplification value of 64
-       means to double the volume.
-"
diff --git a/m4/gengetopt/ao_write.m4 b/m4/gengetopt/ao_write.m4
deleted file mode 100644 (file)
index 29112d7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for libao"
-
-include(header.m4)
-<qu>
-
-option "driver" d
-#~~~~~~~~~~~~~~~~
-"Select a output driver by name"
-string typestr = "name"
-optional
-details = "
-       If this is not given, the driver with the highest priority
-       (see below) will be used.
-"
-
-option "ao-option" o
-#~~~~~~~~~~~~~~~~~~~
-"Pass a key-value pair to the libao driver"
-string typestr = "key:value"
-optional
-multiple
-details = "
-       For each time this option is given, the supplied key-value
-       pair is appended to the list of options for the driver. Invalid
-       keys are silently ignored.
-"
-
-</qu>
diff --git a/m4/gengetopt/audioc.m4 b/m4/gengetopt/audioc.m4
deleted file mode 100644 (file)
index f216204..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-args "--unamed-opts=command --conf-parser --no-handle-version --no-handle-help"
-
-purpose "Communicate with para_audiod through a local socket"
-
-include(header.m4)
-<qu>
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket (default=/var/paraslash/audiod.socket.$HOSTNAME)"
-       string typestr="filename"
-       optional
-
-
-option "bufsize" b
-#~~~~~~~~~~~~~~~~~
-"size of internal buffer"
-       int typestr="bytes"
-       default="8192"
-       optional
-</qu>
-
-define(CURRENT_PROGRAM,para_audioc)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/audioc.history)
-include(loglevel.m4)
-include(history_file.m4)
-include(complete.m4)
diff --git a/m4/gengetopt/audiod.m4 b/m4/gengetopt/audiod.m4
deleted file mode 100644 (file)
index 75c0458..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-args "--no-handle-help --no-handle-version --conf-parser"
-
-purpose "Connect to para_server, receive, decode and play audio streams"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_audiod)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/audiod.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(color.m4)
-include(config_file.m4)
-include(logfile.m4)
-include(log_timing.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-########################
-section "Audiod options"
-########################
-
-option "force" F
-#~~~~~~~~~~~~~~~
-"force startup"
-flag off
-details="
-       If this flag is not given, para_audiod refuses to start if the
-       well-known socket file (see the --socket option) already exists
-       because this usually means that para_audiod is already running
-       and listening on that socket. After a crash or if para_audiod
-       received a SIGKILL signal, a stale socket file might remain and
-       you have to use --force once to force startup of para_audiod.
-"
-
-option "mode" m
-#~~~~~~~~~~~~~~
-"startup mode"
-string typestr="mode"
-default="on"
-optional
-details="
-       Para_audiod supports three modes of operation: On, off and
-       standby (sb).  This option selects the mode that should be
-       used on startup. If para_audiod operates in \"on\" mode, it
-       will connect to para_server in order to receive its status
-       information. If para_server announces the availability of an
-       audio stream, para_audiod will automatically download, decode
-       and play the audio stream according to the given stream I/O
-       options, see below.
-
-       In \"standby\" mode, para_audiod will only receive the
-       status information from para_server but will not download
-       the audio stream.
-
-       In \"off\" mode, para_audiod does not connect para_server at
-       all, but still listens on the local socket for connections.
-"
-
-option "socket" s
-#~~~~~~~~~~~~~~~~
-"well-known socket"
-string typestr="filename"
-optional
-details="
-       Para_audiod uses a \"well-known\" socket to listen
-       on for connections from para_audioc. This socket is a
-       special file in the file system; its location defaults to
-       /var/paraslash/audiod_sock.<host_name>.
-
-       para_audioc, the client program used to connect to para_audiod,
-       opens this socket in order to talk to para_audiod.  If the
-       default value for para_audiod is changed, para_audioc must be
-       instructed to use also \"filename\" for connecting para_audiod.
-"
-
-option "user-allow" -
-#~~~~~~~~~~~~~~~~~~~~
-"allow this user to connect to audiod"
-string typestr = "username"
-optional
-multiple
-details = "
-       Allow the user identified by username (either a string or
-       a UID) to connect to para_audiod. This option may be given
-       multiple times. If not specified at all, all users are allowed
-       to connect.
-
-       This feature is based on the ability to send unix
-       credentials through local sockets using ancillary data
-       (SCM_CREDENTIALS). Currently it only works on Linux. On
-       other operating systems the option is silently ignored and
-       all local users are allowed to connect.
-"
-
-option "clock-diff-count" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sync clock on startup"
-int typestr="count"
-default="0"
-optional
-details="
-       Check the clock difference between the host running para_server
-       and the local host running para_audiod that many times before
-       starting any stream I/0. Set this to non-zero for non-local
-       setups if the clocks of these two hosts are not synchronized
-       by ntp or similar.
-"
-
-#############################
-section "Stream I/O options"
-#############################
-
-option "receiver" r
-#~~~~~~~~~~~~~~~~~~
-"select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-multiple
-details="
-       This option may be given multiple times, for each audio format
-       separately. If multiple definitions for an audio format are
-       given, the first one is selected.
-
-       The \"receiver_spec\" consists of an audio format specifier
-       and one or more receiver arguments, separated by a colon.
-
-       The audio format specifier is a regular expression which
-       specifies the set of audio formats for which this option
-       should apply.
-
-       If any receiver options are present, the whole receiver
-       argument must be quoted:
-
-               -r 'mp3:http -i my.host.org -p 8009'
-
-       Since a single dot '.' matches the name of any audio format,
-       specifying '.' instead of 'mp3' above activates the http
-       receiver for all audio formats.
-
-"
-
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify the filter configuration."
-string typestr = "filter_spec"
-optional
-multiple
-details = "
-       This option may be given multiple times. The \"filter_spec\"
-       consists of an audio format specifier (see above), the name
-       of the filter, and any options for that filter. Note that
-       order matters.
-
-       The compiled-in defaults apply to all audio formats for which
-       no --filter option was given. These defaults depend on the
-       receiver being used.
-
-       For HTTP streams, only the decoder for the current audio
-       format is activated. UDP and DCCP streams, on the other
-       hand, are sent FEC-encoded by para_server. In order to play
-       such streams, the receiver output must be FEC-decoded first,
-       i.e. fed to the fecdec filter. Therefore the default for UDP
-       and DCCP streams is to activate the fecdec filter, followed
-       by the decoding filter for the audio format.
-
-       Examples:
-
-               --filter 'mp3:mp3dec'
-
-               --filter 'mp3|aac:compress --inertia 5 --damp 2'
-
-               --filter '.:fecdec'
-
-"
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"Specify stream writer."
-string typestr="writer_spec"
-optional
-multiple
-details="
-       May be given multiple times, even multiple times for the same
-       audio format.  Default value is \"alsa\" for all supported
-       audio formats. Example:
-
-               --writer 'aac|wma:oss'
-
-"
-
-option "stream-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"time for client sync"
-int typestr="milliseconds"
-default="200"
-optional
-details="
-       Add the given amount of milliseconds to the stream start time
-       announced by para_server and do not send data to the writer
-       before that time (modulo clock difference).
-
-       This is useful mainly for synchronizing the audio output of
-       different clients.
-"
-</qu>
diff --git a/m4/gengetopt/channels.m4 b/m4/gengetopt/channels.m4
deleted file mode 100644 (file)
index 64c2518..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<qu>
-option "channels" c
-#~~~~~~~~~~~~~~~~~~
-"specify number of channels"
-int typestr = "num"
-default = "2"
-optional
-details = "
-       It is only necessary to specify this option for raw audio. If
-       it is not given, the channel count is queried from the parent
-       buffer tree nodes (e.g. the decoder) or the wav header. Only
-       if this fails, the default value applies.
-"
-</qu>
diff --git a/m4/gengetopt/client.m4 b/m4/gengetopt/client.m4
deleted file mode 100644 (file)
index a5a27a0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-args "--unamed-opts=command --no-handle-error --conf-parser --no-handle-version --no-handle-help"
-
-purpose "Communicate with para_server through the paraslash control port"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_client)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/client.history)
-<qu>
-option "hostname" i
-#~~~~~~~~~~~~~~~~~~
-"ip or host to connect"
-string typestr = "host"
-default = "localhost"
-optional
-
-option "user" u
-#~~~~~~~~~~~~~~
-"paraslash username"
-string typestr = "username"
-default = "<current user>"
-optional
-
-option "server-port" p
-#~~~~~~~~~~~~~~~~~~~~~
-"port to connect"
-int typestr = "port"
-default = "2990"
-optional
-
-option "key-file" k
-#~~~~~~~~~~~~~~~~~~
-"path to private key"
-string typestr = "filename"
-optional
-details = "
-       If not given, the following files are tried, in order:
-       $HOME/.paraslash/key.$LOGNAME, $HOME/.ssh/id_rsa. It is a fatal
-       error if the key file can not be opened, or is world-readable.
-"
-</qu>
-
-include(loglevel.m4)
-include(config_file.m4)
-include(history_file.m4)
-include(complete.m4)
diff --git a/m4/gengetopt/color.m4 b/m4/gengetopt/color.m4
deleted file mode 100644 (file)
index eb08178..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<qu>
-
-option "color" C
-#~~~~~~~~~~~~~~~
-"activate color output"
-enum typestr="when"
-values = "yes","no","auto"
-default = "auto"
-optional
-
-option "log-color" -
-#~~~~~~~~~~~~~~~~~~~
-"select a color for one type of log message"
-string typestr="color_spec"
-multiple
-optional
-details="
-       The format of \"color_spec\" is [fg [bg]] [attr].
-
-       Valid colors for \"fg\" and \"bg\" are \"normal\", \"black\",
-       \"red\", \"green\", \"yellow\", \"blue\", \"magenta\",
-       \"cyan\", and \"white\".
-
-       The \"attr\" value must be one of \"bold\", \"dim\", \"ul\",
-       \"blink\", \"reverse\".
-
-       Examples:
-
-               --log-color \"debug:green\"
-               --log-color \"info:yellow bold\"
-               --log-color \"notice:white red bold\"
-"
-
-</qu>
diff --git a/m4/gengetopt/complete.m4 b/m4/gengetopt/complete.m4
deleted file mode 100644 (file)
index 14e737c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "complete" -
-#~~~~~~~~~~~~~~~~~~
-"print possible command line completions"
-       flag off
-       details = "
-       If this flag is given, </qu>CURRENT_PROGRAM<qu> reads the environment
-       variables COMP_LINE and COMP_POINT to obtain the current command line
-       and the cursor position respectively, prints possible completions
-       to stdout and exits.
-"
-</qu>
diff --git a/m4/gengetopt/compress_filter.m4 b/m4/gengetopt/compress_filter.m4
deleted file mode 100644 (file)
index 501f46c..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Dynamically adjust the volume of an audio stream"
-
-option "blocksize" b
-#~~~~~~~~~~~~~~~~~~~
-"adjust block size"
-int typestr="number"
-default="15"
-optional
-details = "
-       Larger blocksize means fewer volume adjustments per time unit.
-"
-
-option "aggressiveness" a
-#~~~~~~~~~~~~~~~~~~~~~~~~
- "controls the maximum amount to amplify by"
-int typestr="number"
-default="4"
-optional
-
-option "inertia" i
-#~~~~~~~~~~~~~~~~~
- "how much inertia ramping has"
- int typestr="number"
-default="6"
-optional
-
-option "target-level" t
-#~~~~~~~~~~~~~~~~~~~~~~
-"target signal level (0-32768)"
-int typestr="number"
-default="20000"
-optional
-
-option "damp" d
-#~~~~~~~~~~~~~~
-"if non-zero, scale down after normalizing"
-int typestr="number"
-default="0"
-optional
diff --git a/m4/gengetopt/config_file.m4 b/m4/gengetopt/config_file.m4
deleted file mode 100644 (file)
index 29f66b4..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<qu>
-option "config-file" c
-#~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_CONFIG_FILE<qu>')"
-string typestr="filename"
-optional
-details="
-       </qu>CURRENT_PROGRAM<qu> reads its config file right after parsing
-       the options that were given at the command line. If an
-       option is given both at the command line and in the
-       config file, the value that was specified at the command line
-       takes precedence.
-"
-</qu>
diff --git a/m4/gengetopt/daemon.m4 b/m4/gengetopt/daemon.m4
deleted file mode 100644 (file)
index ebead6a..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<qu>
-option "daemon" d
-#~~~~~~~~~~~~~~~~
-"run as background daemon"
-flag off
-details = "
-       If this option is given and no logfile was specified, all
-       messages go to /dev/null.
-"
-</qu>
diff --git a/m4/gengetopt/dccp_recv.m4 b/m4/gengetopt/dccp_recv.m4
deleted file mode 100644 (file)
index 1ba3fb5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive a DCCP audio stream"
-
-option "host" i
-"ip or host"
-string default="localhost"
-optional
-details="
-       Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-"port to connect to"
-int
-default="8000"
-optional
-
-option "ccid" c
-"CCID preference(s) for this connection"
-int
-# restrict the maximum number of times this option can be passed
-optional multiple(-10)
-# currently known CCIDs:
-# - CCID-2 (RFC 4341),
-# - CCID-3 (RFC 4342),
-# - CCID-4 (RFC 5622),
-# - CCID-248 ... CCID-254 are experimental (RFC 4340, 19.5)
-values="2", "3", "4", "248", "249", "250", "251", "252", "253", "254"
-details="
-       When present exactly once, this option mandates the CCID for the
-       sender-receiver connection. If it is passed more than once, it sets
-       a preference list where the order of appearance signifies descending
-       priority. For example, passing 4, 2, 3 creates the preference list
-       (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
-
-       The request is reconciled with the CCIDs on the server through the
-       'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
-       can be listed by calling 'para_client si'.
-
-"
diff --git a/m4/gengetopt/fade.m4 b/m4/gengetopt/fade.m4
deleted file mode 100644 (file)
index 59389ff..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "An alarm clock and volume-fader for OSS and ALSA"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_fade)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/fade.conf)
-<qu>
-section "General options"
-#########################
-
-</qu>
-include(config_file.m4)
-include(loglevel.m4)
-<qu>
-option "mode" o
-#~~~~~~~~~~~~~~
-"how to fade volume"
-       enum typestr = "mode"
-       values = "sleep", "fade", "set", "snooze"
-       default = "sleep"
-       optional
-       details="
-               para_fade knows the following modes:
-
-               sleep mode: Change to the initial volume and select
-               the initial afs mood/playlist. Then fade out until
-               the fade-out volume is reached. Switch to the
-               sleep mood/playlist until wake time minus fade-in
-               time. Finally switch to the wake mood/playlist and
-               fade to the fade-in volume.
-
-               fade: Fade the volume to the given value in the
-               given time.
-
-               set: Just set the value and exit.
-
-               snooze: Fade out, sleep a bit and fade in.
-"
-
-option "mixer-api" a
-#~~~~~~~~~~~~~~~~~~~
-"choose the mixer API"
-       string typestr = "api"
-       optional
-       details = "
-               ALSA is preferred over OSS if both APIs are supported
-               and this option is not given. To see the supported
-               mixer APIs, use this option with an invalid string
-               as the mixer API, e.g. --mixer-api help.
-       "
-
-option "mixer-device" m
-#~~~~~~~~~~~~~~~~~~~~~~
-"choose mixer device"
-       string typestr = "device"
-       optional
-       details = "
-               The default device (used if this option is not given)
-               depends on the selected mixer API. For ALSA, the
-               default is 'hw:0' which corresponds to the first sound
-               device. For OSS, '/dev/mixer' is used as the default.
-       "
-
-option "mixer-channel" C
-#~~~~~~~~~~~~~~~~~~~~~~~
-"select the analog mixer channel"
-       string typestr = "channel"
-       optional
-       details = "
-               For the ALSA mixer API, the possible values are
-               determined at runtime depending on the hardware and
-               can be printed by specifying an invalid mixer channel,
-               for example --mixer-channel help. The default channel
-               is 'Master'.
-
-               For OSS the possible values are invariably 'volume',
-               'bass', 'treble', 'synth', 'pcm', 'speaker', 'line',
-               'mic', 'cd', 'imix', 'altpcm', 'reclev', 'igain',
-               'ogain'. However, not all listed channels might be
-               supported on any particular hardware. The default
-               channel is 'volume'.
-       "
-
-section "Options for sleep mode"
-################################
-
-option "ivol" -
-#~~~~~~~~~~~~~~
-"set initial volume"
-       string typestr = "[channel:]volume"
-       default = "60"
-       optional
-       multiple
-       details = "
-               Used as the start volume, before fading out to the
-               fade out volume. The channel part may be omitted, in
-               which case the default channel is used. This option
-               may be given multiple times.
-       "
-
-option "fo-mood" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade out"
-       string typestr = "mood_spec"
-       default = "m/fade"
-       optional
-       details = "
-               Select this mood right after setting the
-               volume. Example: --fo-mood m/sleep
-"
-
-option "fo-time" -
-#~~~~~~~~~~~~~~~~~
-"fall asleep fade out time"
-       int typestr = "seconds"
-       default = "1800"
-       optional
-       details = "
-               No fading if set to 0.
-       "
-
-option "fo-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade out to"
-       int typestr = "volume"
-       default = "20"
-       optional
-
-option "sleep-mood" -
-#~~~~~~~~~~~~~~~~~~~~
-"sleep time afs mood/playlist"
-       string typestr = "mood_spec"
-       default = "m/sleep"
-       optional
-       details = "
-               Select the given afs mood/playlist after the fade
-               out is complete. If unset, the \"stop\" command is
-               sent to para_server.
-       "
-
-option "wake-hour" H
-#~~~~~~~~~~~~~~~~~~~
-"(0-23) (default: now + 9 hours)"
-       int typestr = "hour"
-       optional
-
-option "wake-min" M
-#~~~~~~~~~~~~~~~~~~
-"(0-59)"
-       int typestr = "minutes"
-       default = "0"
-       optional
-
-option "fi-mood" -
-#~~~~~~~~~~~~~~~~~
-"afs mood/playlist during fade in"
-       string typestr = "mood_spec"
-       default = "m/wake"
-       optional
-       details = "
-               Change to this afs mood/playlist on wake time.
-       "
-
-option "fi-time" -
-#~~~~~~~~~~~~~~~~~
-"wake up fade in time"
-       int typestr="seconds"
-       default="1200"
-       optional
-       details = "
-               No fading in if set to 0.
-       "
-
-option "fi-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to at wake time"
-       int typestr = "volume"
-       default = "80"
-       optional
-
-section "Options for snooze mode"
-#################################
-
-option "so-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-out time"
-       int typestr = "seconds"
-       default = "30"
-       optional
-
-option "so-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to before snooze"
-       int typestr = "volume"
-       default = "20"
-       optional
-
-option "snooze-time" -
-#~~~~~~~~~~~~~~~~~~~~~
-"delay"
-       int typestr = "seconds"
-       default = "600"
-       optional
-
-option "si-time" -
-#~~~~~~~~~~~~~~~~~
-"snooze-in time"
-       int typestr = "seconds"
-       default = "180"
-       optional
-
-option "si-vol" -
-#~~~~~~~~~~~~~~~~
-"volume to fade to after snooze"
-       int typestr = "volume"
-       default = "80"
-       optional
-
-section "Options for fade mode"
-###############################
-
-option "fade-vol" f
-#~~~~~~~~~~~~~~~~~~
-"volume to fade to"
-       int typestr = "volume"
-       default = "50"
-       optional
-
-option "fade-time" t
-#~~~~~~~~~~~~~~~~~~~
-"fading time"
-       int typestr = "seconds"
-       default = "5"
-       optional
-
-section "Options for set mode"
-##############################
-
-option "val" -
-"value to set"
-       int typestr = "value"
-       default = "0"
-       optional
-
-</qu>
diff --git a/m4/gengetopt/file_write.m4 b/m4/gengetopt/file_write.m4
deleted file mode 100644 (file)
index 4f98884..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin that writes to a local file"
-
-option "filename" f
-#~~~~~~~~~~~~~~~~~~
-"specify output file name"
-string typestr="filename"
-optional
-details="
-       Defaults to a random filename in ~/.paraslash.
-"
-
diff --git a/m4/gengetopt/filter.m4 b/m4/gengetopt/filter.m4
deleted file mode 100644 (file)
index b8b49f6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-args "--no-handle-help --no-handle-version --conf-parser"
-
-purpose "Decode or process audio data from STDIN to STDOUT"
-
-include(header.m4)
-include(loglevel.m4)
-<qu>
-option "filter" f
-#~~~~~~~~~~~~~~~~
-"Specify filter."
-string typestr="filter_spec"
-optional
-multiple
-details="
-       May be given multiple times to 'pipe' the stream through
-       arbitrary many filters (without copying the data). The same
-       filter may appear more than once, order matters.
-
-       Options for a particular filter may be specified for each
-       given '--filter' option separately. You will have to quote
-       these options like this:
-
-               --filter 'compress --inertia 5 --damp 2'
-"
-</qu>
diff --git a/m4/gengetopt/group.m4 b/m4/gengetopt/group.m4
deleted file mode 100644 (file)
index 2a59ad9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-option "group" g
-#~~~~~~~~~~~~~~~
-"set group id"
-string typestr="group"
-optional
-details="
-       This option sets the group id according to 'group'. This option
-       is silently ignored if EUID != 0. Otherwise, real/effective
-       GID and the saved set-group ID are all set to the GID given by
-       'group'. Must not be given in the config file.
-"
-
diff --git a/m4/gengetopt/gui.m4 b/m4/gengetopt/gui.m4
deleted file mode 100644 (file)
index a6b718e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Show para_audiod status in a curses window"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_gui)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/gui.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(config_file.m4)
-include(loglevel.m4)
-
-<qu>
-option "timeout" t
-#~~~~~~~~~~~~~~~~~
-"set timeout"
-int typestr = "milliseconds"
-default = "30"
-optional
-
-option "theme" T
-#~~~~~~~~~~~~~~~
-"select startup theme"
-string typestr = "name"
-optional
-details = "
-       If this option is not given the default theme is used.
-       If the given name is not a valid theme name, the list of
-       available themes is printed and the program terminates.
-"
-
-option "stat-cmd" s
-#~~~~~~~~~~~~~~~~~~
-"command to read status items from"
-string typestr = "command"
-default = "para_audioc -- stat -p"
-optional
-details = "
-       On a host on which the para_audiod service is not is running, the
-       default command will fail. An alternative is
-
-               para_client -- stat -p
-
-       This command connects para_server instead of para_audiod. However,
-       no timing information about the current audio file is printed.
-"
-
-#---------------------------------
-section "Mapping keys to commands"
-#---------------------------------
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to command c using mode m."
-
-string typestr = "k:m:c"
-optional
-multiple
-details = "
-       Mode may be d, x or p for display, external and paraslash
-       commands, respectively. Of course, this option may be given
-       multiple times, one for each key mapping.
-"
-</qu>
diff --git a/m4/gengetopt/header.m4 b/m4/gengetopt/header.m4
deleted file mode 100644 (file)
index c423187..0000000
+++ /dev/null
@@ -1 +0,0 @@
-changequote(<qu>,</qu>)
diff --git a/m4/gengetopt/history_file.m4 b/m4/gengetopt/history_file.m4
deleted file mode 100644 (file)
index 73e98a7..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "history-file" -
-#~~~~~~~~~~~~~~~~~~~~~~
-"(default='</qu>DEFAULT_HISTORY_FILE<qu>')"
-string typestr = "filename"
-optional
-details = "
-       If </qu>CURRENT_PROGRAM<qu> runs in interactive mode, it reads the history
-       file on startup. Upon exit, the in-memory history is appended
-       to the history file.
-"
-</qu>
diff --git a/m4/gengetopt/http_recv.m4 b/m4/gengetopt/http_recv.m4
deleted file mode 100644 (file)
index 6db3ff0..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive an HTTP audio stream"
-
-include(header.m4)
-
-<qu>
-option "host" i
-#~~~~~~~~~~~~~~
-"ip or host"
-string
-default="localhost"
-optional
-details="
-       Both IPv4 and IPv6 addresses are supported.
-"
-
-option "port" p
-#~~~~~~~~~~~~~~
-"tcp port to connect to"
-int default="8000"
-optional
-</qu>
diff --git a/m4/gengetopt/log_timing.m4 b/m4/gengetopt/log_timing.m4
deleted file mode 100644 (file)
index ac0ea84..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "log-timing" T
-#~~~~~~~~~~~~~~~~~~~~
-"show milliseconds in log messages"
-flag off
-details = "
-       Selecting this option causes milliseconds to be included in
-       the log message output. This allows to measure the interval
-       between log messages in milliseconds which is useful for
-       identifying timing problems.
-"
-</qu>
diff --git a/m4/gengetopt/logfile.m4 b/m4/gengetopt/logfile.m4
deleted file mode 100644 (file)
index 070d736..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<qu>
-option "logfile" L
-#~~~~~~~~~~~~~~~~~
-"where to write log output"
-string typestr="filename"
-optional
-details="
-       If this option is not given, </qu>CURRENT_PROGRAM<qu> writes the log
-       messages to stderr.
-"
-</qu>
diff --git a/m4/gengetopt/loglevel.m4 b/m4/gengetopt/loglevel.m4
deleted file mode 100644 (file)
index 162d030..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<qu>
-option "loglevel" l
-#~~~~~~~~~~~~~~~~~~
-"set loglevel"
-string typestr="level"
-values = "debug","info","notice","warning","error","crit","emerg"
-default="warning"
-optional
-details="
-       Log only messages with severity greater or equal the given
-       value.
-
-       debug: Produces really noisy output.
-       info: Still noisy, but won't fill up the disk quickly.
-       notice: Indicates normal, but significant event.
-       warning: Unexpected events that can be handled.
-       error: Unhandled error condition.
-       crit: System might be unreliable.
-       emerg: Last message before exit.
-"
-
-</qu>
diff --git a/m4/gengetopt/makefile b/m4/gengetopt/makefile
deleted file mode 100644 (file)
index 0db9d10..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-define ggo_opts
-       --output-dir=$(cmdline_dir) \
-       --set-version="$(GIT_VERSION)" \
-       --arg-struct-name=$(*F)_args_info \
-       --file-name=$(*F).cmdline \
-       --func-name=$(*F)_cmdline_parser \
-       --set-package="para_$(*F)"
-endef
-
-.PRECIOUS: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h $(ggo_dir)/%.ggo
-
-$(cmdline_dir)/%.cmdline.h $(cmdline_dir)/%.cmdline.c: $(ggo_dir)/%.ggo | $(cmdline_dir)
-       @[ -z "$(Q)" ] || echo 'GGO $<'
-       $(Q) $(GENGETOPT) $(ggo_opts) < $<
-ifeq ($(ggo_descriptions_declared),no)
-       @echo 'extern const char *$(*F)_args_info_description;' >> $(cmdline_dir)/$(*F).cmdline.h
-endif
-
-$(m4depdir)/%.m4d: $(m4_ggo_dir)/%.m4 | $(m4depdir)
-       @[ -z "$(Q)" ] || echo 'M4D $<'
-       $(Q) $(M4) -I $(m4_ggo_dir) -s $< \
-       | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
-       print "$(ggo_dir)/$(*F).ggo: " $$3}}' | sort | uniq > $@
-
-$(ggo_dir)/%.ggo: $(m4_ggo_dir)/%.m4 $(m4_ggo_dir)/header.m4 | $(ggo_dir)
-       @[ -z "$(Q)" ] || echo 'M4 $<'
-       $(Q) $(M4) -I $(m4_ggo_dir) $< > $@
diff --git a/m4/gengetopt/mp3dec_filter.m4 b/m4/gengetopt/mp3dec_filter.m4
deleted file mode 100644 (file)
index 8b18783..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Decode an mp3 stream"
-
-include(header.m4)
-
-<qu>
-option "ignore-crc" i
-#~~~~~~~~~~~~~~~~~~~~
-"ignore CRC information in the audio stream."
-flag off
-details="
-       This causes frames with CRC errors to be decoded and played
-       anyway. This option is not recommended, but since some encoders
-       have been known to generate bad CRC information, this option
-       is a work-around to play streams from such encoders.
-"
-</qu>
diff --git a/m4/gengetopt/oss_write.m4 b/m4/gengetopt/oss_write.m4
deleted file mode 100644 (file)
index 578d813..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Output plugin for the Open Sound System"
-
-option "device" d
-#~~~~~~~~~~~~~~~~
-"set PCM device"
-string typestr="device"
-default="/dev/dsp"
-optional
-details = ""
diff --git a/m4/gengetopt/play.m4 b/m4/gengetopt/play.m4
deleted file mode 100644 (file)
index 1687559..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-args "--unamed-opts=audio_file --no-handle-version --conf-parser --no-handle-help"
-
-purpose "Command line audio player"
-
-description "para_play operates either in command mode or in insert
-mode. In insert mode it presents a prompt and allows to enter commands
-like stop, play, pause etc. In command mode the current audio file
-is shown and the program reads single key strokes from stdin. Keys
-may be mapped to commands. Whenever a mapped key is pressed, the
-associated command is executed."
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_play)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
-define(DEFAULT_HISTORY_FILE,~/.paraslash/play.history)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-include(loglevel.m4)
-include(config_file.m4)
-include(history_file.m4)
-<qu>
-
-###############################
-section "Options for para_play"
-###############################
-
-option "randomize" z
-#~~~~~~~~~~~~~~~~~~~
-"randomize playlist at startup."
-flag off
-
-option "key-map" k
-#~~~~~~~~~~~~~~~~~
-"Map key k to a command."
-
-string typestr = "key:command [args]"
-optional
-multiple
-details = "
-       This option may be given multiple times, one for each key
-       mapping. Example:
-               5:jmp 50
-"
-</qu>
diff --git a/m4/gengetopt/prebuffer_filter.m4 b/m4/gengetopt/prebuffer_filter.m4
deleted file mode 100644 (file)
index 20ff86f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Delay processing of an audio stream"
-
-option "duration" d
-#~~~~~~~~~~~~~~~~~~
-"prebuffer time"
-int typestr="milliseconds"
-default="200"
-optional
-details="
-       Wait that many milliseconds before letting data go through.
-       The time interval starts when the first data byte is seen by
-       this filter.
-"
-
-option "size" s
-#~~~~~~~~~~~~~~
-"amount of data to prebuffer"
-int typestr="bytes"
-default="0"
-optional
-details="
-       Wait until that many data bytes are available in the input buffer.
-       The default value of zero means to not prebuffer by size at all.
-       If both --duration and --size options are given and non-zero, the
-       filter waits until both conditions are met.
-"
diff --git a/m4/gengetopt/priority.m4 b/m4/gengetopt/priority.m4
deleted file mode 100644 (file)
index 0b37dc0..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-option "priority" -
-#~~~~~~~~~~~~~~~~~~
-"adjust scheduling priority"
-int typestr = "prio"
-default = "0"
-optional
-details = "
-       The priority (also known as nice value) is a value in the range -20
-       to 19. Lower priorities cause more favorable scheduling. Since only
-       privileged processes may request a negative priority, specifying
-       a negative value works only if the daemon is started with root
-       privileges.
-
-       Failure to set the given priority value is not considered an error
-       but a log message is printed in this case.
-"
diff --git a/m4/gengetopt/recv.m4 b/m4/gengetopt/recv.m4
deleted file mode 100644 (file)
index 9a9a880..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-args "--no-handle-help --no-handle-version"
-
-purpose "A command line HTTP/DCCP/UDP stream grabber"
-
-include(header.m4)
-include(loglevel.m4)
-
-<qu>
-option "receiver" r
-"Select receiver"
-string typestr="receiver_spec"
-default="http"
-optional
-details="
-       Any options for the selected receiver must
-       be quoted. Example:
-
-               -r 'http -i www.paraslash.org -p 8009'
-"
-</qu>
diff --git a/m4/gengetopt/resample_filter.m4 b/m4/gengetopt/resample_filter.m4
deleted file mode 100644 (file)
index 2e5f400..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Transform raw audio to a different sample rate"
-
-include(header.m4)
-
-option "converter" C
-#~~~~~~~~~~~~~~~~~~~
-"choose converter type"
-enum typestr = "type"
-values = "best", "medium", "fastest", "zero_order_hold", "linear"
-default = "medium"
-optional
-
-details = "
-       best: This is a bandlimited interpolator derived from the
-       mathematical sinc function and this is the highest quality
-       sinc based converter, providing a worst case Signal-to-Noise
-       Ratio (SNR) of 97 decibels (dB) at a bandwidth of 97%.
-
-       medium: This is another bandlimited interpolator much like the
-       previous one. It has an SNR of 97dB and a bandwidth of 90%. The
-       speed of the conversion is much faster than the previous one.
-
-       fastest: This is the fastest bandlimited interpolator and
-       has an SNR of 97dB and a bandwidth of 80%.
-
-       zero_order_hold: A Zero Order Hold converter (interpolated
-       value is equal to the last value). The quality is poor but
-       the conversion speed is blindlingly fast.
-
-       linear: A linear converter. Again the quality is poor, but
-       the conversion speed is blindingly fast.
-"
-
-include(channels.m4)
-include(sample_rate.m4)
-include(sample_format.m4)
-
-option "dest-sample-rate" d
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"sample rate to convert to"
-int typestr = "rate"
-default = "44100"
-optional
diff --git a/m4/gengetopt/sample_format.m4 b/m4/gengetopt/sample_format.m4
deleted file mode 100644 (file)
index c998f9d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<qu>
-option "sample-format" f
-#~~~~~~~~~~~~~~~~~~~~~~~
-"specify sample format"
-# This must match the enum sample_format of para.h
-values = "S8", "U8", "S16_LE", "S16_BE", "U16_LE", "U16_BE" enum
-default = "S16_LE"
-optional
-details = "
-       It is only necessary to specify this for raw audio. See the
-       discussion of the --channels option.
-"
-</qu>
diff --git a/m4/gengetopt/sample_rate.m4 b/m4/gengetopt/sample_rate.m4
deleted file mode 100644 (file)
index a8332a4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<qu>
-option "sample-rate" s
-#~~~~~~~~~~~~~~~~~~~~~
-"do not guess the input sample rate"
-int typestr = "num"
-default = "44100"
-optional
-details = "
-       It is only necessary to specify this for raw audio. See the
-       discussion of the --channels option.
-"
-</qu>
diff --git a/m4/gengetopt/server.m4 b/m4/gengetopt/server.m4
deleted file mode 100644 (file)
index 48e7a1f..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-args "--conf-parser --no-handle-version --no-handle-help"
-
-purpose "Manage and stream audio files"
-
-include(header.m4)
-define(CURRENT_PROGRAM,para_server)
-define(DEFAULT_CONFIG_FILE,~/.paraslash/server.conf)
-
-<qu>
-#########################
-section "General options"
-#########################
-</qu>
-
-include(loglevel.m4)
-include(log_timing.m4)
-include(color.m4)
-include(daemon.m4)
-include(user.m4)
-include(group.m4)
-include(priority.m4)
-
-<qu>
-option "port" p
-#~~~~~~~~~~~~~~
-"listening port"
-int typestr="portnumber"
-default="2990"
-optional
-details="
-       para_server listens on this tcp port for incoming connections
-       from clients such as para_client. If the default port is
-       changed, the corresponding option of para_client must be used
-       to connect to para_server.
-"
-
-#############################
-section "Configuration files"
-#############################
-</qu>
-
-include(logfile.m4)
-include(config_file.m4)
-
-<qu>
-option "user-list" -
-#~~~~~~~~~~~~~~~~~~~
-"(default='~/.paraslash/server.users')"
-
-string typestr="filename"
-optional
-
-
-##################################
-section "virtual streaming system"
-##################################
-
-
-option "autoplay" a
-#~~~~~~~~~~~~~~~~~~
-"start playing on startup"
-flag off
-
-option "autoplay-delay" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-"time to wait before streaming"
-int typestr="ms"
-default="0"
-optional
-dependon="autoplay"
-details="
-       If para_server is started with the autoplay option, this option
-       may be used to set up a delay before para_server streams its
-       first audio file. This is useful for example if para_server
-       and para_audiod are started during system startup. The delay
-       time should be choosen large enough so that para_audiod is
-       already up when para_server starts to stream. Of course, this
-       option depends on the autoplay option.
-"
-option "announce-time" A
-#~~~~~~~~~~~~~~~~~~~~~~~
-"grace time for clients"
-
-int typestr="ms"
-default="300"
-optional
-details="
-       Clients such as para_audiod connect to para_server and execute
-       the stat command to find out whether an audio stream is
-       currently available. This sets the delay betweeen announcing
-       the stream via the output of the stat command and sending
-       the first chunk of data.
-"
-
-#############################
-section "audio file selector"
-#############################
-
-option "afs-database-dir" D
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"location of the database"
-string typestr="path"
-optional
-details="
-       Where para_server should look for the osl database of the audio
-       file selector. The default is '~/.paraslash/afs_database-0.4'.
-"
-
-option "afs-socket" s
-#~~~~~~~~~~~~~~~~~~~~
-"Command socket for afs"
-string typestr="path"
-default="/var/paraslash/afs_command_socket-0.4"
-optional
-details="
-       For each server command that is handled by the audio file
-       selector, the child process of para_server connects to the
-       audio file selector via a local socket. This option specifies
-       the location of that socket in the file system.
-"
-option "afs-initial-mode" i
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"Mood or playlist to load on startup."
-string typestr="<specifier>/<name>"
-optional
-
-details="
-       The argument of this option must be prefixed with either 'p/'
-       or 'm/' to indicate whether a playlist or a mood should be
-       loaded. Example:
-               --afs-initial-mode p/foo
-       loads the playlist named 'foo'.
-"
-
-#####################
-section "http sender"
-#####################
-
-
-option "http-port" -
-#~~~~~~~~~~~~~~~~~~~
-"tcp port for http streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
-       The http sender of para_server listens on this port for
-       incoming connections. Clients are expected to send the usual
-       http request message such as 'GET / HTTP/'.
-"
-
-option "http-default-deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the http ACL a whitelist"
-flag off
-details="
-       The default is to use blacklists instead, i.e. connections
-       to the http sender are allowed unless the connecting host
-       matches a pattern given by a http-access option. This allows
-       to use access control the other way round: Connections are
-       denied from hosts which are not explicitly allowed by one or
-       more http-access options.
-"
-
-option "http-access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the http ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
-       Add given host/network to access control list (whitelist if
-       http-default-deny was given, blacklist otherwise) before
-       opening the tcp port. This option can be given multiple
-       times. Example: '192.168.0.0/24' whitelists/blacklists the
-       256 hosts 192.168.0.x
-"
-
-option "http-no-autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not open tcp port on startup"
-flag off
-details="
-       If this option is given, the http sender does not listen on
-       its tcp port. It may be instructed to open this port at a
-       later time by using the sender command.
-"
-
-option "http-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
-       The http sender will refuse connections if already that number
-       of clients are currently connected. A non-positive value
-       (the default) allows an unlimited number of simultaneous
-       connections.
-"
-
-#####################
-section "dccp sender"
-#####################
-
-
-option "dccp-port" -
-#~~~~~~~~~~~~~~~~~~~
-"port for dccp streaming"
-int typestr="portnumber"
-default="8000"
-optional
-details="
-       See http-port for details.
-"
-
-option "dccp-default-deny" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"make the dccp ACL a whitelist"
-flag off
-details="
-       See http-default-deny for details.
-"
-
-option "dccp-access" -
-#~~~~~~~~~~~~~~~~~~~~~
-"add an entry to the dccp ACL"
-string typestr="a.b.c.d/n"
-optional
-multiple
-details="
-       See http-access for details.
-"
-
-option "dccp-max-clients" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"maximal number of connections"
-int typestr="number"
-default="-1"
-optional
-details="
-       See http-max-clients for details.
-"
-
-option "dccp-max-slice-size" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"Upper bound for the FEC slice size"
-int typestr = "size"
-optional
-default = "0"
-details = "
-       If this value is non-positive (the default) the dccp sender
-       uses the maximum packet size (MPS) of the connection as the
-       slice size. The MPS is a network parameter and depends on
-       the path maximum transmission unit (path MTU) of an incoming
-       connection, i.e. on the largest packet size that can be
-       transmitted without causing fragmentation.
-
-       This option allows to use a value less than the MPS in order
-       to fine-tune application performance. Values greater than
-       the MPS of an incoming connection can not be set.
-"
-
-option "dccp-data-slices-per-group" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"The number of non-redundant slices per FEC group"
-int typestr = "num"
-optional
-default = "3"
-details = "
-       This determines the number of slices in each FEC group that are
-       necessary to decode the group. The given number must be smaller
-       than the value of the dccp-slices-per-group option below.
-
-       Note that the duration of a FEC group is proportional to the
-       product dccp-max-slice-size * dccp-data-slices-per-group.
-"
-
-option "dccp-slices-per-group" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"The total number of slices per FEC group"
-int typestr = "num"
-optional
-default = "4"
-details = "
-       This value must be larger than the value of the argument to
-       --dccp-data-slices-per-group. The difference of the two values is
-       the number of redundant slices, that is, the number of slices which
-       may be lost without causing interruptions in the audio stream.
-
-       Increase this value if you are on a lossy network.
-"
-
-####################
-section "udp sender"
-####################
-
-option "udp-target" -
-#~~~~~~~~~~~~~~~~~~~~
-"add udp target with optional port"
-string typestr="host[:port]"
-optional
-multiple
-details="
-       Add given host/port to the list of targets. The 'host' argument
-       can be either an IPv4/v6 address or hostname (RFC 3986 syntax).
-       The 'port' argument is an optional port number. If the 'port'
-       part is absent, the 'udp-default-port' value is used.
-
-       The following examples are possible targets:
-       '10.10.1.2:8000' (host:port); '10.10.1.2' (with default port);
-       '224.0.1.38:1500' (IPv4 multicast); 'localhost:8001' (hostname
-       with port); '[::1]:8001' (IPv6 localhost); '[badc0de::1]' (IPv6
-       host with default port); '[FF00::beef]:1500' (IPv6 multicast).
-
-       This option can be given multiple times, for multiple targets.
-"
-
-option "udp-no-autostart" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not start sending"
-flag off
-details="
-       If this option is given, udp streaming may be activated at
-       a later time by using the sender command.
-"
-
-option "udp-default-port" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"udp port to send to"
-int typestr="port"
-default="8000"
-optional
-
-option "udp-mcast-iface" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"outgoing udp multicast interface"
-string
-optional
-
-option "udp-header-interval" H
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"duration for sending header"
-int typestr = "ms"
-default = "2000"
-optional
-details = "
-       As the udp sender has no idea about connected clients it
-       sends the audio file header periodically if necessary. This
-       option specifies the duration between subsequent headers are
-       sent. Smaller values decrease the average time clients have
-       to wait before starting playback, larger values decrease
-       network traffic.
-
-       Note that this affects only ogg/* and wma streams. Other
-       audio formats, including mp3, don't need an audio file header.
-"
-
-option "udp-ttl" t
-#~~~~~~~~~~~~~~~~~
-"set time to live value"
-int typestr="num"
-default="-1"
-optional
-details="
-       This option applies exclusively to multicast UDPv4/v6 streaming.
-
-       For the sending UDPv4 socket it sets the multicast Time-To-Live
-       value to \"num\".  Traditional TTL scope values are: 0=host,
-       1=network, 32=same site, 64=same region, 128=same continent,
-       255=unrestricted. Please note however that this scoping is not
-       a good solution: RFC 2365 e.g. presents a better alternative.
-
-       When using UDPv6 multicasting, the option sets the number of
-       multicast hops (as described in RFC 3493); a value of -1
-       allows the kernel to auto-select the hop value.
-"
-</qu>
diff --git a/m4/gengetopt/sync_filter.m4 b/m4/gengetopt/sync_filter.m4
deleted file mode 100644 (file)
index 1e6f5f8..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Synchronize playback between multiple clients."
-
-option "buddy" b
-#~~~~~~~~~~~~~~~
-"host to synchronize with"
-multiple
-string typestr = "url"
-optional
-details = "
-       This option may be given multiple times, one per buddy. Each
-       value may be given as a host, port pair in either IPv4 or
-       IPv6 form, with port being optional. If no port was specified
-       the listening port (as specified with --port, see below)
-       is used to send the synchronization packet to this buddy.
-"
-
-option "port" p
-#~~~~~~~~~~~~~~
-"UDP port for incoming synchronization packets"
-int typestr = "portnumber"
-default = "29900"
-optional
-details = "
-       The sync filter receives incoming synchronization packets on
-       this UDP port.
-"
-
-option "timeout" t
-#~~~~~~~~~~~~~~~~~
-"how long to wait for other clients"
-int typestr = "milliseconds"
-default = "2000"
-optional
-details = "
-       Once the sync filter receives its first chunk of input, a
-       synchronization period of the given number of milliseconds
-       begins. Playback is deferred until a synchronization packet
-       has been received from each defined buddy, or until the end
-       of the period. Buddies which did not send a synchronization
-       packet in time are temporarily disabled and are not waited for
-       during subsequent synchronization periods. They are re-enabled
-       automatically when another synchronization packet arrives.
-"
diff --git a/m4/gengetopt/udp_recv.m4 b/m4/gengetopt/udp_recv.m4
deleted file mode 100644 (file)
index dcdad4f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-args "--no-version --no-help"
-
-purpose "Receive an UDP audio stream"
-
-option "host" i
-"ip or host to receive udp packets from"
-string default="224.0.1.38"
-optional
-details="
-       The default address resolves to DANTZ.MCAST.NET and activates
-       multicast.
-"
-
-option "port" p "udp port"
-int typestr="portnumber"
-default="8000"
-optional
-
-option "iface" I "receiving udp multicast interface"
-string
-optional
diff --git a/m4/gengetopt/user.m4 b/m4/gengetopt/user.m4
deleted file mode 100644 (file)
index 1bd5c59..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<qu>
-option "user" u
-#~~~~~~~~~~~~~~
-"run as the given user"
-string typestr="name"
-optional
-details="
-       </qu>CURRENT_PROGRAM<qu> does not need any special privileges.
-
-       If started as root (EUID == 0) this option must
-       be given at the command line (not in the configuration
-       file) so that </qu>CURRENT_PROGRAM<qu> can drop the root
-       privileges right after parsing the command line options,
-       but before parsing the configuration file. In this case,
-       real/effective/saved UID are all set to the UID of 'name'. As
-       the configuration file is read afterwards, those options that
-       have a default value depending on the UID (e.g. the directory
-       for the configuration file) are computed by using the uid of
-       'name'. This option has no effect if </qu>CURRENT_PROGRAM<qu>
-       is started as a non-root user (i.e.  EUID != 0).
-" </qu>
diff --git a/m4/gengetopt/write.m4 b/m4/gengetopt/write.m4
deleted file mode 100644 (file)
index 6667cb8..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-args "--no-handle-help --no-handle-version"
-
-purpose "Play wav or raw audio"
-
-include(header.m4)
-include(loglevel.m4)
-
-option "writer" w
-#~~~~~~~~~~~~~~~~
-"select stream writer"
-string typestr="name"
-optional
-multiple
-details="
-       May be given multiple times, and the same writer may be specified more
-       than once. If this option is not given, the first supported writer
-       is started. The list of supported writers is shown in the help output.
-"
-
-include(channels.m4)
-include(sample_rate.m4)
-include(sample_format.m4)
diff --git a/m4/lls/afh.suite.m4 b/m4/lls/afh.suite.m4
new file mode 100644 (file)
index 0000000..cf83b97
--- /dev/null
@@ -0,0 +1,94 @@
+m4_define(PROGRAM, para_afh)
+[suite afh]
+version-string = GIT_VERSION()
+[supercommand para_afh]
+       purpose = print or modify meta information about audio file(s)
+       [description]
+               By default para_afh prints information about the given audio files,
+               such as format, duration, bitrate, etc., and the content of the most
+               common meta tags (artist, title, album, year, comment). In modify
+               mode it changes the meta tags to the values given at the command line.
+       [/description]
+       non-opts-name = file...
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       [option modify-section]
+               summary = Printing meta information
+               flag ignored
+       [option chunk-table]
+               short_opt = c
+               summary = print also the chunk table
+               [help]
+                       The chunk table of an audio file is an array of offsets where each
+                       offset corresponds to chunk of encoded data. The exact meaning of
+                       chunk depends on the audio format.
+
+                       Programs which are unaware of the particular audio format can read the
+                       chunk table to obtain the timing information needed to stream the file.
+               [/help]
+       [option parser-friendly]
+               short_opt = p
+               summary = do not use human-readable output format
+               [help]
+                       This option only affects the format of the chunk table, so it has no
+                       effect if --chunk-table is not given.
+
+                       The human-readable output (the default) consists of one output line
+                       per chunk and the output contains also the chunk number, the duration
+                       and the size of each chunk. The parser-friendly output prints only
+                       the offsets, in one line.
+               [/help]
+       [option modify-section]
+               summary = Modifying meta tags
+               flag ignored
+       [option modify]
+               short_opt = m
+               summary = modify (rather than print) tags
+               [help]
+                       If this option is given, para_afh creates a temporary copy of the given
+                       file(s), with meta tags changed according to the options below. On
+                       errors, the temporary file is removed, leaving the original file
+                       unchanged. On success, if --backup is given, the original file is
+                       moved away. Finally the temporary file is renamed to the name of the
+                       original file.
+               [/help]
+       [option backup]
+               short_opt = b
+               summary = create backup of the original file
+               [help]
+                       The backup suffix is '~'. That is, a single tilde character is appended
+                       to the given file name.
+               [/help]
+       [option year]
+               short_opt = y
+               summary = set the year tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option title]
+               short_opt = t
+               summary = set the title tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option artist]
+               short_opt = a
+               summary = set the artist tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option album]
+               short_opt = A
+               summary = set the album tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
+       [option comment]
+               short_opt = C
+               summary = set the comment tag
+               arg_info = required_arg
+               arg_type = string
+               typestr = string
diff --git a/m4/lls/audioc.suite.m4 b/m4/lls/audioc.suite.m4
new file mode 100644 (file)
index 0000000..1c037bc
--- /dev/null
@@ -0,0 +1,39 @@
+m4_define(PROGRAM, para_audioc)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/audioc.history)
+[suite audioc]
+version-string = GIT_VERSION()
+[supercommand para_audioc]
+       purpose = communicate with para_audiod through a local socket
+       non-opts-name = [command [options]]
+       [description]
+               The client program to control para_audiod at runtime. It allows to
+               enable/disable streaming, to receive status info, or to grab the
+               audio stream at any point of the decoding process.
+
+               If no command is given, para_audioc enters interactive mode.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(complete.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option socket]
+               short_opt = s
+               summary = path to well-known socket
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       The default path of the socket is
+                       /var/paraslash/audiod.socket.$HOSTNAME.
+               [/help]
+       [option bufsize]
+               short_opt = b
+               summary = size of internal buffer
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = bytes
+               default_val = 8192
diff --git a/m4/lls/audiod.suite.m4 b/m4/lls/audiod.suite.m4
new file mode 100644 (file)
index 0000000..31f6042
--- /dev/null
@@ -0,0 +1,191 @@
+m4_define(PROGRAM, para_audiod)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/audiod.conf)
+[suite audiod]
+version-string = GIT_VERSION()
+[supercommand para_audiod]
+       purpose = connect to para_server, receive, decode and play audio streams
+       [description]
+               para_audiod runs on a host with an audio device and connects
+               para_server to obtain status information and receive the current audio
+               stream. The stream is transformed through any number of filters and
+               then written to the configured output device.
+
+               Moreover, para_audiod listens on a local socket and sends status
+               information to local clients on request. Access to the local socket
+               can be restricted by means of Unix socket credentials, if available.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(logfile.m4)
+       m4_include(color.m4)
+       m4_include(log-timing.m4)
+       m4_include(daemon.m4)
+       m4_include(user.m4)
+       m4_include(group.m4)
+       m4_include(priority.m4)
+       m4_include(per-command-options-section.m4)
+       [option force]
+               summary = force startup
+               short_opt = F
+               [help]
+                       If this flag is not given, para_audiod refuses to start if the
+                       well-known socket file (see the --socket option) already exists
+                       because this usually means that para_audiod is already running and
+                       listening on that socket. After a crash or if para_audiod received
+                       a SIGKILL signal, a stale socket file might remain and you have to
+                       use --force once to force startup of para_audiod.
+               [/help]
+       [option mode]
+               summary = select startup mode
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               values = {AUDIOD_ON = "on", AUDIOD_OFF = "off",  AUDIOD_STANDBY = "sb"}
+               default_val = on
+               [help]
+                       para_audiod supports three modes of operation: On, off and standby
+                       (sb).  This option selects the mode that should be used at startup. If
+                       mode is "on", para_audiiod connects para_server to receive status
+                       information. If the server announces the availability of an audio
+                       stream, para_audiod downloads, decodes and plays the audio stream
+                       according to the given stream I/O options, see below.
+
+                       In "standby" mode, para_audiod only receives the status information
+                       from para_server but does not download the audio stream.
+
+                       In "off" mode, para_audiod does not connect para_server at all,
+                       but still listens on the local socket.
+               [/help]
+       [option socket]
+               short_opt = s
+               summary = path to the well-known socket
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       para_audiod listens on a "well-known" socket for connections
+                       from para_audioc. This socket is a special file in the file system;
+                       its location defaults to /var/paraslash/audiod_sock.$hostname.
+
+                       para_audioc, the client program used to connect to para_audiod,
+                       opens this socket in order to talk to para_audiod. If the default
+                       value for para_audiod is changed, para_audioc must be instructed to
+                       use also <path> to connect para_audiod.
+               [/help]
+       [option user-allow]
+               summary = allow this user to connect to audiod
+               arg_info = required_arg
+               arg_type = string
+               typestr = username
+               [help]
+                       Allow the user identified by username (either a string or a UID) to
+                       connect to para_audiod. This option may be given multiple times. If
+                       not specified at all, all users are allowed to connect.
+
+                       This feature is based on the ability to send unix credentials through
+                       local sockets using ancillary data (SCM_CREDENTIALS). Currently it
+                       only works on Linux. On other operating systems the option is silently
+                       ignored and all local users are allowed to connect.
+               [/help]
+       [option clock-diff-count]
+               summary = sync clock on startup
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               [help]
+                       Check the clock difference between the host running para_server and
+                       the local host running para_audiod this many times before starting
+                       any stream I/0. Set this to non-zero for non-local setups if the
+                       clocks of these two hosts are not synchronized by ntp or similar.
+               [/help]
+       [option Stream-IO-options]
+               summary = Stream I/O options
+               flag ignored
+       [option receiver]
+               short_opt = r
+               summary = select receiver
+               arg_info = required_arg
+               arg_type = string
+               typestr = receiver_spec
+               default_val = http
+               flag multiple
+               [help]
+                       This option may be given multiple times, for each audio format
+                       separately. If multiple definitions for an audio format are given,
+                       the first one is selected.
+
+                       The <receiver_spec> consists of an audio format specifier and one or
+                       more receiver arguments, separated by a colon.
+
+                       The audio format specifier is a regular expression which specifies
+                       the set of audio formats for which this option should apply.
+
+                       If any receiver options are present, the whole receiver argument must
+                       be quoted:
+
+                               -r 'mp3:http -i my.host.org -p 8009'
+
+                       Since a single dot '.' matches the name of any audio format, specifying
+                       '.' instead of 'mp3' above activates the http receiver for all audio
+                       formats.
+               [/help]
+       [option filter]
+               short_opt = f
+               summary = specify the filter configuration
+               arg_info = required_arg
+               arg_type = string
+               typestr = filter_spec
+               flag multiple
+               [help]
+                       This option may be given multiple times. The <filter_spec< consists
+                       of an audio format specifier (see above), the name of the filter,
+                       and any options for that filter. Note that order matters.
+
+                       The compiled-in defaults apply to all audio formats for which no
+                       --filter option was given. These defaults depend on the receiver
+                       being used.
+
+                       For HTTP streams, only the decoder for the current audio format
+                       is activated. UDP and DCCP streams, on the other hand, are sent
+                       FEC-encoded by para_server. In order to play such streams, the
+                       receiver output must be FEC-decoded first, i.e. fed to the fecdec
+                       filter. Therefore the default for UDP and DCCP streams is to activate
+                       the fecdec filter, followed by the decoding filter for the audio
+                       format.
+
+                       Examples:
+
+                               --filter 'mp3:mp3dec'
+
+                               --filter 'mp3|aac:compress --inertia 5 --damp 2'
+
+                               --filter '.:fecdec'
+               [/help]
+       [option writer]
+               short_opt = w
+               summary = specify one or more stream writers
+               arg_info = required_arg
+               arg_type = string
+               typestr = writer_spec
+               flag multiple
+               [help]
+                       May be given multiple times, even multiple times for the same audio
+                       format. The default is to use the first supported writer. Example:
+
+                               --writer 'aac|wma:oss'
+               [/help]
+       [option stream-delay]
+               summary = specify time interval for client sync
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               [help]
+                       Check the clock difference between the host running para_server and
+                       the local host running para_audiod this many times before starting
+                       any stream I/0. Set this to non-zero for non-local setups if the
+                       clocks of these two hosts are not synchronized by ntp or similar.
+               [/help]
diff --git a/m4/lls/audiod_cmd.suite.m4 b/m4/lls/audiod_cmd.suite.m4
new file mode 100644 (file)
index 0000000..11de987
--- /dev/null
@@ -0,0 +1,100 @@
+[suite audiod_cmd]
+caption = list of audiod commands
+[subcommand cycle]
+       purpose = switch to next operating mode
+       [description]
+               Cycle through the three operation modes (on, standby, off).
+       [/description]
+
+[subcommand help]
+       purpose = display command list or help for given command
+       non-opts-name = [subcommand]
+       [description]
+               When I was younger, so much younger than today, I never needed
+               anybody's help in any way. But now these days are gone, I'm not so self
+               assured. Now I find I've changed my mind and opened up the doors. --
+               Beatles: Help
+       [/description]
+
+[subcommand grab]
+       purpose = grab the audio stream
+       [description]
+               grab ('splice') the audio stream at any position in the buffer tree
+               and send that data back to the client.
+       [/description]
+       [option mode]
+               short_opt = m
+               summary = change grab mode
+               arg_info = required_arg
+               arg_type = string
+               typestr = mode
+               default_val = s
+               [help]
+                       The various grab modes only differ in what happens if an attempt to
+                       write the grabbed audio data would block. Sloppy mode ("s") ignores
+                       the write, pedantic mode ("p") aborts and aggressive mode ("a")
+                       tries to write anyway.
+               [/help]
+       [option parent]
+               short_opt = p
+               summary = Grab output of the given node of the buffer tree
+               arg_info = required_arg
+               arg_type = string
+               typestr = node
+       [option name]
+               short_opt = n
+               summary = Name of the new buffer tree node. Defaults to 'grab'
+               arg_info = optional_arg
+               arg_type = string
+               typestr = string
+       [option one-shot]
+               short_opt = o
+               summary = One-shot mode: Stop grabbing if audio file changes
+
+[subcommand off]
+       purpose = deactivate para_audiod
+       [description]
+               Close connection to para_server and stop all decoders.
+       [/description]
+
+[subcommand on]
+       purpose = activate para_audiod
+       [description]
+               Establish connection to para_server, retrieve para_server's current
+               status. If playing, start corresponding decoder. Otherwise stop
+               all decoders.
+       [/description]
+
+[subcommand sb]
+       purpose = enter standby mode
+       [description]
+               Stop all decoders but leave connection to para_server open.
+       [/description]
+
+[subcommand stat]
+       purpose = print status information
+       non-opts-name = [item...]
+       [description]
+               Dump given status items (all if none given) to stdout.
+       [/description]
+       [option parser-friendly]
+               short_opt = p
+               summary = use parser-friendly output format
+
+[subcommand version]
+       purpose = print the version of para_audiod
+       [option verbose]
+               short_opt = v
+               summary = print detailed (multi-line) version text
+
+[subcommand tasks]
+       purpose = list current tasks
+       [description]
+               Print the list of task ids together with the status of each task.
+       [/description]
+
+[subcommand term]
+       purpose = terminate para_audiod
+       [description]
+               Stop all decoders, shut down connection to para_server and exit.
+       [/description]
diff --git a/m4/lls/client.suite.m4 b/m4/lls/client.suite.m4
new file mode 100644 (file)
index 0000000..30230c4
--- /dev/null
@@ -0,0 +1,63 @@
+m4_define(PROGRAM, para_client)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/client.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/client.conf)
+[suite client]
+version-string = GIT_VERSION()
+[supercommand para_client]
+       purpose = Communicate with para_server through the paraslash control port
+       non-opts-name = [command [options]]
+       [description]
+               para_client is the program used to connect to a running instance of
+               para_server. If a command is provided, this command is sent to the
+               server and any output received from the server is echoed to stdout. If
+               no command is given, para_client enters interactive mode.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(config-file.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option hostname]
+               short_opt = i
+               summary = IP address or name of the host where para_server is running
+               typestr = host
+               arg_info = required_arg
+               arg_type = string
+               default_val = localhost
+               [help]
+                       Both IPv4 and IPv6 addresses are supported.
+               [/help]
+       [option user]
+               short_opt = u
+               summary = the paraslash username to authenticate as
+               arg_info = required_arg
+               arg_type = string
+               typestr = name
+               [help]
+                       If this is not given, the current username is assumed.
+               [/help]
+       [option server-port]
+               short_opt = p
+               summary = the port on the remote host to connect
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 2990
+               [help]
+                       If this is not given, the current username is assumed.
+               [/help]
+       [option key-file]
+               short_opt = k
+               summary = path to private key
+               arg_info = required_arg
+               arg_type = string
+               typestr = filename
+               [help]
+                       If not given, the following files are tried, in order:
+                       $HOME/.paraslash/key.$LOGNAME, $HOME/.ssh/id_rsa. It is a fatal error
+                       if the key file can not be opened, or is world-readable.
+               [/help]
+       m4_include(complete.m4)
diff --git a/m4/lls/copyright.m4 b/m4/lls/copyright.m4
new file mode 100644 (file)
index 0000000..9e5c541
--- /dev/null
@@ -0,0 +1,15 @@
+.SH COPYRIGHT
+Written by Andre Noll
+.br
+Copyright (C) COPYRIGHT_YEAR() Andre Noll
+.br
+License: GNU GPL version 2
+.br
+This is free software: you are free to change and redistribute it.
+.br
+There is NO WARRANTY, to the extent permitted by law.
+.br
+Report bugs to
+.MT <maan@tuebingen.mpg.de>
+Andre Noll
+.ME
diff --git a/m4/lls/filter.suite.m4 b/m4/lls/filter.suite.m4
new file mode 100644 (file)
index 0000000..0cd69eb
--- /dev/null
@@ -0,0 +1,37 @@
+m4_define(PROGRAM, para_filter)
+[suite filter]
+version-string = GIT_VERSION()
+[supercommand para_filter]
+       purpose = decode or process audio data from STDIN to STDOUT
+       [description]
+               This program allows to specify a chain of filters which transform the
+               audio stream read from STDIN. A common mode of operation is to decode
+               an mp3 file with the mp3dec filter, but many other filters exist which
+               transform the audio stream in different ways.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option filter]
+               short_opt = f
+               summary = add one filter to the filter chain
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = filter_spec
+               [help]
+                       A filter specifier begins with the name of a supported filter,
+                       optionally followed by zero or more options for the named filter.
+                       Filter name and options must be separated by whitespace. If the
+                       there is at least one option, the filter specifier needs to be quoted
+                       like this:
+
+                               --filter 'compress --inertia 5 --damp 2'
+
+                       This option may be specified multiple times to 'pipe' the stream
+                       through all given filters (in a single thread without copying the
+                       data). The same filter may appear more than once, and order matters.
+               [/help]
diff --git a/m4/lls/filter_cmd.suite.m4 b/m4/lls/filter_cmd.suite.m4
new file mode 100644 (file)
index 0000000..d269d23
--- /dev/null
@@ -0,0 +1,205 @@
+[suite filter_cmd]
+caption = filters
+[subcommand aacdec]
+       purpose = decode an aac stream
+[subcommand amp]
+       purpose = amplify (scale) a raw audio stream
+       [option amp]
+               short_opt = a
+               summary = amplification value
+               typestr = number
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 32
+               [help]
+                       The amplification value determines the scaling factor by which the
+                       amplitude of the audio stream is multiplied. The formula for the
+                       scaling factor is
+
+                               factor = 1 + amp / 64.
+
+                       For example, an amplification value of zero results in a scaling factor
+                       of one while an amplification value of 64 means to double the volume.
+               [/help]
+[subcommand compress]
+       purpose = dynamically adjust the volume of an audio stream
+       [option blocksize]
+               short_opt = b
+               summary = use blocks of size 2**bits
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 15
+               [help]
+                       Larger blocksize means fewer volume adjustments per time unit.
+               [/help]
+       [option aggressiveness]
+               short_opt = a
+               summary = controls the maximum amount to amplify by
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 4
+       [option inertia]
+               short_opt = i
+               summary = how much inertia ramping has
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 6
+       [option target-level]
+               short_opt = t
+               summary = target signal level (0-32768)
+               typestr = level
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 20000
+       [option damp]
+               short_opt = d
+               summary = if non-zero, scale down after normalizing
+               typestr = bits
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 0
+[subcommand fecdec]
+       purpose = decode a (lossy) input stream using forward error correction
+[subcommand flacdec]
+       purpose = decode a flac stream
+[subcommand mp3dec]
+       purpose = decode an mp3 stream
+       [option ignore-crc]
+               short_opt = i
+               summary = ignore CRC information in the audio stream
+               [help]
+                       This causes frames with CRC errors to be decoded and played
+                       anyway. This option is not recommended, but since some encoders
+                       have been known to generate bad CRC information, this option is a
+                       work-around to play streams from such encoders.
+               [/help]
+[subcommand oggdec]
+       purpose = decode an ogg/vorbis stream
+[subcommand opusdec]
+       purpose = decode an ogg/opus stream
+[subcommand prebuffer]
+       purpose = delay processing of an audio stream
+       [option duration]
+               short_opt = d
+               summary = length of the prebuffer period
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 200
+               [help]
+                       Wait this many milliseconds before letting data go through. The time
+                       interval starts when the first data byte is seen in the input queue.
+               [/help]
+       [option size]
+               short_opt = s
+               summary = amount of data to prebuffer
+               typestr = bytes
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 0
+               [help]
+                       Wait until this many data bytes are available in the input queue. The
+                       default value of zero means to not prebuffer by size. If both
+                       --duration and --size are given and non-zero, the prebuffer filter
+                       waits until both conditions are met.
+               [/help]
+[subcommand resample]
+       purpose = transform raw audio to a different sample rate
+       [option converter]
+               short_opt = C
+               summary = set conversion algorithm
+               typestr = type
+               arg_info = required_arg
+               arg_type = string
+               values = {
+                       # RCT: resample conversion type
+                       RCT_BEST = "best",
+                       RCT_MEDIUM = "medium",
+                       RCT_FASTEST = "fastest",
+                       RCT_ZERO_ORDER_HOLD = "zero_order_hold",
+                       RCT_LINEAR = "linear"
+               }
+               default_val = medium
+               [help]
+                       best: This is a bandlimited interpolator derived from the mathematical
+                       sinc function and this is the highest quality sinc based converter,
+                       providing a worst case Signal-to-Noise Ratio (SNR) of 97 decibels
+                       (dB) at a bandwidth of 97%.
+
+                       medium: This is another bandlimited interpolator much like the previous
+                       one. It has an SNR of 97dB and a bandwidth of 90%. The speed of the
+                       conversion is much faster than the previous one.
+
+                       fastest: This is the fastest bandlimited interpolator and has an SNR
+                       of 97dB and a bandwidth of 80%.
+
+                       zero_order_hold: A Zero Order Hold converter (interpolated value
+                       is equal to the last value). The quality is poor but the conversion
+                       speed is blindlingly fast.
+
+                       linear: A linear converter. Again the quality is poor, but the
+                       conversion speed is blindingly fast.
+               [/help]
+       [option dest-sample-rate]
+               short_opt = d
+               summary = sample rate to convert to
+               typestr = rate
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 44100
+       m4_include(channels.m4)
+       m4_include(sample-rate.m4)
+       m4_include(sample-format.m4)
+[subcommand spxdec]
+       purpose = decode an ogg/speex stream
+[subcommand sync]
+       purpose = synchronize playback between multiple clients
+       [option buddy]
+               short_opt = b
+               summary = client to synchronize with
+               typestr = url
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               [help]
+                       This option may be given multiple times, one per buddy. Each value
+                       may be given as a host, port pair in either IPv4 or IPv6 form, with
+                       port being optional. If no port was specified the listening port (as
+                       specified with --port, see below) is used to send the synchronization
+                       packet to this buddy.
+               [/help]
+       [option port]
+               short_opt = p
+               summary = UDP port for incoming synchronization packets
+               typestr = portnumber
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 29900
+               [help]
+                       The sync filter expects incoming synchronization packets on this
+                       UDP port.
+               [/help]
+       [option timeout]
+               short_opt = t
+               summary = how long to wait for other clients
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 2000
+               [help]
+                       Once the sync filter receives its first chunk of input,
+                       a synchronization period of the given number of milliseconds
+                       begins. Playback is deferred until a synchronization packet has
+                       been received from each defined buddy, or until the end of the
+                       period. Buddies which did not send a synchronization packet in time
+                       are temporarily disabled and are not waited for during subsequent
+                       synchronization periods. They are re-enabled automatically when
+                       another synchronization packet arrives.
+               [/help]
+[subcommand wav]
+       purpose = insert a Microsoft wave header into a raw audio stream
+[subcommand wmadec]
+       purpose = decode a wma stream
diff --git a/m4/lls/gui.suite.m4 b/m4/lls/gui.suite.m4
new file mode 100644 (file)
index 0000000..e0e45ef
--- /dev/null
@@ -0,0 +1,58 @@
+m4_define(PROGRAM, para_gui)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/gui.conf)
+[suite gui]
+version-string = GIT_VERSION()
+[supercommand para_gui]
+       purpose = show para_audiod status in a curses window
+       [description]
+               para_gui is a curses program which displays status information
+               about para_server and para_audiod in a terminal. Keys may be
+               mapped to user-defined commands which are executed when the key is
+               pressed. Command output is shown in a simple pager.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option theme]
+               short_opt = T
+               summary = select startup theme
+               arg_info = required_arg
+               arg_type = string
+               typestr = name
+               [help]
+                       If this option is not given the default theme is used. If the given
+                       name is not a valid theme name, the list of available themes is
+                       printed and the program terminates.
+               [/help]
+       [option stat-cmd]
+               short_opt = s
+               summary = command to read status items from
+               arg_info = required_arg
+               arg_type = string
+               typestr = command
+               default_val = para_audioc -- stat -p
+               [help]
+                       On a host on which the para_audiod service is not is running, the
+                       default command will fail. An alternative is
+
+                               para_client -- stat -p
+
+                       This command connects para_server instead of para_audiod. However,
+                       no timing information about the current audio file is printed.
+               [/help]
+       [option key-map]
+               short_opt = k
+               summary = map a key to a command using one of three possible modes
+               arg_info = required_arg
+               arg_type = string
+               typestr = k:m:c
+               flag multiple
+               [help]
+                       Mode may be d, x or p for display, external and paraslash commands,
+                       respectively. Of course, this option may be given multiple times,
+                       one for each key mapping. See the manual for more information.
+               [/help]
diff --git a/m4/lls/include/channels.m4 b/m4/lls/include/channels.m4
new file mode 100644 (file)
index 0000000..ae26b73
--- /dev/null
@@ -0,0 +1,13 @@
+[option channels]
+       short_opt = c
+       summary = specify number of channels
+       typestr = num
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 2
+       [help]
+               It is only necessary to specify this option for raw audio. If it is
+               not given, the channel count is queried from the parent buffer tree
+               nodes (e.g. the decoder) or the wav header. Only if this query fails,
+               the default value applies.
+       [/help]
diff --git a/m4/lls/include/color.m4 b/m4/lls/include/color.m4
new file mode 100644 (file)
index 0000000..a23b325
--- /dev/null
@@ -0,0 +1,29 @@
+[option color]
+       short_opt = C
+       summary = activate color output
+       typestr = when
+       arg_info = required_arg
+       arg_type = string
+       values = {COLOR_YES = "yes", COLOR_NO = "no", COLOR_AUTO = "auto"}
+       default_val = auto
+[option log-color]
+       summary = select a color for one type of log message
+       typestr = color_spec
+       arg_info = required_arg
+       arg_type = string
+       flag multiple
+       [help]
+               The format of <color_spec> is [<fg> [<bg>]] [<attr>].
+
+               Valid colors for <fg> and <bg> are "normal", "black", "red", "green",
+               "yellow", "blue", "magenta", "cyan", and "white".
+
+               The <attr> value must be one of "bold", "dim", "ul", "blink",
+               "reverse".
+
+       Examples:
+
+               --log-color "debug:green"
+               --log-color "info:yellow bold"
+               --log-color "notice:white red bold"
+       [/help]
diff --git a/m4/lls/include/common-option-section.m4 b/m4/lls/include/common-option-section.m4
new file mode 100644 (file)
index 0000000..2a3b3e6
--- /dev/null
@@ -0,0 +1,8 @@
+[option common-option-section]
+       summary = Common options
+       flag ignored
+       [help]
+               The following options are implemented generically and are available
+               for many of the commands.
+       [/help]
+
diff --git a/m4/lls/include/complete.m4 b/m4/lls/include/complete.m4
new file mode 100644 (file)
index 0000000..c9f135c
--- /dev/null
@@ -0,0 +1,8 @@
+[option complete]
+       summary = print possible command line completions
+       [help]
+               If this flag is given, the environment variables COMP_LINE and
+               COMP_POINT are examined to obtain the current command line and the
+               cursor position respectively. Possible completions are written to
+               stdout and the program exits.
+       [/help]
diff --git a/m4/lls/include/config-file.m4 b/m4/lls/include/config-file.m4
new file mode 100644 (file)
index 0000000..a5de133
--- /dev/null
@@ -0,0 +1,14 @@
+[option config-file]
+       short_opt = c
+       summary = path to alternative config file
+       arg_info = required_arg
+       arg_type = string
+       typestr = filename
+       [help]
+               default: DEFAULT_CONFIG_FILE()
+
+               PROGRAM() reads its config file right after parsing the options that
+               were given at the command line. If an option is given both at the
+               command line and in the config file, the value that was specified at
+               the command line takes precedence.
+       [/help]
diff --git a/m4/lls/include/daemon.m4 b/m4/lls/include/daemon.m4
new file mode 100644 (file)
index 0000000..5c04712
--- /dev/null
@@ -0,0 +1,7 @@
+[option daemon]
+       short_opt = d
+       summary = run as background daemon
+       [help]
+               If this option is given and no logfile was specified, all messages
+               go to /dev/null.
+       [/help]
diff --git a/m4/lls/include/detailed-help.m4 b/m4/lls/include/detailed-help.m4
new file mode 100644 (file)
index 0000000..33a10f8
--- /dev/null
@@ -0,0 +1,3 @@
+
+[option detailed-help]
+       summary = print help, including all details, and exit
diff --git a/m4/lls/include/group.m4 b/m4/lls/include/group.m4
new file mode 100644 (file)
index 0000000..8d49cc7
--- /dev/null
@@ -0,0 +1,12 @@
+[option group]
+       short_opt = g
+       summary = set group id
+       arg_info = required_arg
+       arg_type = string
+       typestr = groupname
+       [help]
+               This option sets the group id according to <group>. This option is
+               silently ignored if EUID != 0. Otherwise, real/effective GID and the
+               saved set-group ID are all set to the GID given by <group>. Must not
+               be given in the config file.
+       [/help]
diff --git a/m4/lls/include/help.m4 b/m4/lls/include/help.m4
new file mode 100644 (file)
index 0000000..ff6ce3e
--- /dev/null
@@ -0,0 +1,3 @@
+[option help]
+       summary = print help and exit
+       short_opt = h
diff --git a/m4/lls/include/history-file.m4 b/m4/lls/include/history-file.m4
new file mode 100644 (file)
index 0000000..deb3d7e
--- /dev/null
@@ -0,0 +1,13 @@
+[option history-file]
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = location of the file for the command history list
+[help]
+       If PROGRAM() runs in interactive mode, it reads the history file
+       on startup. Upon exit, the in-memory history is appended to the
+       history file.
+
+       If this option is not given, the history file is expected at
+       DEFAULT_HISTORY_FILE().
+[/help]
diff --git a/m4/lls/include/host.m4 b/m4/lls/include/host.m4
new file mode 100644 (file)
index 0000000..3c0f0eb
--- /dev/null
@@ -0,0 +1,10 @@
+[option host]
+       short_opt = i
+       summary = IP address or hostname
+       typestr = host
+       arg_info = required_arg
+       arg_type = string
+       default_val = localhost
+       [help]
+               Both IPv4 and IPv6 addresses are supported.
+       [/help]
diff --git a/m4/lls/include/log-timing.m4 b/m4/lls/include/log-timing.m4
new file mode 100644 (file)
index 0000000..a7364e1
--- /dev/null
@@ -0,0 +1,9 @@
+[option log-timing]
+       short_opt = T
+       summary = show milliseconds in log messages
+       [help]
+               Selecting this option causes milliseconds to be included in
+               the log message output. This allows to measure the interval
+               between log messages in milliseconds which is useful for
+               identifying timing problems.
+       [/help]
diff --git a/m4/lls/include/logfile.m4 b/m4/lls/include/logfile.m4
new file mode 100644 (file)
index 0000000..e3d40a1
--- /dev/null
@@ -0,0 +1,10 @@
+[option logfile]
+short_opt = L
+arg_info = required_arg
+arg_type = string
+typestr = filename
+summary = where to write log output
+[help]
+       If this option is not given, PROGRAM() writes the log messages
+       to stderr.
+[/help]
diff --git a/m4/lls/include/loglevel.m4 b/m4/lls/include/loglevel.m4
new file mode 100644 (file)
index 0000000..06bea3d
--- /dev/null
@@ -0,0 +1,23 @@
+m4_define(`downcase', `m4_translit(`$*', `A-Z', `a-z')')
+m4_define(`SUITE_LOGLEVELS', `m4_patsubst(`$*', `LL_\([A-Z]+\)',
+       `LSGLL_\1 = "downcase(`\1')" ')')
+[option loglevel]
+       summary = control amount of logging
+       short_opt = l
+       arg_info = required_arg
+       arg_type = string
+       typestr = severity
+       values = {SUITE_LOGLEVELS(LOGLEVELS())}
+       default_val = warning
+       [help]
+               Log only messages with severity greater or equal than the given
+               value. Possible values:
+
+               debug: Produces really noisy output.
+               info: Still noisy, but won't fill up the disk quickly.
+               notice: Indicates normal, but significant event.
+               warning: Unexpected events that can be handled.
+               error: Unhandled error condition.
+               crit: System might be unreliable.
+               emerg: Last message before exit.
+       [/help]
diff --git a/m4/lls/include/per-command-options-section.m4 b/m4/lls/include/per-command-options-section.m4
new file mode 100644 (file)
index 0000000..a303808
--- /dev/null
@@ -0,0 +1,3 @@
+[option per-command-option-section]
+       summary = Options for PROGRAM()
+       flag ignored
diff --git a/m4/lls/include/port.m4 b/m4/lls/include/port.m4
new file mode 100644 (file)
index 0000000..8dd63b6
--- /dev/null
@@ -0,0 +1,7 @@
+[option port]
+       short_opt = p
+       summary = TCP port to connect to
+       typestr = portnumber
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 8000
diff --git a/m4/lls/include/priority.m4 b/m4/lls/include/priority.m4
new file mode 100644 (file)
index 0000000..70d44b4
--- /dev/null
@@ -0,0 +1,13 @@
+[option priority]
+       summary = adjust scheduling priority
+       arg_info = required_arg
+       arg_type = int32
+       typestr = prio
+       default_val = 0
+       [help]
+               The priority (also known as nice value) is a value in the range -20
+               to 19. Lower priorities cause more favorable scheduling. Since only
+               privileged processes may request a negative priority, specifying
+               a negative value works only if the daemon is started with root
+               privileges.
+       [/help]
diff --git a/m4/lls/include/sample-format.m4 b/m4/lls/include/sample-format.m4
new file mode 100644 (file)
index 0000000..0daad19
--- /dev/null
@@ -0,0 +1,20 @@
+[option sample-format]
+       short_opt = f
+       summary = specify sample format
+       typestr = format
+       arg_info = required_arg
+       arg_type = string
+       # TODO: dedup this from enum in para.h.
+       values = {
+               SAMPLE_FORMAT_S8 = "S8",
+               SAMPLE_FORMAT_U8 = "U8",
+               SAMPLE_FORMAT_S16_LE = "S16_LE",
+               SAMPLE_FORMAT_S16_BE = "S16_BE",
+               SAMPLE_FORMAT_U16_LE = "U16_LE",
+               SAMPLE_FORMAT_U16_BE = "U16_BE"
+       }
+       default_val = S16_LE
+       [help]
+               It is only necessary to specify this for raw audio. See the discussion
+               of the --channels option.
+       [/help]
diff --git a/m4/lls/include/sample-rate.m4 b/m4/lls/include/sample-rate.m4
new file mode 100644 (file)
index 0000000..1433521
--- /dev/null
@@ -0,0 +1,11 @@
+[option sample-rate]
+       short_opt = s
+       summary = do not guess the input sample rate
+       typestr = rate
+       arg_info = required_arg
+       arg_type = uint32
+       default_val = 44100
+       [help]
+               It is only necessary to specify this for raw audio. See the discussion
+               of the --channels option.
+       [/help]
diff --git a/m4/lls/include/user.m4 b/m4/lls/include/user.m4
new file mode 100644 (file)
index 0000000..6d4d31b
--- /dev/null
@@ -0,0 +1,21 @@
+[option user]
+       short_opt = u
+       summary = run as the given user
+       arg_info = required_arg
+       arg_type = string
+       typestr = username
+       [help]
+               PROGRAM() does not need any special privileges.
+
+               If started as root (EUID == 0) this option must be given at the
+               command line (not in the configuration file) so that PROGRAM()
+               can drop the root privileges right after parsing the command line
+               options, but before parsing the configuration file. In this case,
+               real/effective/saved UID are all set to the UID of <username>. As
+               the configuration file is read afterwards, those options that have
+               a default value depending on the UID (e.g. the directory for the
+               configuration file) are computed by using the uid of <username>. This
+               option has no effect if PROGRAM() is started as a non-root user (i.e.
+               EUID != 0).
+       [/help]
+
diff --git a/m4/lls/include/version.m4 b/m4/lls/include/version.m4
new file mode 100644 (file)
index 0000000..a84ab9d
--- /dev/null
@@ -0,0 +1,3 @@
+[option version]
+       summary = print version and exit
+       short_opt = V
diff --git a/m4/lls/makefile b/m4/lls/makefile
new file mode 100644 (file)
index 0000000..cf3965e
--- /dev/null
@@ -0,0 +1,30 @@
+.PRECIOUS: $(lls_suite_dir)/%.suite $(lsg_h)
+lls_m4_include_dir := $(lls_m4_dir)/include
+
+$(lls_suite_dir)/%.m4d: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+       @[ -z "$(Q)" ] || echo 'M4D $<'
+
+       $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -s $< \
+       | awk '{if ($$1 ~ /#line/) {gsub(/"/, "", $$3); if ($$3 != "$<") \
+       print "$(lls_suite_dir)/$(*F).suite: " $$3}}' | sort | uniq > $@
+
+$(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
+       $(Q) $(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
+               -D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+               $< > $@
+
+$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGC $<'
+       $(Q) lopsubgen --gen-c --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.h: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGH $<'
+       $(Q) lopsubgen --gen-header --output-dir $(lls_suite_dir) < $<
+
+$(lls_suite_dir)/%.lsg.man: $(lls_suite_dir)/%.suite
+       @[ -z "$(Q)" ] || echo 'LSGM $<'
+       $(Q) lopsubgen --gen-man --output-dir $(lls_suite_dir) < $<
+
+$(object_dir)/%.o: $(lls_suite_dir)/%.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(STRICT_CFLAGS) $<
diff --git a/m4/lls/mixer.suite.m4 b/m4/lls/mixer.suite.m4
new file mode 100644 (file)
index 0000000..37ecbd8
--- /dev/null
@@ -0,0 +1,238 @@
+m4_define(PROGRAM, para_mixer)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/mixer.conf)
+[suite mixer]
+version-string = GIT_VERSION()
+caption = List of subcommands
+[supercommand para_mixer]
+       purpose = alarm clock and volume-fader for OSS and ALSA
+       synopsis = [<options>] -- [<subcommand>] [<subcommand_options>]
+       [description]
+               para_mixer adjusts the settings of an audio mixing device. It can
+               set the level of a mixer channel, or fade the level from one value
+               to another in a given time period. The sleep and snooze subcommands
+               contact para_server to start or stop streaming.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [help]
+               These options apply to several subcommands.
+       [/help]
+       [option mixer-api]
+               short_opt = a
+               summary = select alternative mixer API
+               arg_info = required_arg
+               arg_type = string
+               typestr = api
+               [help]
+                       ALSA is preferred over OSS if both APIs are supported and this option
+                       is not given. To see the supported mixer APIs, use this option with
+                       an invalid string as the mixer API, e.g. --mixer-api help.
+               [/help]
+       [option mixer-device]
+               short_opt = m
+               summary = set mixer device
+               arg_info = required_arg
+               arg_type = string
+               typestr = device
+               [help]
+                       The default device (used if this option is not given) depends
+                       on the selected mixer API. For ALSA, the default is 'hw:0' which
+                       corresponds to the first sound device. For OSS, '/dev/mixer' is used
+                       as the default.
+               [/help]
+       [option mixer-channel]
+               short_opt = C
+               summary = select a mixer channel
+               arg_info = required_arg
+               arg_type = string
+               typestr = channel
+               [help]
+                       For the ALSA mixer API, the possible values are determined at runtime
+                       depending on the hardware and can be printed by specifying an invalid
+                       mixer channel, for example --mixer-channel help. The default channel is
+                       'Master'.
+
+                       For OSS the possible values are invariably 'volume', 'bass', 'treble',
+                       'synth', 'pcm', 'speaker', 'line', 'mic', 'cd', 'imix', 'altpcm',
+                       'reclev', 'igain', 'ogain'. However, not all listed channels might be
+                       supported on any particular hardware. The default channel is 'volume'.
+               [/help]
+       [option fade-exponent]
+               summary = set non-linear time scale for fading
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = value
+               default_val = 0
+               [help]
+                       This option affects the fade, snooze and sleep subcommands. It is
+                       ignored in set mode.
+
+                       The argument must be a number between 0 and 100. The default value
+                       0 corresponds to linear scaling. That is, the value of the mixer
+                       channel is increased or decreased in fixed time intervals until the
+                       destination value is reached. Exponents between 1 and 99 cause low
+                       channel values to be increased more quickly than high channel values.
+                       Large exponents cause greater differences. The special value 100 sets
+                       the destination value immediately. The command then sleeps for the
+                       configured fade time.
+               [/help]
+[subcommand help]
+       purpose = print subcommand help
+       non-opts-name = [<subcommand>]
+       [option long]
+               summary = print the long help text
+               short_opt = l
+
+[subcommand set]
+       purpose = set a channel to the given value and exit
+       [option val]
+               summary = mixer channel value to set
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = value
+               default_val = 10
+[subcommand fade]
+       purpose = fade a channel to the given value in the given time
+       [option fade-vol]
+               short_opt = f
+               summary = destination volume for fading
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 50
+       [option fade-time]
+               short_opt = t
+               summary = duration of fade period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 5
+[subcommand snooze]
+       purpose = fade out, pause, sleep, play, fade in
+       [option so-time]
+               summary = duration of fade-out period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 30
+       [option so-vol]
+               summary = destination volume for fade-out
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 20
+       [option snooze-time]
+               summary = delay between end of fade-out and begin of fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 600
+       [option si-time]
+               summary = duration of fade-in period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 180
+       [option si-vol]
+               summary = destination volume for fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 80
+[subcommand sleep]
+       purpose = stream, fade out, sleep, fade in
+       [description]
+               Change to the initial volume and select the initial mood/playlist.
+               Fade out to the given fade-out volume in the specified time. Switch
+               to the sleep mood/playlist and wait until wake time minus fade-in
+               time. Finally, switch to the wake mood/playlist and fade in to the
+               fade-in volume.
+       [/description]
+       [option ivol]
+               summary = set initial volume
+               arg_info = required_arg
+               arg_type = string
+               default_val = 60
+               flag multiple
+               typestr = [channel:]volume
+               [help]
+                       Used as the start volume, before fading out to the fade-out volume. The
+                       channel part may be omitted, in which case the default channel is
+                       used. This option may be given multiple times.
+               [/help]
+       [option fo-mood]
+               summary = mood or playlist for fade-out
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/fade
+               [help]
+                       This mood (or playlist) is selected right after setting the initial
+                       volume.
+               [/help]
+       [option fo-time]
+               summary = duration of fade-out period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 1800
+               [help]
+                       No fading if this is set to 0.
+               [/help]
+       [option fo-vol]
+               summary = destination volume for fade-out
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 20
+       [option sleep-mood]
+               summary = mood/playlist between fade-out and fade-in
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/sleep
+               [help]
+                       Select the given mood or playlist after the fade-out. If unset,
+                       playback is stopped until fade-in starts.
+               [/help]
+       [option wake-time]
+               short_opt = w
+               summary = when to start fade in
+               arg_info = required_arg
+               arg_type = string
+               typestr = [+][HH][:MM]
+               default_val = +9:00
+               [help]
+                       If the optional plus character is given, the wake time is computed as
+                       now + HH hours + MM minutes. Otherwise the HH:MM argument is considered
+                       an absolute time (referring to either the current or the next day).
+               [/help]
+       [option fi-mood]
+               summary = mood or playlist for fade-in
+               arg_info = required_arg
+               arg_type = string
+               typestr = mood_spec
+               default_val = m/wake
+               [help]
+                       This mood or playlist is selected right before fade-in begins.
+               [/help]
+       [option fi-time]
+               summary = duration of fade-in period
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = seconds
+               default_val = 1200
+               [help]
+                       No fading if this is set to 0.
+               [/help]
+       [option fi-vol]
+               summary = destination volume for fade-in
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = volume
+               default_val = 80
diff --git a/m4/lls/play.suite.m4 b/m4/lls/play.suite.m4
new file mode 100644 (file)
index 0000000..0fbba0c
--- /dev/null
@@ -0,0 +1,40 @@
+m4_define(PROGRAM, para_play)
+m4_define(DEFAULT_HISTORY_FILE, ~/.paraslash/play.history)
+m4_define(DEFAULT_CONFIG_FILE,~/.paraslash/play.conf)
+[suite play]
+version-string = GIT_VERSION()
+[supercommand para_play]
+       purpose = command line audio player
+       non-opts-name = <audio_file>...
+       [description]
+               para_play operates either in command mode or in insert mode. In
+               insert mode it presents a prompt and allows to enter commands like
+               stop, play, pause etc. In command mode the current audio file and the
+               playback position are shown and the program reads single key strokes
+               from stdin. Keys may be mapped to commands so that the configured
+               command is executed when a mapped key is pressed.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(config-file.m4)
+       m4_include(history-file.m4)
+       m4_include(per-command-options-section.m4)
+       [option randomize]
+               short_opt = z
+               summary = randomize playlist at startup
+       [option key-map]
+               short_opt = k
+               summary = map a key to a command
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = key:command [args]
+               [help]
+                       This option may be given multiple times, one for each key
+                       mapping. Example:
+
+                               --key-map 5:jmp 50
+               [/help]
diff --git a/m4/lls/play_cmd.suite.m4 b/m4/lls/play_cmd.suite.m4
new file mode 100644 (file)
index 0000000..195b47b
--- /dev/null
@@ -0,0 +1,92 @@
+[suite play_cmd]
+caption = list of commands
+[subcommand help]
+       purpose = list commands or keybindings, or print command-specific help
+       non-opts-name = [command]
+       [description]
+
+               This command acts differently depending on whether it is executed in
+               command mode or in insert mode.
+
+               In command mode, the help command prints the list of defined
+               keybindings. In insert mode, if no argument is given, it prints the
+               list of available commands. When called with the name of a command
+               as first argument, it prints the description of this command.
+       [/description]
+
+[subcommand fg]
+       purpose = enter command mode
+       [description]
+               In this mode, file name and play time of the current audio file are
+               displayed. Hit CTRL+C or : to switch to input mode.
+       [/description]
+[subcommand next]
+       purpose = load the next file of the playlist
+
+[subcommand prev]
+       purpose = load the previous file of the playlist
+
+[subcommand bg]
+       purpose = enter insert mode
+       [description]
+               Only useful if called in command mode via a key binding. The default
+               key bindings map the colon key to this command, so pressing : in
+               command mode activates insert mode.
+       [/description]
+
+[subcommand jmp]
+       purpose = change playback position
+       non-opts-name = percent
+       [description]
+               The percent argument must be an integer between 0 and 100, inclusively.
+       [/description]
+
+[subcommand ff]
+       purpose = set playback position relative to the current position
+       non-opts-name = seconds
+       [description]
+               Negative values mean to jump backwards the given amount of seconds.
+       [/description]
+
+[subcommand ls]
+       purpose = list the playlist
+       [description]
+               This prints all paths of the playlist. The currently
+               active file is marked with an asterisk.
+       [/description]
+
+[subcommand info]
+       purpose = print information about the current file
+       [description]
+               The output contains the playlist position, the path
+               and information provided by the audio format handler.
+       [/description]
+
+[subcommand play]
+       purpose = start or resume playback
+       non-opts-name = [num]
+       [description]
+               If no argument is given, playback starts at the current
+               position. Otherwise, the corresponding file is loaded
+               and playback is started at the beginning of the file.
+       [/description]
+
+[subcommand pause]
+       purpose = suspend playback
+       [description]
+               When paused, it is still possible to jump around in
+               the file via the jmp and ff commands.
+       [/description]
+
+[subcommand tasks]
+       purpose = print list of active tasks
+       [description]
+               Mainly useful for debugging.
+       [/description]
+
+[subcommand quit]
+       purpose = exit para_play
+       [description]
+               Pressing CTRL+D causes EOF on stdin which also exits
+               para_play.
+       [/description]
diff --git a/m4/lls/recv.suite.m4 b/m4/lls/recv.suite.m4
new file mode 100644 (file)
index 0000000..793a2f5
--- /dev/null
@@ -0,0 +1,22 @@
+m4_define(PROGRAM, para_recv)
+[suite recv]
+version-string = GIT_VERSION()
+[supercommand para_recv]
+       purpose = a command line HTTP/DCCP/UDP stream grabber
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option receiver]
+               short_opt = r
+               summary = select the receiver
+               arg_info = required_arg
+               arg_type = string
+               typestr = receiver_spec
+               [help]
+                       Any options for the selected receiver must be quoted. Example:
+
+                               -r 'http -i www.paraslash.org -p 8009'
+               [/help]
diff --git a/m4/lls/recv_cmd.suite.m4 b/m4/lls/recv_cmd.suite.m4
new file mode 100644 (file)
index 0000000..674a448
--- /dev/null
@@ -0,0 +1,102 @@
+[suite recv_cmd]
+caption = receivers
+[subcommand afh]
+       purpose = make an audio stream from a local file
+       [description]
+               The afh (audio format handler) receiver extracts selected parts of
+               the given audio file without decoding the file. Only complete chunks
+               with respect to the underlying audio format are extracted.
+       [/description]
+       [option filename]
+               short_opt = f
+               summary = file to open
+               typestr = filename
+               arg_info = required_arg
+               arg_type = string
+       [option begin-chunk]
+               short_opt = b
+               summary = skip the beginning of the file
+               typestr = chunk_num
+               arg_info = required_arg
+               arg_type = int32
+               [help]
+                       The argument must be an integer between -num_chunks and num_chunks -
+                       1, inclusively, where num_chunks is the total number of chunks. If
+                       chunk_num is negative, the given number of chunks are counted backwards
+                       from the end of the file. For example --begin-chunk -100 instructs
+                       the afh receiver to start at chunk num_chunks - 100. This is useful
+                       for cutting off the beginning of an audio file.
+               [/help]
+       [option end-chunk]
+               short_opt = e
+               summary = only write up to chunk chunk_num
+               typestr = chunk_num
+               arg_info = required_arg
+               arg_type = int32
+               [help]
+                       For the chunk_num argument the same rules as for --begin-chunk
+                       apply. The default is to write up to the last chunk.
+               [/help]
+       [option just-in-time]
+               short_opt = j
+               summary = use timed writes
+               [help]
+                       Write the specified data chunks 'just in time', i.e., delay the write
+                       until data is needed by the decoder/player for an uninterrupted audio
+                       stream. This may be useful for third-party software.
+               [/help]
+       [option no-header]
+               short_opt = h
+               summary = do not write an audio file header
+               [help]
+                       Some audio formats store information about the audio file in
+                       a format-specific header which is needed to decode any part of
+                       the file. For such formats the afh receiver generates a suitable
+                       header. This option changes the default behaviour, i.e. no header
+                       is written.
+               [/help]
+[subcommand http]
+       purpose = receive an audio stream over HTTP
+       m4_include(host.m4)
+       m4_include(port.m4)
+[subcommand dccp]
+       purpose = receive an audio stream over DCCP
+       m4_include(host.m4)
+       m4_include(port.m4)
+       [option ccid]
+               short_opt = c
+               summary = CCID preference(s) for this connection
+               typestr = id
+               arg_info = required_arg
+               arg_type = uint32
+               flag multiple
+               [help]
+                       When present exactly once, this option mandates the CCID for the
+                       sender-receiver connection. If it is passed more than once, it sets
+                       a preference list where the order of appearance signifies descending
+                       priority. For example, passing 4, 2, 3 creates the preference list
+                       (CCID-4, CCID-2, CCID-3), assigning CCID-4 highest preference.
+
+                       The request is reconciled with the CCIDs on the server through the
+                       'server-priority' mechanism of RFC 4340 6.3.1/10. The server CCIDs
+                       can be listed by calling 'para_client si'.
+               [/help]
+[subcommand udp]
+       purpose = receive an audio stream over UDP
+       [option host]
+               short_opt = i
+               summary = IP address or hostname
+               typestr = host
+               arg_info = required_arg
+               arg_type = string
+               default_val = 224.0.1.38
+               [help]
+                       The default address resolves to DANTZ.MCAST.NET and activates
+                       multicast.
+               [/help]
+       m4_include(port.m4)
+       [option iface]
+               summary = receiving udp multicast interface
+               typestr = iface-name
+               arg_info = required_arg
+               arg_type = string
diff --git a/m4/lls/server.suite.m4 b/m4/lls/server.suite.m4
new file mode 100644 (file)
index 0000000..5bba85d
--- /dev/null
@@ -0,0 +1,358 @@
+m4_define(PROGRAM, para_server)
+m4_define(DEFAULT_CONFIG_FILE, ~/.paraslash/server.conf)
+[suite server]
+version-string = GIT_VERSION()
+[supercommand para_server]
+       purpose = manage and stream audio files
+       [description]
+               para_server streams audio files over a local or remote network. It
+               is controlled by para_client(1), which connects para_server through
+               the paraslash control service.
+
+               On startup the server spawns a second process, the audio file selector,
+               which maintains the database of all known audio files. This database
+               contains file format, duration and tag information of each known file
+               and statistics such as last-played time, and the number of times each
+               file was streamed. Lyrics and cover art may be added to the database
+               and associated with one or more audio files.
+
+               Besides ordinary playlists the audio file selector supports so-called
+               moods. Moods instruct the server to determine the files to be streamed
+               and their order in terms of properties stored in the database.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(config-file.m4)
+       m4_include(priority.m4)
+       m4_include(daemon.m4)
+       m4_include(logfile.m4)
+       m4_include(user.m4)
+       m4_include(group.m4)
+       m4_include(loglevel.m4)
+       m4_include(log-timing.m4)
+       m4_include(color.m4)
+       m4_include(per-command-options-section.m4)
+       [option port]
+               short_opt = p
+               summary = listening port of the paraslash control service
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 2990
+               [help]
+                       para_server listens on this TCP port for incoming connections
+                       from clients such as para_client. If the default port is changed,
+                       the corresponding option of para_client must be used to connect
+                       to para_server.
+               [/help]
+       [option user-list]
+               summary = file which contains user names and credentials
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               [help]
+                       This file contains one line per user of the form
+
+                               user <username> <key> <perms>
+
+                       See the manual for more information.
+               [/help]
+       [option vss]
+               summary = Options for the virtual streaming system
+               flag ignored
+       [option autoplay]
+               summary = start streaming on startup
+               short_opt = a
+               [help]
+                       The default is to defer streaming until para_client connects and
+                       executes the "play" command.
+               [/help]
+       [option autoplay-delay]
+               summary = time to wait before streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 0
+               [help]
+                       This option is ignored if --autplay is not given. Otherwise, its
+                       argument defines for how long streaming is delayed at startup.
+
+                       This is useful in init scripts to set the delay large enough to make
+                       sure para_audiod is up when para_server starts to stream.
+               [/help]
+       [option announce-time]
+               short_opt = A
+               summary = grace time for data connections
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 300
+               [help]
+                       para_server tells para_audiod through the control service connection
+                       whether an audio stream is currently available. This option defines
+                       the delay between announcing the stream and sending the first chunk
+                       of audio data.
+               [/help]
+       [option afs]
+               summary = Options for the audio file selector
+               flag ignored
+       [option afs-database-dir]
+               summary = location of the afs database
+               short_opt = D
+               arg_info = required_arg
+               arg_type = string
+               typestr = directory
+               [help]
+                       The directory which contains the database for the audio file
+                       selector. The default is ~/.paraslash/afs_database-0.4.
+
+                       If no database was found, the "init" command must be executed to
+                       initialize the database. Once initialized, audio files may added with
+                       the "add" command.
+               [/help]
+       [option afs-socket]
+               summary = socket for afs connections
+               short_opt = s
+               arg_info = required_arg
+               arg_type = string
+               typestr = path
+               default_val = /var/paraslash/afs_command_socket-0.4
+               [help]
+                       Server commands communicate with the audio file selector, via a
+                       local socket. This option specifies the location of the socket in
+                       the file system.
+               [/help]
+       [option afs-initial-mode]
+               summary = mood or playlist to load on startup
+               short_opt = i
+               arg_info = required_arg
+               arg_type = string
+               typestr = specifier/name
+               [help]
+                       The argument of this option consists of a prefix, either 'm/' or
+                       'p/', to indicate whether a mood or a playlist should be loaded,
+                       followed by the name of the mood or playlist. Example:
+
+                               --afs-initial-mode p/foo
+
+                       loads the playlist named "foo".
+
+                       If this option is not given, the dummy mood is loaded at startup.
+               [/help]
+       [option http]
+               summary = Options for the http sender
+               flag ignored
+       [option http-port]
+               summary = TCP port for http streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+               [help]
+                       The http sender of para_server listens on this port for incoming
+                       connections. Clients are expected to send the usual http request
+                       message such as 'GET / HTTP/'.
+               [/help]
+       [option http-default-deny]
+               summary = make the http access control list a whitelist
+               [help]
+                       The default is to use blacklists, i.e. connections to the http sender
+                       are allowed unless the connecting host matches a pattern given by a
+                       http-access option. This allows to use access control the other way
+                       round: Connections are denied from hosts which are not explicitly
+                       allowed by one or more http-access options.
+               [/help]
+       [option http-access]
+               summary = add an entry to the http access control list
+               arg_info = required_arg
+               arg_type = string
+               typestr = a.b.c.d/n
+               flag multiple
+               [help]
+                       Add the given host/network to access control list (whitelist if
+                       http-default-deny was given, blacklist otherwise) before opening
+                       the tcp port. This option can be given multiple times. Example:
+
+                               --http-access 192.168.0.0/24
+
+                       whitelists/blacklists the 256 hosts 192.168.0.x.
+
+                       This option may be given multiple times to blacklist/whitelist any
+                       number of hosts or networks.
+               [/help]
+       [option http-no-autostart]
+               summary = do not open TCP port for http streaming on startup
+               [help]
+                       If this option is given, the http sender does not listen on its TCP
+                       port until the "sender" command is executed to open the port.
+               [/help]
+       [option http-max-clients]
+               summary = maximal number of simultaneous http connections
+               arg_info = required_arg
+               arg_type = int32
+               typestr = number
+               default_val = -1
+               [help]
+                       The http sender will refuse connections if already that number of
+                       clients are currently connected. A non-positive value (the default)
+                       allows for an unlimited number of simultaneous connections.
+               [/help]
+       [option dccp]
+               summary = Options for the dccp sender
+               flag ignored
+       [option dccp-port]
+               summary = port for dccp streaming
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+               [help]
+                       See --http-port for details.
+               [/help]
+       [option dccp-default-deny]
+               summary = make the dccp access control list a whitelist
+               [help]
+                       See http-default-deny for details.
+               [/help]
+       [option dccp-access]
+               summary = add an entry to the dccp access control list
+               arg_info = required_arg
+               arg_type = string
+               typestr = a.b.c.d/n
+               flag multiple
+               [help]
+                       See --http-access for details.
+               [/help]
+       [option dccp-max-clients]
+               summary = maximal number of simultaneous dccp connections
+               arg_info = required_arg
+               arg_type = int32
+               typestr = number
+               default_val = -1
+               [help]
+                       See --http-max-clients for details.
+               [/help]
+       [option dccp-max-slice-size]
+               summary = upper bound for the FEC slice size
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = bytes
+               default_val = 0
+               [help]
+                       If this value is zero (the default) the dccp sender uses the maximum
+                       packet size (MPS) of the connection as the slice size. The MPS is a
+                       network parameter and depends on the path maximum transmission unit
+                       (path MTU) of an incoming connection, i.e. on the largest packet size
+                       that can be transmitted without causing fragmentation.
+
+                       This option allows to use a value less than the MPS in order to
+                       fine-tune application performance. Values greater than the MPS of an
+                       incoming connection can not be set.
+               [/help]
+       [option dccp-data-slices-per-group]
+               summary = the number of non-redundant slices per FEC group
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               default_val = 3
+               [help]
+                       This determines the number of slices in each FEC group which are
+                       necessary to decode the group. The given number must be smaller than
+                       the argument to the --dccp-slices-per-group option below.
+
+                       Note that the duration of a FEC group is proportional to the
+                       product dccp-max-slice-size * dccp-data-slices-per-group.
+               [/help]
+       [option dccp-slices-per-group]
+               summary = the total number of slices per FEC group
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = count
+               default_val = 4
+               [help]
+                       This value must be larger than the value of the argument to
+                       --dccp-data-slices-per-group. The difference of the two values is
+                       the number of redundant slices, that is, the number of slices which
+                       may be lost without causing interruptions in the audio stream.
+
+                       Increase this value if you are on a lossy network.
+               [/help]
+       [option udp]
+               summary = Options for the udp sender
+               flag ignored
+       [option udp-target]
+               summary = add udp target with optional port
+               arg_info = required_arg
+               arg_type = string
+               typestr = host[:port]
+               flag multiple
+               [help]
+                       Add the given host/port to the list of targets. The "host" argument
+                       can be either an IPv4/v6 address or hostname (RFC 3986 syntax). The
+                       "port" argument is an optional port number. If the "port" part is
+                       absent, the "--udp-default-port" value (see below) is used.
+
+                       The following examples are possible targets: "10.10.1.2:8000"
+                       (host:port); "10.10.1.2" (with default port); "224.0.1.38:1500"
+                       (IPv4 multicast); "localhost:8001" (hostname with port); "[::1]:8001"
+                       (IPv6 localhost); "[badc0de::1]" (IPv6 host with default port);
+                       "[FF00::beef]:1500" (IPv6 multicast).
+
+                       This option can be given multiple times, for multiple targets.
+               [/help]
+       [option udp-default-port]
+               summary = default port for udp targets
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = portnumber
+               default_val = 8000
+       [option udp-no-autostart]
+               summary = do not send the audio stream to UDP targets
+               [help]
+                       If this option is given, udp streaming may be activated at a later
+                       time by executing the "sender" command.
+               [/help]
+       [option udp-mcast-iface]
+               summary = outgoing udp multicast interface
+               arg_info = required_arg
+               arg_type = string
+               typestr = interface
+       [option udp-header-interval]
+               short_opt = H
+               summary = duration for sending header
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = milliseconds
+               default_val = 2000
+               [help]
+                       As the udp sender has no idea about connected clients it sends the
+                       audio file header periodically if necessary. This option specifies the
+                       duration between subsequent headers are sent. Smaller values decrease
+                       the average time clients have to wait before starting playback,
+                       larger values decrease network traffic.
+
+                       Note that this affects only ogg/* and wma streams. Other audio formats,
+                       including mp3, don't need an audio file header.
+               [/help]
+       [option udp-ttl]
+               short_opt = t
+               summary = set time to live value
+               arg_info = required_arg
+               arg_type = int32
+               typestr = num
+               default_val = -1
+               [help]
+                       This option applies exclusively to multicast UDPv4/v6 streaming.
+
+                       For the sending UDPv4 socket it sets the multicast Time-To-Live value
+                       to "num".  Traditional TTL scope values are: 0=host, 1=network, 32=same
+                       site, 64=same region, 128=same continent, 255=unrestricted. Please
+                       note however that this scoping is not a good solution: RFC 2365
+                       e.g. presents a better alternative.
+
+                       When using UDPv6 multicasting, the option sets the number of multicast
+                       hops (as described in RFC 3493); a value of -1 allows the kernel to
+                       auto-select the hop value.
+               [/help]
diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4
new file mode 100644 (file)
index 0000000..751998a
--- /dev/null
@@ -0,0 +1,680 @@
+[suite server_cmd]
+caption = list of server commands
+aux_info_prefix = Permissions:
+
+[introduction]
+       The server process listens on a network socket and accepts connections
+       from para_client or para_audiod. For the connection to succeed the
+       connecting peer must authenticate as one of the users stored in the
+       user table of para_server. Each entry of the user table contains the
+       set of permission bits that are granted to the user. Authenticated
+       users may execute one of the commands below if the set of permission
+       bits of the command is a subset of the permission bits that are
+       granted to the user.
+[/introduction]
+
+[subcommand add]
+       purpose = add or update audio files
+       non-opts-name = path...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Each path must be absolute and refer to either an audio file or a
+               directory. In case of a directory, all audio files in that directory
+               are added recursively. Note that the given paths refer to files or
+               directories on the host on which para_server is running.
+       [/description]
+       [option all]
+               short_opt = a
+               summary = add all files
+               [help]
+                       The default is to add only files ending in a known suffix for a
+                       supported audio format.
+               [/help]
+       [option lazy]
+               short_opt = l
+               summary = add files lazily
+               [help]
+                       If the path already exists in the database, skip this file. This
+                       operation is really cheap. Useful to update large directories after
+                       some files have been added.
+               [/help]
+       [option force]
+               short_opt = f
+               summary = force adding/updating
+               [help]
+                       Recompute the audio format handler data even if a file with the same
+                       path and the same hash value exists.
+               [/help]
+       [option verbose]
+               short_opt = v
+               summary = enable verbose mode
+               [help]
+                       Print what is being done.
+               [/help]
+
+[subcommand addatt]
+       purpose = add new attribute(s)
+       non-opts-name = attribute...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               This adds new attributes to the attribute table. At most 64 attributes
+               may be defined.
+       [/description]
+
+[subcommand check]
+       purpose = run integrity checks on database tables
+       aux_info = AFS_READ
+       [description]
+               If no options are given, all checks are run.
+       [/description]
+       [option aft]
+               short_opt = a
+               summary = run audio file table checks
+               [help]
+                       Report stale paths and invalid image and lyrics ids of the audio
+                       file table.
+               [/help]
+       [option attribute]
+               short_opt = A
+               summary = check for invalid attributes
+               [help]
+                       Report audio files whose attribute bitmask is invalid, i.e., has a bit
+                       set which does not correspond to any attribute of the attribute table.
+               [/help]
+       [option mood]
+               short_opt = m
+               summary = check for invalid mood definitions
+               [help]
+                       Run syntax checks on all moods of the mood table.
+               [/help]
+       [option playlist]
+               short_opt = p
+               summary = find invalid paths in playlists
+               [help]
+                       Check all playlists for paths not contained in the audio file table.
+               [/help]
+
+[subcommand cpsi]
+       purpose = copy selected parts of the audio file selector info
+       non-opts-name = source pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               If no option, or only --verbose is given, all fields of the audio
+               file selector info structure are copied to each row of the audio file
+               table whose path matches at least one of the given patterns. Otherwise,
+               only those fields which correspond to the given options are copied.
+       [/description]
+       [option attribute-bitmap]
+               short_opt = a
+               summary = copy the attribute bitmap
+       [option image-id]
+               short_opt = i
+               summary = copy the image id
+       [option lyrics-id]
+               short_opt = y
+               summary = copy the lyrics id
+       [option lastplayed]
+               short_opt = l
+               summary = copy the lastplayed timestamp
+       [option numplayed]
+               short_opt = n
+               summary = copy the numplayed counter
+       [option verbose]
+               short_opt = v
+               summary = enable verbose mode
+
+[subcommand ff]
+       purpose = jump N seconds forward or backward
+       synopsis = n[-]
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               This sets the 'R' (reposition request) bit of the vss status flags
+               which enqueues a request to jump n seconds forwards or backwards.
+
+               Example:
+
+                    para_client ff 30-
+
+               jumps 30 seconds backwards.
+
+       [/description]
+
+[subcommand help]
+       purpose = list available commands or print command-specific help
+       non-opts-name = [command]
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               Without any arguments, help prints a list of available commands. When
+               called with a command name as first argument, it prints the description
+               of this command.
+       [/description]
+
+[subcommand hup]
+       purpose = reload config file, log file and user list
+       aux_info = VSS_WRITE
+       [description]
+               Reread the config file and the user list file, close and reopen the log
+               file, and ask the afs process to do the same. Sending the HUP signal
+               to the server process has the same effect as running this command.
+       [/description]
+
+[subcommand init]
+       purpose = initialize the database tables for the audio file selector
+       synopsis = [table_name...]
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               When invoked without arguments, this command creates all
+               tables: audio_files, attributes, scores, moods, lyrics, images,
+               playlists. Otherwise only the given tables are created.
+       [/description]
+
+[subcommand jmp]
+       purpose = reposition the current stream
+       non-opts-name = n
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'R' (reposition request) bit of the vss status flags and
+               enqueue a request to jump to n% of the current audio file, where 0 <=
+               n <= 100.
+       [/description]
+
+[subcommand ls]
+       purpose = list audio files which match a pattern
+       non-opts-name = [pattern...]
+       aux_info = AFS_READ
+       [description]
+               If no pattern is given, all files are listed.  Otherwise, the command
+               lists all files of the audio file table whose path matches at least
+               one of the given patterns.
+       [/description]
+       [option listing-mode]
+               short_opt = l
+               summary = use alternative output format
+               arg_type = string
+               arg_info = optional_arg
+               typestr = mode
+               default_val = long
+               [help]
+                       The optional mode argument is either a single character or a word
+                       according to the following list.
+
+                       short (s). List only the path or basename (last component of the path),
+                       depending on whether -p is also given. This listing mode acts as if
+                       --listing-mode had not been given.
+
+                       long (l). Show detailed information. This is the default if no argument
+                       to --listing-mode is supplied.
+
+                       verbose (v). Multi-line output, one row per data field stored in the
+                       audio file table.
+
+                       parser-friendly (p). Like verbose listing mode, but use numerical
+                       values for the names of the output fields and prefix each line with
+                       a length field.
+
+                       mbox (m). Generate output suitable to be viewed with a mail
+                       program. One "mail" per matching audio file.
+
+                       chunk-table (c). Print path (or basename, depending on whether -p is
+                       also given), chunk time and chunk offsets.
+
+               [/help]
+       [option full-path]
+               short_opt = F
+               summary = list full paths, match full paths against patterns
+               [help]
+                       This option is the default, so it does nothing. Deprecated as of
+                       v0.6.0, scheduled for removal in v0.6.1.
+               [/help]
+       [option basename]
+               short_opt = b
+               summary = list and match basenames only
+               [help]
+                       Print only the basename of each matching file and match only the
+                       basenames of the paths stored in the audio file table against the
+                       given patterns. The default is to print and match the full path.
+               [/help]
+       [option admissible]
+               short_opt = a
+               summary = list only admissible files
+               [help]
+                       List only files which are admissible with respect to the current mood
+                       or playlist.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+       [option unix-date]
+               short_opt = d
+               summary = print dates as seconds after the epoch
+       [option sort]
+               short_opt = s
+               summary = change sort order
+               arg_type = string
+               arg_info = required_arg
+               typestr = order
+               default_val = path
+               [help]
+                       The sort order must be given as an required argument. Like for
+                       --listing-mode, this argument may either be a single character or a
+                       word, according to the following list.
+
+                       path (p). Sort alphabetically by path or basename (see -p). This is
+                       the default if --sort is not given.
+
+                       score (s). Iterate over the entries of the score table, rather than
+                       the audio file table. This sort order implies --admissible, since
+                       the score table contains only admissible files.
+
+                       lastplayed (l)
+
+                       numplayed (n)
+
+                       frequency (f)
+
+                       channels (c)
+
+                       image-id (i)
+
+                       lyrics-id (y)
+
+                       bitrate (b)
+
+                       duration (d)
+
+                       audio-format (a)
+
+                       If --sort is not given, path sort is implied.
+               [/help]
+
+[subcommand lsatt]
+       purpose = list attributes
+       aux_info = AFS_READ
+       [description]
+               Print the list of all defined attributes which match the given
+               pattern. If no pattern is given, the full list is printed.
+       [/description]
+
+       [option id-sort]
+               short_opt = i
+               summary = sort attributes by id
+               [help]
+                       The default is to sort alphabetically by name.
+
+                       Attributes are internally represented as an 64 bit array. The attribute
+                       id is the bit number in this array.
+               [/help]
+       [option long]
+               short_opt = l
+               summary = print long listing
+               [help]
+                       The long listing prints the attribute id in addition to the name of
+                       the attribute. The id is printed as a decimal number and is separated
+                       from the name by a tab character.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+
+[subcommand mvatt]
+       purpose = rename an attribute
+       synopsis = source dest
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Rename the attribute given by the first argument to the destination
+               given by the second argument. It is an error if the destination
+               attribute exists.
+       [/description]
+
+[subcommand next]
+       purpose = close the stream and start to stream the next audio file
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'N' (next audio file) bit of the vss status flags. This
+               instructs the server to close the current stream, if any. The 'P'
+               (playing) bit is not modified by this command. If it is on, playing
+               continues with the next audio file.
+
+               This command is equivalent to stop if paused, and has no effect
+               if stopped.
+       [/description]
+
+[subcommand nomore]
+       purpose = stop playing after current audio file
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'O' (no more) bit of the vss status flags which asks
+               para_server to clear the 'P' (playing) bit after the 'N' (next audio
+               file) bit transitions from off to on (because the end of the current
+               audio file is reached). Use this command instead of stop if you don't
+               like sudden endings.
+       [/description]
+
+[subcommand pause]
+       purpose = suspend the current stream
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Clear the 'P' (playing) bit of the vss status flags.
+       [/description]
+
+[subcommand play]
+       purpose = start or resume playback
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Set the 'P' (playing) bit of the vss status flags.
+       [/description]
+
+[subcommand rm]
+       purpose = remove rows from the audio file table
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all rows of the audio file table which match any of the given
+               patterns. Note that this affects only the database table; the command
+               won't touch your audio files on disk.
+       [/description]
+       [option verbose]
+               short_opt = v
+               summary = print paths of deleted rows
+       [option force]
+               short_opt = f
+               summary = don't complain if nothing was removed
+       [option pathname-match]
+               short_opt = p
+               summary = modify matching behaviour
+               [help]
+                       Match a slash in the path only with a slash in pattern and not by an
+                       asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+                       expression ([]) containing a slash (see fnmatch(3)).
+               [/help]
+
+[subcommand rmatt]
+       purpose = remove attribute(s)
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all attributes which match any given pattern. All information
+               about the removed attributes in the audio file table is lost.
+       [/description]
+
+[subcommand select]
+       purpose = activate a mood or a playlist
+       non-opts-name = specifier/name
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               The specifier is either 'm' or 'p' to indicate whether a playlist or
+               a mood should be activated. Example:
+
+                       select m/foo
+
+               activates the mood named 'foo'.
+       [/description]
+
+[subcommand sender]
+       purpose = control paraslash senders
+       synopsis = [sender cmd [arguments]]
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Send a command to a specific sender. The following commands are
+               available, but not all senders support every command.
+
+                      help, on, off, add, delete, allow, deny, status.
+
+               The help command prints the help text of the given sender. If no
+               command is given the list of available senders is shown.
+
+               Example:
+
+                       para_client sender http help
+
+       [/description]
+
+[subcommand setatt]
+       purpose = set or unset attributes
+       synopsis = attribute{+|-}... pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Set ('+') or unset ('-') the given attributes for all audio files
+               matching the given pattern. Example:
+
+                       setatt rock+ punk+ pop- '*foo.mp3'
+
+               sets the 'rock' and the 'punk' attribute and unsets the 'pop' attribute
+               of all files ending with 'foo.mp3'.
+       [/description]
+
+[subcommand si]
+       purpose = print server info
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               Show server and afs PID, number of connections, uptime and more.
+       [/description]
+
+[subcommand stat]
+       purpose = print information about the current audio file
+       aux_info = VSS_READ
+       [option num]
+               short_opt = n
+               summary = number of times to show the status info
+               arg_info = required_arg
+               arg_type = uint32
+               typestr = num
+               [help]
+                       Exit after the status information has been shown num times. If this
+                       option is not given, the command runs in an endless loop.
+               [/help]
+       [option parser-friendly]
+               short_opt = p
+               summary = enable parser-friendly output
+               [help]
+                       Show status item identifiers as numerical values and prefix each
+                       status item with its size in bytes.
+               [/help]
+
+[subcommand stop]
+       purpose = stop playback
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of
+               the vss status flags, effectively stopping playback.
+       [/description]
+
+[subcommand tasks]
+       purpose = list active server tasks
+       aux_info = NO_PERMISSION_REQUIRED
+       [description]
+               For each task, print ID, status and name. This is mostly useful
+               for debugging.
+       [/description]
+
+[subcommand term]
+       purpose = ask the server to terminate
+       aux_info = VSS_READ | VSS_WRITE
+       [description]
+               Shut down the server. Instead of this command, you can also send
+               SIGINT or SIGTERM to the para_server process. It should never be
+               necessary to send SIGKILL.
+       [/description]
+
+[subcommand touch]
+       purpose = manipulate the afs information of audio files
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               This command modifies the afs info structure of all rows of the audio
+               file table whose path matches at least one of the given patters.
+
+               If at least one option is given which takes a number as its argument,
+               only those fields of the afs info structure are updated which
+               correspond to the given options while all other fields stay unmodified.
+
+               If no such option is given, the lastplayed field is set to the current
+               time and the value of the numplayed field is increased by one while
+               all other fields are left unchanged. This mimics what happens when
+               the virtual streaming system selects the file for streaming.
+
+               If the file is admissible for the current mood (or contained in the
+               current playlist), its score is recomputed according to the changed
+               values.
+       [/description]
+       [option numplayed]
+               short_opt = n
+               summary = set the numplayed count manually
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The numplayed count of an audio file is the number of times the file
+                       was selected for streaming. It is one of the inputs to the scoring
+                       function which determines the order in which admissible files are
+                       streamed.
+
+                       The virtual streaming system increases this number automatically each
+                       time it opens the file for streaming.
+               [/help]
+       [option lastplayed]
+               short_opt = l
+               summary = set the lastplayed time manually
+               arg_type = uint64
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The lastplayed time of an audio file is the time when the file was
+                       last opened for streaming.
+
+                       Like the numplayed count, it is an input for the scoring function
+                       and is updated automatically by the virtual streaming system.
+
+                       The argument must be a number of seconds since the epoch. Example:
+
+                               touch -l=$(date +%s) file
+
+                       sets the lastplayed time of 'file' to the current time.
+               [/help]
+       [option image-id]
+               short_opt = i
+               summary = set the image id
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The afs info structure of each row of the audio file table contains
+                       a slot for the image id of the audio file that corresponds to the
+                       row. The image id stored in this slot refers to the key in the image
+                       table that identifies the blob.
+
+                       When a new audio file is added to the audio file table, its image
+                       id starts out as zero, indicating that there is no image associated
+                       with the file. Setting the image id to a non-zero number associates
+                       the file with a particular blob of the image table, for example the
+                       cover art of the album in jpg format.
+               [/help]
+       [option lyrics-id]
+               short_opt = y
+               summary = set the lyrics id
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       This option works just like --image-id, but sets the lyrics ID rather
+                       than the image id.
+               [/help]
+       [option amp]
+               short_opt = a
+               summary = set the amplification value (0-255)
+               arg_type = uint32
+               arg_info = required_arg
+               typestr = num
+               [help]
+                       The amplification value of an audio file is a number which is stored
+                       in the afs info structure.
+
+                       The value determines the scaling factor by which the amplitude of
+                       the decoded samples should be multiplied in order to normalize the
+                       volume. A value of zero means no amplification, 64 means the amplitude
+                       should be multiplied by a factor of two, 128 by three and so on.
+
+                       The amp filter of para_audiod amplifies the volume according to
+                       this value.
+               [/help]
+       [option verbose]
+               short_opt = v
+               summary = explain what is being done
+       [option pathname-match]
+               short_opt = p
+               summary = modify matching behaviour
+               [help]
+                       Match a slash in the path only with a slash in pattern and not by an
+                       asterisk (*) or a question mark (?) metacharacter, nor by a bracket
+                       expression ([]) containing a slash (see fnmatch(3)).
+               [/help]
+
+[subcommand version]
+       purpose = print the git version string of para_server
+       aux_info = NO_PERMISSION_REQUIRED
+       [option verbose]
+               short_opt = v
+               summary = print detailed (multi-line) version text
+
+m4_define(`BLOB_COMMANDS', `
+[subcommand rm`$2']
+       purpose = remove `$1' blob(s)
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all `$1' blobs which match any of the given patterns.
+       [/description]
+
+[subcommand mv`$2']
+       purpose = rename `$1' blob(s)
+       non-opts-name = source dest
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Rename `$1' source to dest. The command fails if the source `$1'
+               does not exist or if the destination `$1' already exists.
+       [/description]
+
+[subcommand add`$2']
+       purpose = add a blob to the `$1' table
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Read from stdin and ask the audio file selector to create a blob in
+               the `$1' table. If the named blob already exists, it gets replaced
+               with the new data.
+       [/description]
+
+[subcommand cat`$2']
+       purpose = dump a `$1' blob to stdout
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ
+
+[subcommand ls`$2']
+       purpose = list blobs of type `$1' which match a pattern
+       non-opts-name = [pattern...]
+       aux_info = AFS_READ
+       [description]
+               Print the list of all blobs which match the given pattern. If no
+               pattern is given, the full list is printed.
+       [/description]
+       [option id-sort]
+               short_opt = i
+               summary = sort by identifier
+               [help]
+                       The default is to sort alphabetically by name.
+               [/help]
+       [option long]
+               short_opt = l
+               summary = long listing
+               [help]
+                       Print identifier and name. The default is to print only the name.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+')
+
+BLOB_COMMANDS(`moods', `mood')
+BLOB_COMMANDS(`playlist', `pl')
+BLOB_COMMANDS(`image', `img')
+BLOB_COMMANDS(`lyrics', `lyr')
diff --git a/m4/lls/write.suite.m4 b/m4/lls/write.suite.m4
new file mode 100644 (file)
index 0000000..a2f50df
--- /dev/null
@@ -0,0 +1,35 @@
+m4_define(PROGRAM, para_write)
+[suite write]
+version-string = GIT_VERSION()
+[supercommand para_write]
+       purpose = play wav or raw audio
+       [description]
+               para_write reads audio data from stdin and starts one supported writer.
+       [/description]
+       m4_include(common-option-section.m4)
+       m4_include(help.m4)
+       m4_include(detailed-help.m4)
+       m4_include(version.m4)
+       m4_include(loglevel.m4)
+       m4_include(per-command-options-section.m4)
+       [option writer]
+               short_opt = w
+               summary = select writer to start
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               typestr = 'name [options]'
+               [help]
+                       May be given multiple times, and the same writer may be specified more
+                       than once. If this option is not given, the first supported writer
+                       is started. The list of supported writers is shown in the help output.
+
+                       Options for a particular writer may be specified for each given
+                       '--writer' option separately. You will have to quote these options
+                       like this:
+
+                               --writer 'oss --device /dev/dsp2'
+               [/help]
+       m4_include(channels.m4)
+       m4_include(sample-rate.m4)
+       m4_include(sample-format.m4)
diff --git a/m4/lls/write_cmd.suite.m4 b/m4/lls/write_cmd.suite.m4
new file mode 100644 (file)
index 0000000..9d8ee75
--- /dev/null
@@ -0,0 +1,87 @@
+[suite write_cmd]
+caption = writers
+[subcommand alsa]
+       purpose = native ALSA output plugin
+       [option device]
+               short_opt = d
+               summary = set PCM device
+               typestr = device
+               arg_info = required_arg
+               arg_type = string
+               default_val = default
+               [help]
+                       Check for the presence of a /proc/asound/ directory to see if ALSA
+                       is present in your kernel. The file /proc/asound/devices contains
+                       all devices ALSA knows about.
+               [/help]
+       [option buffer-time]
+               short_opt = B
+               summary = duration of the ALSA buffer
+               typestr = milliseconds
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 170
+               [help]
+                       This is only a hint as ALSA might pick a slightly different time,
+                       depending on the sound hardware. The chosen value is shown in debug
+                       output as BUFFER_TIME.
+
+                       If synchronization between multiple clients is desired, the same
+                       buffer time should be configured for all clients.
+               [/help]
+[subcommand ao]
+       purpose = output plugin for libao
+       [option driver]
+               short_opt = d
+               summary = select a output driver by name
+               typestr = name
+               arg_info = required_arg
+               arg_type = string
+               [help]
+                       If this is not given, the driver with the highest priority (see below)
+                       will be used.
+               [/help]
+       [option ao-option]
+               short_opt = o
+               summary = pass a key-value pair to the libao driver
+               typestr = key:value
+               arg_info = required_arg
+               arg_type = string
+               flag multiple
+               [help]
+                       For each time this option is given, the supplied key-value pair is
+                       appended to the list of options for the driver. Invalid keys are
+                       silently ignored.
+               [/help]
+[subcommand oss]
+       purpose = output plugin for the Open Sound System
+       [option device]
+               short_opt = d
+               summary = set PCM device
+               typestr = path
+               arg_info = required_arg
+               arg_type = string
+               default_val = /dev/dsp
+[subcommand osx]
+       purpose = output plugin for Mac OS coreaudio
+       [option numbuffers]
+               short_opt = n
+               summary = number of audio buffers to allocate
+               typestr = num
+               arg_info = required_arg
+               arg_type = uint32
+               default_val = 20
+               [help]
+                       Increase if you get buffer underruns.
+               [/help]
+[subcommand file]
+       purpose = output plugin that writes to a local file
+       [option filename]
+               short_opt = f
+               summary = specify output file name
+               typestr = path
+               arg_info = required_arg
+               arg_type = string
+               [help]
+                       Defaults to a random filename in ~/.paraslash.
+               [/help]
diff --git a/man_util.bash b/man_util.bash
deleted file mode 100755 (executable)
index cb7519c..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env bash
-
-# Receivers, filters, writers are called "modules" in this script
-print_modhelp()
-{
-       local ggo="$1"
-
-       $GENGETOPT --show-detailed-help \
-               --set-version "" \
-               --set-package "" \
-               < "$ggo" | awk 'BEGIN {
-                       have_purpose=0
-                       have_usage=0
-               } {
-                       if (!have_purpose) {
-                               if ($0 ~ /^ *$/)
-                                       next
-                               printf(" (%s):", $0)
-                               have_purpose=1
-                               next
-                       }
-                       if (!have_usage) {
-                               if ($0 ~ /^Usage: /) {
-                                       have_usage=1
-                               }
-                               next
-                       }
-                       print $0
-               }'
-}
-
-make_help()
-{
-       local target="$1" module ggo
-
-       ggo="$GGO_DIR/$1.ggo"
-       $GENGETOPT --show-detailed-help \
-               --set-version "$VERSION" \
-               --set-package "para_$1" \
-               < "$ggo"
-
-       if [[ "$target" == 'recv' || "$target" == 'audiod' ]]; then
-               for module in $RECEIVERS; do
-                       ggo="$GGO_DIR/${module}_recv.ggo"
-                       [[ ! -f "$ggo" ]] && continue
-                       printf "\nOptions for the $module receiver"
-                       print_modhelp "$ggo"
-               done
-       fi
-       if [[ "$target" == 'filter' || "$target" == 'audiod' ]]; then
-               for module in $FILTERS; do
-                       ggo="$GGO_DIR/${module}_filter.ggo"
-                       [[ ! -f "$ggo" ]] && continue
-                       printf "\nOptions for the $module filter"
-                       print_modhelp "$ggo"
-               done
-       fi
-       if [[ "$target" == 'write' || "$target" == 'audiod' ]]; then
-               for module in $WRITERS; do
-                       ggo="$GGO_DIR/${module}_write.ggo"
-                       [[ ! -f "$ggo" ]] && continue
-                       printf "\nOptions for the $module writer"
-                       print_modhelp "$ggo"
-               done
-       fi
-}
-
-set -u
-
-(($# != 1)) && exit 1
-
-# These must be set by the caller (make or help2man)
-export COMMAND_LISTS FILTERS GENGETOPT GGO_DIR HELP2MAN HELP2MAN_DIR \
-       RECEIVERS VERSION WRITERS
-
-# If either --version or --help-xxx was given, we are being called by help2man
-if [[ "$1" == "--version" ]]; then
-       echo "$VERSION"
-       exit $?
-fi
-if [[ "$1" =~ --help- ]]; then
-       make_help "${1#--help-}"
-       exit $?
-fi
-
-# Called by make, run help2man
-output_file="$1"
-target="${output_file##*/para_}"
-target="${target%.*}" # server, audiod, filter, ...
-link="$HELP2MAN_DIR/para_$target"
-
-cl_opts=
-for cl in $COMMAND_LISTS; do
-       cl_opts+=" --include $cl"
-done
-
-# Create a symlink para_$target, pointing to this script. This hack is
-# necessary because help2man always includes the name of the executable in its
-# output.
-ln -sf "$PWD/$0" "$link"
-
-# This will call us again twice, with either --help-$target or --version given.
-$HELP2MAN --no-info --help-option "--help-$target" $cl_opts \
-       "$link" > "$output_file"
-if (($? != 0)); then
-       rm -f "$output_file"
-       exit 1
-fi
diff --git a/mix.h b/mix.h
index 60c4392fabab76bb58ad1c2ee0ee211d52469ad7..5d68b2bb0b34df798e38efad7b0ceeff687982fc 100644 (file)
--- a/mix.h
+++ b/mix.h
@@ -4,7 +4,7 @@
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
 
-/** \file mix.h Mixer API (used by para_fade). */
+/** \file mix.h Mixer API for para_mixer. */
 
 /**
  * Opaque structure which corresponds to an instance of a mixer.
@@ -16,10 +16,14 @@ struct mixer_handle;
 
 /**
  * Operations provided by each mixer plugin.
+ *
+ * Each mixer plugin must define a non-static instance of this structure, with
+ * all pointers initialized to non-NULL values. No other symbols need to be
+ * exported.
  */
 struct mixer {
-       /** Called on startup, must fill in all other members. */
-       void (*init)(struct mixer *self);
+       /** Used to identify the mixer. */
+       const char * const name;
        /** Return a handle that can be passed to other methods. */
        int (*open)(const char *dev, struct mixer_handle **handle);
        /** Returns a string of all valid mixer channels. */
@@ -34,3 +38,6 @@ struct mixer {
        /** Free all resources associated with the given handle. */
        void (*close)(struct mixer_handle **handle);
 };
+
+/** Declared even if unsupported because it does not hurt and avoids ifdefs. */
+extern const struct mixer alsa_mixer, oss_mixer;
diff --git a/mixer.c b/mixer.c
new file mode 100644 (file)
index 0000000..0c08e2f
--- /dev/null
+++ b/mixer.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mixer.c A volume fader and alarm clock for OSS. */
+
+#include <regex.h>
+#include <lopsub.h>
+#include <math.h>
+
+#include "mixer.lsg.h"
+#include "para.h"
+#include "fd.h"
+#include "string.h"
+#include "mix.h"
+#include "error.h"
+#include "version.h"
+
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+/* At least one of the two is defined if this file gets compiled. */
+static const struct mixer *mixers[] = {
+#ifdef HAVE_ALSA
+       &alsa_mixer,
+#endif
+#ifdef HAVE_OSS
+       &oss_mixer,
+#endif
+};
+
+#define NUM_SUPPORTED_MIXERS (ARRAY_SIZE(mixers))
+#define FOR_EACH_MIXER(i) for ((i) = 0; (i) < NUM_SUPPORTED_MIXERS; (i)++)
+
+static struct lls_parse_result *lpr, *sub_lpr;
+
+#define CMD_PTR(_cmd) (lls_cmd(LSG_MIXER_CMD_ ## _cmd, mixer_suite))
+#define OPT_RESULT(_cmd, _opt) (lls_opt_result( \
+       LSG_MIXER_ ## _cmd ## _OPT_ ## _opt, (LSG_MIXER_CMD_ ## _cmd == 0)? lpr : sub_lpr))
+#define OPT_GIVEN(_cmd, _opt) (lls_opt_given(OPT_RESULT(_cmd, _opt)))
+#define OPT_STRING_VAL(_cmd, _opt) (lls_string_val(0, OPT_RESULT(_cmd, _opt)))
+#define OPT_UINT32_VAL(_cmd, _opt) (lls_uint32_val(0, OPT_RESULT(_cmd, _opt)))
+
+typedef int (*mixer_subcommand_handler_t)(const struct mixer *, struct mixer_handle *);
+
+#define EXPORT_CMD(_cmd) const mixer_subcommand_handler_t \
+       lsg_mixer_com_ ## _cmd ## _user_data = &com_ ## _cmd;
+
+static int loglevel;
+static __printf_2_3 void date_log(int ll, const char *fmt, ...)
+{
+       va_list argp;
+       time_t t1;
+       struct tm *tm;
+
+       if (ll < loglevel)
+               return;
+       time(&t1);
+       tm = localtime(&t1);
+       fprintf(stderr, "%d:%02d:%02d ", tm->tm_hour, tm->tm_min, tm->tm_sec);
+       va_start(argp, fmt);
+       vprintf(fmt, argp);
+       va_end(argp);
+}
+__printf_2_3 void (*para_log)(int, const char*, ...) = date_log;
+
+static int set_channel(const struct mixer *m, struct mixer_handle *h,
+               const char *channel)
+{
+
+       PARA_NOTICE_LOG("using %s mixer channel\n", channel?
+               channel : "default");
+       return m->set_channel(h, channel);
+}
+
+static void millisleep(int ms)
+{
+       struct timespec ts;
+
+       PARA_INFO_LOG("sleeping %dms\n", ms);
+       if (ms < 0)
+               return;
+       ts.tv_sec = ms / 1000,
+       ts.tv_nsec = (ms % 1000) * 1000 * 1000;
+       nanosleep(&ts, NULL);
+}
+
+/*
+ * This implements the inverse function of t -> t^alpha, scaled to the time
+ * interval [0,T] and the range given by old_vol and new_vol. It returns the
+ * amount of milliseconds until the given volume is reached.
+ */
+static unsigned volume_time(double vol, double old_vol, double new_vol,
+               double T, double alpha)
+{
+       double c, d, x;
+
+       if (old_vol < new_vol) {
+               c = old_vol;
+               d = new_vol;
+       } else {
+               c = new_vol;
+               d = old_vol;
+       }
+
+       x = T * exp(log(((vol - c) / (d - c))) / alpha);
+       assert(x <= T);
+       if (old_vol < new_vol)
+               return x;
+       else
+               return T - x;
+}
+
+/* Fade to new volume in fade_time seconds. */
+static int fade(const struct mixer *m, struct mixer_handle *h, uint32_t new_vol,
+               uint32_t fade_time)
+{
+       int i, T, old_vol, ret, slept, incr;
+       double ms, alpha;
+       uint32_t fe = OPT_UINT32_VAL(PARA_MIXER, FADE_EXPONENT);
+
+       if (fade_time <= 0 || fe >= 100) {
+               ret = m->set(h, new_vol);
+               if (ret < 0)
+                       return ret;
+               goto sleep;
+       }
+       alpha = (100 - fe) / 100.0;
+       ret = m->get(h);
+       if (ret < 0)
+               return ret;
+       old_vol = ret;
+       if (old_vol == new_vol)
+               goto sleep;
+       PARA_NOTICE_LOG("fading %s from %d to %u in %u seconds\n",
+               OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL), old_vol,
+               new_vol, fade_time);
+       incr = old_vol < new_vol? 1 : -1;
+       T = fade_time * 1000;
+       i = old_vol;
+       slept = 0;
+       do {
+               ms = volume_time(i + incr, old_vol, new_vol, T, alpha);
+               millisleep(ms - slept);
+               slept = ms;
+               i += incr;
+               ret = m->set(h, i);
+               if (ret < 0)
+                       return ret;
+       } while (i != new_vol);
+       return 1;
+sleep:
+       sleep(fade_time);
+       return ret;
+}
+
+static int com_fade(const struct mixer *m, struct mixer_handle *h)
+{
+       uint32_t new_vol = OPT_UINT32_VAL(FADE, FADE_VOL);
+       uint32_t fade_time = OPT_UINT32_VAL(FADE, FADE_TIME);
+       return fade(m, h, new_vol, fade_time);
+}
+EXPORT_CMD(fade);
+
+static void client_cmd(const char *cmd)
+{
+       int ret, status, fds[3] = {0, 0, 0};
+       pid_t pid;
+       char *cmdline = make_message(BINDIR "/para_client %s", cmd);
+
+       PARA_NOTICE_LOG("%s\n", cmdline);
+       ret = para_exec_cmdline_pid(&pid, cmdline, fds);
+       free(cmdline);
+       if (ret < 0) {
+               PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+               goto fail;
+       }
+       do
+               pid = waitpid(pid, &status, 0);
+       while (pid == -1 && errno == EINTR);
+       if (pid < 0) {
+               PARA_ERROR_LOG("%s\n", strerror(errno));
+               goto fail;
+       }
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               goto fail;
+       return;
+fail:
+       PARA_EMERG_LOG("command \"%s\" failed\n", cmd);
+       exit(EXIT_FAILURE);
+}
+
+static void change_afs_mode(const char *afs_mode)
+{
+       char *cmd;
+
+       client_cmd("stop");
+       if (!afs_mode)
+               return;
+       cmd = make_message("select %s", afs_mode);
+       client_cmd(cmd);
+       free(cmd);
+}
+
+static int set_initial_volume(const struct mixer *m, struct mixer_handle *h)
+{
+       int i, ret;
+
+       for (i = 0; i < OPT_GIVEN(SLEEP, IVOL); i++) {
+               const char *val = lls_string_val(i, OPT_RESULT(SLEEP, IVOL));
+               char *p, *ch, *arg = para_strdup(val);
+               int32_t iv;
+               p = strchr(arg, ':');
+               if (p) {
+                       *p = '\0';
+                       p++;
+                       ch = arg;
+               } else {
+                       p = arg;
+                       ch = NULL;
+               }
+               ret = para_atoi32(p, &iv);
+               if (ret < 0) {
+                       free(arg);
+                       return ret;
+               }
+               ret = set_channel(m, h, ch);
+               if (!ch)
+                       ch = "default";
+               if (ret < 0) {
+                       PARA_WARNING_LOG("ignoring channel %s\n", ch);
+                       ret = 0;
+               } else {
+                       PARA_INFO_LOG("initial volume %s: %d\n", ch, iv);
+                       ret = m->set(h, iv);
+               }
+               free(arg);
+               if (ret < 0)
+                       return ret;
+       }
+       return 1;
+}
+
+static int com_sleep(const struct mixer *m, struct mixer_handle *h)
+{
+       time_t t1, wake_time_epoch;
+       unsigned int delay;
+       struct tm *tm;
+       int ret;
+       const char *wake_time = OPT_STRING_VAL(SLEEP, WAKE_TIME);
+       const char *fo_mood = OPT_STRING_VAL(SLEEP, FO_MOOD);
+       const char *fi_mood = OPT_STRING_VAL(SLEEP, FI_MOOD);
+       const char *sleep_mood = OPT_STRING_VAL(SLEEP, SLEEP_MOOD);
+       int fit = OPT_UINT32_VAL(SLEEP, FI_TIME);
+       int fot = OPT_UINT32_VAL(SLEEP, FO_TIME);
+       int fiv = OPT_UINT32_VAL(SLEEP, FI_VOL);
+       int fov = OPT_UINT32_VAL(SLEEP, FO_VOL);
+       int32_t hour, min = 0;
+       char *tmp;
+       char *wt = para_strdup(wake_time + (wake_time[0] == '+'));
+
+       /* calculate wake time */
+       time(&t1);
+       tmp = strchr(wt, ':');
+       if (tmp) {
+               *tmp = '\0';
+               tmp++;
+               ret = para_atoi32(tmp, &min);
+               if (ret < 0) {
+                       free(wt);
+                       return ret;
+               }
+       }
+       ret = para_atoi32(wt, &hour);
+       free(wt);
+       if (ret < 0)
+               return ret;
+       if (wake_time[0] == '+') { /* relative */
+               t1 += hour * 60 * 60 + min * 60;
+               tm = localtime(&t1);
+       } else {
+               tm = localtime(&t1);
+               if (tm->tm_hour > hour || (tm->tm_hour == hour && tm->tm_min> min)) {
+                       t1 += 86400; /* wake time is tomorrow */
+                       tm = localtime(&t1);
+               }
+               tm->tm_hour = hour;
+               tm->tm_min = min;
+               tm->tm_sec = 0;
+       }
+       wake_time_epoch = mktime(tm);
+       PARA_INFO_LOG("waketime: %d:%02d\n", tm->tm_hour, tm->tm_min);
+       client_cmd("stop");
+       sleep(1);
+       if (fot) {
+               ret = set_initial_volume(m, h);
+               if (ret < 0)
+                       return ret;
+               change_afs_mode(fo_mood);
+               client_cmd("play");
+               ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+               if (ret < 0)
+                       return ret;
+               ret = fade(m, h, fov, fot);
+               if (ret < 0)
+                       return ret;
+       } else {
+               ret = m->set(h, fov);
+               if (ret < 0)
+                       return ret;
+       }
+       if (OPT_GIVEN(SLEEP, SLEEP_MOOD)) {
+               change_afs_mode(sleep_mood);
+               client_cmd("play");
+       } else
+               client_cmd("stop");
+       if (!fit)
+               return 1;
+       change_afs_mode(fi_mood);
+       for (;;) {
+               time(&t1);
+               if (wake_time_epoch <= t1 + fit)
+                       break;
+               delay = wake_time_epoch - t1 - fit;
+               PARA_INFO_LOG("sleeping %u seconds (%u:%02u)\n",
+                       delay, delay / 3600,
+                       (delay % 3600) / 60);
+               sleep(delay);
+       }
+       client_cmd("play");
+       ret = fade(m, h, fiv, fit);
+       PARA_INFO_LOG("fade complete, returning\n");
+       return ret;
+}
+EXPORT_CMD(sleep);
+
+static int com_snooze(const struct mixer *m, struct mixer_handle *h)
+{
+       int ret, val;
+
+       if (OPT_UINT32_VAL(SNOOZE, SO_TIME) == 0)
+               return 1;
+       ret = m->get(h);
+       if (ret < 0)
+               return ret;
+       val = ret;
+       if (val < OPT_UINT32_VAL(SNOOZE, SO_VOL))
+               ret = m->set(h, OPT_UINT32_VAL(SNOOZE, SO_VOL));
+       else
+               ret = fade(m, h, OPT_UINT32_VAL(SNOOZE, SO_VOL),
+                       OPT_UINT32_VAL(SNOOZE, SO_TIME));
+       if (ret < 0)
+               return ret;
+       client_cmd("pause");
+       PARA_NOTICE_LOG("%" PRIu32 " seconds snooze time...\n",
+               OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+       sleep(OPT_UINT32_VAL(SNOOZE, SNOOZE_TIME));
+       client_cmd("play");
+       return fade(m, h, OPT_UINT32_VAL(SNOOZE, SI_VOL),
+               OPT_UINT32_VAL(SNOOZE, SI_TIME));
+}
+EXPORT_CMD(snooze);
+
+static int com_set(const struct mixer *m, struct mixer_handle *h)
+{
+       return m->set(h, OPT_UINT32_VAL(SET, VAL));
+}
+EXPORT_CMD(set);
+
+static const struct mixer *get_mixer_or_die(void)
+{
+       int i;
+
+       if (!OPT_GIVEN(PARA_MIXER, MIXER_API))
+               i = 0; /* default: use first mixer */
+       else
+               FOR_EACH_MIXER(i)
+                       if (!strcmp(mixers[i]->name,
+                                       OPT_STRING_VAL(PARA_MIXER, MIXER_API)))
+                               break;
+       if (i < NUM_SUPPORTED_MIXERS) {
+               PARA_NOTICE_LOG("using %s mixer API\n", mixers[i]->name);
+               return mixers[i];
+       }
+       printf("available mixer APIs: ");
+       FOR_EACH_MIXER(i)
+               printf("%s%s%s ", i == 0? "[" : "", mixers[i]->name,
+                       i == 0? "]" : "");
+       printf("\n");
+       exit(EXIT_FAILURE);
+}
+
+static void show_subcommands(void)
+{
+       const struct lls_command *cmd;
+       int i;
+       printf("Subcommands:\n");
+       for (i = 1; (cmd = lls_cmd(i, mixer_suite)); i++) {
+               const char *name = lls_command_name(cmd);
+               const char *purpose = lls_purpose(cmd);
+               printf("%-20s%s\n", name, purpose);
+       }
+}
+
+
+static int com_help(__a_unused const struct mixer *m,
+               __a_unused struct mixer_handle *h)
+{
+       const struct lls_command *cmd;
+       const struct lls_opt_result *r_l = OPT_RESULT(HELP, LONG);
+       char *txt, *errctx;
+       const char *name;
+       int ret;
+
+       ret = lls_check_arg_count(sub_lpr, 0, 1, NULL);
+       if (ret < 0)
+               return ret;
+       if (lls_num_inputs(sub_lpr) == 0) {
+               show_subcommands();
+               return 0;
+       }
+       name = lls_input(0, sub_lpr);
+       ret = lls(lls_lookup_subcmd(name, mixer_suite, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       cmd = lls_cmd(ret, mixer_suite);
+       if (lls_opt_given(r_l))
+               txt = lls_long_help(cmd);
+       else
+               txt = lls_short_help(cmd);
+       printf("%s", txt);
+       free(txt);
+       return 0;
+}
+EXPORT_CMD(help);
+
+static void handle_help_flags(void)
+{
+       char *help;
+
+       if (OPT_GIVEN(PARA_MIXER, DETAILED_HELP))
+               help = lls_long_help(CMD_PTR(PARA_MIXER));
+       else if (OPT_GIVEN(PARA_MIXER, HELP))
+               help = lls_short_help(CMD_PTR(PARA_MIXER));
+       else
+               return;
+       printf("%s", help);
+       free(help);
+       show_subcommands();
+       exit(EXIT_SUCCESS);
+}
+
+static int parse_and_merge_config_file(const struct lls_command *cmd)
+{
+       int ret;
+       int cf_argc;
+       char **cf_argv;
+       char *cf, *errctx = NULL;
+       struct lls_parse_result **lprp, *cf_lpr, *merged_lpr;
+       void *map;
+       size_t sz;
+       const char *subcmd_name;
+
+       if (cmd == lls_cmd(0, mixer_suite)) {
+               lprp = &lpr;
+               subcmd_name = NULL;
+       } else {
+               lprp = &sub_lpr;
+               subcmd_name = lls_command_name(cmd);
+       }
+       if (OPT_GIVEN(PARA_MIXER, CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(PARA_MIXER, CONFIG_FILE));
+       else {
+               char *home = para_homedir();
+               cf = make_message("%s/.paraslash/mixer.conf", home);
+               free(home);
+       }
+       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(PARA_MIXER, CONFIG_FILE))
+                       goto free_cf;
+       } else {
+               ret = lls(lls_convert_config(map, sz, subcmd_name, &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, &cf_lpr, &errctx));
+               lls_free_argv(cf_argv);
+               if (ret < 0)
+                       goto free_cf;
+               ret = lls(lls_merge(*lprp, cf_lpr, cmd, &merged_lpr, &errctx));
+               lls_free_parse_result(cf_lpr, cmd);
+               if (ret < 0)
+                       goto free_cf;
+               lls_free_parse_result(*lprp, cmd);
+               *lprp = merged_lpr;
+               loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+       }
+       ret = 1;
+free_cf:
+       free(cf);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       return ret;
+}
+
+/**
+ * The main function of para_mixer.
+ *
+ * The executable is linked with the alsa or the oss mixer API, or both. It has
+ * a custom log function which prefixes log messages with the current date.
+ *
+ * \param argc Argument counter.
+ * \param argv Argument vector.
+ *
+ * \return EXIT_SUCCESS or EXIT_FAILURE.
+ */
+int main(int argc, char *argv[])
+{
+       const struct lls_command *cmd = CMD_PTR(PARA_MIXER);
+       int ret;
+       char *errctx;
+       const char *subcmd;
+       const struct mixer *m;
+       struct mixer_handle *h;
+       unsigned n;
+
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       loglevel = OPT_UINT32_VAL(PARA_MIXER, LOGLEVEL);
+       version_handle_flag("mixer", OPT_GIVEN(PARA_MIXER, VERSION));
+       handle_help_flags();
+
+       n = lls_num_inputs(lpr);
+       if (n == 0) {
+               show_subcommands();
+               ret = 0;
+               goto free_lpr;
+       }
+       ret = parse_and_merge_config_file(cmd);
+       if (ret < 0)
+               goto free_lpr;
+       subcmd = lls_input(0, lpr);
+       ret = lls(lls_lookup_subcmd(subcmd, mixer_suite, &errctx));
+       if (ret < 0)
+               goto fail;
+       cmd = lls_cmd(ret, mixer_suite);
+       ret = lls(lls_parse(n, argv + argc - n, cmd, &sub_lpr, &errctx));
+       if (ret < 0)
+               goto free_lpr;
+       ret = parse_and_merge_config_file(cmd);
+       if (ret < 0)
+               goto free_lpr;
+       m = get_mixer_or_die();
+       ret = m->open(OPT_STRING_VAL(PARA_MIXER, MIXER_DEVICE), &h);
+       if (ret < 0)
+               goto free_sub_lpr;
+       ret = set_channel(m, h, OPT_STRING_VAL(PARA_MIXER, MIXER_CHANNEL));
+       if (ret == -E_BAD_CHANNEL) {
+               char *channels = m->get_channels(h);
+               printf("Available channels: %s\n", channels);
+               free(channels);
+       }
+       if (ret < 0)
+               goto close_mixer;
+       ret = (*(mixer_subcommand_handler_t *)(lls_user_data(cmd)))(m ,h);
+close_mixer:
+       m->close(&h);
+free_sub_lpr:
+       lls_free_parse_result(sub_lpr, cmd);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR(PARA_MIXER));
+       if (ret >= 0)
+               return EXIT_SUCCESS;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       return EXIT_FAILURE;
+}
diff --git a/mm.c b/mm.c
index 92856ec3ef903eb905b0379c6eaff605bf6f8d06..47e88864c8e4b7cf514341d60eb844bd844b392e 100644 (file)
--- a/mm.c
+++ b/mm.c
@@ -9,6 +9,7 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
diff --git a/mood.c b/mood.c
index 79f47e5a3d20801832d5ad396bbd5b3b98aeed08..acc5583ffffc00bbaa743d07e30a7b1d458cb451 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -8,6 +8,7 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
index 5f6935d1a5be70c7fa89dd0368bca48e34664422..b053aaa8f3fd33171aca682fe53dd85906bcca11 100644 (file)
@@ -8,12 +8,12 @@
 
 #include <mad.h>
 #include <regex.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "mp3dec_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -171,25 +171,15 @@ err:
 static void mp3dec_open(struct filter_node *fn)
 {
        struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
-       struct mp3dec_filter_args_info *mp3_conf = fn->conf;
 
        fn->private_data = pmd;
        mad_stream_init(&pmd->stream);
        mad_frame_init(&pmd->frame);
        mad_synth_init(&pmd->synth);
-       if (mp3_conf->ignore_crc_given)
+       if (FILTER_CMD_OPT_GIVEN(MP3DEC, IGNORE_CRC, fn->lpr))
                mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC);
 }
 
-static int mp3dec_parse_config(int argc, char **argv, void **config)
-{
-       struct mp3dec_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       mp3dec_filter_cmdline_parser(argc, argv, conf);
-       *config = conf;
-       return 1;
-}
-
 static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
 {
        struct filter_node *fn = btr_context(btrn);
@@ -198,28 +188,10 @@ static int mp3dec_execute(struct btr_node *btrn, const char *cmd, char **result)
        return decoder_execute(cmd, pmd->sample_rate, pmd->channels, result);
 }
 
-static void mp3dec_free_config(void *conf)
-{
-       mp3dec_filter_cmdline_parser_free(conf);
-}
-/**
- * The init function of the mp3dec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void mp3dec_filter_init(struct filter *f)
-{
-       struct mp3dec_filter_args_info dummy;
-
-       mp3dec_filter_cmdline_parser_init(&dummy);
-       f->open = mp3dec_open;
-       f->close = mp3dec_close;
-       f->parse_config = mp3dec_parse_config;
-       f->free_config = mp3dec_free_config;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = mp3dec_post_select;
-       f->execute = mp3dec_execute;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(mp3dec_filter);
-}
+const struct filter lsg_filter_cmd_com_mp3dec_user_data = {
+       .open = mp3dec_open,
+       .close = mp3dec_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = mp3dec_post_select,
+       .execute = mp3dec_execute,
+};
index 04be7020e57f29351b73501203db6b1bfa1dbb39..9fa80c5588fc8c8d08653a91a291b9f95ba8e183 100644 (file)
@@ -12,7 +12,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -264,16 +263,10 @@ out:
        return ret;
 }
 
-/**
- * The init function of the ogg vorbis decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void oggdec_filter_init(struct filter *f)
-{
-       f->open = ogg_open;
-       f->close = ogg_close;
-       f->pre_select = ogg_pre_select;
-       f->post_select = ogg_post_select;
-       f->execute = oggdec_execute;
-}
+const struct filter lsg_filter_cmd_com_oggdec_user_data = {
+       .open = ogg_open,
+       .close = ogg_close,
+       .pre_select = ogg_pre_select,
+       .post_select = ogg_post_select,
+       .execute = oggdec_execute
+};
index 282229855d0d97519f886483c249d8e3f4b00b4f..6f9d526c7d31f243b66679eb6555abdb0f2360d9 100644 (file)
@@ -50,7 +50,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -284,18 +283,10 @@ static void opusdec_pre_select(struct sched *s, void *context)
                return sched_min_delay(s);
 }
 
-/**
- * The init function of the opusdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void opusdec_filter_init(struct filter *f)
-{
-       f->open = opusdec_open;
-       f->close = opusdec_close;
-       f->pre_select = opusdec_pre_select;
-       f->post_select = opusdec_post_select;
-       f->execute = opusdec_execute;
-}
+const struct filter lsg_filter_cmd_com_opusdec_user_data = {
+       .open = opusdec_open,
+       .close = opusdec_close,
+       .pre_select = opusdec_pre_select,
+       .post_select = opusdec_post_select,
+       .execute = opusdec_execute,
+};
index 8e87452b816e68f03f0931e79efe007d7491b6c8..7fbdba5b770923988e646b96e74582f8ff4e8a1f 100644 (file)
--- a/oss_mix.c
+++ b/oss_mix.c
@@ -135,19 +135,13 @@ static void oss_mix_close(struct mixer_handle **handle)
        *handle = NULL;
 }
 
-/**
- * The init function of the OSS mixer.
- *
- * \param self The structure to initialize.
- *
- * \sa struct \ref mixer, \ref alsa_mix_init().
- */
-void oss_mix_init(struct mixer *self)
-{
-       self->open = oss_mix_open;
-       self->get_channels = oss_mix_get_channels;
-       self->set_channel = oss_mix_set_channel;
-       self->get = oss_mix_get;
-       self->set = oss_mix_set;
-       self->close = oss_mix_close;
-}
+/** The mixer operations for the OSS mixer. */
+const struct mixer oss_mixer = {
+       .name = "oss",
+       .open = oss_mix_open,
+       .get_channels = oss_mix_get_channels,
+       .set_channel = oss_mix_set_channel,
+       .close = oss_mix_close,
+       .get = oss_mix_get,
+       .set = oss_mix_set
+};
index 20186667e2f383880538275d61d1607203ed2b8a..6f646c904016f92b13ea1b04f4a8cbb9f31243b8 100644 (file)
@@ -9,17 +9,16 @@
 #include <regex.h>
 #include <sys/ioctl.h>
 #include <sys/soundcard.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 "oss_write.cmdline.h"
 #include "error.h"
 
 /** Data specific to the oss writer. */
@@ -106,11 +105,11 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate,
 {
        int ret, format;
        unsigned ch, rate;
-       struct oss_write_args_info *conf = wn->conf;
        struct private_oss_write_data *powd = para_calloc(sizeof(*powd));
+       const char *dev = WRITE_CMD_OPT_STRING_VAL(OSS, DEVICE, wn->lpr);
 
-       PARA_INFO_LOG("opening %s\n", conf->device_arg);
-       ret = para_open(conf->device_arg, O_WRONLY, 0);
+       PARA_INFO_LOG("opening %s\n", dev);
+       ret = para_open(dev, O_WRONLY, 0);
        if (ret < 0)
                goto err_free;
        powd->fd = ret;
@@ -175,8 +174,7 @@ err:
        close(powd->fd);
 err_free:
        free(powd);
-       PARA_ERROR_LOG("failed to init %s: %s\n", conf->device_arg,
-               para_strerror(-ret));
+       PARA_ERROR_LOG("failed to init %s: %s\n", dev, para_strerror(-ret));
        return ret;
 }
 
@@ -240,37 +238,8 @@ out:
        return ret;
 }
 
-__malloc static void *oss_parse_config_or_die(int argc, char **argv)
-{
-       struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
-
-       /* exits on errors */
-       oss_write_cmdline_parser(argc, argv, conf);
-       return conf;
-}
-
-static void oss_free_config(void *conf)
-{
-       oss_write_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the oss writer.
- *
- * \param w Pointer to the writer to initialize.
- *
- * \sa struct writer.
- */
-void oss_write_init(struct writer *w)
-{
-       struct oss_write_args_info dummy;
-
-       oss_write_cmdline_parser_init(&dummy);
-       w->close = oss_close;
-       w->pre_select = oss_pre_select;
-       w->post_select = oss_post_select;
-       w->parse_config_or_die = oss_parse_config_or_die;
-       w->free_config = oss_free_config;
-       w->help = (struct ggo_help)DEFINE_GGO_HELP(oss_write);
-       oss_write_cmdline_parser_free(&dummy);
-}
+const struct writer lsg_write_cmd_com_oss_user_data = {
+       .pre_select = oss_pre_select,
+       .post_select = oss_post_select,
+       .close = oss_close,
+};
diff --git a/osx_write.c b/osx_write.c
new file mode 100644 (file)
index 0000000..0517892
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file osx_write.c paraslash's output plugin for MacOs */
+
+/*
+ * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze
+ * <skortze@sourceforge.net>
+ */
+
+#include <regex.h>
+#include <sys/types.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 "buffer_tree.h"
+#include "write.h"
+#include "ipc.h"
+#include "error.h"
+
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+/** Data specific to the osx writer. */
+struct private_osx_write_data {
+       /** The main CoreAudio handle. */
+       AudioUnit audio_unit;
+       /** True if we wrote some audio data. */
+       bool playing;
+       /** Sample rate of the current audio stream. */
+       unsigned sample_rate;
+       /** Sample format of the current audio stream */
+       unsigned sample_format;
+       /** Number of channels of the current audio stream. */
+       unsigned channels;
+       /**
+        * Serializes access to buffer tree nodes between the writer and
+        * the callback which runs in a different thread.
+        */
+       int mutex;
+       /**
+        * The btr node of the callback.
+        *
+        * Although access to the btr node is serialized between the writer and
+        * the callback via the above mutex, this does not stop other buffer
+        * tree nodes, for example the decoder, to race against the osx
+        * callback.
+        *
+        * However, since all operations on buffer tree nodes are local in the
+        * sense that they only affect one level in the buffer tree (i.e.
+        * parent or child nodes, but not the grandparent or the
+        * grandchildren), we may work around this problem by using another
+        * buffer tree node for the callback.
+        *
+        * The writer grabs the mutex in its post_select method and pushes down
+        * the buffers to the callback node.
+        */
+       struct btr_node *callback_btrn;
+};
+
+/* This function writes the address and the number of bytes to one end of the socket.
+ * The post_select() function then fills the buffer and notifies the callback also
+ * through the socket.
+ */
+static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags *af,
+               __a_unused const AudioTimeStamp *ts, __a_unused  UInt32 bus_number,
+               __a_unused UInt32 num_frames, AudioBufferList *abl)
+{
+       int i;
+       struct writer_node *wn = cb_arg;
+       struct private_osx_write_data *powd;
+       size_t samples_have, samples_want = 0;
+
+       powd = wn->private_data;
+       mutex_lock(powd->mutex);
+       powd = wn->private_data;
+       if (!powd || !wn->btrn)
+               goto out;
+       /*
+        * We fill with zeros if no data was yet written and we do not have
+        * enough to fill all buffers.
+        */
+       if (!powd->playing) {
+               size_t want = 0, have =
+                       btr_get_input_queue_size(powd->callback_btrn);
+               for (i = 0; i < abl->mNumberBuffers; i++)
+                       want += abl->mBuffers[i].mDataByteSize;
+               if (have < want) {
+                       PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
+                               have, want);
+                       for (i = 0; i < abl->mNumberBuffers; i++)
+                               memset(abl->mBuffers[i].mData, 0,
+                                       abl->mBuffers[i].mDataByteSize);
+                       goto out;
+               }
+               powd->playing = true;
+       }
+
+       for (i = 0; i < abl->mNumberBuffers; i++) {
+               /* what we have to fill */
+               void *dest = abl->mBuffers[i].mData;
+               size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes;
+
+               samples_want = sz / wn->min_iqs;
+               while (samples_want > 0) {
+                       char *buf;
+                       btr_merge(powd->callback_btrn, wn->min_iqs);
+                       samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs;
+                       //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want,
+                       //      dest, samples_have);
+                       samples = PARA_MIN(samples_have, samples_want);
+                       if (samples == 0)
+                               break;
+                       bytes = samples * wn->min_iqs;
+                       memcpy(dest, buf, bytes);
+                       btr_consume(powd->callback_btrn, bytes);
+                       samples_want -= samples;
+                       dest += bytes;
+               }
+               if (samples_want == 0)
+                       continue;
+               if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0)
+                       PARA_INFO_LOG("zero-padding (%zu samples)\n",
+                               samples_want);
+               memset(dest, 0, samples_want * wn->min_iqs);
+               break;
+       }
+out:
+       mutex_unlock(powd->mutex);
+       return noErr;
+}
+
+static int core_audio_init(struct writer_node *wn)
+{
+       struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
+       Component comp;
+       int ret;
+       int32_t val;
+       AURenderCallbackStruct input_callback;
+       ComponentDescription desc = {
+               .componentType = kAudioUnitType_Output,
+               .componentSubType = kAudioUnitSubType_DefaultOutput,
+               .componentManufacturer = kAudioUnitManufacturer_Apple,
+       };
+       AudioStreamBasicDescription format = {
+               .mFormatID = kAudioFormatLinearPCM,
+               .mFramesPerPacket = 1,
+       };
+       struct btr_node *btrn = wn->btrn;
+       struct btr_node_description bnd;
+
+       PARA_INFO_LOG("wn: %p\n", wn);
+       ret = -E_DEFAULT_COMP;
+       comp = FindNextComponent(NULL, &desc);
+       if (!comp)
+               goto e0;
+       ret = -E_OPEN_COMP;
+       if (OpenAComponent(comp, &powd->audio_unit))
+               goto e0;
+       ret = -E_UNIT_INIT;
+       if (AudioUnitInitialize(powd->audio_unit))
+               goto e1;
+       get_btr_sample_rate(btrn, &val);
+       powd->sample_rate = val;
+       get_btr_channels(btrn, &val);
+       powd->channels = val;
+       get_btr_sample_format(btrn, &val);
+       powd->sample_format = val;
+       /*
+        * Choose PCM format. We tell the Output Unit what format we're going
+        * to supply data to it. This is necessary if you're providing data
+        * through an input callback AND you want the DefaultOutputUnit to do
+        * any format conversions necessary from your format to the device's
+        * format.
+        */
+
+       format.mSampleRate = powd->sample_rate;
+       format.mChannelsPerFrame = powd->channels;
+
+       switch (powd->sample_format) {
+       case SF_S8:
+       case SF_U8:
+               wn->min_iqs = powd->channels;
+               format.mBitsPerChannel = 8;
+               format.mBytesPerPacket = powd->channels;
+               format.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
+               break;
+       default:
+               wn->min_iqs = powd->channels * 2;
+               format.mBytesPerPacket = powd->channels * 2;
+               format.mBitsPerChannel = 16;
+               format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+       }
+       format.mBytesPerFrame = format.mBytesPerPacket;
+
+       if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE)
+               format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+
+       input_callback = (AURenderCallbackStruct){osx_callback, wn};
+       ret = -E_STREAM_FORMAT;
+       if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat,
+                       kAudioUnitScope_Input, 0, &format, sizeof(format)))
+               goto e2;
+       ret = -E_ADD_CALLBACK;
+       if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
+                       kAudioUnitScope_Input, 0, &input_callback,
+                       sizeof(input_callback)) < 0)
+               goto e2;
+
+       ret = mutex_new();
+       if (ret < 0)
+               goto e2;
+       powd->mutex = ret;
+       /* set up callback btr node */
+       bnd.name = "cb_node";
+       bnd.parent = btrn;
+       bnd.child = NULL;
+       bnd.handler = NULL;
+       bnd.context = powd;
+       powd->callback_btrn = btr_new_node(&bnd);
+       wn->private_data = powd;
+       return 1;
+e2:
+       AudioUnitUninitialize(powd->audio_unit);
+e1:
+       CloseComponent(powd->audio_unit);
+e0:
+       free(powd);
+       wn->private_data = NULL;
+       return ret;
+}
+
+static void osx_write_close(struct writer_node *wn)
+{
+       struct private_osx_write_data *powd = wn->private_data;
+
+       if (!powd)
+               return;
+       PARA_INFO_LOG("closing writer node %p\n", wn);
+       mutex_destroy(powd->mutex);
+       free(powd);
+       wn->private_data = NULL;
+}
+
+/* must be called with the mutex held */
+static inline bool need_drain_delay(struct private_osx_write_data *powd)
+{
+       if (!powd->playing)
+               return false;
+       return btr_get_input_queue_size(powd->callback_btrn) != 0;
+}
+
+static void osx_write_pre_select(struct sched *s, void *context)
+{
+       struct writer_node *wn = context;
+       struct private_osx_write_data *powd = wn->private_data;
+       int ret;
+       bool drain_delay_nec = false;
+
+       if (!powd) {
+               ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret != 0)
+                       sched_min_delay(s);
+               return;
+       }
+
+       mutex_lock(powd->mutex);
+       ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL);
+       if (ret < 0)
+               drain_delay_nec = need_drain_delay(powd);
+       mutex_unlock(powd->mutex);
+
+       if (drain_delay_nec)
+               return sched_request_timeout_ms(50, s);
+       if (ret != 0)
+               return sched_min_delay(s);
+       sched_request_timeout_ms(50, s);
+}
+
+static int osx_write_post_select(__a_unused struct sched *s, void *context)
+{
+       struct writer_node *wn = context;
+       struct private_osx_write_data *powd = wn->private_data;
+       struct btr_node *btrn = wn->btrn;
+       int ret;
+
+       ret = task_get_notification(wn->task);
+       if (ret < 0)
+               goto fail;
+       if (!powd) {
+               ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret == 0)
+                       return 0;
+               if (ret < 0)
+                       goto fail;
+               ret = core_audio_init(wn);
+               if (ret < 0)
+                       goto fail;
+               powd = wn->private_data;
+               ret = -E_UNIT_START;
+               if (AudioOutputUnitStart(powd->audio_unit) != noErr) {
+                       AudioUnitUninitialize(powd->audio_unit);
+                       CloseComponent(powd->audio_unit);
+                       btr_remove_node(&powd->callback_btrn);
+                       goto fail;
+               }
+       }
+       mutex_lock(powd->mutex);
+       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL);
+       if (ret > 0)
+               btr_pushdown(btrn);
+       if (ret < 0 && need_drain_delay(powd))
+               ret = 0;
+       mutex_unlock(powd->mutex);
+       if (ret >= 0)
+               return 0;
+fail:
+       assert(ret < 0);
+       if (powd && powd->callback_btrn) {
+               AudioOutputUnitStop(powd->audio_unit);
+               AudioUnitUninitialize(powd->audio_unit);
+               CloseComponent(powd->audio_unit);
+               btr_remove_node(&powd->callback_btrn);
+       }
+       btr_remove_node(&wn->btrn);
+       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       return ret;
+}
+
+struct writer lsg_write_cmd_com_osx_user_data = {
+       .close = osx_write_close,
+       .pre_select = osx_write_pre_select,
+       .post_select = osx_write_post_select,
+};
diff --git a/para.h b/para.h
index 12d236391dcfb13e18d50450164517346e77398e..3cd1b1621509330f0df7d6d6f7f1b02095a09153 100644 (file)
--- a/para.h
+++ b/para.h
@@ -223,24 +223,9 @@ enum sample_format {SAMPLE_FORMATS};
 #define SAMPLE_FORMAT(a, b) b
 /** \endcond sample_format */
 
-/** Debug loglevel, gets really noisy. */
-#define LL_DEBUG 0
-/** Still noisy, but won't fill your disk. */
-#define LL_INFO  1
-/** Normal, but significant event. */
-#define LL_NOTICE 2
-/** Unexpected event that can be handled. */
-#define LL_WARNING 3
-/** Unhandled error condition. */
-#define LL_ERROR 4
-/** System might be unreliable. */
-#define LL_CRIT 5
-/** Last message before exit. */
-#define LL_EMERG 6
-/** Number of all loglevels. */
-#define NUM_LOGLEVELS 7
-
-/** \cond log */
+/** Debug, Info, etc. */
+enum loglevels {LOGLEVELS, NUM_LOGLEVELS};
+
 #define PARA_DEBUG_LOG(f,...) para_log(LL_DEBUG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_INFO_LOG(f,...) para_log(LL_INFO, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_NOTICE_LOG(f,...) para_log(LL_NOTICE, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
@@ -248,4 +233,3 @@ enum sample_format {SAMPLE_FORMATS};
 #define PARA_ERROR_LOG(f,...) para_log(LL_ERROR, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_CRIT_LOG(f,...) para_log(LL_CRIT, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
 #define PARA_EMERG_LOG(f,...) para_log(LL_EMERG, "%s: " f, __FUNCTION__, ## __VA_ARGS__)
-/** \endcond log */
diff --git a/play.c b/play.c
index fac551aa161d14f258e1495a4530462ca1146357..10451039726037cf0d0628d8e5d5ddb1acb75b45 100644 (file)
--- a/play.c
+++ b/play.c
@@ -7,14 +7,17 @@
 /** \file play.c Paraslash's standalone player. */
 
 #include <regex.h>
-#include <fnmatch.h>
 #include <signal.h>
+#include <inttypes.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
+#include "play_cmd.lsg.h"
+#include "write_cmd.lsg.h"
+#include "play.lsg.h"
 #include "para.h"
 #include "list.h"
-#include "play.cmdline.h"
 #include "error.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "version.h"
 #include "string.h"
@@ -23,7 +26,6 @@
 #include "afh.h"
 #include "recv.h"
 #include "write.h"
-#include "write_common.h"
 #include "fd.h"
 
 /**
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
+static struct lls_parse_result *play_lpr;
+
+#define CMD_PTR (lls_cmd(0, play_suite))
+#define OPT_RESULT(_name) \
+       (lls_opt_result(LSG_PLAY_PARA_PLAY_OPT_ ## _name, play_lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
 /**
  * Describes a request to change the state of para_play.
  *
@@ -98,13 +109,15 @@ struct play_task {
        char *afhi_txt;
 };
 
-/* Activate the afh receiver. */
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-/** Initialization code for a receiver struct. */
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-/** This expands to the array of all receivers. */
-DEFINE_RECEIVER_ARRAY;
+typedef int (*play_cmd_handler_t)(struct play_task *pt,
+               struct lls_parse_result *lpr);
+struct play_command_info {
+       play_cmd_handler_t handler;
+};
+#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
+       const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 static int loglevel = LL_WARNING;
 
@@ -113,86 +126,105 @@ INIT_STDERR_LOGGING(loglevel);
 
 char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
 
-/** Iterate over all files in the playlist. */
-#define FOR_EACH_PLAYLIST_FILE(i) for (i = 0; i < conf.inputs_num; i++)
-static struct play_args_info conf;
-
 static struct sched sched = {.max_fileno = 0};
 static struct play_task play_task;
-static struct receiver *afh_recv;
 
-static void check_afh_receiver_or_die(void)
-{
-       int i;
+#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
+#define AFH_RECV ((struct receiver *)lls_user_data(AFH_RECV_CMD))
 
-       FOR_EACH_RECEIVER(i) {
-               struct receiver *r = receivers + i;
-               if (strcmp(r->name, "afh"))
-                       continue;
-               afh_recv = r;
-               return;
-       }
-       PARA_EMERG_LOG("fatal: afh receiver not found\n");
-       exit(EXIT_FAILURE);
+static unsigned *shuffle_map;
+
+static const char *get_playlist_file(unsigned idx)
+{
+       return lls_input(shuffle_map[idx], play_lpr);
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help help = DEFINE_GGO_HELP(play);
-       unsigned flags = conf.detailed_help_given?
-               GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+       char *help;
 
-       ggo_print_help(&help, flags);
-       printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
-       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);
 }
 
 static void parse_config_or_die(int argc, char *argv[])
 {
-       int i, ret;
-       char *config_file;
-       struct play_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
+       const struct lls_command *cmd = CMD_PTR;
+       int i, ret, cf_argc;
+       char *cf, *errctx, **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
+       unsigned num_kmas;
+       void *map;
+       size_t sz;
 
-       play_cmdline_parser_ext(argc, argv, &conf, &params);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("play", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       if (conf.config_file_given)
-               config_file = para_strdup(conf.config_file_arg);
+       ret = lls(lls_parse(argc, argv, cmd, &play_lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+       version_handle_flag("play", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else {
                char *home = para_homedir();
-               config_file = make_message("%s/.paraslash/play.conf", home);
+               cf = make_message("%s/.paraslash/play.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);
-               goto err;
-       }
-       if (ret) {
-               params.initialize = 0;
-               params.check_required = 1;
-               play_cmdline_parser_config_file(config_file, &conf, &params);
-               loglevel = get_loglevel_by_name(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;
+               ret = 0;
+               goto free_cf;
        }
-       for (i = 0; i < conf.key_map_given; i++) {
-               char *kma = conf.key_map_arg[i];
+       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, &cf_lpr, &errctx));
+       lls_free_argv(cf_argv);
+       if (ret < 0)
+               goto free_cf;
+       ret = lls(lls_merge(play_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
+       lls_free_parse_result(cf_lpr, cmd);
+       if (ret < 0)
+               goto free_cf;
+       lls_free_parse_result(play_lpr, cmd);
+       play_lpr = merged_lpr;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL);
+
+       ret = lls(lls_check_arg_count(play_lpr, 1, INT_MAX, &errctx));
+       if (ret < 0)
+               goto free_cf;
+       num_kmas = OPT_GIVEN(KEY_MAP);
+       for (i = 0; i < num_kmas; i++) {
+               const char *kma = lls_string_val(i, OPT_RESULT(KEY_MAP));
                if (*kma && strchr(kma + 1, ':'))
                        continue;
                PARA_EMERG_LOG("invalid key map arg: %s\n", kma);
-               goto err;
+               goto free_cf;
        }
-       free(config_file);
-       return;
-err:
-       free(config_file);
+       ret = 1;
+free_cf:
+       free(cf);
+       if (ret >= 0)
+               return;
+       lls_free_parse_result(play_lpr, cmd);
+fail:
+       if (errctx)
+               PARA_EMERG_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
        exit(EXIT_FAILURE);
 }
 
@@ -230,12 +262,13 @@ static long unsigned get_play_time(struct play_task *pt)
        return result;
 }
 
+
 static void wipe_receiver_node(struct play_task *pt)
 {
        PARA_NOTICE_LOG("cleaning up receiver node\n");
        btr_remove_node(&pt->rn.btrn);
-       afh_recv->close(&pt->rn);
-       afh_recv->free_config(pt->rn.conf);
+       AFH_RECV->close(&pt->rn);
+       lls_free_parse_result(pt->rn.lpr, AFH_RECV_CMD);
        memset(&pt->rn, 0, sizeof(struct receiver_node));
 }
 
@@ -261,8 +294,8 @@ static int get_playback_error(struct play_task *pt)
 
 static int eof_cleanup(struct play_task *pt)
 {
-       struct writer *w = writers + DEFAULT_WRITER;
-       const struct filter *decoder = filter_get(pt->fn.filter_num);
+       const struct filter *decoder;
+       const struct writer *w = writer_get(-1); /* default writer */
        int ret;
 
        ret = get_playback_error(pt);
@@ -272,9 +305,10 @@ static int eof_cleanup(struct play_task *pt)
        task_reap(&pt->wn.task);
        w->close(&pt->wn);
        btr_remove_node(&pt->wn.btrn);
-       w->free_config(pt->wn.conf);
+       lls_free_parse_result(pt->wn.lpr, WRITE_CMD(pt->wn.wid));
        memset(&pt->wn, 0, sizeof(struct writer_node));
 
+       decoder = filter_get(pt->fn.filter_num);
        task_reap(&pt->fn.task);
        if (decoder->close)
                decoder->close(&pt->fn);
@@ -299,34 +333,42 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b)
        return para_random(100) - 50;
 }
 
-static void shuffle(char **base, size_t num)
+static void init_shuffle_map(void)
 {
+       unsigned n, num_inputs = lls_num_inputs(play_lpr);
+       shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+       for (n = 0; n < num_inputs; n++)
+               shuffle_map[n] = n;
+       if (!OPT_GIVEN(RANDOMIZE))
+               return;
        srandom(time(NULL));
-       qsort(base, num, sizeof(char *), shuffle_compare);
+       qsort(shuffle_map, num_inputs, sizeof(unsigned), shuffle_compare);
 }
 
 static struct btr_node *new_recv_btrn(struct receiver_node *rn)
 {
        return btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = afh_recv->name, .context = rn,
-                       .handler = afh_recv->execute));
+               EMBRACE(.name = lls_command_name(AFH_RECV_CMD), .context = rn,
+                       .handler = AFH_RECV->execute));
 }
 
 static int open_new_file(struct play_task *pt)
 {
        int ret;
-       char *tmp, *path = conf.inputs[pt->next_file], *afh_recv_conf[] =
-               {"play", "-f", path, "-b", "0", NULL};
+       const char *path = get_playlist_file(pt->next_file);
+       char *tmp = para_strdup(path), *errctx;
+       char *argv[] = {"play", "-f", tmp, "-b", "0", NULL};
 
        PARA_NOTICE_LOG("next file: %s\n", path);
        wipe_receiver_node(pt);
        pt->start_chunk = 0;
        pt->rn.btrn = new_recv_btrn(&pt->rn);
-       pt->rn.conf = afh_recv->parse_config(ARRAY_SIZE(afh_recv_conf) - 1,
-               afh_recv_conf);
-       assert(pt->rn.conf);
-       pt->rn.receiver = afh_recv;
-       ret = afh_recv->open(&pt->rn);
+       ret = lls(lls_parse(ARRAY_SIZE(argv) - 1, argv, AFH_RECV_CMD,
+               &pt->rn.lpr, &errctx));
+       free(tmp);
+       assert(ret >= 0);
+       pt->rn.receiver = AFH_RECV;
+       ret = AFH_RECV->open(&pt->rn);
        if (ret < 0) {
                PARA_ERROR_LOG("could not open %s\n", path);
                goto fail;
@@ -368,6 +410,7 @@ static int load_file(struct play_task *pt)
        char *tmp, buf[20];
        int ret;
        const struct filter *decoder;
+       static struct lls_parse_result *filter_lpr, *writer_lpr;
 
        btr_remove_node(&pt->rn.btrn);
        if (!pt->rn.receiver || pt->next_file != pt->current_file) {
@@ -387,30 +430,31 @@ static int load_file(struct play_task *pt)
        /* set up decoding filter */
        af = audio_format_name(pt->audio_format_num);
        tmp = make_message("%sdec", af);
-       PARA_INFO_LOG("decoder: %s\n", tmp);
-       ret = check_filter_arg(tmp, &pt->fn.conf);
+       ret = filter_setup(tmp, &pt->fn.conf, &filter_lpr);
        freep(&tmp);
        if (ret < 0)
                goto fail;
        pt->fn.filter_num = ret;
+       pt->fn.lpr = filter_lpr;
        decoder = filter_get(ret);
        pt->fn.btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
-                       .handler = decoder->execute, .context = &pt->fn));
+               EMBRACE(.name = filter_name(pt->fn.filter_num),
+                       .parent = pt->rn.btrn, .handler = decoder->execute,
+                       .context = &pt->fn));
        if (decoder->open)
                decoder->open(&pt->fn);
        PARA_INFO_LOG("buffer tree:\n");
        btr_log_tree(pt->rn.btrn, LL_INFO);
 
        /* setup default writer */
-       pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num);
-
+       pt->wn.wid = check_writer_arg_or_die(NULL, &writer_lpr);
+       pt->wn.lpr = writer_lpr;
        /* success, register tasks */
        pt->rn.task = task_register(
                &(struct task_info) {
-                       .name = afh_recv->name,
-                       .pre_select = afh_recv->pre_select,
-                       .post_select = afh_recv->post_select,
+                       .name = lls_command_name(AFH_RECV_CMD),
+                       .pre_select = AFH_RECV->pre_select,
+                       .post_select = AFH_RECV->post_select,
                        .context = &pt->rn
                }, &sched);
        sprintf(buf, "%s decoder", af);
@@ -431,9 +475,10 @@ fail:
 static int next_valid_file(struct play_task *pt)
 {
        int i, j = pt->current_file;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       FOR_EACH_PLAYLIST_FILE(i) {
-               j = (j + 1) % conf.inputs_num;
+       for (i = 0; i < num_inputs; i++) {
+               j = (j + 1) % num_inputs;
                if (!pt->invalid[j])
                        return j;
        }
@@ -478,11 +523,12 @@ static void kill_stream(struct play_task *pt)
 static int previous_valid_file(struct play_task *pt)
 {
        int i, j = pt->current_file;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       FOR_EACH_PLAYLIST_FILE(i) {
+       for (i = 0; i < num_inputs; i++) {
                j--;
                if (j < 0)
-                       j = conf.inputs_num - 1;
+                       j = num_inputs - 1;
                if (!pt->invalid[j])
                        return j;
        }
@@ -533,7 +579,7 @@ static const char *default_keyseqs[] = {INTERNAL_KEYMAP_ENTRIES};
 static const char *default_commands[] = {INTERNAL_KEYMAP_ENTRIES};
 #undef KEYMAP_ENTRY
 #define NUM_INTERNALLY_MAPPED_KEYS ARRAY_SIZE(default_commands)
-#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + conf.key_map_given)
+#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + OPT_GIVEN(KEY_MAP))
 #define FOR_EACH_MAPPED_KEY(i) for (i = 0; i < NUM_MAPPED_KEYS; i++)
 
 static inline bool is_internal_key(int key)
@@ -564,9 +610,9 @@ static inline int get_key_map_idx(int key)
                get_internal_key_map_idx(key) : get_user_key_map_idx(key);
 }
 
-static inline char *get_user_key_map_arg(int key)
+static inline const char *get_user_key_map_arg(int key)
 {
-       return conf.key_map_arg[get_user_key_map_idx(key)];
+       return lls_string_val(get_user_key_map_idx(key), OPT_RESULT(KEY_MAP));
 }
 
 static inline char *get_internal_key_map_seq(int key)
@@ -653,24 +699,6 @@ static char **get_mapped_keyseqs(void)
        return result;
 }
 
-#include "play.command_list.h"
-
-typedef int play_command_handler_t(struct play_task *, int, char**);
-static play_command_handler_t PLAY_COMMAND_HANDLERS;
-
-/* defines one command of para_play */
-struct pp_command {
-       const char *name;
-       play_command_handler_t *handler;
-       const char *description;
-       const char *usage;
-       const char *help;
-};
-
-static struct pp_command pp_cmds[] = {DEFINE_PLAY_CMD_ARRAY};
-#define FOR_EACH_COMMAND(c) for (c = 0; pp_cmds[c].name; c++)
-
-#include "play.completion.h"
 static struct i9e_completer pp_completers[];
 
 I9E_DUMMY_COMPLETER(jmp);
@@ -693,7 +721,14 @@ static void help_completer(struct i9e_completion_info *ci,
        result->matches = i9e_complete_commands(ci->word, pp_completers);
 }
 
-static struct i9e_completer pp_completers[] = {PLAY_COMPLETERS {.name = NULL}};
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
+static struct i9e_completer pp_completers[] = {
+#define LSG_PLAY_CMD_CMD(_name) {.name = #_name, \
+       .completer = _name ## _completer}
+       LSG_PLAY_CMD_SUBCOMMANDS
+#undef LSG_PLAY_CMD_CMD
+       {.name = NULL}
+};
 
 static void attach_stdout(struct play_task *pt, const char *name)
 {
@@ -709,79 +744,82 @@ static void detach_stdout(struct play_task *pt)
        btr_remove_node(&pt->btrn);
 }
 
-static int com_quit(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_quit(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->rq = CRT_TERM_RQ;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(quit);
 
-static int com_help(struct play_task *pt, int argc, char **argv)
+static int com_help(struct play_task *pt, struct lls_parse_result *lpr)
 {
-       int i;
-       char *buf;
+       int i, ret;
+       char *buf, *errctx;
        size_t sz;
+       const struct lls_command *cmd;
 
-       if (argc > 2)
-               return -E_PLAY_SYNTAX;
-       if (argc < 2) {
-               if (pt->background)
-                       FOR_EACH_COMMAND(i) {
-                               sz = xasprintf(&buf, "%s\t%s\n", pp_cmds[i].name,
-                                       pp_cmds[i].description);
-                               btr_add_output(buf, sz, pt->btrn);
-                       }
-               else {
-                       FOR_EACH_MAPPED_KEY(i) {
-                               bool internal = is_internal_key(i);
-                               int idx = get_key_map_idx(i);
-                               char *seq = get_key_map_seq_safe(i);
-                               char *cmd = get_key_map_cmd(i);
-                               sz = xasprintf(&buf,
-                                       "%s key #%d: %s -> %s\n",
-                                       internal? "internal" : "user-defined",
-                                       idx, seq, cmd);
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       if (lls_num_inputs(lpr) == 0) {
+               if (pt->background) {
+                       for (i = 1; (cmd = lls_cmd(i, play_cmd_suite)); i++) {
+                               sz = xasprintf(&buf, "%s\t%s\n",
+                                       lls_command_name(cmd), lls_purpose(cmd));
                                btr_add_output(buf, sz, pt->btrn);
-                               free(seq);
-                               free(cmd);
                        }
+                       return 0;
+               }
+               FOR_EACH_MAPPED_KEY(i) {
+                       bool internal = is_internal_key(i);
+                       int idx = get_key_map_idx(i);
+                       char *seq = get_key_map_seq_safe(i);
+                       char *kmc = get_key_map_cmd(i);
+                       sz = xasprintf(&buf, "%s key #%d: %s -> %s\n",
+                               internal? "internal" : "user-defined",
+                               idx, seq, kmc);
+                       btr_add_output(buf, sz, pt->btrn);
+                       free(seq);
+                       free(kmc);
                }
                return 0;
        }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(pp_cmds[i].name, argv[1]))
-                       continue;
-               sz = xasprintf(&buf,
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\t%s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       pp_cmds[i].description,
-                       pp_cmds[i].usage,
-                       pp_cmds[i].help
-               );
-               btr_add_output(buf, sz, pt->btrn);
-               return 0;
+       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), play_cmd_suite,
+               &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
        }
-       return -E_BAD_PLAY_CMD;
+       cmd = lls_cmd(ret, play_cmd_suite);
+       buf = lls_long_help(cmd);
+       assert(buf);
+       btr_add_output(buf, strlen(buf), pt->btrn);
+       return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(help);
 
-static int com_info(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_info(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
        char *buf;
        size_t sz;
        static char dflt[] = "[no information available]";
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        sz = xasprintf(&buf, "playlist_pos: %u\npath: %s\n",
-               pt->current_file, conf.inputs[pt->current_file]);
+               pt->current_file, get_playlist_file(pt->current_file));
        btr_add_output(buf, sz, pt->btrn);
        buf = pt->afhi_txt? pt->afhi_txt : dflt;
        btr_add_output_dont_free(buf, strlen(buf), pt->btrn);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(info);
 
 static void list_file(struct play_task *pt, int num)
 {
@@ -789,19 +827,17 @@ static void list_file(struct play_task *pt, int num)
        size_t sz;
 
        sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
-               "*" : " ", num, conf.inputs[num]);
+               "*" : " ", num, get_playlist_file(num));
        btr_add_output(buf, sz, pt->btrn);
 }
 
-static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_tasks(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
        static char state;
        char *buf;
        size_t sz;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
-
        buf = get_task_list(&sched);
        btr_add_output(buf, strlen(buf), pt->btrn);
        state = get_playback_state(pt);
@@ -809,36 +845,35 @@ static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
        btr_add_output(buf, sz, pt->btrn);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(tasks);
 
-static int com_ls(struct play_task *pt, int argc, char **argv)
+static int com_ls(struct play_task *pt,
+       __a_unused struct lls_parse_result *lpr)
 {
-       int i, j, ret;
+       int i;
+       unsigned num_inputs = lls_num_inputs(play_lpr);
 
-       if (argc == 1) {
-               FOR_EACH_PLAYLIST_FILE(i)
-                       list_file(pt, i);
-               return 0;
-       }
-       for (j = 1; j < argc; j++) {
-               FOR_EACH_PLAYLIST_FILE(i) {
-                       ret = fnmatch(argv[j], conf.inputs[i], 0);
-                       if (ret == 0) /* match */
-                               list_file(pt, i);
-               }
-       }
+       for (i = 0; i < num_inputs; i++)
+               list_file(pt, i);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(ls);
 
-static int com_play(struct play_task *pt, int argc, char **argv)
+static int com_play(struct play_task *pt, struct lls_parse_result *lpr)
 {
        int32_t x;
        int ret;
-       char state;
+       char state, *errctx;
 
-       if (argc > 2)
-               return -E_PLAY_SYNTAX;
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
        state = get_playback_state(pt);
-       if (argc == 1) {
+       if (lls_num_inputs(lpr) == 0) {
                if (state == 'P')
                        return 0;
                pt->next_file = pt->current_file;
@@ -846,24 +881,24 @@ static int com_play(struct play_task *pt, int argc, char **argv)
                pt->playing = true;
                return 0;
        }
-       ret = para_atoi32(argv[1], &x);
+       ret = para_atoi32(lls_input(0, lpr), &x);
        if (ret < 0)
                return ret;
-       if (x < 0 || x >= conf.inputs_num)
+       if (x < 0 || x >= lls_num_inputs(play_lpr))
                return -ERRNO_TO_PARA_ERROR(EINVAL);
        kill_stream(pt);
        pt->next_file = x;
        pt->rq = CRT_FILE_CHANGE;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(play);
 
-static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_pause(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
        char state;
        long unsigned seconds, ss;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        state = get_playback_state(pt);
        pt->playing = false;
        if (state != 'P')
@@ -879,14 +914,13 @@ static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
        kill_stream(pt);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(pause);
 
-static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
-
+static int com_prev(struct play_task *pt,
+       __a_unused struct lls_parse_result *lpr)
 {
        int ret;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        ret = previous_valid_file(pt);
        if (ret < 0)
                return ret;
@@ -896,13 +930,13 @@ static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
        pt->start_chunk = 0;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(prev);
 
-static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_next(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
        int ret;
 
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        ret = next_valid_file(pt);
        if (ret < 0)
                return ret;
@@ -912,37 +946,44 @@ static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
        pt->start_chunk = 0;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(next);
 
-static int com_fg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_fg(struct play_task *pt,
+               __a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->background = false;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(fg);
 
-static int com_bg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_bg(struct play_task *pt,
+       __a_unused struct lls_parse_result *lpr)
 {
-       if (argc != 1)
-               return -E_PLAY_SYNTAX;
        pt->background = true;
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(bg);
 
-static int com_jmp(struct play_task *pt, int argc, char **argv)
+static int com_jmp(struct play_task *pt, struct lls_parse_result *lpr)
 {
        int32_t percent;
        int ret;
+       char *errctx;
 
-       if (argc != 2)
-               return -E_PLAY_SYNTAX;
-       ret = para_atoi32(argv[1], &percent);
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       ret = para_atoi32(lls_input(0, lpr), &percent);
        if (ret < 0)
                return ret;
        if (percent < 0 || percent > 100)
                return -ERRNO_TO_PARA_ERROR(EINVAL);
        if (percent == 100)
-               return com_next(pt, 1, (char *[]){"next", NULL});
+               return com_next(pt, NULL);
        if (pt->playing && !pt->fn.btrn)
                return 0;
        pt->start_chunk = percent * pt->num_chunks / 100;
@@ -952,15 +993,22 @@ static int com_jmp(struct play_task *pt, int argc, char **argv)
        kill_stream(pt);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(jmp);
 
-static int com_ff(struct play_task *pt, int argc, char **argv)
+static int com_ff(struct play_task *pt, struct lls_parse_result *lpr)
 {
        int32_t seconds;
+       char *errctx;
        int ret;
 
-       if (argc != 2)
-               return -E_PLAY_SYNTAX;
-       ret = para_atoi32(argv[1], &seconds);
+       ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               return ret;
+       }
+       ret = para_atoi32(lls_input(0, lpr), &seconds);
        if (ret < 0)
                return ret;
        if (pt->playing && !pt->fn.btrn)
@@ -977,34 +1025,38 @@ static int com_ff(struct play_task *pt, int argc, char **argv)
        kill_stream(pt);
        return 0;
 }
+EXPORT_PLAY_CMD_HANDLER(ff);
 
 static int run_command(char *line, struct play_task *pt)
 {
-       int i, ret, argc;
+       int ret, argc;
        char **argv = NULL;
+       char *errctx = NULL;
+       const struct play_command_info *pci;
+       struct lls_parse_result *lpr;
+       const struct lls_command *cmd;
 
        attach_stdout(pt, __FUNCTION__);
        ret = create_argv(line, " ", &argv);
-       if (ret < 0) {
-               PARA_ERROR_LOG("parse error: %s\n", para_strerror(-ret));
-               return 0;
-       }
+       if (ret < 0)
+               goto out;
        if (ret == 0)
                goto out;
        argc = ret;
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(pp_cmds[i].name, argv[0]))
-                       continue;
-               ret = pp_cmds[i].handler(pt, argc, argv);
-               if (ret < 0)
-                       PARA_WARNING_LOG("%s: %s\n", pt->background?
-                               "" : argv[0], para_strerror(-ret));
-               ret = 1;
+       ret = lls(lls_lookup_subcmd(argv[0], play_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       PARA_WARNING_LOG("invalid command: %s\n", argv[0]);
-       ret = 0;
+       cmd = lls_cmd(ret, play_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       pci = lls_user_data(cmd);
+       ret = pci->handler(pt, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
        free_argv(argv);
        return ret;
 }
@@ -1058,8 +1110,8 @@ static void session_open(struct play_task *pt)
        struct sigaction act;
 
        PARA_NOTICE_LOG("\n%s\n", version_text("play"));
-       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/play.history",
@@ -1202,7 +1254,7 @@ static unsigned get_time_string(struct play_task *pt, char **result)
                length? (seconds * 100 + length / 2) / length : 0,
                length / 60,
                length % 60,
-               conf.inputs[pt->current_file]
+               get_playlist_file(pt->current_file)
        );
 }
 
@@ -1259,25 +1311,20 @@ int main(int argc, char *argv[])
 {
        int ret;
        struct play_task *pt = &play_task;
+       unsigned num_inputs;
 
        /* needed this early to make help work */
        recv_init();
-       filter_init();
-       writer_init();
 
        sched.default_timeout.tv_sec = 5;
-
        parse_config_or_die(argc, argv);
-       if (conf.inputs_num == 0)
-               print_help_and_die();
-       check_afh_receiver_or_die();
-
+       AFH_RECV->init();
        session_open(pt);
-       if (conf.randomize_given)
-               shuffle(conf.inputs, conf.inputs_num);
-       pt->invalid = para_calloc(sizeof(*pt->invalid) * conf.inputs_num);
+       num_inputs = lls_num_inputs(play_lpr);
+       init_shuffle_map();
+       pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
        pt->rq = CRT_FILE_CHANGE;
-       pt->current_file = conf.inputs_num - 1;
+       pt->current_file = num_inputs - 1;
        pt->playing = true;
        pt->task = task_register(&(struct task_info){
                .name = "play",
diff --git a/play.cmd b/play.cmd
deleted file mode 100644 (file)
index 459ad8c..0000000
--- a/play.cmd
+++ /dev/null
@@ -1,77 +0,0 @@
-BN: play
-SF: play.c
-SN: list of commands
----
-N: help
-D: Display command list or help for given command.
-U: help [command]
-H: This command acts differently depending on whether it is executed in command
-H: mode or in insert mode.  In command mode, the list of keybindings is printed.
-H: In insert mode, if no command is given, the list of commands is shown.
-H: Otherwise, the help for the given command is printed.
----
-N: next
-D: Load next file.
-U: next
-H: Closes the current file and loads the next file of the playlist.
----
-N: prev
-D: Load previous file.
-U: prev
-H: Closes the current file and loads the previous file of the playlist.
----
-N: fg
-D: Enter command mode.
-U: fg
-H: In this mode, file name and play time are displayed.  Hit CTRL+C to switch to
-H: input mode.
----
-N: bg
-D: Enter input mode.
-U: bg
-H: Only useful if called in command mode via a key binding. The default key
-H: bindings map this command to the colon key, so pressing : in command mode
-H: activates insert mode.
----
-N: jmp
-D: Jump to position in current file.
-U: jmp <percent>
-H: The <percent> argument should be an integer between 0 and 100.
----
-N: ff
-D: Jump forwards or backwards.
-U: ff <seconds>
-H: Negative values mean to jmp backwards the given amount of seconds.
----
-N: ls
-D: List playlist.
-U: ls
-H: This prints all paths of the playlist. The currently active file is
-H: marked with an asterisk.
----
-N: info
-D: Print information about the current file.
-U: info
-H: This is the audio file selector info.
----
-N: play
-D: Start or resume playing.
-U: play [<num>]
-H: Without <num>, starts playing at the current position. Otherwise, the
-H: corresponding file is loaded and playback is started.
----
-N: pause
-D: Stop playing.
-U: pause
-H: When paused, it is still possible to jump around in the file via the jmp and ff
-H: comands.
----
-N: tasks
-D: Print list of active tasks.
-U: tasks
-H: Mainly useful for debugging.
----
-N: quit
-D: Exit para_play.
-U: quit
-H: Pressing CTRL+D causes EOF on stdin which also exits para_play.
index 8ea1854bddfc004db52d6d0afe577d6601960b07..b9e52c75672f9ada9ad4a949f5fa0747369db3e6 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
index 6078da0757f885eec2fec0dcb9fbda88cb99f454..8ca1630d8112242ee65b3feb63e2883ffdac7f86 100644 (file)
@@ -7,12 +7,12 @@
 /** \file prebuffer_filter.c Paraslash's prebuffering filter. */
 
 #include <regex.h>
+#include <lopsub.h>
 
 #include "para.h"
-#include "prebuffer_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -20,8 +20,6 @@
 
 /** Data specific to the prebuffer filter. */
 struct private_prebuffer_data {
-       /** The configuration data for this instance of the filter. */
-       struct prebuffer_filter_args_info *conf;
        /** Number of bytes prebuffered or -1 if no longer prebuffering. */
        int prebuffered;
        /** End of prebuffering period. */
@@ -34,16 +32,16 @@ static void prebuffer_pre_select(struct sched *s, void *context)
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
-       struct prebuffer_filter_args_info *conf = ppd->conf;
        struct timeval diff;
 
        if (iqs == 0)
                return;
        if (ppd->barrier.tv_sec == 0) {
+               uint32_t duration = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER,
+                       DURATION, fn->lpr);
                struct timeval tv;
-               PARA_INFO_LOG("prebuffer period %dms\n",
-                       conf->duration_arg);
-               ms2tv(conf->duration_arg, &tv);
+               PARA_INFO_LOG("prebuffer period %" PRIu32 "ms\n", duration);
+               ms2tv(duration, &tv);
                tv_add(&tv, now, &ppd->barrier);
        }
        if (tv_diff(&ppd->barrier, now, &diff) < 0)
@@ -62,66 +60,27 @@ static int prebuffer_post_select(__a_unused struct sched *s, void *context)
        struct btr_node *btrn = fn->btrn;
        size_t iqs = btr_get_input_queue_size(btrn);
        struct private_prebuffer_data *ppd = fn->private_data;
-       struct prebuffer_filter_args_info *conf = ppd->conf;
+       uint32_t size = FILTER_CMD_OPT_UINT32_VAL(PREBUFFER, SIZE, fn->lpr);
 
        if (ppd->barrier.tv_sec == 0)
                return 0;
        if (tv_diff(now, &ppd->barrier, NULL) < 0)
                return 0;
-       if (iqs < conf->size_arg)
+       if (iqs < size)
                return 0;
        btr_splice_out_node(&fn->btrn);
        return -E_PREBUFFER_SUCCESS;
 }
 
-static int prebuffer_parse_config(int argc, char **argv, void **config)
-{
-       struct prebuffer_filter_args_info *conf = para_calloc(sizeof(*conf));
-       int ret;
-
-       prebuffer_filter_cmdline_parser(argc, argv, conf);
-       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-       if (conf->duration_arg < 0)
-               goto err;
-       if (conf->size_arg < 0)
-               goto err;
-       PARA_NOTICE_LOG("prebuffering %ims, %i bytes\n", conf->duration_arg,
-               conf->size_arg);
-       *config = conf;
-       return 1;
-err:
-       free(conf);
-       return ret;
-}
-
 static void prebuffer_open(struct filter_node *fn)
 {
        struct private_prebuffer_data *ppd = para_calloc(sizeof(*ppd));
-
-       ppd->conf = fn->conf;
        fn->private_data = ppd;
 }
 
-static void prebuffer_free_config(void *conf)
-{
-       prebuffer_filter_cmdline_parser_free(conf);
-}
-
-/**
- * The init function of the prebuffer filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void prebuffer_filter_init(struct filter *f)
-{
-       struct prebuffer_filter_args_info dummy;
-
-       prebuffer_filter_cmdline_parser_init(&dummy);
-       f->open = prebuffer_open;
-       f->close = prebuffer_close;
-       f->parse_config = prebuffer_parse_config;
-       f->free_config = prebuffer_free_config;
-       f->pre_select = prebuffer_pre_select;
-       f->post_select = prebuffer_post_select;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(prebuffer_filter);
-}
+const struct filter lsg_filter_cmd_com_prebuffer_user_data = {
+       .open = prebuffer_open,
+       .close = prebuffer_close,
+       .pre_select = prebuffer_pre_select,
+       .post_select = prebuffer_post_select,
+};
diff --git a/recv.c b/recv.c
index 9de3033fee92925a4cd1446c8895f59055c82486..9842c7dc8f168fe5e4cd3daf2ee0d9f3f34df358 100644 (file)
--- a/recv.c
+++ b/recv.c
@@ -8,14 +8,16 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <inttypes.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
+#include "recv.lsg.h"
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "recv.cmdline.h"
 #include "fd.h"
 #include "string.h"
 #include "error.h"
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-DEFINE_RECEIVER_ARRAY;
-
-/** The gengetopt args info struct. */
-static struct recv_args_info conf;
+#define CMD_PTR (lls_cmd(0, recv_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_RECV_PARA_RECV_OPT_ ## _name, lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
+#define OPT_STRING_VAL(_name, _lpr) (lls_string_val(0, OPT_RESULT(_name, _lpr)))
 
 static int loglevel;
 /** Always log to stderr. */
 INIT_STDERR_LOGGING(loglevel);
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(recv);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_receiver_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
-       exit(0);
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_receiver_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+       exit(EXIT_SUCCESS);
 }
 
 /**
@@ -60,41 +67,42 @@ __noreturn static void print_help_and_die(void)
  */
 int main(int argc, char *argv[])
 {
-       int ret, r_opened = 0, receiver_num;
-       struct receiver *r = NULL;
+       int ret;
+       const struct receiver *r = NULL;
        struct receiver_node rn;
        struct stdout_task sot = {.btrn = NULL};
        static struct sched s;
        struct task_info ti;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr; /* command line */
+       struct lls_parse_result *receiver_lpr; /* receiver specific options */
+       char *errctx;
 
-       recv_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       version_handle_flag("recv", conf.version_given);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("recv", OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
        recv_init();
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-
        memset(&rn, 0, sizeof(struct receiver_node));
-       rn.conf = check_receiver_arg(conf.receiver_arg, &receiver_num);
-       if (!rn.conf) {
-               PARA_EMERG_LOG("invalid receiver specifier\n");
-               ret = -E_RECV_SYNTAX;
-               goto out;
-       }
-       r = &receivers[receiver_num];
+       ret = check_receiver_arg(OPT_STRING_VAL(RECEIVER, lpr), &receiver_lpr);
+       if (ret < 0)
+               goto free_lpr;
+       cmd = lls_cmd(ret, recv_cmd_suite);
+       r = lls_user_data(cmd);
        rn.receiver = r;
+       rn.lpr = receiver_lpr;
        rn.btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = r->name));
+               EMBRACE(.name = lls_command_name(cmd)));
        ret = r->open(&rn);
        if (ret < 0)
-               goto out;
-       r_opened = 1;
-
+               goto remove_btrn;
        sot.btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.parent = rn.btrn, .name = "stdout"));
        stdout_task_register(&sot, &s);
 
-       ti.name = r->name;
+       ti.name = lls_command_name(cmd);
        ti.pre_select = r->pre_select;
        ti.post_select = r->post_select;
        ti.context = &rn;
@@ -104,15 +112,19 @@ int main(int argc, char *argv[])
        s.default_timeout.tv_usec = 0;
        ret = schedule(&s);
        sched_shutdown(&s);
-out:
-       if (r_opened)
-               r->close(&rn);
-       btr_remove_node(&rn.btrn);
+       r->close(&rn);
        btr_remove_node(&sot.btrn);
-       if (rn.conf)
-               r->free_config(rn.conf);
-
-       if (ret < 0)
+remove_btrn:
+       btr_remove_node(&rn.btrn);
+       lls_free_parse_result(receiver_lpr, cmd);
+free_lpr:
+       lls_free_parse_result(lpr, CMD_PTR);
+out:
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+       }
        return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/recv.h b/recv.h
index 1a0de659e92be1d6f68838c3408b8859c4278a43..68978a38c90f2dada11f21d0b55b8a078e4b4bef 100644 (file)
--- a/recv.h
+++ b/recv.h
  */
 struct receiver_node {
        /** Points to the corresponding receiver. */
-       struct receiver *receiver;
+       const struct receiver *receiver;
        /** Receiver-specific data. */
        void *private_data;
-       /** Pointer to the configuration data for this instance. */
-       void *conf;
+       /** The parsed command line options for this instance. */
+       struct lls_parse_result *lpr;
        /** The task associated with this instance. */
        struct task *task;
        /** The receiver node is always the root of the buffer tree. */
@@ -44,34 +44,13 @@ struct receiver_node {
  */
 struct receiver {
        /**
-        * The name of the receiver.
-        */
-       const char *name;
-       /**
-        * The receiver init function.
+        * The optional receiver init function.
         *
-        * It must fill in all other function pointers and is assumed to succeed.
+        * Performs any initialization needed before the receiver can be opened.
         *
         * \sa http_recv_init udp_recv_init.
         */
-       void (*init)(struct receiver *r);
-       /**
-        * The command line parser of the receiver.
-        *
-        * It should check whether the command line options given by \a argc
-        * and \a argv are valid.  On success, it should return a pointer to
-        * the receiver-specific configuration data determined by \a argc and
-        * \a argv.  Note that this might be called more than once with
-        * different values of \a argc and \a argv.
-        */
-       void *(*parse_config)(int argc, char **argv);
-       /**
-        * Deallocate the configuration structure of a receiver node.
-        *
-        * This calls the receiver-specific cleanup function generated by
-        * gengetopt.
-        */
-       void (*free_config)(void *conf);
+       void (*init)(void);
        /**
         * Open one instance of the receiver.
         *
@@ -117,8 +96,6 @@ struct receiver {
         */
        int (*post_select)(struct sched *s, void *context);
 
-       /** The two help texts of this receiver. */
-       struct ggo_help help;
        /**
         * Answer a buffer tree query.
         *
@@ -128,31 +105,23 @@ struct receiver {
        btr_command_handler execute;
 };
 
-/** Define an array of all available receivers. */
-#define DEFINE_RECEIVER_ARRAY struct receiver receivers[] = { \
-       HTTP_RECEIVER \
-       DCCP_RECEIVER \
-       UDP_RECEIVER \
-       AFH_RECEIVER \
-       {.name = NULL}};
+#define RECV_CMD(_num) (lls_cmd(_num, recv_cmd_suite))
+
+#define RECV_CMD_OPT_RESULT(_recv, _opt, _lpr) \
+       (lls_opt_result(LSG_RECV_CMD_ ## _recv ## _OPT_ ## _opt, _lpr))
+#define RECV_CMD_OPT_GIVEN(_recv, _opt, _lpr) \
+       (lls_opt_given(RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_STRING_VAL(_recv, _opt, _lpr) \
+       (lls_string_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_UINT32_VAL(_recv, _opt, _lpr) \
+       (lls_uint32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
+#define RECV_CMD_OPT_INT32_VAL(_recv, _opt, _lpr) \
+       (lls_int32_val(0, RECV_CMD_OPT_RESULT(_recv, _opt, _lpr)))
 
 /** Iterate over all available receivers. */
-#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++)
+#define FOR_EACH_RECEIVER(i) for (i = 1; lls_cmd(i, recv_cmd_suite); i++)
 
 void recv_init(void);
-void *check_receiver_arg(char *ra, int *receiver_num);
-void print_receiver_helps(unsigned flags);
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp);
+void print_receiver_helps(bool detailed);
 int generic_recv_pre_select(struct sched *s, struct receiver_node *rn);
-
-/** \cond receiver */
-extern void http_recv_init(struct receiver *r);
-#define HTTP_RECEIVER {.name = "http", .init = http_recv_init},
-extern void dccp_recv_init(struct receiver *r);
-#define DCCP_RECEIVER {.name = "dccp", .init = dccp_recv_init},
-extern void udp_recv_init(struct receiver *r);
-#define UDP_RECEIVER {.name = "udp", .init = udp_recv_init},
-#define AFH_RECEIVER /* not active by default */
-
-extern struct receiver receivers[];
-/** \endcond receiver */
-
index 59630dfcc5f5f6fc5cfa973fd25c4476366b6b92..7bb775fe214af8880bc1244743760b59a2188e46 100644 (file)
@@ -7,11 +7,14 @@
 /** \file recv_common.c common functions of para_recv and para_audiod */
 
 #include <regex.h>
+#include <inttypes.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 "string.h"
@@ -23,92 +26,93 @@ void recv_init(void)
 {
        int i;
 
-       FOR_EACH_RECEIVER(i)
-               receivers[i].init(&receivers[i]);
-}
-
-static void *parse_receiver_args(int receiver_num, char *options)
-{
-       struct receiver *r = &receivers[receiver_num];
-       char **argv;
-       int argc;
-       void *conf;
-
-       if (options) {
-               argc = create_shifted_argv(options, " \t", &argv);
-               if (argc < 0)
-                       return NULL;
-       } else {
-               argc = 1;
-               argv = para_malloc(2 * sizeof(char*));
-               argv[1] = NULL;
+       FOR_EACH_RECEIVER(i) {
+               const struct lls_command *cmd = RECV_CMD(i);
+               const struct receiver *r = lls_user_data(cmd);
+               if (r && r->init)
+                       r->init();
        }
-       argv[0] = make_message("%s_recv", r->name);
-       conf = r->parse_config(argc, argv);
-       free_argv(argv);
-       return conf;
 }
 
 /**
  * Check if the given string is a valid receiver specifier.
  *
- * \param \ra string of the form receiver_name:options
- * \param receiver_num contains the number of the receiver upon success
+ * \param \ra string of the form receiver_name [options...]
+ * \param lprp Filled in on success, undefined else.
  *
  * This function checks whether \a ra starts with the name of a receiver,
  * optionally followed by options for that receiver. If a valid receiver name
  * was found the remaining part of \a ra is passed to the receiver's config
  * parser.
  *
- * \return On success, a pointer to the receiver-specific gengetopt args info
- * struct is returned and \a receiver_num contains the number of the receiver.
- * On errors, the function returns \p NULL.
+ * If a NULL pointer or an empty string is passed as the first argument, the
+ * hhtp receiver with no options is assumed.
+ *
+ * \return On success the number of the receiver is returned. On errors, the
+ * function calls exit(EXIT_FAILURE).
  */
-void *check_receiver_arg(char *ra, int *receiver_num)
+int check_receiver_arg(const char *ra, struct lls_parse_result **lprp)
 {
-       int j;
+       int ret, argc, receiver_num;
+       char *errctx = NULL, **argv;
+       const struct lls_command *cmd;
 
-       PARA_DEBUG_LOG("checking %s\n", ra);
-       for (j = 0; receivers[j].name; j++) {
-               const char *name = receivers[j].name;
-               size_t len = strlen(name);
-               char c;
-               if (strlen(ra) < len)
-                       continue;
-               if (strncmp(name, ra, len))
-                       continue;
-               c = ra[len];
-               if (c && c != ' ')
-                       continue;
-               if (c && !receivers[j].parse_config)
-                       return NULL;
-               *receiver_num = j;
-               return parse_receiver_args(j, c? ra + len + 1: NULL);
+       *lprp = NULL;
+       if (!ra || !*ra) {
+               argc = 1;
+               argv = para_malloc(2 * sizeof(char*));
+               argv[0] = para_strdup("http");
+               argv[1] = NULL;
+       } else {
+               ret = create_argv(ra, " \t\n", &argv);
+               if (ret < 0) {
+                       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+                       exit(EXIT_FAILURE);
+               }
+               argc = ret;
        }
-       PARA_ERROR_LOG("receiver not found\n");
-       return NULL;
+       ret = lls(lls_lookup_subcmd(argv[0], recv_cmd_suite, &errctx));
+       if (ret < 0) {
+               PARA_EMERG_LOG("%s: %s\n", errctx? errctx : argv[0],
+                       para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       receiver_num = ret;
+       cmd = RECV_CMD(receiver_num);
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
+       ret = receiver_num;
+       free_argv(argv);
+       return ret;
 }
 
 /**
  * Print out the help texts to all receivers.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
  */
-void print_receiver_helps(unsigned flags)
+void print_receiver_helps(bool detailed)
 {
        int i;
 
-       printf_or_die("\nAvailable receivers: ");
-       FOR_EACH_RECEIVER(i)
-               printf_or_die("%s%s", i? " " : "", receivers[i].name);
-       printf_or_die("\n");
+       printf("\nAvailable receivers: ");
+       FOR_EACH_RECEIVER(i) {
+               const struct lls_command *cmd = RECV_CMD(i);
+               printf("%s%s", i? " " : "", lls_command_name(cmd));
+       }
+       printf("\n\n");
        FOR_EACH_RECEIVER(i) {
-               struct receiver *r = receivers + i;
-               if (!r->help.short_help)
+               const struct lls_command *cmd = RECV_CMD(i);
+               char *help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
                        continue;
-               printf_or_die("\n%s: %s", r->name,
-                       r->help.purpose);
-               ggo_print_help(&r->help, flags);
+               printf("%s\n", help);
+               free(help);
        }
 }
 
index 1699ed2c03afcc84cc7b8b3e2e8a7f91aca13cc5..72a1864eeafb69982cc1f0e49a1013eb81872341 100644 (file)
@@ -8,20 +8,24 @@
 
 #include <regex.h>
 #include <samplerate.h>
+#include <lopsub.h>
 
-#include "resample_filter.cmdline.h"
+#include "filter_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "check_wav.h"
 
+#define U32_OPTVAL(_opt, _lpr) (FILTER_CMD_OPT_UINT32_VAL(RESAMPLE, _opt, _lpr))
+#define OPT_GIVEN(_opt, _lpr) (FILTER_CMD_OPT_GIVEN(RESAMPLE, _opt, _lpr))
+
+/* effective values, may differ from config arg */
 struct resample_context {
-       int channels;
+       uint32_t channels;
        int source_sample_rate;
        float ratio;
        SRC_STATE *src_state;
@@ -32,10 +36,8 @@ static int resample_execute(struct btr_node *btrn, const char *cmd, char **resul
 {
        struct filter_node *fn = btr_context(btrn);
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
-
-       return decoder_execute(cmd, conf->dest_sample_rate_arg, ctx->channels,
-               result);
+       uint32_t dsr = U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr);
+       return decoder_execute(cmd, dsr, ctx->channels, result);
 }
 
 static void resample_close(struct filter_node *fn)
@@ -54,13 +56,12 @@ static void resample_close(struct filter_node *fn)
 static void resample_open(struct filter_node *fn)
 {
        struct resample_context *ctx = para_calloc(sizeof(*ctx));
-       struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
        struct wav_params wp;
 
        fn->private_data = ctx;
        fn->min_iqs = 2;
-       COPY_WAV_PARMS(&wp, conf);
+       LLS_COPY_WAV_PARMS(&wp, LSG_FILTER_CMD_RESAMPLE, fn->lpr);
        ctx->cwc = check_wav_init(btr_parent(btrn), btrn, &wp, NULL);
        btr_log_tree(btr_parent(btr_parent(btrn)), LL_INFO);
 }
@@ -95,18 +96,17 @@ static int resample_set_params(struct filter_node *fn)
 {
        int ret;
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
+       struct lls_parse_result *lpr = fn->lpr;
 
-       ctx->channels = conf->channels_arg;
-       if (!conf->channels_given) {
+       ctx->channels = U32_OPTVAL(CHANNELS, lpr);
+       if (!OPT_GIVEN(CHANNELS, lpr)) {
                ret = get_btr_val("channels", btrn);
                if (ret >= 0)
                        ctx->channels = ret;
        }
-
-       ctx->source_sample_rate = conf->sample_rate_arg;
-       if (!conf->sample_rate_given) {
+       ctx->source_sample_rate = U32_OPTVAL(SAMPLE_RATE, lpr);
+       if (!OPT_GIVEN(SAMPLE_RATE, lpr)) {
                ret = get_btr_val("sample_rate", btrn);
                if (ret >= 0)
                        ctx->source_sample_rate = ret;
@@ -114,44 +114,37 @@ static int resample_set_params(struct filter_node *fn)
        /* reject all sample formats except 16 bit signed, little endian */
        ret = get_btr_val("sample_format", btrn);
        if (ret >= 0 && ret != SF_S16_LE) {
-               const char *sample_formats[] = {SAMPLE_FORMATS};
+               const char * const sample_formats[] = {SAMPLE_FORMATS};
                PARA_ERROR_LOG("unsupported sample format: %s\n",
                        sample_formats[ret]);
                return -ERRNO_TO_PARA_ERROR(EINVAL);
        }
-       ctx->ratio = (float)conf->dest_sample_rate_arg / ctx->source_sample_rate;
+       ctx->ratio = U32_OPTVAL(DEST_SAMPLE_RATE, lpr)
+               / (float)ctx->source_sample_rate;
        return 1;
 }
 
 static int resample_init(struct filter_node *fn)
 {
-       int ret, converter;
+       int ret;
+       const uint32_t trafo[] = {
+               [RCT_BEST] = SRC_SINC_BEST_QUALITY,
+               [RCT_MEDIUM] = SRC_SINC_MEDIUM_QUALITY,
+               [RCT_FASTEST] = SRC_SINC_FASTEST,
+               [RCT_ZERO_ORDER_HOLD] = SRC_ZERO_ORDER_HOLD,
+               [RCT_LINEAR] = SRC_LINEAR
+       };
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
+       const struct lls_option *o_c = FILTER_CMD_OPT(RESAMPLE, CONVERTER);
+       uint32_t converter = U32_OPTVAL(CONVERTER, fn->lpr);
 
+       PARA_INFO_LOG("converter type: %s\n",
+               lls_enum_string_val(converter, o_c));
        ret = resample_set_params(fn);
        if (ret < 0)
                return ret;
-       switch (conf->converter_arg) {
-       case converter_arg_best:
-               converter = SRC_SINC_BEST_QUALITY;
-               break;
-       case converter_arg_medium:
-               converter = SRC_SINC_MEDIUM_QUALITY;
-               break;
-       case converter_arg_fastest:
-               converter = SRC_SINC_FASTEST;
-               break;
-       case converter_arg_zero_order_hold:
-               converter = SRC_ZERO_ORDER_HOLD;
-               break;
-       case converter_arg_linear:
-               converter = SRC_LINEAR;
-               break;
-       default:
-               assert(0);
-       }
-       ctx->src_state = src_new(converter, conf->channels_arg, &ret);
+       ctx->src_state = src_new(trafo[converter],
+               U32_OPTVAL(CHANNELS, fn->lpr), &ret);
        if (!ctx->src_state) {
                PARA_ERROR_LOG("%s\n", src_strerror(ret));
                return -E_LIBSAMPLERATE;
@@ -201,7 +194,6 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
        int ret;
        struct filter_node *fn = context;
        struct resample_context *ctx = fn->private_data;
-       struct resample_filter_args_info *conf = fn->conf;
        struct btr_node *btrn = fn->btrn;
        int16_t *in, *out;
        size_t in_bytes, num_frames;
@@ -218,7 +210,7 @@ static int resample_post_select(__a_unused struct sched *s, void *context)
                if (ret <= 0)
                        goto out;
        }
-       if (ctx->source_sample_rate == conf->dest_sample_rate_arg) {
+       if (ctx->source_sample_rate == U32_OPTVAL(DEST_SAMPLE_RATE, fn->lpr)) {
                /*
                 * No resampling necessary. We do not splice ourselves out
                 * though, since our children might want to ask us through the
@@ -251,58 +243,45 @@ out:
        return ret;
 }
 
-static int resample_parse_config(int argc, char **argv, void **config)
+static void *resample_setup(const struct lls_parse_result *lpr)
 {
-       int ret, val, given;
-       struct resample_filter_args_info *conf = para_calloc(sizeof(*conf));
-
-       resample_filter_cmdline_parser(argc, argv, conf);
+       int given;
+       uint32_t u32;
 
        /* sanity checks */
-       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-       val = conf->channels_arg;
-       given = conf->channels_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       val = conf->sample_rate_arg;
-       given = conf->sample_rate_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       val = conf->dest_sample_rate_arg;
-       given = conf->dest_sample_rate_given;
-       if (val < 0 || (val == 0 && given))
-               goto err;
-       *config = conf;
-       return 1;
-err:
-       free(conf);
-       return ret;
+       u32 = U32_OPTVAL(CHANNELS, lpr);
+       given = OPT_GIVEN(CHANNELS, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: zero channels?!\n");
+               exit(EXIT_FAILURE);
+       }
+       u32 = U32_OPTVAL(SAMPLE_RATE, lpr);
+       given = OPT_GIVEN(SAMPLE_RATE, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: input sample rate can not be 0\n");
+               exit(EXIT_FAILURE);
+       }
+       u32 = U32_OPTVAL(DEST_SAMPLE_RATE, lpr);
+       given = OPT_GIVEN(DEST_SAMPLE_RATE, lpr);
+       if (u32 == 0 && given) {
+               PARA_EMERG_LOG("fatal: destination sample rate can not be 0\n");
+               exit(EXIT_FAILURE);
+       }
+       return NULL;
 }
 
-static void resample_free_config(void *conf)
+static void resample_teardown(__a_unused const struct lls_parse_result *lpr,
+               void *conf)
 {
-       if (!conf)
-               return;
-       resample_filter_cmdline_parser_free(conf);
        free(conf);
 }
 
-/**
- * The init function of the resample filter.
- *
- * \param f Structure to initialize.
- */
-void resample_filter_init(struct filter *f)
-{
-       struct resample_filter_args_info dummy;
-
-       resample_filter_cmdline_parser_init(&dummy);
-       f->close = resample_close;
-       f->open = resample_open;
-       f->pre_select = resample_pre_select;
-       f->post_select = resample_post_select;
-       f->parse_config = resample_parse_config;
-       f->free_config = resample_free_config;
-       f->execute = resample_execute;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(resample_filter);
-}
+const struct filter lsg_filter_cmd_com_resample_user_data = {
+       .setup = resample_setup,
+       .open = resample_open,
+       .pre_select = resample_pre_select,
+       .post_select = resample_post_select,
+       .close = resample_close,
+       .teardown = resample_teardown,
+       .execute = resample_execute
+};
diff --git a/score.c b/score.c
index 81b3ded0021f41115dcc78a8f9605d722195c389..ddd3c7a25057998c0328893553b6b246cebcda64 100644 (file)
--- a/score.c
+++ b/score.c
@@ -7,6 +7,7 @@
 /** \file score.c Scoring functions to determine the audio file streaming order. */
 #include <regex.h>
 #include <osl.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
diff --git a/send.h b/send.h
index 0c74f0ea93208ec6b24878c963e671a4da8adb8d..5420523490808bd68ce4daad23589eb0609fc372 100644 (file)
--- a/send.h
+++ b/send.h
@@ -182,10 +182,10 @@ struct sender_status {
 
 void shutdown_client(struct sender_client *sc, struct sender_status *ss);
 void shutdown_clients(struct sender_status *ss);
-void init_sender_status(struct sender_status *ss, char **access_arg, int num_access_args,
-       int port, int max_clients, int default_deny);
+void init_sender_status(struct sender_status *ss,
+               const struct lls_opt_result *acl_opt_result, int port,
+               int max_clients, int default_deny);
 char *generic_sender_status(struct sender_status *ss, const char *name);
-
 void generic_com_allow(struct sender_command_data *scd,
                struct sender_status *ss);
 void generic_com_deny(struct sender_command_data *scd,
index ec7ab67108334c85085b46b84ada1db45eb91a82..988e8d48daf502bc70f0ba6cb7377dd1dc90429c 100644 (file)
@@ -13,6 +13,7 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
 #include "para.h"
 #include "error.h"
@@ -133,19 +134,33 @@ int send_queued_chunks(int fd, struct chunk_queue *cq)
  * Initialize a struct sender status.
  *
  * \param ss The struct to initialize.
- * \param access_arg The array of access arguments given at the command line.
- * \param num_access_args The number of elements in \a access_arg.
+ * \param acl_opt_result Contains array of --{http|dccp}-access arguments.
  * \param port The tcp or dccp port to listen on.
  * \param max_clients The maximal number of simultaneous connections.
  * \param default_deny Whether a blacklist should be used for access control.
  */
-void init_sender_status(struct sender_status *ss, char **access_arg,
-       int num_access_args, int port, int max_clients, int default_deny)
+void init_sender_status(struct sender_status *ss,
+               const struct lls_opt_result *acl_opt_result, int port,
+               int max_clients, int default_deny)
 {
+       int i;
+
        ss->listen_fd = -1;
        INIT_LIST_HEAD(&ss->client_list);
        ss->port = port;
-       acl_init(&ss->acl, access_arg, num_access_args);
+
+       /* Initialize an access control list */
+       INIT_LIST_HEAD(&ss->acl);
+       for (i = 0; i < lls_opt_given(acl_opt_result); i++) {
+               const char *arg = lls_string_val(i, acl_opt_result);
+               char addr[16];
+               int mask;
+               if (!parse_cidr(arg, addr, sizeof(addr), &mask))
+                       PARA_WARNING_LOG("ACL syntax error: %s, ignoring\n",
+                               arg);
+               else
+                       acl_add_entry(&ss->acl, addr, mask);
+       }
        ss->num_clients = 0;
        ss->max_clients = max_clients;
        ss->default_deny = default_deny;
index a023b1526931c1eeeb3fcec7be3a6b6c72662c69..43ede2a91b752667ad4090e2742c4a844bf66250 100644 (file)
--- a/server.c
+++ b/server.c
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
-#include "server.cmdline.h"
 #include "afh.h"
 #include "string.h"
 #include "afs.h"
@@ -62,7 +63,6 @@
 #include "signal.h"
 #include "user_list.h"
 #include "color.h"
-#include "ggo.h"
 #include "version.h"
 
 /** Array of error strings. */
@@ -80,12 +80,15 @@ __printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
 struct misc_meta_data *mmd;
 
 /**
- * The configuration of para_server
+ * The active value for all config options of para_server.
  *
- * It also contains the options for the audio file selector, audio format
- * handler and all supported senders.
+ * It is computed by merging the parse result of the command line options with
+ * the parse result of the config file.
  */
-struct server_args_info conf;
+struct lls_parse_result *server_lpr = NULL;
+
+/* Command line options (no config file options). Used in handle_sighup(). */
+static struct lls_parse_result *cmdline_lpr;
 
 /** A random value used in child context for authentication. */
 uint32_t afs_socket_cookie;
@@ -93,9 +96,6 @@ uint32_t afs_socket_cookie;
 /** The mutex protecting the shared memory area containing the mmd struct. */
 int mmd_mutex;
 
-/** The file containing user information (public key, permissions). */
-static char *user_list_file = NULL;
-
 static struct sched sched;
 static struct signal_task *signal_task;
 
@@ -162,71 +162,97 @@ err_out:
 /**
  * (Re-)read the server configuration files.
  *
- * \param override Passed to gengetopt to activate the override feature.
+ * \param reload Whether config file overrides command line.
  *
- * This function also re-opens the logfile and sets the global \a
- * user_list_file variable.
+ * This function also re-opens the logfile and the user list. On SIGHUP it is
+ * called from both server and afs context.
  */
-void parse_config_or_die(int override)
+void parse_config_or_die(bool reload)
 {
-       char *home = para_homedir();
        int ret;
-       char *cf;
+       char *cf = NULL, *errctx = NULL, *user_list_file = NULL;
+       void *map;
+       size_t sz;
+       int cf_argc;
+       char **cf_argv;
+       struct lls_parse_result *cf_lpr, *merged_lpr;
+       char *home = para_homedir();
 
        daemon_close_log();
-       if (conf.config_file_given)
-               cf = para_strdup(conf.config_file_arg);
+       if (OPT_GIVEN(CONFIG_FILE))
+               cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
        else
                cf = make_message("%s/.paraslash/server.conf", home);
-       free(user_list_file);
-       if (!conf.user_list_given)
-               user_list_file = make_message("%s/.paraslash/server.users", home);
-       else
-               user_list_file = para_strdup(conf.user_list_arg);
-       ret = file_exists(cf);
-       if (conf.config_file_given && !ret)  {
-               ret = -1;
-               PARA_EMERG_LOG("can not read config file %s\n", cf);
-               goto out;
+       if (!mmd || getpid() != mmd->afs_pid) {
+               if (OPT_GIVEN(USER_LIST))
+                       user_list_file = para_strdup(OPT_STRING_VAL(USER_LIST));
+               else
+                       user_list_file = make_message("%s/.paraslash/server.users", home);
        }
-       if (ret) {
-               int tmp = conf.daemon_given;
-               struct server_cmdline_parser_params params = {
-                       .override = override,
-                       .initialize = 0,
-                       .check_required = 1,
-                       .check_ambiguity = 0,
-                       .print_errors = !conf.daemon_given
-               };
-               server_cmdline_parser_config_file(cf, &conf, &params);
-               daemon_set_loglevel(conf.loglevel_arg);
-               conf.daemon_given = tmp;
+       free(home);
+       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;
+               ret = 0;
+               server_lpr = cmdline_lpr;
+               goto success;
        }
-       if (conf.logfile_given) {
-               daemon_set_logfile(conf.logfile_arg);
+       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;
+       if (reload) /* config file overrides command line */
+               ret = lls(lls_merge(cf_lpr, cmdline_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       else /* command line options overrride config file options */
+               ret = lls(lls_merge(cmdline_lpr, cf_lpr, CMD_PTR, &merged_lpr,
+                       &errctx));
+       lls_free_parse_result(cf_lpr, CMD_PTR);
+       if (ret < 0)
+               goto free_cf;
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(server_lpr, CMD_PTR);
+       server_lpr = merged_lpr;
+success:
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       if (OPT_GIVEN(LOGFILE)) {
+               daemon_set_logfile(OPT_STRING_VAL(LOGFILE));
                daemon_open_log_or_die();
        }
-
-       if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no,
-                       conf.logfile_given)) {
+       if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
+                       COLOR_NO, OPT_GIVEN(LOGFILE))) {
                int i;
-               for (i = 0; i < conf.log_color_given; i++)
-                       daemon_set_log_color_or_die(conf.log_color_arg[i]);
+               for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
+                       daemon_set_log_color_or_die(lls_string_val(i,
+                               OPT_RESULT(LOG_COLOR)));
        }
        daemon_set_flag(DF_LOG_PID);
        daemon_set_flag(DF_LOG_LL);
        daemon_set_flag(DF_LOG_TIME);
-       if (conf.log_timing_given)
+       if (OPT_GIVEN(LOG_TIMING))
                daemon_set_flag(DF_LOG_TIMING);
+       daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
+       if (user_list_file)
+               init_user_list(user_list_file);
        ret = 1;
-out:
+free_cf:
        free(cf);
-       free(home);
-       if (ret > 0)
-               return;
        free(user_list_file);
-       user_list_file = NULL;
-       exit(EXIT_FAILURE);
+       if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+               exit(EXIT_FAILURE);
+       }
 }
 
 /*
@@ -234,9 +260,9 @@ out:
  */
 static void handle_sighup(void)
 {
+
        PARA_NOTICE_LOG("SIGHUP\n");
-       parse_config_or_die(1); /* reopens log */
-       init_user_list(user_list_file); /* reload user list */
+       parse_config_or_die(true);
        if (mmd->afs_pid)
                kill(mmd->afs_pid, SIGHUP);
 }
@@ -369,7 +395,8 @@ static int command_post_select(struct sched *s, void *context)
         */
        for (i = sct->argc - 1; i >= 0; i--)
                memset(sct->argv[i], 0, strlen(sct->argv[i]));
-       sprintf(sct->argv[0], "para_server (serving %s)", peer_name);
+       i = sct->argc - 1 - lls_num_inputs(cmdline_lpr);
+       sprintf(sct->argv[i], "para_server (serving %s)", peer_name);
        handle_connect(new_fd, peer_name);
        /* never reached*/
 out:
@@ -387,7 +414,7 @@ static void init_server_command_task(int argc, char **argv)
        PARA_NOTICE_LOG("initializing tcp command socket\n");
        sct->argc = argc;
        sct->argv = argv;
-       ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
+       ret = para_listen_simple(IPPROTO_TCP, OPT_UINT32_VAL(PORT));
        if (ret < 0)
                goto err;
        sct->listen_fd = ret;
@@ -426,7 +453,8 @@ static int init_afs(int argc, char **argv)
 
                for (i = argc - 1; i >= 0; i--)
                        memset(argv[i], 0, strlen(argv[i]));
-               sprintf(argv[0], "para_server (afs)");
+               i = argc - lls_num_inputs(cmdline_lpr) - 1;
+               sprintf(argv[i], "para_server (afs)");
                close(afs_server_socket[0]);
                afs_init(afs_socket_cookie, afs_server_socket[1]);
        }
@@ -445,45 +473,46 @@ static int init_afs(int argc, char **argv)
        return afs_server_socket[0];
 }
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(server);
-       bool d = conf.detailed_help_given;
+       char *help;
+       bool d = OPT_GIVEN(DETAILED_HELP);
 
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       exit(0);
+       if (d)
+               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);
 }
 
 static void server_init(int argc, char **argv)
 {
-       struct server_cmdline_parser_params params = {
-               .override = 0,
-               .initialize = 1,
-               .check_required = 0,
-               .check_ambiguity = 0,
-               .print_errors = 1
-       };
-       int afs_socket, daemon_pipe = -1;
+       int ret, afs_socket, daemon_pipe = -1;
+       char *errctx;
 
        valid_fd_012();
-       init_random_seed_or_die();
        /* parse command line options */
-       server_cmdline_parser_ext(argc, argv, &conf, &params);
-       daemon_set_loglevel(conf.loglevel_arg);
-       version_handle_flag("server", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-       daemon_set_priority(conf.priority_arg);
-       daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg);
-       /* parse config file, open log and set defaults */
-       parse_config_or_die(0);
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &cmdline_lpr, &errctx));
+       if (ret < 0)
+               goto fail;
+       server_lpr = cmdline_lpr;
+       daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+       daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
+               OPT_STRING_VAL(GROUP));
+       version_handle_flag("server", OPT_GIVEN(VERSION));
+       handle_help_flags();
+       parse_config_or_die(false);
+       /* become daemon */
+       if (OPT_GIVEN(DAEMON))
+               daemon_pipe = daemonize(true /* parent waits for SIGTERM */);
+       init_random_seed_or_die();
        daemon_log_welcome("server");
        init_ipc_or_die(); /* init mmd struct and mmd->lock */
        daemon_set_start_time();
-       init_user_list(user_list_file);
-       /* become daemon */
-       if (conf.daemon_given)
-               daemon_pipe = daemonize(true /* parent waits for us */);
        PARA_NOTICE_LOG("initializing audio format handlers\n");
        afh_init();
 
@@ -491,16 +520,14 @@ static void server_init(int argc, char **argv)
         * Although afs uses its own signal handling we must ignore SIGUSR1
         * _before_ the afs child process gets born by init_afs() below.  It's
         * racy to do this in the child because the parent might send SIGUSR1
-        * before the child gets a chance to ignore this signal -- only the
-        * good die young.
-        */
-       para_sigaction(SIGUSR1, SIG_IGN);
-       /*
-        * We have to block SIGCHLD before the afs process is being forked off.
-        * Otherwise, para_server does not notice if afs dies before the
+        * before the child gets a chance to ignore this signal.
+        *
+        * We also have to block SIGCHLD before the afs process is created
+        * because otherwise para_server does not notice if afs dies before the
         * SIGCHLD handler has been installed for the parent process by
         * init_signal_task() below.
         */
+       para_sigaction(SIGUSR1, SIG_IGN);
        para_block_signal(SIGCHLD);
        PARA_NOTICE_LOG("initializing the audio file selector\n");
        afs_socket = init_afs(argc, argv);
@@ -517,6 +544,13 @@ static void server_init(int argc, char **argv)
                close(daemon_pipe);
        }
        PARA_NOTICE_LOG("server init complete\n");
+       return;
+fail:
+       assert(ret < 0);
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 static void status_refresh(void)
@@ -572,9 +606,10 @@ int main(int argc, char *argv[])
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
        sched_shutdown(&sched);
-       if (ret < 0) {
+       lls_free_parse_result(server_lpr, CMD_PTR);
+       if (server_lpr != cmdline_lpr)
+               lls_free_parse_result(cmdline_lpr, CMD_PTR);
+       if (ret < 0)
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
-               exit(EXIT_FAILURE);
-       }
-       exit(EXIT_SUCCESS);
+       exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
diff --git a/server.cmd b/server.cmd
deleted file mode 100644 (file)
index 5f46ba1..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-BN: server
-SF: command.c
-SN: list of server commands
----
-N: ff
-P: VSS_READ | VSS_WRITE
-D: Jump N seconds forward or backward.
-U: ff n[-]
-H: This sets the 'R' (reposition request) bit of the vss status flags
-H: which enqueues a request to jump n seconds forwards or backwards.
-H:
-H: Example:
-H:
-H:     para_client ff 30-
-H:
-H: jumps 30 seconds backwards.
----
-N: help
-P: 0
-D: Print online help.
-U: help [command]
-H: Without any arguments, help prints a list of available commands. When
-H: called with a command name as first argument, it prints the description
-H: of that command.
----
-N: hup
-P: VSS_WRITE
-D: Reload config file, log file and user list.
-U: hup
-H: Reread the config file and the user list file, close and reopen the log
-H: file, and ask the afs process to do the same. Sending the HUP signal to
-H: the server process has the same effect.
----
-N: jmp
-P: VSS_READ | VSS_WRITE
-D: Jump to the given position.
-U: jmp n
-H: Set the 'R' (reposition request) bit of the vss status flags and enqueue a
-H: request to jump to n% of the current audio file, where 0 <= n <= 100.
----
-N: next
-P: VSS_READ | VSS_WRITE
-D: Close the current audio file.
-U: next
-H: Set the 'N' (next audio file) bit of the vss status flags which instructs the
-H: server to close its current audio file if necessary. If the 'P' bit (playing)
-H: is on, playing continues with the next audio file. This command is equivalent
-H: to stop if paused, and has no effect if stopped.
----
-N: nomore
-P: VSS_READ | VSS_WRITE
-D: Stop playing after current audio file.
-U: nomore
-H: Set the 'O' (no more) bit of the vss status flags which asks para_server to
-H: clear the 'P' (playing) bit after the 'N' (next audio file) bit transitions
-H: from off to on (because the end of the current audio file is reached). Use this
-H: command instead of stop if you don't like sudden endings.
----
-N: pause
-P: VSS_READ | VSS_WRITE
-D: Pause current audio file.
-U: pause
-H: Clear the 'P' (playing) bit of the vss status flags.
----
-N: play
-P: VSS_READ | VSS_WRITE
-D: Start or resume playing.
-U: play
-H: Set the 'P' (playing) bit of the vss status flags.
----
-N: sender
-P: VSS_READ | VSS_WRITE
-D: Control paraslash senders.
-U: sender [s cmd [arguments]]
-H: Send a command to a specific sender. The following commands are available, but
-H: not all senders support every command.
-H:
-H:     help, on, off, add, delete, allow, deny, status.
-H:
-H: The help command prints the help text of the given sender. If no command is
-H: given the list of compiled in senders is shown.
-H:
-H: Example:
-H:
-H:     para_client sender http help
----
-N: si
-P: 0
-D: Print server info.
-U: si
-H: Show server and afs PID, number of connections, uptime and more.
----
-N: stat
-P: VSS_READ
-D: Print information about the current audio file.
-U: stat [-n=num] [-p]
-H: If -n is given, exit after the status information has been shown n times.
-H: Otherwise, the command runs in an endless loop.
-H:
-H: The -p option activates parser-friendly output: Each status item is
-H: prefixed with its size in bytes and the status item identifiers are
-H: printed as numerical values.
----
-N: stop
-P: VSS_READ | VSS_WRITE
-D: Stop playing.
-U: stop
-H: Clear the 'P' (playing) bit and set the 'N' (next audio file) bit of the vss
-H: status flags, effectively stopping playback.
----
-N: tasks
-P: 0
-D: List server tasks.
-U: tasks
-H: For each task, print ID, status and name.
----
-N: term
-P: VSS_READ | VSS_WRITE
-D: Ask the server to terminate.
-U: term
-H: Shut down the server. Instead of this command, you can also send SIGINT or
-H: SIGTERM to the para_server process. It should never be necessary to send
-H: SIGKILL.
----
-N: version
-P: 0
-D: Print the git version string of para_server.
-U: version
-H: Show version and other info.
index 7f276ab92edb4d06d312fc1c1c12f0003b7ae9ac..0bfca305fc5b6465aec22db6b5b0c155a682a141 100644 (file)
--- a/server.h
+++ b/server.h
@@ -87,9 +87,37 @@ struct misc_meta_data {
        struct audio_file_data afd;
 };
 
-/** Command line options for para_server. */
-extern struct server_args_info conf;
+extern struct lls_parse_result *server_lpr;
+
+/**
+ * Get a reference to the supercommand of para_server.
+ *
+ * This is needed for parsing the command line and for the ENUM_STRING_VAL()
+ * macro below. The latter macro is used in command.c, so CMD_PTR() can not
+ * be made local to server.c.
+ */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
+/** Get the parse result of an option to para_server. */
+#define OPT_RESULT(_name) (lls_opt_result( \
+               LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
+
+/** How many times a server option was given. */
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type string. */
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type uint32. */
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+
+/** The (first) argument to a server option of type int32. */
+#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
+
+/** Get the string which corresponds to an enum constant. */
+#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
+       lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
 
 __noreturn void handle_connect(int fd, const char *peername);
-void parse_config_or_die(int override);
+void parse_config_or_die(bool reload);
 char *server_get_tasks(void);
index 644d287aaf6a4daba32dd023c9770691aa3f4d24..3266d090a8a8d24674963422b63883718cf3cfe8 100644 (file)
@@ -50,7 +50,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -308,16 +307,10 @@ fail:
        return ret;
 }
 
-/**
- * The init function of the ogg/speex decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void spxdec_filter_init(struct filter *f)
-{
-       f->open = spxdec_open;
-       f->close = speexdec_close;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = speexdec_post_select;
-       f->execute = speexdec_execute;
-}
+const struct filter lsg_filter_cmd_com_spxdec_user_data = {
+       .open = spxdec_open,
+       .close = speexdec_close,
+       .pre_select = generic_filter_pre_select,
+       .post_select = speexdec_post_select,
+       .execute = speexdec_execute,
+};
index 82e86e90cf50c4bf77faac44183286c536caab26..efc10116f788a79bee5e2e75dfdfc0e036d06f1a 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "filter_cmd.lsg.h"
 #include "para.h"
-#include "sync_filter.cmdline.h"
 #include "list.h"
 #include "net.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -42,7 +42,7 @@ struct sync_buddy {
        struct list_head node;
 };
 
-/* Allocated in ->open() */
+/* Allocated in ->open(), stored in fn->private_data */
 struct sync_filter_context {
        int listen_fd;
        struct list_head buddies;
@@ -50,12 +50,6 @@ struct sync_filter_context {
        bool ping_sent;
 };
 
-/* Allocated and freed in ->parse_config() and ->free_config(). */
-struct sync_filter_config {
-       struct sync_filter_args_info *conf;
-       struct sync_buddy_info *buddy_info;
-};
-
 #define FOR_EACH_BUDDY(_buddy, _list) \
        list_for_each_entry(_buddy, _list, node)
 #define FOR_EACH_BUDDY_SAFE(_buddy, _tmp_buddy, _list) \
@@ -90,59 +84,59 @@ static void sync_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
-static void sync_free_config(void *conf)
+static void sync_teardown(const struct lls_parse_result *lpr, void *conf)
 {
-       struct sync_filter_config *sfc = conf;
-       int i;
+       struct sync_buddy_info *sbi = conf;
+       int i, num_buddies = FILTER_CMD_OPT_GIVEN(SYNC, BUDDY, lpr);
 
-       for (i = 0; i < sfc->conf->buddy_given; i++) {
-               free(sfc->buddy_info[i].host);
-               freeaddrinfo(sfc->buddy_info[i].ai);
+       for (i = 0; i < num_buddies; i++) {
+               free(sbi[i].host);
+               freeaddrinfo(sbi[i].ai);
        }
-       sync_filter_cmdline_parser_free(sfc->conf);
-       free(sfc);
+       free(sbi);
 }
 
 static void sync_open(struct filter_node *fn)
 {
        int i, ret;
-       struct sync_filter_config *sfc = fn->conf;
        struct sync_buddy *buddy;
        struct sync_filter_context *ctx;
-
-       assert(sfc);
+       struct sync_buddy_info *sbi = fn->conf;
+       uint32_t port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, fn->lpr);
+       unsigned buddy_given;
+       const struct lls_opt_result *r_b;
 
        ctx = fn->private_data = para_calloc(sizeof(*ctx));
        INIT_LIST_HEAD(&ctx->buddies);
-       ctx->listen_fd = -1;
 
        /* create socket to listen for incoming packets */
        ret = makesock(
                IPPROTO_UDP,
                true /* passive */,
                NULL /* no host required */,
-               sfc->conf->port_arg,
+               port,
                NULL /* no flowopts */
        );
        if (ret < 0) {
-               PARA_ERROR_LOG("could not create UDP listening socket %d\n",
-                       sfc->conf->port_arg);
+               PARA_ERROR_LOG("could not create UDP listening socket %u\n",
+                       port);
                return;
        }
        ctx->listen_fd = ret;
        PARA_INFO_LOG("listening on fd %d\n", ctx->listen_fd);
 
-       for (i = 0; i < sfc->conf->buddy_given; i++) {
-               struct sync_buddy_info *sbi = sfc->buddy_info + i;
-               const char *url = sfc->conf->buddy_arg[i];
+       r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, fn->lpr);
+       buddy_given = lls_opt_given(r_b);
+       for (i = 0; i < buddy_given; i++) {
                int fd;
+               const char *url = lls_string_val(i, r_b);
 
                /* make buddy udp socket from address info */
                assert(sbi->ai);
                ret = makesock_addrinfo(
                        IPPROTO_UDP,
                        false /* not passive */,
-                       sbi->ai,
+                       sbi[i].ai,
                        NULL /* no flowopts */
                );
                if (ret < 0) {
@@ -160,7 +154,7 @@ static void sync_open(struct filter_node *fn)
                }
                buddy = para_malloc(sizeof(*buddy));
                buddy->fd = fd;
-               buddy->sbi = sbi;
+               buddy->sbi = sbi + i;
                buddy->ping_received = false;
                para_list_add(&buddy->node, &ctx->buddies);
 
@@ -172,61 +166,50 @@ fail:
 }
 
 /*
- * At parse config time, we build an array of struct sync_buddy_info with one
- * entry for each buddy given in the arguments. This array is not affected by
- * sync_close(), so information stored there can be used for multiple instances
- * (para_audiod). We store the resolved url and the ->disabled bit in this
- * array.
+ * Build an array of struct sync_buddy_info with one entry for each buddy given
+ * in the arguments. This array is not affected by sync_close(), so information
+ * stored there can be used for multiple instances (para_audiod). We store the
+ * resolved url and the ->disabled bit in this array.
  */
-static int sync_parse_config(int argc, char **argv, void **result)
+static void *sync_setup(const struct lls_parse_result *lpr)
 {
-       int i, ret, n;
-       struct sync_filter_config *sfc;
-       struct sync_filter_args_info *conf = para_malloc(sizeof(*conf));
-
-       sync_filter_cmdline_parser(argc, argv, conf); /* exits on error */
-       sfc = para_calloc(sizeof(*sfc));
-       sfc->conf = conf;
-       n = conf->buddy_given;
-       sfc->buddy_info = para_malloc((n + 1) * sizeof(*sfc->buddy_info));
-       PARA_INFO_LOG("initializing buddy info array of length %d\n", n);
+       int i, ret;
+       unsigned n;
+       struct sync_buddy_info *sbi;
+       const struct lls_opt_result *r_b;
+
+       r_b = FILTER_CMD_OPT_RESULT(SYNC, BUDDY, lpr);
+       n = lls_opt_given(r_b);
+       sbi = para_malloc(n * sizeof(*sbi));
+       PARA_INFO_LOG("initializing buddy info array of length %u\n", n);
        for (i = 0; i < n; i++) {
-               const char *url = conf->buddy_arg[i];
+               const char *url = lls_string_val(i, r_b);
                size_t len = strlen(url);
                char *host = para_malloc(len + 1);
                int port;
                struct addrinfo *ai;
-               struct sync_buddy_info *sbi = sfc->buddy_info + i;
 
                if (!parse_url(url, host, len, &port)) {
-                       free(host);
                        PARA_ERROR_LOG("could not parse url %s\n", url);
-                       ret = -ERRNO_TO_PARA_ERROR(EINVAL);
-                       goto fail;
+                       exit(EXIT_FAILURE);
                }
                if (port < 0)
-                       port = conf->port_arg;
+                       port = FILTER_CMD_OPT_UINT32_VAL(SYNC, PORT, lpr);
                ret = lookup_address(IPPROTO_UDP, false /* not passive */,
                        host, port, &ai);
                if (ret < 0) {
-                       PARA_ERROR_LOG("host lookup failure for %s\n", url);
-                       free(host);
-                       goto fail;
+                       PARA_ERROR_LOG("host lookup failure for %s: %s\n",
+                               url, para_strerror(-ret));
+                       exit(EXIT_FAILURE);
                }
-               sbi->url = url;
-               sbi->host = host;
-               sbi->port = port;
-               sbi->ai = ai;
-               sbi->disabled = false;
+               sbi[i].url = url;
+               sbi[i].host = host;
+               sbi[i].port = port;
+               sbi[i].ai = ai;
+               sbi[i].disabled = false;
                PARA_DEBUG_LOG("buddy #%d: %s\n", i, url);
        }
-       *result = sfc;
-       return 1;
-fail:
-       assert(ret < 0);
-       PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-       sync_free_config(sfc);
-       return ret;
+       return sbi;
 }
 
 /*
@@ -260,11 +243,12 @@ static void sync_disable_active_buddies(struct sync_filter_context *ctx)
 }
 
 static void sync_set_timeout(struct sync_filter_context *ctx,
-               struct sync_filter_config *sfc)
+               struct lls_parse_result *lpr)
 {
+       uint32_t ms = FILTER_CMD_OPT_UINT32_VAL(SYNC, TIMEOUT, lpr);
        struct timeval to;
 
-       ms2tv(sfc->conf->timeout_arg, &to);
+       ms2tv(ms, &to);
        tv_add(now, &to, &ctx->timeout);
 }
 
@@ -273,7 +257,6 @@ static void sync_pre_select(struct sched *s, void *context)
        int ret;
        struct filter_node *fn = context;
        struct sync_filter_context *ctx = fn->private_data;
-       struct sync_filter_config *sfc = fn->conf;
 
        if (list_empty(&ctx->buddies))
                return sched_min_delay(s);
@@ -286,7 +269,7 @@ static void sync_pre_select(struct sched *s, void *context)
        if (ret == 0)
                return;
        if (ctx->timeout.tv_sec == 0) { /* must ping buddies */
-               sync_set_timeout(ctx, sfc);
+               sync_set_timeout(ctx, fn->lpr);
                return sched_min_delay(s);
        }
        if (sync_complete(ctx)) /* push down what we have */
@@ -310,7 +293,6 @@ static int sync_post_select(__a_unused struct sched *s, void *context)
        int ret;
        struct filter_node *fn = context;
        struct sync_filter_context *ctx = fn->private_data;
-       struct sync_filter_config *sfc = fn->conf;
        struct sync_buddy *buddy, *tmp;
 
        if (list_empty(&ctx->buddies))
@@ -324,7 +306,7 @@ static int sync_post_select(__a_unused struct sched *s, void *context)
        if (ret == 0)
                return 0;
        if (ctx->timeout.tv_sec == 0)
-               sync_set_timeout(ctx, sfc);
+               sync_set_timeout(ctx, fn->lpr);
        else {
                if (tv_diff(&ctx->timeout, now, NULL) < 0) {
                        sync_disable_active_buddies(ctx);
@@ -395,21 +377,11 @@ out:
        return ret;
 }
 
-/**
- * The synchronization filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void sync_filter_init(struct filter *f)
-{
-       struct sync_filter_args_info dummy;
-
-       sync_filter_cmdline_parser_init(&dummy);
-       f->open = sync_open;
-       f->close = sync_close;
-       f->pre_select = sync_pre_select;
-       f->post_select = sync_post_select;
-       f->parse_config = sync_parse_config;
-       f->free_config = sync_free_config;
-       f->help = (struct ggo_help)DEFINE_GGO_HELP(sync_filter);
-}
+const struct filter lsg_filter_cmd_com_sync_user_data = {
+       .setup = sync_setup,
+       .open = sync_open,
+       .pre_select = sync_pre_select,
+       .post_select = sync_post_select,
+       .close = sync_close,
+       .teardown = sync_teardown
+};
index 1963748f81547ac5706b992597cc41955abffa6d..5329ef0f3aaf85b7f50f0b460e28026ca4067378 100755 (executable)
@@ -28,7 +28,7 @@ declare -a commands=() cmdline=() required_objects=() good=() bad=()
 i=0
 commands[$i]="help"
 cmdline[$i]="help"
-good[$i]='help server  ----'
+good[$i]='help ----'
 
 let i++
 commands[$i]="init"
index fa32d4112e0ee80592b04c40214ffc7a868c9d89..ee1949c05a04d4fd4b81f2b9c847a403a753e2f5 100755 (executable)
@@ -13,8 +13,6 @@ filter/receiver/writer options as appropriate '
 
 . ${0%/*}/test-lib.sh
 
-rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer
-
 grep_man()
 {
        local regex="$1" exe="$2"
@@ -24,20 +22,19 @@ grep_man()
 # check that options of all reveivers/filters/writers are contained
 # in the man pages
 
-regex="$rfw_regex"
-test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv"
-test_expect_success 'para_filter: filter options' "grep_man '$regex' filter"
-test_expect_success 'para_write: writer options' "grep_man '$regex' write"
+test_expect_success 'para_recv: receiver options' "grep_man 'RECEIVERS' recv"
+test_expect_success 'para_filter: filter options' "grep_man 'FILTERS' filter"
+test_expect_success 'para_write: writer options' "grep_man 'WRITERS' write"
 test_require_objects "audiod"
 if [[ -n "$result" ]]; then
        test_skip 'para_audiod' "missing object(s): $result"
 else
        test_expect_success 'para_audiod: receivers' \
-               "grep_man 'Options for the http receiver' audiod"
+               "grep_man 'RECEIVERS' audiod"
        test_expect_success 'para_audiod: filters' \
-               "grep_man 'Options for the compress filter' audiod"
+               "grep_man 'FILTERS' audiod"
        test_expect_success 'para_audiod: writers' \
-               "grep_man 'Options for the file writer' audiod"
+               "grep_man 'WRITERS' audiod"
 fi
 
 # check various command lists
@@ -56,7 +53,7 @@ missing_objects="$result"
 if [[ -n "$missing_objects" ]]; then
        test_skip "para_server" "missing object(s): $missing_objects"
 else
-       regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS'
+       regex='LIST OF SERVER COMMANDS.\{100,\}'
        test_expect_success 'para_server: server/afs commands' \
                "grep_man '$regex' server"
 fi
index 1f9913e3cf9ba49b4cb76cc5b985d914cb527b69..9c6516e2abdd9500848263f324dd7afbe8246829 100644 (file)
@@ -311,7 +311,7 @@ fi
 fixup_dirs
 
 [[ -z "$o_executables" ]] && o_executables="para_afh para_audioc para_audiod
-       para_client para_fade para_filter para_gui para_recv para_server
+       para_client para_mixer para_filter para_gui para_recv para_server
        para_write"
 for exe in $o_executables; do
        export $(tr 'a-z' 'A-Z' <<< $exe)="$o_executables_dir/$exe"
index b803b4976b52d0e89fdae2c3ee7edbdb8a0a311b..a5dfc8794f6bab030a2c29c7d48e42961c9c9e02 100644 (file)
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "recv_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "portable_io.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "recv.h"
-#include "udp_recv.cmdline.h"
 #include "string.h"
 #include "net.h"
 #include "fd.h"
@@ -103,13 +103,6 @@ static void udp_recv_close(struct receiver_node *rn)
        btr_pool_free(rn->btrp);
 }
 
-static void *udp_recv_parse_config(int argc, char **argv)
-{
-       struct udp_recv_args_info *tmp = para_calloc(sizeof(*tmp));
-       udp_recv_cmdline_parser(argc, argv, tmp);
-       return tmp;
-}
-
 /*
  * Perform AF-independent joining of multicast receive addresses.
  *
@@ -173,58 +166,33 @@ err:
 
 static int udp_recv_open(struct receiver_node *rn)
 {
-       struct udp_recv_args_info *c = rn->conf;
-       char  *iface = c->iface_given ? c->iface_arg : NULL;
+       struct lls_parse_result *lpr = rn->lpr;
+       const char *iface = RECV_CMD_OPT_STRING_VAL(UDP, IFACE, lpr);
+       const char *host = RECV_CMD_OPT_STRING_VAL(UDP, HOST, lpr);
+       uint32_t port = RECV_CMD_OPT_UINT32_VAL(UDP, PORT, lpr);
        int ret;
 
-       ret = makesock(IPPROTO_UDP, 1, c->host_arg, c->port_arg, NULL);
+       ret = makesock(IPPROTO_UDP, 1, host, port, NULL);
        if (ret < 0)
-               goto err;
+               return ret;
        rn->fd = ret;
-
        ret = mcast_receiver_setup(rn->fd, iface);
-       if (ret < 0) {
-               close(rn->fd);
+       if (ret < 0)
                goto err;
-       }
-
        ret = mark_fd_nonblocking(rn->fd);
-       if (ret < 0) {
-               close(rn->fd);
+       if (ret < 0)
                goto err;
-       }
-       PARA_INFO_LOG("receiving from %s:%d, fd=%d\n", c->host_arg,
-               c->port_arg, rn->fd);
+       PARA_INFO_LOG("receiving from %s:%u, fd=%d\n", host, port, rn->fd);
        rn->btrp = btr_pool_new("udp_recv", 320 * 1024);
        return rn->fd;
 err:
+       close(rn->fd);
        return ret;
 }
 
-static void udp_recv_free_config(void *conf)
-{
-       udp_recv_cmdline_parser_free(conf);
-       free(conf);
-}
-
-/**
- * The init function of the udp receiver.
- *
- * \param r Pointer to the receiver struct to initialize.
- *
- * Initialize all function pointers of \a r.
- */
-void udp_recv_init(struct receiver *r)
-{
-       struct udp_recv_args_info dummy;
-
-       udp_recv_cmdline_parser_init(&dummy);
-       r->open = udp_recv_open;
-       r->close = udp_recv_close;
-       r->pre_select = udp_recv_pre_select;
-       r->post_select = udp_recv_post_select;
-       r->parse_config = udp_recv_parse_config;
-       r->free_config = udp_recv_free_config;
-       r->help = (struct ggo_help)DEFINE_GGO_HELP(udp_recv);
-       udp_recv_cmdline_parser_free(&dummy);
-}
+const struct receiver lsg_recv_cmd_com_udp_user_data = {
+       .open = udp_recv_open,
+       .close = udp_recv_close,
+       .pre_select = udp_recv_pre_select,
+       .post_select = udp_recv_post_select,
+};
index 425118a135edfee7224f40256dc087a803115aaf..8c9eebc94c3a5ef684ef86340b70d3140cd6fcda 100644 (file)
@@ -15,8 +15,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
-#include "server.cmdline.h"
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "string.h"
@@ -94,11 +95,11 @@ static int mcast_sender_setup(struct sender_client *sc)
 {
        struct sockaddr_storage ss;
        socklen_t sslen = sizeof(ss);
-       int ttl = conf.udp_ttl_arg, id = 0;
+       int ttl = OPT_INT32_VAL(UDP_TTL), id = 0;
        const int on = 1;
 
-       if (conf.udp_mcast_iface_given) {
-               char *iface = conf.udp_mcast_iface_arg;
+       if (OPT_GIVEN(UDP_MCAST_IFACE)) {
+               const char *iface = OPT_STRING_VAL(UDP_MCAST_IFACE);
 
                id = if_nametoindex(iface);
                if (id == 0)
@@ -174,7 +175,7 @@ static int udp_resolve_target(const char *url, struct sender_command_data *scd)
        ret = parse_fec_url(url, scd);
        if (ret)
                return ret;
-       port = scd->port > 0 ? scd->port : conf.udp_default_port_arg;
+       port = scd->port > 0 ? scd->port : OPT_UINT32_VAL(UDP_DEFAULT_PORT);
 
        ret = para_connect_simple(IPPROTO_UDP, scd->host, port);
        if (ret < 0)
@@ -372,7 +373,7 @@ static char *udp_status(void)
                "port: %s\n"
                "targets: %s\n",
                (sender_status == SENDER_on)? "on" : "off",
-               stringify_port(conf.udp_default_port_arg, "udp"),
+               stringify_port(OPT_UINT32_VAL(UDP_DEFAULT_PORT), "udp"),
                tgts? tgts : "(none)"
        );
        free(tgts);
@@ -385,10 +386,11 @@ static void udp_init_target_list(void)
        int i;
 
        INIT_LIST_HEAD(&targets);
-       for (i = 0; i < conf.udp_target_given; i++) {
-               if (udp_resolve_target(conf.udp_target_arg[i], &scd) < 0)
+       for (i = 0; i < OPT_GIVEN(UDP_TARGET); i++) {
+               const char *arg = lls_string_val(i, OPT_RESULT(UDP_TARGET));
+               if (udp_resolve_target(arg, &scd) < 0)
                        PARA_CRIT_LOG("not adding requested target '%s'\n",
-                                       conf.udp_target_arg[i]);
+                                       arg);
                else
                        udp_com_add(&scd);
        }
@@ -438,7 +440,7 @@ void udp_send_init(struct sender *s)
        s->client_cmds[SENDER_delete] = udp_com_delete;
        sender_status = SENDER_off;
        udp_init_target_list();
-       if (!conf.udp_no_autostart_given)
+       if (!OPT_GIVEN(UDP_NO_AUTOSTART))
                sender_status = SENDER_on;
        PARA_DEBUG_LOG("udp sender init complete\n");
 }
index 3a77e98fa8c4636406035ce66f7842235832db4d..948ed503093e53d1df0e29430619c22092489235 100644 (file)
@@ -15,6 +15,7 @@
  * is read at server startup.
  */
 enum server_command_permissions {
+       NO_PERMISSION_REQUIRED = 0, /** None of the below. */
        AFS_READ = 1, /** Read-only operation on the AFS database. */
        AFS_WRITE = 2, /** Read-write operation on the AFS database. */
        VSS_READ = 4, /** Read-only operation on the virtual streaming system. */
diff --git a/vss.c b/vss.c
index 32e36a997fadadca81df24e12a776b974423420e..3632cf54557350f35dc19ec8a2ce07ce9a6d5c0f 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -19,7 +19,9 @@
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "portable_io.h"
@@ -29,7 +31,6 @@
 #include "afs.h"
 #include "server.h"
 #include "net.h"
-#include "server.cmdline.h"
 #include "list.h"
 #include "send.h"
 #include "sched.h"
@@ -1182,10 +1183,8 @@ void init_vss_task(int afs_socket, struct sched *s)
        static struct vss_task vss_task_struct, *vsst = &vss_task_struct;
        int i;
        char *hn = para_hostname(), *home = para_homedir();
-       long unsigned announce_time = conf.announce_time_arg > 0?
-                       conf.announce_time_arg : 300,
-               autoplay_delay = conf.autoplay_delay_arg > 0?
-                       conf.autoplay_delay_arg : 0;
+       long unsigned announce_time = OPT_UINT32_VAL(ANNOUNCE_TIME),
+               autoplay_delay = OPT_UINT32_VAL(AUTOPLAY_DELAY);
        vsst->header_interval.tv_sec = 5; /* should this be configurable? */
        vsst->afs_socket = afs_socket;
        ms2tv(announce_time, &vsst->announce_tv);
@@ -1198,7 +1197,7 @@ void init_vss_task(int afs_socket, struct sched *s)
        free(hn);
        free(home);
        mmd->sender_cmd_data.cmd_num = -1;
-       if (conf.autoplay_given) {
+       if (OPT_GIVEN(AUTOPLAY)) {
                struct timeval tmp;
                mmd->vss_status_flags |= VSS_PLAYING;
                mmd->new_vss_status_flags |= VSS_PLAYING;
index 88047adbb71c2becef6afe13f45ab96ddd3b1774..eb38cd7d9626cc9be41957e2f6a04619503bb891 100644 (file)
@@ -12,7 +12,6 @@
 #include "error.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
@@ -120,15 +119,9 @@ err:
        return ret;
 }
 
-/**
- * The init function of the wav filter.
- *
- * \param f Structure to initialize.
- */
-void wav_filter_init(struct filter *f)
-{
-       f->close = wav_close;
-       f->open = wav_open;
-       f->pre_select = wav_pre_select;
-       f->post_select = wav_post_select;
-}
+const struct filter lsg_filter_cmd_com_wav_user_data = {
+       .close = wav_close,
+       .open = wav_open,
+       .pre_select = wav_pre_select,
+       .post_select = wav_post_select,
+};
index b226943b199a224e6b38e4685235707982e23f11..9e8b59cee3e6cbb27ba7345c064c0da227046626 100644 (file)
@@ -29,7 +29,7 @@
        [<a href="para_filter.man.html">para_filter</a>]
        [<a href="para_write.man.html">para_write</a>]
        [<a href="para_gui.man.html">para_gui</a>]
-       [<a href="para_fade.man.html">para_fade</a>]
+       [<a href="para_mixer.man.html">para_mixere</a>]
        [<a href="para_play.man.html">para_play</a>]
 </p>
 
index dff8a22a659c7c61f33ce6fef8534dd89f133526..5216c6b5cf03153b797611776045e6ed06bd6110 100644 (file)
@@ -166,7 +166,7 @@ window. Appearance can be customized via themes. para_gui provides
 key-bindings for the most common server commands and new key-bindings
 can be added easily.
 
-### para_fade ###
+### para_mixer ###
 
 An alarm clock and volume-fader for OSS and ALSA.
 
@@ -186,7 +186,7 @@ Requirements
 
        git clone git://git.tuebingen.mpg.de/osl
        cd osl && make && sudo make install && sudo ldconfig
-       sudo apt-get install autoconf libssl-dev help2man gengetopt m4 \
+       sudo apt-get install autoconf libssl-dev m4 \
               libmad0-dev libid3tag0-dev libasound2-dev libvorbis-dev \
               libfaad-dev libspeex-dev libFLAC-dev libsamplerate-dev realpath \
               libasound2-dev libao-dev libreadline-dev libncurses-dev \
@@ -202,6 +202,13 @@ code repository, execute
 
                git clone git://git.tuebingen.mpg.de/osl
 
+- [lopsub](http://people.tuebingen.mpg.de/maan/lopsub/). The long
+option parser for subcommands generates the command line and config
+file parsers for all paraslash executables. Clone the source code
+repository with
+
+               git clone git://git.tuebingen.mpg.de/lopsub
+
 - [gcc](ftp://ftp.gnu.org/pub/gnu/gcc) or
 [clang](http://clang.llvm.org). All gcc versions >= 4.2 are currently
 supported. Clang version 1.1 or newer should work as well.
@@ -213,13 +220,6 @@ disto. On BSD systems the gnu make executable is often called gmake.
 during compilation require the _Bourne again shell_.  It is most
 likely already installed.
 
-- [gengetopt](ftp://ftp.gnu.org/pub/gnu/gengetopt/) is needed to
-generate the C code for the command line parsers of all paraslash
-executables.
-
-- [help2man](ftp://ftp.gnu.org/pub/gnu/help2man) is used to create
-the man pages.
-
 - [m4](ftp://ftp.gnu.org/pub/gnu/m4/). Some source files are generated
 from templates by the m4 macro processor.
 
index 692ea0f3dde1d25996a874aff00e1a6a1f7b043d..525ed3150cda5affb9b479029b7e9aa5f461a0aa 100644 (file)
@@ -24,7 +24,6 @@
 #include "para.h"
 #include "error.h"
 #include "list.h"
-#include "ggo.h"
 #include "string.h"
 #include "sched.h"
 #include "buffer_tree.h"
@@ -1260,16 +1259,10 @@ static void wmadec_open(struct filter_node *fn)
        fn->min_iqs = 4096;
 }
 
-/**
- * The init function of the wma decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void wmadec_filter_init(struct filter *f)
-{
-       f->open = wmadec_open;
-       f->close = wmadec_close;
-       f->execute = wmadec_execute;
-       f->pre_select = generic_filter_pre_select;
-       f->post_select = wmadec_post_select;
-}
+const struct filter lsg_filter_cmd_com_wmadec_user_data = {
+       .open = wmadec_open,
+       .close = wmadec_close,
+       .execute = wmadec_execute,
+       .pre_select = generic_filter_pre_select,
+       .post_select = wmadec_post_select,
+};
diff --git a/write.c b/write.c
index 9ff3bc8406a252819a686d4d6e14d330951a6278..a1907b6f46a04f966722117e894f9fe0a69edb11 100644 (file)
--- a/write.c
+++ b/write.c
@@ -8,17 +8,17 @@
 
 #include <regex.h>
 #include <sys/types.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
+#include "write.lsg.h"
 #include "para.h"
 #include "string.h"
-#include "write.cmdline.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "stdin.h"
 #include "buffer_tree.h"
 #include "write.h"
-#include "write_common.h"
 #include "fd.h"
 #include "error.h"
 #include "version.h"
 /** Array of error strings. */
 DEFINE_PARA_ERRLIST;
 
-static struct write_args_info conf;
+#define CMD_PTR (lls_cmd(0, write_suite))
+#define OPT_RESULT(_name, _lpr) \
+       (lls_opt_result(LSG_WRITE_PARA_WRITE_OPT_ ## _name, _lpr))
+#define OPT_GIVEN(_name, _lpr) (lls_opt_given(OPT_RESULT(_name, _lpr)))
+#define OPT_UINT32_VAL(_name, _lpr) (lls_uint32_val(0, OPT_RESULT(_name, _lpr)))
 
 static struct stdin_task sit;
-
 static int loglevel;
 INIT_STDERR_LOGGING(loglevel)
 
-__noreturn static void print_help_and_die(void)
+static void handle_help_flag(struct lls_parse_result *lpr)
 {
-       struct ggo_help h = DEFINE_GGO_HELP(write);
-       bool d = conf.detailed_help_given;
-
-       ggo_print_help(&h, d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS);
-       print_writer_helps(d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS);
-       exit(0);
-}
-
-/*
- * Parse config and register a task for a writer node.
- *
- * \param arg Command line arguments.
- * \param parent The new node will be a child of \a parent.
- * \param wn The writer node.
- *
- * If arg is \p NULL, the OS-dependent default writer is used with no
- * arguments. The default writers are alsa for Linux and oss for *BSD, and the
- * file writer if neither is supported.
- *
- * Once the writer configuration has been retrieved from the ->parse_config
- * callback a writer node is created, its buffer tree node is added to the
- * buffer tree as a child of the given parent.
- *
- * Finally, the new writer node's task structure is initialized and registered
- * to the paraslash scheduler.
- *
- * \return Standard.
- */
-static void setup_writer_node(const char *arg, struct btr_node *parent,
-               struct writer_node *wn, struct sched *s)
-{
-       wn->conf = check_writer_arg_or_die(arg, &wn->writer_num);
-       register_writer_node(wn, parent, s);
+       char *help;
+
+       if (OPT_GIVEN(DETAILED_HELP, lpr))
+               help = lls_long_help(CMD_PTR);
+       else if (OPT_GIVEN(HELP, lpr))
+               help = lls_short_help(CMD_PTR);
+       else
+               return;
+       printf("%s\n", help);
+       free(help);
+       print_writer_helps(OPT_GIVEN(DETAILED_HELP, lpr));
+       exit(EXIT_SUCCESS);
 }
 
 struct write_task {
@@ -88,9 +70,9 @@ static int write_post_select(__a_unused struct sched *s, void *context)
        return check_wav_post_select(wt->cwc);
 }
 
-static int setup_and_schedule(void)
+static int setup_and_schedule(struct lls_parse_result *lpr)
 {
-       int i, ret;
+       int i, n, ret, writer_given = OPT_GIVEN(WRITER, lpr);
        struct btr_node *cw_btrn;
        struct writer_node *wns;
        static struct sched s;
@@ -101,7 +83,7 @@ static int setup_and_schedule(void)
                EMBRACE(.name = "stdin"));
        stdin_task_register(&sit, &s);
 
-       COPY_WAV_PARMS(&wp, &conf);
+       LLS_COPY_WAV_PARMS(&wp, LSG_WRITE_PARA_WRITE, lpr);
        wt.cwc = check_wav_init(sit.btrn, NULL, &wp, &cw_btrn);
        wt.task = task_register(&(struct task_info) {
                .name = "write",
@@ -109,28 +91,26 @@ static int setup_and_schedule(void)
                .post_select = write_post_select,
                .context = &wt,
        }, &s);
-       if (!conf.writer_given) {
-               wns = para_calloc(sizeof(*wns));
-               setup_writer_node(NULL, cw_btrn, wns, &s);
-               i = 1;
-       } else {
-               wns = para_calloc(conf.writer_given * sizeof(*wns));
-               for (i = 0; i < conf.writer_given; i++)
-                       setup_writer_node(conf.writer_arg[i], cw_btrn,
-                               wns + i, &s);
-       }
 
+       n = writer_given? writer_given : 1;
+       wns = para_calloc(n * sizeof(*wns));
+       for (i = 0; i < n; i++) {
+               const char *arg = i < writer_given?
+                       lls_string_val(i, OPT_RESULT(WRITER, lpr)) : NULL;
+               wns[i].wid = check_writer_arg_or_die(arg, &wns[i].lpr);
+               register_writer_node(wns + i, cw_btrn, &s);
+       }
        s.default_timeout.tv_sec = 10;
        s.default_timeout.tv_usec = 50000;
        ret = schedule(&s);
        if (ret >= 0) {
                int j, ts;
-               for (j = 0; j < i; j++) {
+               for (j = 0; j < n; j++) {
                        struct writer_node *wn = wns + j;
                        ts = task_status(wn->task);
                        assert(ts < 0);
                        if (ts != -E_WRITE_COMMON_EOF && ts != -E_BTR_EOF) {
-                               const char *name = writer_names[wn->writer_num];
+                               const char *name = writer_name(wn->wid);
                                PARA_ERROR_LOG("%s: %s\n", name,
                                        para_strerror(-ts));
                                if (ret >= 0)
@@ -138,14 +118,12 @@ static int setup_and_schedule(void)
                        }
                }
        }
-       for (i--; i >= 0; i--) {
+       for (i = n - 1; i >= 0; i--) {
                struct writer_node *wn = wns + i;
-               struct writer *w = writers + wn->writer_num;
-
-               w->close(wn);
+               writer_get(wn->wid)->close(wn);
                btr_remove_node(&wn->btrn);
-               w->free_config(wn->conf);
-               free(wn->conf);
+               lls_free_parse_result(wns[i].lpr,
+                       lls_cmd(wn->wid, write_cmd_suite));
        }
        free(wns);
        check_wav_shutdown(wt.cwc);
@@ -167,18 +145,23 @@ static int setup_and_schedule(void)
 int main(int argc, char *argv[])
 {
        int ret;
-
-       write_cmdline_parser(argc, argv, &conf);
-       loglevel = get_loglevel_by_name(conf.loglevel_arg);
-       writer_init();
-       version_handle_flag("write", conf.version_given);
-       if (conf.help_given || conf.detailed_help_given)
-               print_help_and_die();
-
-       ret = setup_and_schedule();
+       struct lls_parse_result *lpr;
+       char *errctx;
+
+       ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       loglevel = OPT_UINT32_VAL(LOGLEVEL, lpr);
+       version_handle_flag("write",  OPT_GIVEN(VERSION, lpr));
+       handle_help_flag(lpr);
+       ret = setup_and_schedule(lpr);
+       lls_free_parse_result(lpr, CMD_PTR);
+out:
        if (ret < 0) {
+               if (errctx)
+                       PARA_ERROR_LOG("%s\n", errctx);
+               free(errctx);
                PARA_ERROR_LOG("%s\n", para_strerror(-ret));
-               exit(EXIT_FAILURE);
        }
-       exit(EXIT_SUCCESS);
+       return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/write.h b/write.h
index c7c227eefb146db15160b70d8cb25618adb1f0b3..fd3983ddf262f1edec986680c856c1d7f7c9c511 100644 (file)
--- a/write.h
+++ b/write.h
@@ -6,19 +6,16 @@
 
 /** \file write.h Writer-related structures. */
 
-/** The list of supported writers. */
-enum writer_enum {WRITER_ENUM};
-
 /**
  * Describes one running instance of a writer.
  */
 struct writer_node {
-       /** The number of this writer. */
-       int writer_num;
+       /** The ID of this writer. */
+       int wid;
        /** Writer-specific data. */
        void *private_data;
-       /** The writer-specific configuration of this node. */
-       void *conf;
+       /** The parsed command line, merged with options given in the config file. */
+       struct lls_parse_result *lpr;
        /** The buffer tree node associated with this writer node. */
        struct btr_node *btrn;
        /** The task of this writer node. */
@@ -29,30 +26,6 @@ struct writer_node {
 
 /** Describes one supported writer. */
 struct writer {
-       /**
-        * The init function of the writer.
-        *
-        * It must fill in all other function pointers of the given
-        * writer structure.
-        */
-       void (*init)(struct writer *w);
-       /**
-        * The command line parser of the writer.
-        *
-        * It should check whether the command line options given by \a argv
-        * and \a argc are valid and return a pointer to the writer-specific
-        * configuration data determined by these options. This function must
-        * either succeed or call exit(). Note that parse_config_or_die() might
-        * be called more than once with different values of \a options. \sa
-        * \ref free_config().
-        */
-       void *(*parse_config_or_die)(int argc, char **argv);
-       /**
-        * Deallocate all configuration resources.
-        *
-        * This should free whatever was allocated by \ref parse_config_or_die().
-        */
-       void (*free_config)(void *config);
        /**
         * Prepare the fd sets for select.
         *
@@ -72,8 +45,6 @@ struct writer {
         * This function is assumed to succeed.
         */
        void (*close)(struct writer_node *);
-       /** The short and the log help text of this writer. */
-       struct ggo_help help;
        /**
         * The callback handler.
         *
@@ -83,14 +54,23 @@ struct writer {
        btr_command_handler execute;
 };
 
-/** Loop over each supported writer. */
-#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
-
-/** Declare the init functions of all supported writers. */
-DECLARE_WRITER_INITS;
+#define WRITE_CMD(_num) (lls_cmd(_num, write_cmd_suite))
 
-/** Array containing the name of each writer. */
-extern const char *writer_names[];
+#define WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr) \
+       (lls_opt_result(LSG_WRITE_CMD_ ## _cmd ## _OPT_ ## _opt, _lpr))
+#define WRITE_CMD_OPT_GIVEN(_cmd, _opt, _lpr) \
+       (lls_opt_given(WRITE_CMD_OPT_RESULT(_cmd, _opt, _lpr)))
+#define WRITE_CMD_OPT_UINT32_VAL(_cmd, _opt, _lpr) \
+       (lls_uint32_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
+#define WRITE_CMD_OPT_STRING_VAL(_cmd, _opt, _lpr) \
+       (lls_string_val(0, WRITE_CMD_OPT_RESULT(_cmd, _opt, (_lpr))))
 
-/** The writer structure for each supported writer. */
-extern struct writer writers[NUM_SUPPORTED_WRITERS];
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp);
+const struct writer *writer_get(int wid);
+const char *writer_name(int wid);
+void register_writer_node(struct writer_node *wn, struct btr_node *parent,
+               struct sched *s);
+void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
+void get_btr_channels(struct btr_node *btrn, int32_t *result);
+void get_btr_sample_format(struct btr_node *btrn, int32_t *result);
+void print_writer_helps(bool detailed);
index cdb67e58fd65bf4438522fd0bed4ef9092aaaeaf..d4d1b10b2ec597fa58ecf77dfad82ac01efa5d0c 100644 (file)
 /** \file write_common.c common functions of para_audiod and para_write */
 
 #include <regex.h>
+#include <lopsub.h>
 
+#include "write_cmd.lsg.h"
 #include "para.h"
 #include "string.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "write.h"
 #include "error.h"
-#include "write_common.h"
 
-/** the array containing the names of all supported writers */
-const char *writer_names[] ={WRITER_NAMES};
+/** Loop over all writers. */
+#define FOR_EACH_WRITER(i) for (i = 1; lls_cmd(i, write_cmd_suite); i++)
 
-/** the array of supported writers */
-struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
 
-/**
- * Call the init function of each supported paraslash writer.
- */
-void writer_init(void)
+static inline bool writer_supported(int wid)
+{
+       return lls_user_data(WRITE_CMD(wid));
+}
+
+/* simply return the first available writer */
+static int default_writer_id(void)
 {
        int i;
 
        FOR_EACH_WRITER(i)
-               writers[i].init(&writers[i]);
+               if (writer_supported(i))
+                       return i;
+       assert(0); /* the file writer should always be available */
 }
+
 /**
- * Check if given string is a valid command line for any writer.
+ * Return the writer structure from a writer ID.
  *
- * \param \wa String of the form writer_name:options.
- * \param writer_num Contains the number of the writer upon success.
+ * \param wid If non-positive, a pointer to the default writer is returned.
  *
- * This function checks whether \a wa starts with the name of a supported
- * paraslash writer, optionally followed by a colon and any options for that
- * writer.  If a valid writer name was found and further are present, the
- * remaining part of \a wa is passed to that writer's config parser.
+ * \return Pointer to a (constant) struct writer.
+ */
+const struct writer *writer_get(int wid)
+{
+       if (wid < 0)
+               wid = default_writer_id();
+       return lls_user_data(WRITE_CMD(wid));
+}
+
+/**
+ * Return name of the writer identified by a writer ID.
+ *
+ * \param wid If non-positive, the name of the default writer is returned.
  *
- * \return On success, a pointer to the gengetopt args info struct is returned
- * and \a writer_num contains the number of the writer. Otherwise this function
- * prints an error message and calls exit().
+ * \return The returned buffer must not be freed by the caller.
  */
-void *check_writer_arg_or_die(const char *wa, int *writer_num)
+const char *writer_name(int wid)
 {
-       int i, ret, argc;
-       const char *cmdline;
-       char **argv;
-       void *conf;
+       if (wid <= 0)
+               wid = default_writer_id();
+       return lls_command_name(WRITE_CMD(wid));
+}
 
-       if (!wa || !*wa) {
-               i = DEFAULT_WRITER;
-               cmdline = NULL;
-               goto check;
-       }
-       PARA_INFO_LOG("checking %s\n", wa);
-       FOR_EACH_WRITER(i) {
-               const char *name = writer_names[i];
-               size_t len = strlen(name);
-               char c;
+/**
+ * Check if the given string is a valid command line for any writer.
+ *
+ * \param wa String of the form writer_name options.
+ * \param lprp Contains the parsed command line on success.
+ *
+ * If wa is \p NULL, the (configuration-dependent) default writer is assumed.
+ * Otherwise, the function checks whether \a wa starts with the name of a
+ * supported writer. If a valid writer name was found, the rest of the command
+ * line is passed to the config parser of this writer.
+ *
+ * \return On success, the positive writer ID is returned. Otherwise the
+ * function prints an error message and calls exit().
+ */
+int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp)
+{
+       int ret, writer_num, argc;
+       char **argv = NULL, *errctx = NULL;
+       const struct lls_command *cmd;
 
-               if (strlen(wa) < len)
-                       continue;
-               if (strncmp(name, wa, len))
-                       continue;
-               c = wa[len];
-               if (!c || c == ' ') {
-                       cmdline = c? wa + len + 1 : NULL;
-                       goto check;
-               }
-       }
-       PARA_EMERG_LOG("invalid writer %s\n", wa);
-       exit(EXIT_FAILURE);
-check:
-       ret = create_shifted_argv(cmdline, " \t", &argv);
-       if (ret < 0) {
-               PARA_EMERG_LOG("%s: %s\n", wa, para_strerror(-ret));
-               exit(EXIT_FAILURE);
+       if (!wa || !*wa) {
+               writer_num = default_writer_id();
+               cmd = WRITE_CMD(writer_num);
+               argv = para_malloc(2 * sizeof(char *));
+               argc = 1;
+               argv[0] = para_strdup(lls_command_name(cmd));
+               argv[1] = NULL;
+               goto parse;
        }
+       ret = create_argv(wa, " \t\n", &argv);
+       if (ret < 0)
+               goto fail;
        argc = ret;
-       argv[0] = make_message("%s_write", writer_names[i]);
-       *writer_num = i;
-       conf = writers[i].parse_config_or_die(argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], write_cmd_suite, &errctx));
+       if (ret < 0)
+               goto free_argv;
+       writer_num = ret;
+       cmd = WRITE_CMD(writer_num);
+       if (!writer_supported(writer_num)) {
+               ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+               errctx = make_message("%s writer is not supported",
+                       lls_command_name(cmd));
+               goto free_argv;
+       }
+parse:
+       ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx));
+       if (ret >= 0)
+               ret = writer_num;
+free_argv:
        free_argv(argv);
-       return conf;
+       if (ret >= 0)
+               return ret;
+fail:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
+       PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(EXIT_FAILURE);
 }
 
 /**
@@ -99,20 +132,17 @@ check:
  * \param wn The writer node to open.
  * \param parent The parent btr node (the source for the writer node).
  * \param s The scheduler instance to register the task to.
- *
- * The configuration of the writer node stored in \p wn->conf must be
- * initialized before this function may be called.
  */
 void register_writer_node(struct writer_node *wn, struct btr_node *parent,
                struct sched *s)
 {
-       struct writer *w = writers + wn->writer_num;
+       const struct writer *w = writer_get(wn->wid);
 
        wn->btrn = btr_new_node(&(struct btr_node_description)
-               EMBRACE(.name = writer_names[wn->writer_num], .parent = parent,
+               EMBRACE(.name = writer_name(wn->wid), .parent = parent,
                .handler = w->execute, .context = wn));
        wn->task = task_register(&(struct task_info) {
-               .name = writer_names[wn->writer_num],
+               .name = writer_name(wn->wid),
                .pre_select = w->pre_select,
                .post_select = w->post_select,
                .context = wn,
@@ -122,24 +152,29 @@ void register_writer_node(struct writer_node *wn, struct btr_node *parent,
 /**
  * Print the help text of all writers to stdout.
  *
- * \param flags Passed to \ref ggo_print_help().
+ * \param detailed Whether to print the short or the detailed help.
  */
-void print_writer_helps(unsigned flags)
+void print_writer_helps(bool detailed)
 {
        int i;
 
-       printf_or_die("\nAvailable writers: ");
-       FOR_EACH_WRITER(i)
-               printf_or_die("%s%s", i? " " : "", writer_names[i]);
-       printf_or_die("\n");
+       printf("\nAvailable writers: ");
        FOR_EACH_WRITER(i) {
-               struct writer *w = writers + i;
-
-               if (!w->help.short_help)
+               if (!writer_supported(i))
+                       continue;
+               printf("%s%s", i? " " : "", writer_name(i));
+       }
+       printf("\n");
+       FOR_EACH_WRITER(i) {
+               const struct lls_command *cmd = WRITE_CMD(i);
+               char *help;
+               if (!writer_supported(i))
+                       continue;
+               help = detailed? lls_long_help(cmd) : lls_short_help(cmd);
+               if (!help)
                        continue;
-               printf_or_die("\n%s: %s", writer_names[i],
-                       w->help.purpose);
-               ggo_print_help(&w->help, flags);
+               printf("%s\n", help);
+               free(help);
        }
 }
 
diff --git a/write_common.h b/write_common.h
deleted file mode 100644 (file)
index 1828875..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file write_common.h Exported symbols from write_common.c. */
-
-void writer_init(void);
-void *check_writer_arg_or_die(const char *wa, int *writer_num);
-void print_writer_helps(unsigned flags);
-void register_writer_node(struct writer_node *wn, struct btr_node *parent,
-               struct sched *s);
-void get_btr_sample_rate(struct btr_node *btrn, int32_t *result);
-void get_btr_channels(struct btr_node *btrn, int32_t *result);
-void get_btr_sample_format(struct btr_node *btrn, int32_t *result);