diff --git a/.gitignore b/.gitignore index bfd98ee7..6a5c5ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ tests/db_charset_test tests/getpids_test tests/log_unlink_test tests/mcast_recv_test +tests/notification_test tests/rdxml_parse_test tests/reserve_carts_test tests/sas_switch_torture @@ -128,6 +129,7 @@ utils/rddgimport/rddgimport utils/rddiscimport/rddiscimport utils/rdcheckcuts/rdcheckcuts utils/rdmarkerset/rdmarkerset +utils/rdmetadata/rdmetadata utils/rmlsend/rmlsend utils/rdpurgecasts/rdpurgecasts utils/rdchunk/rdchunk diff --git a/ChangeLog b/ChangeLog index 6080ab3b..0ca5f7da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18713,3 +18713,9 @@ 2019-06-02 Patrick Linstruth * Added '--orphaned-audio', '--orphaned-carts', '--orphaned-cuts', and '--orphaned-tracks' command line switches to rddbmgr(8). +2019-06-02 Patrick Linstruth + * Added a 'notification_test' test harness. + * Added rdmetadata(8) utility for setting cart metadata from the + command line. +2019-06-03 Fred Gleason + * Moved rdmetadata(8) to rdmetadata(1). diff --git a/configure.ac b/configure.ac index c0a07337..30135985 100644 --- a/configure.ac +++ b/configure.ac @@ -552,6 +552,7 @@ AC_CONFIG_FILES([rivendell.spec \ utils/rdimport/Makefile \ utils/rdmaint/Makefile \ utils/rdmarkerset/Makefile \ + utils/rdmetadata/Makefile \ utils/rdpopup/Makefile \ utils/rdpurgecasts/Makefile \ utils/rdrender/Makefile \ diff --git a/docs/manpages/Makefile.am b/docs/manpages/Makefile.am index b2246cb8..1abd68f8 100644 --- a/docs/manpages/Makefile.am +++ b/docs/manpages/Makefile.am @@ -37,6 +37,7 @@ all-local: rdclilogedit.1\ rdexport.1\ rdimport.1\ rdmarkerset.8\ + rdmetadata.1\ rdrender.1\ rmlsend.1\ rdservice.8 @@ -47,6 +48,7 @@ man_MANS = rdclilogedit.1\ rdexport.1\ rdimport.1\ rdmarkerset.8\ + rdmetadata.1\ rdrender.1\ rmlsend.1\ rdservice.8 @@ -63,6 +65,8 @@ EXTRA_DIST = rdclilogedit.1\ rdimport.xml\ rdmarkerset.8\ rdmarkerset.xml\ + rdmetadata.1\ + rdmetadata.xml\ rdrender.1\ rdrender.xml\ rmlsend.1\ diff --git a/docs/manpages/rdmetadata.xml b/docs/manpages/rdmetadata.xml new file mode 100644 index 00000000..655e54b5 --- /dev/null +++ b/docs/manpages/rdmetadata.xml @@ -0,0 +1,240 @@ + + + + + rdmetadata + 1 + May 2019 + Linux Audio Manual + + + rdmetadata + Tool for altering Rivendell cart metadata + + + + + Patrick + Linstruth + patrick@deltecent.com + + Application Author + + + + + + + rdmetadata --cart-number=cart + OPTIONS + + + + + Description + + rdmetadata1 is a command-line + tool for setting or altering cart metadata in the Rivendell Radio + Automation System. + + + + Options + + + + schedcode + + + + Add scheduler code schedcode to + cart. + Default action is to leave the scheduler codes unaltered. + + + + + + + agency + + + + Set cart agency metadata + to agency. + Default action is to leave the agency unaltered. + + + + + + + album + + + + Set cart album metadata + to album. + Default action is to leave the album unaltered. + + + + + + + artist + + + + Set cart artist metadata + to artist. + Default action is to leave the artist unaltered. + + + + + + + bpm + + + + Set cart BPM metadata + to bpm. + Default action is to leave the BPM unaltered. + + + + + + + composer + + + + Set cart composer metadata + to composer. + Default action is to leave the composer unaltered. + + + + + + + conductor + + + + Set cart conductor metadata + to conductor. + Default action is to leave the conductor unaltered. + + + + + + + label + + + + Set cart label metadata + to label. + Default action is to leave the label unaltered. + + + + + + + publisher + + + + Set cart publisher metadata + to publisher. + Default action is to leave the publisher unaltered. + + + + + + + schedcode + + + + Remove scheduler code schedcode from + cart. + Default action is to leave the scheduler codes unaltered. + + + + + + + songid + + + + Set cart song ID metadata + to songid. + Default action is to leave the song ID unaltered. + + + + + + + title + + + + Set cart title metadata + to title. + Default action is to leave the title unaltered. + + + + + + + year + + + + Set cart year metadata + to year. + Default action is to leave the year unaltered. + + + + + + + + + + + Print messages to STDOUT describing progress. + + + + + + + See Also + + + rdlibrary1 + + + + + + + diff --git a/lib/rdschedcode.cpp b/lib/rdschedcode.cpp index 67182c43..d4000184 100644 --- a/lib/rdschedcode.cpp +++ b/lib/rdschedcode.cpp @@ -27,6 +27,14 @@ RDSchedCode::RDSchedCode(const QString &code) { sched_code=code; + + // + // Get proper case for code + // + if(exists()) { + sched_code=RDGetSqlValue("SCHED_CODES","CODE",code,"CODE"). + toString(); + } } diff --git a/rivendell.spec.in b/rivendell.spec.in index a2d5c597..79af74ce 100644 --- a/rivendell.spec.in +++ b/rivendell.spec.in @@ -256,6 +256,7 @@ rm -rf $RPM_BUILD_ROOT @LOCAL_PREFIX@/bin/rddelete @LOCAL_PREFIX@/bin/rdexport @LOCAL_PREFIX@/bin/rdimport +@LOCAL_PREFIX@/bin/rdmetadata @LOCAL_PREFIX@/bin/rdpurgecasts @LOCAL_PREFIX@/bin/rdmaint @LOCAL_PREFIX@/bin/rdcollect @@ -383,6 +384,7 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/rdconvert.1.gz %{_mandir}/man1/rdexport.1.gz %{_mandir}/man1/rdimport.1.gz +%{_mandir}/man1/rdmetadata.1.gz %{_mandir}/man1/rdrender.1.gz %{_mandir}/man1/rmlsend.1.gz %{_mandir}/man8/rddbmgr.8.gz diff --git a/tests/Makefile.am b/tests/Makefile.am index 7fd499ab..b884b77f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -37,6 +37,7 @@ noinst_PROGRAMS = audio_convert_test\ getpids_test\ log_unlink_test\ mcast_recv_test\ + notification_test\ rdxml_parse_test\ reserve_carts_test\ stringcode_test\ @@ -81,6 +82,10 @@ dist_mcast_recv_test_SOURCES = mcast_recv_test.cpp mcast_recv_test.h nodist_mcast_recv_test_SOURCES = moc_mcast_recv_test.cpp mcast_recv_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ -lQt3Support +dist_notification_test_SOURCES = notification_test.cpp notification_test.h +nodist_notification_test_SOURCES = moc_notification_test.cpp +notification_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ -lQt3Support + dist_rdxml_parse_test_SOURCES = rdxml_parse_test.cpp rdxml_parse_test.h rdxml_parse_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ -lQt3Support diff --git a/tests/notification_test.cpp b/tests/notification_test.cpp new file mode 100644 index 00000000..329ad445 --- /dev/null +++ b/tests/notification_test.cpp @@ -0,0 +1,70 @@ +// upload_test.cpp +// +// Test Rivendell file uploading. +// +// (C) Copyright 2019 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include + +#include +#include + +#include + +#include "notification_test.h" + +MainObject::MainObject(QObject *parent) + :QObject(parent) +{ + QString err_msg; + + rda=new RDApplication("notification_test","notification_test",NOTIFICATION_TEST_USAGE,this); + if(!rda->open(&err_msg)) { + fprintf(stderr,"notification_test: %s\n",(const char *)err_msg); + exit(1); + } + + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(!rda->cmdSwitch()->processed(i)) { + fprintf(stderr,"rdrepld: unknown command option \"%s\"\n", + (const char *)rda->cmdSwitch()->key(i)); + exit(2); + } + } + + // + // Run the Test + // + connect(rda->ripc(),SIGNAL(notificationReceived(RDNotification *)), + this,SLOT(notificationReceivedData(RDNotification *))); + rda->ripc()-> + connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password()); +} + + +void MainObject::notificationReceivedData(RDNotification *notify) +{ + printf("%s\n",(const char *)notify->write()); +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv,false); + new MainObject(); + return a.exec(); +} diff --git a/tests/notification_test.h b/tests/notification_test.h new file mode 100644 index 00000000..72c93a12 --- /dev/null +++ b/tests/notification_test.h @@ -0,0 +1,43 @@ +// upload_test.h +// +// Test Rivendell Notifications +// +// (C) Copyright 2019 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef NOTIFICATION_TEST_H +#define NOTIFICATION_TEST_H + +#include + +#include + +#define NOTIFICATION_TEST_USAGE "" + +class MainObject : public QObject +{ + Q_OBJECT + public: + MainObject(QObject *parent=0); + + private slots: + void notificationReceivedData(RDNotification *notify); + + private: +}; + + +#endif // NOTIFICATION_TEST_H diff --git a/utils/Makefile.am b/utils/Makefile.am index dd6dbc15..e6cb45fd 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -40,6 +40,7 @@ SUBDIRS = $(ALSACONFIG_RD_OPT)\ rdimport\ rdmaint\ rdmarkerset\ + rdmetadata\ rdpopup\ rdpurgecasts\ rdrender\ diff --git a/utils/rdmetadata/Makefile.am b/utils/rdmetadata/Makefile.am new file mode 100644 index 00000000..add6e882 --- /dev/null +++ b/utils/rdmetadata/Makefile.am @@ -0,0 +1,48 @@ +## Makefile.am +## +## (C) Copyright 2002-2014,2016-2018 Fred Gleason +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License version 2 as +## published by the Free Software Foundation. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public +## License along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +## +## Use automake to process this into a Makefile.in + +AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -I$(top_srcdir)/lib @QT4_CFLAGS@ -DQT3_SUPPORT -I/usr/include/Qt3Support +LIBS = -L$(top_srcdir)/lib +MOC = @QT_MOC@ + +# The dependency for qt's Meta Object Compiler (moc) +moc_%.cpp: %.h + $(MOC) $< -o $@ + +bin_PROGRAMS = rdmetadata + +dist_rdmetadata_SOURCES = rdmetadata.cpp rdmetadata.h + +nodist_rdmetadata_SOURCES = moc_rdmetadata.cpp + +rdmetadata_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT4_LIBS@ -lQt3Support + +EXTRA_DIST = + +CLEANFILES = *~\ + *.idb\ + *ilk\ + *.obj\ + *.pdb\ + *.qm\ + moc_* + +MAINTAINERCLEANFILES = *~\ + Makefile.in\ + moc_* diff --git a/utils/rdmetadata/rdmetadata.cpp b/utils/rdmetadata/rdmetadata.cpp new file mode 100644 index 00000000..25ee6cf6 --- /dev/null +++ b/utils/rdmetadata/rdmetadata.cpp @@ -0,0 +1,315 @@ +// rdmetadata.cpp +// +// Command-line tool for setting Rivendell Cart Metadata +// +// Patrick Linstruth +// (C) Copyright 2019 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "rd.h" +#include "rdapplication.h" +#include "rdcart.h" +#include "rdschedcode.h" + +#include "rdmetadata.h" + +MainObject::MainObject(QObject *parent) + :QObject(parent) +{ + QString err_msg; + + verbose=false; + cartnum=0; + year=0; + bpm=0; + + // + // Open the Database + // + rda=new RDApplication("rdmetadata","rdmetadata",RDMETADATA_USAGE,this); + if(!rda->open(&err_msg)) { + fprintf(stderr,"rdmetadata: %s\n",(const char *)err_msg); + exit(1); + } + + // + // Read Command Options + // + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(rda->cmdSwitch()->key(i)=="--cart-number") { + cartnum=rda->cmdSwitch()->value(i).toUInt(); + cartstring=QString().sprintf("%06u",cartnum); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--artist") { + artist=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--title") { + title=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--album") { + album=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--year") { + year=rda->cmdSwitch()->value(i).toInt(); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--conductor") { + conductor=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--label") { + label=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--agency") { + agency=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--publisher") { + publisher=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--composer") { + composer=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--songid") { + songid=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--bpm") { + bpm=rda->cmdSwitch()->value(i).toInt(); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--add-schedcode") { + add_schedcode=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--rem-schedcode") { + rem_schedcode=rda->cmdSwitch()->value(i); + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--verbose") { + verbose=true; + rda->cmdSwitch()->setProcessed(i,true); + } + if(!rda->cmdSwitch()->processed(i)) { + fprintf(stderr,"rdmetadata: unknown command option \"%s\"\n", + (const char *)rda->cmdSwitch()->key(i)); + exit(2); + } + } + + // + // Sanity Checks + // + if(!cartnum) { + fprintf(stderr,"rdmetadata: --cart-number must be specified\n"); + exit(256); + } + + if(!add_schedcode.isEmpty()) { + RDSchedCode *sched=new RDSchedCode(add_schedcode); + if (!sched->exists()) { + fprintf(stderr,"rdmetadata: scheduler code '%s' does not exist\n", + qPrintable(sched->code())); + exit(256); + } + else { + // Get proper case + add_schedcode=sched->code(); + } + delete sched; + } + + if(!rem_schedcode.isEmpty()) { + RDSchedCode *sched=new RDSchedCode(rem_schedcode); + if (!sched->exists()) { + fprintf(stderr,"rdmetadata: scheduler code '%s' does not exist\n", + qPrintable(sched->code())); + exit(256); + } + else { + // Get proper case + rem_schedcode=sched->code(); + } + delete sched; + } + + // + // RIPCD Connection + // + connect(rda,SIGNAL(userChanged()),this,SLOT(userChangedData())); + rda->ripc()->connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password()); +} + + +void MainObject::userChangedData() +{ + // + // Get User Context + // + disconnect(rda->ripc(),SIGNAL(userChanged()),this,SLOT(userChangedData())); + + // + // Verify Permissions + // + if(!rda->user()->modifyCarts()) { + fprintf(stderr,"%s", + qPrintable(QString("rdmetadata: user \"%1\" has no modify carts permission\n") + .arg(rda->user()->name()))); + rda->log(RDConfig::LogErr, + QString("rdmetadata: user \"%1\" has no modify carts permission\n") + .arg(rda->user()->name())); + exit(256); + } + + updateMetadata(); + + exit(0); +} + + +void MainObject::updateMetadata() +{ + RDCart *cart; + QStringList schedcodes; + + cart=new RDCart(cartnum); + + if(!cart->exists()) { + fprintf(stderr,"rdmetadata: cart %06u does not exist.\n",cartnum); + exit(1); + } + + schedcodes=cart->schedCodesList(); + + if(!artist.isEmpty()) { + cart->setArtist(artist); + Print(QString("rdmetadata: Set cart %1 artist to '%2'").arg(cartstring).arg(artist)); + } + + if(!title.isEmpty()) { + cart->setTitle(title); + Print(QString("rdmetadata: Set cart %1 title to '%2'").arg(cartstring).arg(title)); + } + + if(!album.isEmpty()) { + cart->setAlbum(album); + Print(QString("rdmetadata: Set cart %1 album to '%2'").arg(cartstring).arg(album)); + } + + if(year) { + cart->setYear(year); + Print(QString("rdmetadata: Set cart %1 year to '%2'").arg(cartstring).arg(year)); + } + + if(!conductor.isEmpty()) { + cart->setConductor(conductor); + Print(QString("rdmetadata: Set cart %1 conductor to '%2'").arg(cartstring).arg(conductor)); + } + + if(!label.isEmpty()) { + cart->setLabel(label); + Print(QString("rdmetadata: Set cart %1 label to '%2'").arg(cartstring).arg(label)); + } + + if(!agency.isEmpty()) { + cart->setAgency(agency); + Print(QString("rdmetadata: Set cart %1 agency to '%2'").arg(cartstring).arg(agency)); + } + + if(!publisher.isEmpty()) { + cart->setPublisher(publisher); + Print(QString("rdmetadata: Set cart %1 publisher to '%2'").arg(cartstring).arg(publisher)); + } + + if(!composer.isEmpty()) { + cart->setComposer(composer); + Print(QString("rdmetadata: Set cart %1 composer to '%2'").arg(cartstring).arg(composer)); + } + + if(!songid.isEmpty()) { + cart->setSongId(songid); + Print(QString("rdmetadata: Set cart %1 songid to '%2'").arg(cartstring).arg(songid)); + } + + if(bpm) { + cart->setBeatsPerMinute(bpm); + Print(QString("rdmetadata: Set cart %1 bpm to '%2'").arg(cartstring).arg(bpm)); + } + + if(!add_schedcode.isEmpty()&&!schedcodes.contains(add_schedcode)) { + cart->addSchedCode(add_schedcode); + Print(QString("rdmetadata: Added scheduler code '%1' to cart %2").arg(add_schedcode).arg(cartstring)); + } + + if(!rem_schedcode.isEmpty()&&schedcodes.contains(rem_schedcode)) { + cart->removeSchedCode(rem_schedcode); + Print(QString("rdmetadata: Removed scheduler code '%1' from cart %2").arg(rem_schedcode).arg(cartstring)); + } + + delete cart; + + SendNotification(RDNotification::ModifyAction,cartnum); +} + +void MainObject::SendNotification(RDNotification::Action action,unsigned cartnum) +{ + RDNotification *notify= + new RDNotification(RDNotification::CartType,action,QVariant(cartnum)); + rda->ripc()->sendNotification(*notify); + delete notify; + + // + // Allow the notification to be processed + // + qApp->processEvents(); +} + + + +void MainObject::Print(const QString &msg) +{ + if(verbose) { + printf("%s\n",(const char *)msg); + } +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv,false); + new MainObject(); + return a.exec(); +} diff --git a/utils/rdmetadata/rdmetadata.h b/utils/rdmetadata/rdmetadata.h new file mode 100644 index 00000000..b4420bb5 --- /dev/null +++ b/utils/rdmetadata/rdmetadata.h @@ -0,0 +1,83 @@ +// rdmarkerset.h +// +// Command-line tool for setting Rivendell Cart Metadata +// +// Patrick Linstruth +// (C) Copyright 2019 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + + +#ifndef RDMETADATA_H +#define RDMETADATA_H + +#include + +#include + +#include + +#include "rdnotification.h" + +#define RDMETADATA_USAGE "--cart-number= [options]\n\nThe following options are recognized:\n\n\ + --add_schedcode=\n\ + --agency=\n\ + --album=\n\ + --artist=\n\ + --bpm=\n\ + --composer=\n\ + --conductor=\n\ + --label=