diff --git a/man/Makefile.am b/man/Makefile.am
index 4699b3a78b65a5dbcd83d5e2969ab2304644b8f8..ffa94f9cc1a269ad0b89716faee4669bb59730ea 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,4 +1,4 @@
-lkt_manpages		   = lektor.man lektord.man lkt.man
+lkt_manpages		   = lektor.man lektord.man lkt.man lkt.old.man
 notrans_dist_man1_MANS = $(lkt_manpages)
 MAN                    = $(abs_top_srcdir)/scripts/manpage.bash
 CLEANFILES 			   = $(lkt_manpages)
diff --git a/man/Makefile.in b/man/Makefile.in
index e464bfa443983992fe300025f81a5378b2bf82e4..9f4e3af0fff28732aacddc411a35f3b25ac6f91d 100644
--- a/man/Makefile.in
+++ b/man/Makefile.in
@@ -286,7 +286,7 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-lkt_manpages = lektor.man lektord.man lkt.man
+lkt_manpages = lektor.man lektord.man lkt.man lkt.old.man
 notrans_dist_man1_MANS = $(lkt_manpages)
 CLEANFILES = $(lkt_manpages)
 all: all-am
diff --git a/man/lkt.old.template b/man/lkt.old.template
new file mode 100644
index 0000000000000000000000000000000000000000..3ae343ed531cb4e09e830ab6283cb351a623ce87
--- /dev/null
+++ b/man/lkt.old.template
@@ -0,0 +1,271 @@
+.TH "LKT" "1" "___DATE___" "LEKTOR MK 7"
+.SH "NAME"
+
+lkt \- the lektor command line client
+
+.SH "OVERVIEW"
+\fBlkt\fP is the \fBlektord\fP client, much like \fBmpc\fP but with
+more functionnalities like \fIplaylists\fP and \fIstickers\fP management.
+.PP
+Commands can be used with the minimal name that permit them to be
+distinguished from others commands. Thus, it is possible to use the
+string \fIadm\fP as an alias for \fIadmin\fP. This is like cisco switch
+command line interface.
+.PP
+You may be interested in other related programs like:
+.PP
+.PD 0
+.TP
+\fIlektord\fP       The lektor daemon
+.TP
+.PD
+
+.SH "COMMANDS"
+Here are the \fBlkt\fP commands:
+
+.PP
+\fIBASE-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBcurrent\fP
+Prints informations about the currently playing kara. Can be used to
+display the current kara in a status bar like \fBxmobar\fP or in the
+\fBi3 panel\fP
+.TP
+\fBplay\fP [index]
+Toggle play/pause state. If the playback is stopped, start at a possibly
+specified index
+.TP
+\fBnext\fP
+Play next kara in the queue
+.TP
+\fBprevious\fP
+Play the previous kara in the queue
+.TP
+\fBshuffle\fP
+Shuffle the queue. If the state was stoppped, play from the first kara
+in the queue. If lektor was already playing a kara, it will play it
+from the begening. The current kara will be placed in first position
+in the queue.
+.TP
+\fBstatus\fP
+Prints information about the state of lektor and the currently playing kara
+.TP
+\fBstop\fP
+Stop the playback and reading the queue, the state is now \fIstopped\fP.
+.PP
+\fIPLAYLIST-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBplt create\fP <plt-name>
+Creates a playlist, do nothing if it was already present
+.TP
+\fBplt destroy\fP <plt-name>
+Delete a playlist with all its content, do nothing if the playlist didn't exists
+.TP
+\fBplt delete\fP <plt-name> <query>
+Deletes karas from a playlist with a valid \fIquery\fP
+.TP
+\fBplt add\fP <plt-name> <query>
+Adds karas to a playlist with a valid \fIquery\fP
+.TP
+\fBplt list\fP <plt-name>
+List the content of the playlist named <plt-name>
+.TP
+\fBplt list\fP
+List all the available playlists
+.PP
+
+\fIQUEUE-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBqueue\fP [count]
+Prints the names and ids of the next karas in the queue
+.TP
+\fBqueue pos\fP <pos | from:to>
+Prints the names and ids of karas in the queue. Karas can be designated by
+their position or with a range
+.TP
+\fBqueue pop\fP
+Delete the currently playing kara from the queue and pass to the next one.
+This can work only if the currently playong kara is not the last
+.TP
+\fBqueue add\fP <query>
+Add karas to the queue at the end of it with a valid query
+.TP
+\fBqueue insert\fP <query>
+Add karas to the queue just after the currently playing kara
+.TP
+\fBqueue seek\fP <id>
+Goto to the kara with the specified id in the queue
+.TP
+\fBqueue delete\fP <id>
+Delete karas from the playlist with their id. You can't delete the currently
+playing kara, for that use the \fBpop\fP queue command
+.TP
+\fBqueue clear\fP
+Clear the queue and set the state to \fIstopped\fP
+.TP
+\fBqueue crop\fP
+Crop the queue, delete every kara from it appart from the currently
+playing one
+.TP
+\fBqueue replace\fP <plt-name>
+Replace the queue with the content of a playlist. Keep the playling state
+.PP
+
+\fISEARCH-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBsearch database\fP <query>
+Search and prints the kara that correspond to the query in the database
+.TP
+\fBsearch plt\fP <plt-name> <query>
+Search, prints and add to an existing playlist the karas that match
+the query
+.TP
+\fBsearch plt\fP <plt-name>
+List the content of the playlist named <plt-name>
+.TP
+\fBsearch plt\fP
+List all the available playlists
+.TP
+\fBsearch count\fP <query>
+Search and prints the number of karas that match the query
+.TP
+\fBsearch get\fP <id>
+Get informations about a specific id
+.TP
+\fBsearch queue\fP <query>
+Search in the queue and prints the karas that match the query
+.PP
+
+\fISTICKERS-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBsticker create\fP <name>
+Create a sticker that can be used to tag \fIkara\fP and \fIplt\fP objects
+.TP
+\fBsticker get\fP <type> <uri> [ <name> [ <op> <value> ] ]
+List the stickers of an object \fIuri\fP. The object \fItype\fP can be
+\fIkara\fP or \fIplt\fP
+
+A condition can be defined on the value of the sticker with an operator
+\fIop\fP and an integer value \fIvalue\fP. Supported operators are \fIl\fP
+for 'less than', \fIe\fP for 'equal to' and \fIg\fP for 'greater than'.
+Operations are not strict
+.TP
+\fBsticker set\fP <type> <uri> <name> <value>
+Set the value of a sticker \fIname\fP to \fIvalue\fP for the object with the
+id \fIuri\fP
+.TP
+\fBsticker delete\fP <type> <uri> [name]
+Delete all the stickers or only one (specified by \fIname\fP) of the object
+with the id \fIuri\fP
+.PP
+
+\fIADMIN-COMMANDS\fP
+.PP
+.PD 0
+.TP
+.PD
+\fBadmin ping\fP
+Pings the lektord daemon, prints \fIOK\fP only if the ping succeeded
+.TP
+\fBadmin kill\fP
+Kill the lektord daemon
+.TP
+\fBadmin restart\fP
+Try to restart the lektord daemon
+.TP
+\fBadmin rescan\fP
+Rescan karas from the filesystem. New karas that are not in the database
+will be added to it. Don't synchronize from the repo
+.TP
+\fBadmin populate\fP
+Force a rescan (without taking into account the timestamp). Can be used
+to bootstrap the database from a filesystem
+.TP
+\fBadmin update\fP
+Update the base from the \fIKurisu\fP repo. Don't scan for new files in
+the filesystem
+.TP
+\fBadmin config\fP
+Prints to \fIstdout\fP the default configuration file
+.PP
+
+.SH "OPTIONS"
+Options can be passed to \fBlkt\fP before specifying the command in a
+\fIoption=value\fP format. This is done this way to allow one to make
+an alias of the \fBlkt\fP command.
+.PP
+The possible options are the following:
+.PP
+.PD 0
+.TP
+.PD
+\fBhost\fP
+The hostname or the IP of the machine where the \fBlektord\fP daemon
+is running
+.TP
+\fBport\fP
+The port on which the \fBlektord\fP daemon is listening
+.TP
+\fBpwd\fP
+The password to use for commands that require authentification. This is the
+case of most of the \fIadmin\fP commands
+.PP
+
+.SH "QUERIES"
+Queries are a way of listing karas in the database. They are composed of a
+type and the next of the line is the SQL regex that the kara must verify.
+In SQL regexes, the wildcard is the "%" character and the jocker the
+character "_". Queries are case insensitive.
+.PP
+Valid types for a query are the following: \fIid\fP, \fIlang\fP, \fItype\fP,
+\fIcategory\fP, \fIauthor\fP, \fIquery\fP and \fIplaylist\fP.
+.PP
+For the type \fItype\fP, the valid values are the following: \fIOP\fP,
+\fIED\fP, \fIIS\fP, \fIAMV\fP, \fIVOCA\fP, \fIMV\fP, \fIPV\fP and \fILIVE\fP.
+.PP
+For the type \fIlang\fP, the valid values are the following: \fIjp\fP,
+\fIfr\fP, \fIsp\fP, \fIen\fP, \fIlatin\fP, \fIit\fP, \fIru\fP, \fImulti\fP
+and \fIundefined\fP.
+.PP
+For the \fIcategory\fP type, the valid values are the following:
+\fIvo\fP, \fIva\fP, \fIcdg\fP, \fIamv\fP, \fIvocaloid\fP and \fIautres\fP.
+.PP
+Here are some examples of queries:
+.PP
+.PD 0
+.TP
+.PD
+\fBtype OP\fP
+Select karas that are openings
+.TP
+\fBauthor kubat\fP
+Select karas where the author is "kubat"
+.PP
+
+.SH "EXAMPLES"
+Valid invocations of the \fBlkt\fP command are the following:
+.PP
+.PD 0
+.TP
+.PD
+\fBlkt host=sakura port=6601 pwd=toto admin restart\fP
+Restart the lektord daemon on the \fIsakura\fP PC. This daemon is listening on
+the port \fI6601\fP and the password of the admin user is \fItoto\fP
+.TP
+\fBlkt q a author krocoh\fP
+Add kara that Krocoh has done to the queue
diff --git a/src/Makefile.am b/src/Makefile.am
index b95770d7d3c592a42eadd389e0c620708c12b9d9..eac0e4cbee317428865ae8093a201f975783beb1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -84,9 +84,10 @@ EXTRA_DIST = database/disk.sql database/memory.sql
 ## MAIN FILES ##
 ################
 
-bin_PROGRAMS = lektord lkt
+bin_PROGRAMS = lektord lkt.old lkt
 
 ## The lkt client
+lkt_old_SOURCES = main/lkt-old.c base/cmd.c base/common.c
 lkt_SOURCES = main/lkt.c base/cmd.c base/common.c
 
 ## The lektord server
diff --git a/src/Makefile.in b/src/Makefile.in
index d824d34b88da889e9d0336697d3320b003268696..0c7d8c290652282b4d1e4483e64548e4662b3e77 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -99,7 +99,7 @@ host_triplet = @host@
 
 # TODO pour le Kubat du futur: c'est pas beau, trouver un truc pour que ça se fasse tout seul
 @LKT_STATIC_MODULE_TRUE@am__append_8 = module/thread.c
-bin_PROGRAMS = lektord$(EXEEXT) lkt$(EXEEXT)
+bin_PROGRAMS = lektord$(EXEEXT) lkt.old$(EXEEXT) lkt$(EXEEXT)
 subdir = src
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/config/m4/libtool.m4 \
@@ -212,6 +212,10 @@ am_lkt_OBJECTS = main/lkt.$(OBJEXT) base/cmd.$(OBJEXT) \
 	base/common.$(OBJEXT)
 lkt_OBJECTS = $(am_lkt_OBJECTS)
 lkt_LDADD = $(LDADD)
+am_lkt_old_OBJECTS = main/lkt-old.$(OBJEXT) base/cmd.$(OBJEXT) \
+	base/common.$(OBJEXT)
+lkt_old_OBJECTS = $(am_lkt_old_OBJECTS)
+lkt_old_LDADD = $(LDADD)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -245,7 +249,8 @@ am__depfiles_remade = base/$(DEPDIR)/cmd.Po base/$(DEPDIR)/common.Po \
 	database/$(DEPDIR)/liblektor_la-queue.Plo \
 	database/$(DEPDIR)/liblektor_la-stickers.Plo \
 	database/$(DEPDIR)/liblektor_la-update.Plo \
-	database/$(DEPDIR)/liblektor_la-user.Plo main/$(DEPDIR)/lkt.Po \
+	database/$(DEPDIR)/liblektor_la-user.Plo \
+	main/$(DEPDIR)/lkt-old.Po main/$(DEPDIR)/lkt.Po \
 	main/$(DEPDIR)/server.Po mkv/$(DEPDIR)/liblektor_la-mkv.Plo \
 	mkv/$(DEPDIR)/liblektor_la-utils.Plo \
 	mkv/$(DEPDIR)/liblektor_la-write.Plo \
@@ -277,10 +282,11 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
 SOURCES = $(liblektor_la_SOURCES) $(liblktmodrepo_la_SOURCES) \
-	$(liblktmodsdl_la_SOURCES) $(lektord_SOURCES) $(lkt_SOURCES)
+	$(liblktmodsdl_la_SOURCES) $(lektord_SOURCES) $(lkt_SOURCES) \
+	$(lkt_old_SOURCES)
 DIST_SOURCES = $(am__liblektor_la_SOURCES_DIST) \
 	$(liblktmodrepo_la_SOURCES) $(liblktmodsdl_la_SOURCES) \
-	$(lektord_SOURCES) $(lkt_SOURCES)
+	$(lektord_SOURCES) $(lkt_SOURCES) $(lkt_old_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -477,6 +483,7 @@ liblektor_la_LDFLAGS = -avoid-version -pthread -lsqlite3 \
 @LKT_STATIC_MODULE_TRUE@liblektor_la_LIBADD = liblktmodsdl.la liblktmodrepo.la
 CLEANFILES = database/disk.c database/memory.c
 EXTRA_DIST = database/disk.sql database/memory.sql
+lkt_old_SOURCES = main/lkt-old.c base/cmd.c base/common.c
 lkt_SOURCES = main/lkt.c base/cmd.c base/common.c
 lektord_SOURCES = main/server.c
 lektord_LDADD = liblektor.la
@@ -744,6 +751,12 @@ base/common.$(OBJEXT): base/$(am__dirstamp) \
 lkt$(EXEEXT): $(lkt_OBJECTS) $(lkt_DEPENDENCIES) $(EXTRA_lkt_DEPENDENCIES) 
 	@rm -f lkt$(EXEEXT)
 	$(AM_V_CCLD)$(LINK) $(lkt_OBJECTS) $(lkt_LDADD) $(LIBS)
+main/lkt-old.$(OBJEXT): main/$(am__dirstamp) \
+	main/$(DEPDIR)/$(am__dirstamp)
+
+lkt.old$(EXEEXT): $(lkt_old_OBJECTS) $(lkt_old_DEPENDENCIES) $(EXTRA_lkt_old_DEPENDENCIES) 
+	@rm -f lkt.old$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(lkt_old_OBJECTS) $(lkt_old_LDADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -782,6 +795,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@database/$(DEPDIR)/liblektor_la-stickers.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@database/$(DEPDIR)/liblektor_la-update.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@database/$(DEPDIR)/liblektor_la-user.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@main/$(DEPDIR)/lkt-old.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@main/$(DEPDIR)/lkt.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@main/$(DEPDIR)/server.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@mkv/$(DEPDIR)/liblektor_la-mkv.Plo@am__quote@ # am--include-marker
@@ -1203,6 +1217,7 @@ distclean: distclean-am
 	-rm -f database/$(DEPDIR)/liblektor_la-stickers.Plo
 	-rm -f database/$(DEPDIR)/liblektor_la-update.Plo
 	-rm -f database/$(DEPDIR)/liblektor_la-user.Plo
+	-rm -f main/$(DEPDIR)/lkt-old.Po
 	-rm -f main/$(DEPDIR)/lkt.Po
 	-rm -f main/$(DEPDIR)/server.Po
 	-rm -f mkv/$(DEPDIR)/liblektor_la-mkv.Plo
@@ -1281,6 +1296,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f database/$(DEPDIR)/liblektor_la-stickers.Plo
 	-rm -f database/$(DEPDIR)/liblektor_la-update.Plo
 	-rm -f database/$(DEPDIR)/liblektor_la-user.Plo
+	-rm -f main/$(DEPDIR)/lkt-old.Po
 	-rm -f main/$(DEPDIR)/lkt.Po
 	-rm -f main/$(DEPDIR)/server.Po
 	-rm -f mkv/$(DEPDIR)/liblektor_la-mkv.Plo
diff --git a/src/main/lkt-old.c b/src/main/lkt-old.c
new file mode 100644
index 0000000000000000000000000000000000000000..f6c0987ca2c0b8398f4edb2abee19296cb914196
--- /dev/null
+++ b/src/main/lkt-old.c
@@ -0,0 +1,1154 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <lektor/common.h>
+#include <lektor/net.h>
+#include <lektor/cmd.h>
+#include <lektor/config.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <stdnoreturn.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#define LKT_KEY_VALUE_SEP               ": \n\t\0"
+#define fail_if(cond, msg)              { if (cond) { LOG_ERROR("LKT", "%s", msg); exit(EXIT_FAILURE); } }
+#define fail(msg)                       { LOG_ERROR("LKT", "%s", msg); exit(EXIT_FAILURE); }
+
+#define exit_with_status(sock, buff) {                \
+    read_socket(sock, buff, 2);                       \
+    fail_if(buff[0] != 'O' && buff[1] != 'K', "ACK"); \
+    exit(EXIT_SUCCESS);                               \
+}
+
+/* Type definition. */
+
+typedef struct {
+    const char *host;   /* Serveur host, may be resolved.               */
+    const char *port;   /* Serveur port.                                */
+    const char *pwd;    /* The password for the user.                   */
+    const char **argv;  /* Pointer to the argv from the main function.  */
+    int argc;           /* Argument count of the args.                  */
+} args_t;
+
+/* Global variables. */
+
+static const char *host;
+static const char *port;
+static const char *password;
+static const char *LKT_QUEUE_DEFAULT[] = { "10" };
+
+/* Communication functions and fonction that interact with lektor stuff. */
+
+static int
+lkt_valid_type(const char *type)
+{
+    return (STR_MATCH(type, "query")    ||
+            STR_MATCH(type, "category") ||
+            STR_MATCH(type, "author")   ||
+            STR_MATCH(type, "lang")     ||
+            STR_MATCH(type, "id")       ||
+            STR_MATCH(type, "type")     ||
+            STR_MATCH(type, "playlist"));
+}
+
+static int
+read_socket(FILE *sock, char *buff, size_t max_len)
+{
+    size_t i, len;
+    for (i = 0; i < max_len; ++i) {
+        len = fread(buff + i, sizeof(char), 1, sock);
+        if (buff[i] == '\n' || len != 1)
+            break;
+    }
+    fail_if(len == 0, "Connexion error");
+    return i;
+}
+
+static FILE *
+create_socket(const char *host, const char *port)
+{
+    FILE *socket_desc;
+    struct addrinfo hints, *found;
+    socklen_t sock_len;
+    struct sockaddr sock;
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_flags = 0;
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+
+    getaddrinfo(host, port, &hints, &found); //  <---- Another assert yeeted because it makes php panic
+
+    sock = *found->ai_addr;
+    sock_len = found->ai_addrlen;
+    freeaddrinfo(found);
+
+    int cx = socket(AF_INET, SOCK_STREAM, 0);
+
+    if (cx <= 0 || connect(cx, &sock, sock_len))
+        exit(EXIT_FAILURE);
+
+    socket_desc = fdopen(cx, "r+");
+    fail_if(!socket_desc, "Failed to connect to lektord");
+    return socket_desc;
+}
+
+static FILE *
+lkt_connect(void)
+{
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock = create_socket(host, port);
+    read_socket(sock, buff, LKT_MESSAGE_MAX);
+    return sock;
+}
+
+static void
+write_socket(FILE *sock, const char *format, ...)
+{
+    int size;
+    char buff[LKT_MESSAGE_MAX];
+    va_list ap;
+
+    va_start(ap, format);
+    size = vsnprintf(buff, LKT_MESSAGE_MAX - 1, format, ap);
+    buff[LKT_MESSAGE_MAX - 1] = 0;
+
+    if (size < 0)
+        fail("Connexion error");
+
+    if (fwrite(buff, sizeof(char), size, sock) < (unsigned int) size)
+        fail("Connexion error");
+
+    va_end(ap);
+}
+
+noreturn static inline void
+lkt_send_and_exit(const char *buffer, size_t len)
+{
+    write_socket(lkt_connect(), buffer, len);
+    exit(EXIT_SUCCESS);
+}
+
+static char *
+lkt_skip_key(char *buffer)
+{
+    size_t len = strcspn(buffer, LKT_KEY_VALUE_SEP);
+    char *ret = &buffer[len + 1];
+    len = strspn(ret, ": ");
+    return ret + len;
+}
+
+static inline void
+send_cmd_with_uri(FILE *sock, char *cmd, int argc, const char **argv)
+{
+    int i;
+    char buf[LKT_MESSAGE_MAX] = {0};
+    for (i = 1; i < argc - 1; ++i) {
+        strncat(buf, argv[i], LKT_MESSAGE_MAX - 1);
+        strncat(buf, " ", LKT_MESSAGE_MAX - 1);
+    }
+    strncat(buf, argv[i], LKT_MESSAGE_MAX - 1);
+    strncat(buf, "\n", LKT_MESSAGE_MAX - 1);
+    write_socket(sock, "%s %s://%s", cmd, argv[0], buf);
+}
+
+/* Functions implementing options. */
+
+#define just_send_one_arg(func, cmd)                \
+noreturn void func (struct cmd_args *args) {        \
+    fail_if(args->argc != 1, "Invalid argument");   \
+    FILE *sock = lkt_connect();                     \
+    char buff[2];                                   \
+    write_socket(sock, cmd " %s\n", args->argv[0]); \
+    exit_with_status(sock, buff);                   \
+}
+just_send_one_arg(stickers_create__,  "sticker __create")
+just_send_one_arg(stickers_destroy__, "sticker __destroy")
+just_send_one_arg(plt_destroy__,      "rm")
+just_send_one_arg(plt_create__,       "playlistadd")
+just_send_one_arg(queue_dump__,       "__dump")
+#undef just_send_one_arg
+
+#define just_send(func, msg) /* Just send a simple string functions */  \
+    noreturn void func (struct cmd_args *args) {                        \
+        fail_if(args->argc, "This command takes no arguments");         \
+        lkt_send_and_exit(msg, sizeof(msg));                            \
+    }
+just_send(queue_clear__, "clear\n")
+just_send(queue_crop__,  "crop\n")
+just_send(next__,        "next\n")
+just_send(prev__,        "previous\n")
+just_send(stop__,        "stop\n")
+just_send(shuffle__,     "shuffle\nplay\n")
+#undef just_send
+
+noreturn void
+simple_send_with_password__(const char *cmd)
+{
+    if (!password)
+        fail("Password not provided");
+    FILE *sock = lkt_connect();
+    write_socket(sock, "command_list_begin\n");
+    write_socket(sock, "password %s\n", password);
+    write_socket(sock, "%s\n", cmd);
+    write_socket(sock, "command_list_end\n");
+    exit(EXIT_SUCCESS);
+}
+
+noreturn void
+restart__(struct cmd_args *args)
+{
+    if (args->argc != 0)
+        fail("Invalid argument, the previous command takes no arguments");
+    simple_send_with_password__("__restart");
+}
+
+noreturn void
+kill__(struct cmd_args *args)
+{
+    if (args->argc != 0)
+        fail("Invalid argument, the previous command takes no arguments");
+    simple_send_with_password__("kill");
+}
+
+noreturn void
+rescan_or_update__(struct cmd_args *args, const char *cmd)
+{
+    if (!password)
+        fail("Password not provided");
+    FILE *sock = lkt_connect();
+    char buff[LKT_MESSAGE_MAX];
+    int i;
+
+    /* All the db */
+    if (args->argc == 0) {
+        write_socket(sock, "command_list_begin\n");
+        write_socket(sock, "password %s\n", password);
+        write_socket(sock, "%s\n", cmd);
+        write_socket(sock, "command_list_end\n");
+    }
+
+    /* With a query */
+    else {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        for (i = 0; i < args->argc; ++i)
+            strncat(buff, args->argv[i], LKT_MESSAGE_MAX - 1);
+        write_socket(sock, "command_list_begin\n");
+        write_socket(sock, "password %s\n", password);
+        write_socket(sock, "%s %s\n", cmd, buff);
+        write_socket(sock, "command_list_end\n");
+    }
+
+    exit(EXIT_SUCCESS);
+}
+
+noreturn void
+rescan__(struct cmd_args *args)
+{
+    rescan_or_update__(args, "rescan");
+}
+
+noreturn void
+update__(struct cmd_args *args)
+{
+    rescan_or_update__(args, "update");
+}
+
+noreturn void
+populate__(struct cmd_args *args)
+{
+    rescan_or_update__(args, "__rescan");
+}
+
+noreturn void
+queue_replace__(struct cmd_args *args)
+{
+    if (args->argc != 1)
+        fail("Invalid argument");
+    bool play = false;
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock = lkt_connect();
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
+
+        if (STR_NMATCH(buff, "state", len)) {
+            char *it = lkt_skip_key(buff);
+            play = STR_NMATCH(it, "play", 4);
+            break;
+        }
+
+        if (STR_NMATCH(buff, "OK", 2))
+            break;
+        else if (STR_NMATCH(buff, "ACK", 3))
+            fail("ACK");
+    }
+
+    fclose(sock);
+    sock = lkt_connect();
+    write_socket(sock, "command_list_begin\n");
+    write_socket(sock, "clear\n");
+    write_socket(sock, "add playlist://%s\n", args->argv[0]);
+    if (play)
+        write_socket(sock, "play\n");
+    write_socket(sock, "command_list_end\n");
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+config__(struct cmd_args *args)
+{
+    int ret = EXIT_SUCCESS;
+    if (args->argc == 0)
+        fwrite(lkt_default_config_file, sizeof(char),
+               strlen(lkt_default_config_file), stdout);
+    exit(ret);
+}
+
+noreturn void
+play__(struct cmd_args *args)
+{
+    if (args->argc > 1)
+        fail("Invalid argument");
+
+    int pos = 0;
+    if (args->argc != 0)
+        pos = atoi(args->argv[0]);
+
+    static const char status__[]        = "status\n";
+    static const char cmd_play__[]      = "play\n";
+    static const char cmd_play_from__[] = "play %d\n";
+    static const char cmd_pause__[]     = "pause\n";
+
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock = lkt_connect();
+    write_socket(sock, status__);
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
+
+        if (STR_NMATCH(buff, "state", len)) {
+            fclose(sock);
+
+            if (STR_NMATCH(lkt_skip_key(buff), "stop", 4)) {
+                if (!pos)
+                    lkt_send_and_exit(cmd_play__, sizeof(cmd_play__));  /* In bytes. */
+                else {
+                    write_socket(lkt_connect(), cmd_play_from__, pos);
+                    exit(EXIT_SUCCESS);
+                }
+            }
+
+            else
+                lkt_send_and_exit(cmd_pause__, sizeof(cmd_pause__));    /* In bytes. */
+
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+noreturn void
+ping__(struct cmd_args *args)
+{
+    if (args->argc != 0)
+        fail("Invalid argument, the ping command takes no arguments");
+    char buff[6] = {0};
+    FILE *sock = lkt_connect();
+    write_socket(sock, "ping\n");
+    read_socket(sock, buff, 6 * sizeof(char));
+    if (!STR_NMATCH(buff, "OK", 2))
+        fail("ACK");
+    exit(write(1, "OK\n", sizeof("OK\n")) == 0);
+}
+
+noreturn void
+current__(struct cmd_args *args)
+{
+    if (args->argc != 0)
+        fail("Invalid argument, the current command takes no arguments");
+
+    static const char current_song__[] = "currentsong\n";
+    char buff[LKT_MESSAGE_MAX];
+
+    char *mem = NULL;
+    FILE *sock = lkt_connect();
+
+    write_socket(sock, current_song__);
+    assert(mem = calloc(6 * LKT_MESSAGE_MAX, sizeof(char)));
+    assert(memset(mem, 0, 6 * LKT_MESSAGE_MAX * sizeof(char)));
+
+    char *const title    = &mem[0];
+    char *const author   = &mem[LKT_MESSAGE_MAX];
+    char *const source   = &mem[2 * LKT_MESSAGE_MAX];
+    char *const type     = &mem[3 * LKT_MESSAGE_MAX];
+    char *const category = &mem[4 * LKT_MESSAGE_MAX];
+    char *const language = &mem[5 * LKT_MESSAGE_MAX];
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+
+        const size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
+        char *value = lkt_skip_key(buff);
+        const size_t value_size = strcspn(value, "\n\0") * sizeof(char);
+
+        if (STR_NMATCH("title", buff, len))
+            assert(memcpy(title, value, value_size));
+
+        if (STR_NMATCH("author", buff, len))
+            assert(memcpy(author, value, value_size));
+
+        if (STR_NMATCH("source", buff, len))
+            assert(memcpy(source, value, value_size));
+
+        if (STR_NMATCH("type", buff, len))
+            assert(memcpy(type, value, value_size));
+
+        if (STR_NMATCH("language", buff, len))
+            assert(memcpy(language, value, value_size));
+
+        if (STR_NMATCH("category", buff, len))
+            assert(memcpy(category, value, value_size));
+
+        /* At this point every key has been parsed. */
+        if (STR_NMATCH(buff, "OK", 2))
+            goto ok;
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+    }
+
+ok:
+    if (language[0])
+        printf("%s - %s / %s - %s - %s [ %s ]\n",
+               category, language, source, type, title, author);
+    else
+        printf("%s / %s - %s - %s [ %s ]\n",
+               category, source, type, title, author);
+
+    exit(EXIT_SUCCESS);
+}
+
+noreturn void
+queue_pop__(struct cmd_args *args)
+{
+    fail_if(args->argc, "Invalid argument");
+    int songid = 0;
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock = lkt_connect();
+
+    /* Get lektor's status. */
+    write_socket(sock, "status\n");
+
+#define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (atoi(lkt_skip_key(buff))); continue; }
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
+        assign_int("songid", songid)
+
+        /* At this point every key has been parsed. */
+        if (STR_NMATCH(buff, "OK", 2))
+            break;
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+    }
+#undef assign_int
+
+    fclose(sock);
+    sock = lkt_connect();
+    if (!songid)
+        exit(EXIT_FAILURE);
+    write_socket(sock, "next\ndeleteid %d\n", songid);
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+status__(struct cmd_args *args)
+{
+    if (args->argc != 0)
+        fail("Invalid argument, the status command takes no arguments");
+
+    static const char *const status_str__ = "status\n";
+    static const char *const stats_str__  = "stats\n";
+
+    char buff[LKT_MESSAGE_MAX];
+    char flags[24];
+
+    bool play = false, stopped = true, is_updating = false;
+    int time_pos = 0, time_duration = 0, song_index = 0, plt_len = 0, ret = EXIT_FAILURE, it = 0;
+    size_t len;
+    time_t update_ts = 0;
+
+    memset(flags, 0, 24 * sizeof(char));
+    FILE *sock = lkt_connect();
+
+#define assign_flag(str, f)  if (STR_NMATCH(buff, str, len) && (atoi(lkt_skip_key(buff)) == 1)) { flags[it++] = f; continue; }
+#define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (strtoll(lkt_skip_key(buff), NULL, 0)); continue; }
+#define check_end()                                     \
+    if (STR_NMATCH(buff, "OK", 2)) break;               \
+    else if (STR_NMATCH(buff, "ACK", 3)) goto error;
+
+    /* Get lektor's status */
+    write_socket(sock, status_str__);
+    for (;;) {
+        len       = read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        buff[len] = '\0';
+        len       = strcspn(buff, LKT_KEY_VALUE_SEP);
+
+        if (STR_NMATCH(buff, "state", len)) {
+            char *it = lkt_skip_key(buff);
+            play     = STR_NMATCH(it, "play", 4);
+            stopped  = STR_NMATCH(it, "stop", 4);
+            continue;
+        }
+
+        assign_flag("random",  'r')
+        assign_flag("repeat",  'l')
+        assign_flag("single",  's')
+        assign_flag("consume", 'c')
+
+        assign_int("elapsed",        time_pos)
+        assign_int("duration",       time_duration)
+        assign_int("song",           song_index)
+        assign_int("playlistlength", plt_len)
+
+        check_end()
+    }
+
+    /* Get lektor's stats */
+    write_socket(sock, stats_str__);
+    for (;;) {
+        len       = read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        buff[len] = '\0';
+        len       = strcspn(buff, LKT_KEY_VALUE_SEP);
+
+        assign_int("__is_updating", is_updating)
+        assign_int("db_update",     update_ts)
+
+        check_end()
+    }
+
+    /* End of communication */
+    fclose(sock);
+
+#undef assign_flag
+#undef assign_int
+#undef check_end
+
+    struct tm *p_tm = localtime(&update_ts);
+    len = strftime(buff, LKT_MESSAGE_MAX - 1, "%F %H:%M:%S", p_tm);
+    buff[len] = '\0';
+
+    int  pla_m = time_pos / 60;
+    char pla_s = time_pos % 60;
+    int  dur_m = time_duration / 60;
+    char dur_s = time_duration % 60;
+
+    printf("Lektor: %s\n"
+           "Queue: #%d/%d\n"
+           "Playback: %d:%d/%d:%d\n"
+           "Flags: %s\n"
+           "%s: %s\n",
+           play ? "play" : "stopped",
+           song_index + 1, plt_len,
+           pla_m, pla_s, dur_m, dur_s,
+           flags[0] ? flags : "(none)",
+           is_updating ? "Updating" : "Last update", buff);
+
+    /* If there is a kara loaded in mpv, get this kara. */
+    if (!stopped) {
+        printf("Kara: ");
+        current__(args);
+    }
+
+    ret = EXIT_SUCCESS;
+error:
+    exit(ret);
+}
+
+noreturn void
+queue_delete__(struct cmd_args *args)
+{
+    if (args->argc != 1)
+        fail("Invalid argument, need onlt one argument: queue delete <id>");
+
+    static const char *cmd_id__ = "deleteid %d\n";
+    int dumy = 0;
+    FILE *sock = lkt_connect();
+    char buff[3];
+
+    sscanf(args->argv[0], "%d", &dumy);
+    if (dumy != 0) {
+        write_socket(sock, cmd_id__, dumy);
+        exit_with_status(sock, buff);
+    }
+
+    fail("Invalid argument");
+}
+
+noreturn void
+queue_add__(struct cmd_args *args)
+{
+    char buff[3];
+    fail_if(args->argc < 1, "Invalid arguments");
+    fail_if(!lkt_valid_type(args->argv[0]), "Invalid query type");
+    FILE *sock = lkt_connect();
+    send_cmd_with_uri(sock, "add", args->argc, args->argv);
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+queue_insert__(struct cmd_args *args)
+{
+    char buff[3];
+    fail_if(args->argc < 1, "Invalid arguments");
+    fail_if(!lkt_valid_type(args->argv[0]), "Invalid query type");
+    FILE *sock = lkt_connect();
+    send_cmd_with_uri(sock, "__insert", args->argc, args->argv);
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+queue_seek__(struct cmd_args *args)
+{
+    fail_if(args->argc != 1, "The seek command needs one argument: queue seek <id>");
+
+    char *endptr, buf[3];
+    long id = strtol(args->argv[0], &endptr, 0);
+
+    if ((errno == ERANGE && (id == LONG_MAX || id == LONG_MIN)) ||
+        (errno != 0 && id == 0)                                 ||
+        (endptr == args->argv[0]))
+        fail("Invalid argument, not an integer");
+
+    if (*endptr != '\0')
+        fail("Invalid argument, must be only one integer");
+
+    FILE *sock = lkt_connect();
+    write_socket(sock, "playid %ld\n", id);
+    exit_with_status(sock, buf);
+}
+
+noreturn void
+queue_pos__(struct cmd_args *args)
+{
+    char buff[LKT_MESSAGE_MAX], *endptr;
+
+    if (args->argc != 1)
+        fail("Invalid argument for the pos command: queue pos <arg> where arg is a position or a range");
+
+    long continuation = 0, up = 0;
+
+    continuation = strtol(args->argv[0], &endptr, 0);
+    if ((errno == ERANGE && (continuation == LONG_MAX || continuation == LONG_MIN)) || (errno != 0
+            && continuation == 0) || (endptr == args->argv[0]))
+        fail("Invalid argument, not an integer: queue pos <arg> where arg is a position or a range");
+
+    if (*endptr != '\0') {
+        /* A range */
+        if (*(++endptr) == '\0')
+            fail("Invalid argument, a range is two integers");
+        up = atoi(endptr);
+    }
+
+
+    FILE *sock = NULL;
+redo:
+    sock = lkt_connect();
+    if (up != 0)
+        write_socket(sock, "playlist %d:%d\n", continuation, up);
+    else
+        write_socket(sock, "playlist %d\n", continuation);
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+
+        if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
+            continuation = atoi(lkt_skip_key(buff));
+            if (continuation > 0) {
+                fclose(sock);
+                goto redo;
+            }
+        }
+
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+
+        fprintf(stdout, "%s", buff);
+    }
+}
+
+noreturn void
+queue_list__(struct cmd_args *args)
+{
+    char buff[LKT_MESSAGE_MAX], *endptr;
+    FILE *sock = NULL;
+    long continuation = 0, song_index = 1;
+
+    /* Arguments stuff. */
+
+    if (args->argc == 0)
+        args->argv = LKT_QUEUE_DEFAULT;
+    else if (args->argc != 1)
+        fail("Invalid argument: queue <count?>");
+
+    continuation = strtol(args->argv[0], &endptr, 0);
+    if ((errno == ERANGE && (continuation == LONG_MAX || continuation == LONG_MIN)) || (errno != 0
+            && continuation == 0) || (endptr == args->argv[0]))
+        fail("Invalid argument, not an integer");
+
+    if (*endptr != '\0')
+        fail("Invalid argument");
+
+    /* Get the current pos to get limits for the playlist command. */
+
+    sock = lkt_connect();
+    write_socket(sock, "status\n");
+
+#define assign_int(str, var) if (STR_NMATCH(buff, str, len)) { var = (atoi(lkt_skip_key(buff))); continue; }
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        size_t len = strcspn(buff, LKT_KEY_VALUE_SEP);
+        assign_int("song", song_index);
+
+        /* At this point every key has been parsed. */
+        if (STR_NMATCH(buff, "OK", 2))
+            break;
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+    }
+#undef assign_int
+    fclose(sock);
+
+    /* Get the content of the queue. */
+
+    continuation = labs(continuation);
+    song_index = MAX(song_index + 1, 1);
+    safe_snprintf(buff, LKT_MESSAGE_MAX, "%ld:%ld", song_index, song_index + continuation - 1);
+    args->argc = 1;
+    args->argv[0] = buff;
+    queue_pos__(args);
+}
+
+/* Functions implementing options, but for for playlists. */
+
+noreturn void
+plt_add__(struct cmd_args *args)
+{
+    char buff[2];
+    FILE *sock = lkt_connect();
+    fail_if(args->argc < 3,
+            "Invalid argument, need at least three arguments: plt add <plt> <query>");
+    fail_if(!lkt_valid_type(args->argv[1]), "Invalid argument, type for the query is invalid");
+
+    int i;
+    char buf[LKT_MESSAGE_MAX] = {0};
+    for (i = 2; i < args->argc - 1; ++i) {
+        strncat(buf, args->argv[i], LKT_MESSAGE_MAX - 1);
+        strncat(buf, " ", LKT_MESSAGE_MAX - 1);
+    }
+    strncat(buf, args->argv[i], LKT_MESSAGE_MAX - 1);
+    strncat(buf, "\n", LKT_MESSAGE_MAX - 1);
+    write_socket(sock, "playlistadd %s %s://%s", args->argv[0], args->argv[1], buf);
+
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+plt_delete__(struct cmd_args *args)
+{
+    FILE *sock = lkt_connect();
+    char buff[2];
+    char cmd[128];
+    fail_if(args->argc < 3, "Invalid argument");
+    snprintf(cmd, 128 - 1, "playlistdelete %s", args->argv[0]);
+    cmd[127] = '\0';
+    send_cmd_with_uri(sock, cmd, args->argc - 1, &args->argv[1]);
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+stickers_get__(struct cmd_args *args)
+{
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock;
+    if (args->argc == 2)
+        write_socket(sock = lkt_connect(), "sticker get %s %s\n", args->argv[0], args->argv[1]);
+    else if (args->argc == 3)
+        write_socket(sock = lkt_connect(), "sticker get %s %s %s\n", args->argv[0], args->argv[1],
+                     args->argv[2]);
+    else if (args->argc == 5) {
+        const char *op = args->argv[3];
+        op = op[0] == 'e' ? "=" : op[0] == 'l' ? "<" : op[0] == 'g' ? ">" : NULL;
+        fail_if(!op, "Invalid argument");
+        write_socket(sock = lkt_connect(), "sticker get %s %s %s %s %s\n", args->argv[0],
+                     args->argv[1], args->argv[2], op, args->argv[4]);
+    } else
+        fail("Invalid argument");
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+        fprintf(stdout, "%s", buff);
+    }
+}
+
+noreturn void
+stickers_set__(struct cmd_args *args)
+{
+    fail_if(args->argc != 4, "Invalid argument");
+    FILE *sock = lkt_connect();
+    char buff[2];
+    write_socket(sock, "sticker set %s %s %s %s\n", args->argv[0],
+                 args->argv[1], args->argv[2], args->argv[3]);
+    exit_with_status(sock, buff);
+}
+
+noreturn void
+stickers_delete__(struct cmd_args *args)
+{
+    FILE *sock;
+    char buff[2];
+    if (args->argc == 2)
+        write_socket(sock = lkt_connect(), "sticker delete %s %s",
+                     args->argv[0], args->argv[1]);
+    else if (args->argc == 3)
+        write_socket(sock = lkt_connect(), "sticker delete %s %s %s",
+                     args->argv[0], args->argv[1], args->argv[2]);
+    else
+        fail("Invalid argument");
+    exit_with_status(sock, buff);
+}
+
+/* Search functions. */
+
+noreturn void
+__continuation_calls(const char *cmd)
+{
+    char buff[LKT_MESSAGE_MAX];
+    int continuation = 0;
+    FILE *sock = NULL;
+redo:
+    sock = lkt_connect();
+
+    write_socket(sock, "%d %s\n", continuation, cmd);
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+
+        if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
+            continuation = atoi(lkt_skip_key(buff));
+            fclose(sock);
+            goto redo;
+        }
+
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+
+        fprintf(stdout, "%s", buff);
+    }
+}
+
+noreturn void
+list_plt__(struct cmd_args *args)
+{
+    fail_if(args->argc != 0, "Invalid argument number");
+    __continuation_calls("listplaylists");
+}
+
+noreturn void
+list_plt_content__(struct cmd_args *args)
+{
+    fail_if(args->argc != 1, "Invalid argument number");
+    char buff[LKT_MESSAGE_MAX];
+    safe_snprintf(buff, LKT_MESSAGE_MAX, "listplaylist %s", args->argv[0]);
+    __continuation_calls(buff);
+    exit(EXIT_FAILURE);
+}
+
+noreturn void
+search_with_cmd__(struct cmd_args *args, const char *cmd)
+{
+    fail_if(args->argc < 2,                 "Invalid number of arguments");
+    fail_if(!lkt_valid_type(args->argv[0]), "Invalid type for the query");
+
+    char buff[LKT_MESSAGE_MAX];
+    int continuation = 0, i;
+    FILE *sock = NULL;
+redo:
+    sock = lkt_connect();
+
+    write_socket(sock, "%d %s %s://", continuation, cmd, args->argv[0]);
+    for (i = 1; i < args->argc - 1; ++i)
+        write_socket(sock, "%s ", args->argv[i]);
+    write_socket(sock, "%s\n", args->argv[i]);
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+
+        if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
+            continuation = atoi(lkt_skip_key(buff));
+            fclose(sock);
+            goto redo;
+        }
+
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+
+        fprintf(stdout, "%s", buff);
+    }
+}
+
+noreturn void
+search_get__(struct cmd_args *args)
+{
+    fail_if(args->argc != 1, "Invalid number of arguments");
+    fail_if(!strtol(args->argv[0], NULL, 0), "Invalid id");
+    char buff[LKT_MESSAGE_MAX];
+    FILE *sock = lkt_connect();
+
+    write_socket(sock, "find %s\n", args->argv[0]);
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+        (void) write(1, buff, strlen(buff));
+    }
+}
+
+noreturn void
+search_plt__(struct cmd_args *args)
+{
+    if (args->argc == 0)
+        list_plt__(args);
+    if (args->argc == 1)
+        list_plt_content__(args);
+
+    fail_if(args->argc < 3, "Invalid number of arguments");
+    fail_if(!lkt_valid_type(args->argv[1]), "Invalid type for query");
+    char buff[LKT_MESSAGE_MAX];
+    int continuation = 0, i;
+    FILE *sock = NULL;
+redo:
+    sock = lkt_connect();
+
+    write_socket(sock, "%d listplaylistinfo %s %s://", continuation,
+                 args->argv[0], args->argv[1]);
+    for (i = 2; i < args->argc - 1; ++i)
+        write_socket(sock, "%s ", args->argv[i]);
+    write_socket(sock, "%s\n", args->argv[i]);
+
+    for (;;) {
+        memset(buff, 0, LKT_MESSAGE_MAX * sizeof(char));
+        read_socket(sock, buff, LKT_MESSAGE_MAX - 1);
+
+        if (STR_NMATCH(buff, "continue:", strlen("continue:"))) {
+            continuation = atoi(lkt_skip_key(buff));
+            fclose(sock);
+            goto redo;
+        }
+
+        if (STR_NMATCH(buff, "OK", 2))
+            exit(EXIT_SUCCESS);
+        else if (STR_NMATCH(buff, "ACK", 3))
+            exit(EXIT_FAILURE);
+
+        fprintf(stdout, "%s", buff);
+    }
+}
+
+#define search_with_cmd(func, cmd) /* I don't want to write always the same things */ \
+    noreturn void func (struct cmd_args *args) { search_with_cmd__(args, #cmd); }
+search_with_cmd(search_db__,     search)
+search_with_cmd(search_count__,  count)
+search_with_cmd(search_queue__,  playlistfind)
+#undef search_with_cmd
+
+/* Parsing stuff. */
+
+static struct cmd_opt options_queue[] = {
+    { .name = "insert",     .call = queue_insert__  },
+    { .name = "pos",        .call = queue_pos__     },
+    { .name = "pop",        .call = queue_pop__     },
+    { .name = "add",        .call = queue_add__     },
+    { .name = "seek",       .call = queue_seek__    },
+    { .name = "delete",     .call = queue_delete__  },
+    { .name = "clear",      .call = queue_clear__   },
+    { .name = "crop",       .call = queue_crop__    },
+    { .name = "replace",    .call = queue_replace__ },
+    { .name = "dump",       .call = queue_dump__    },
+    CMD_OPT_DEFAULT(queue_list__),
+};
+
+static struct cmd_opt options_plt[] = {
+    { .name = "add",        .call = plt_add__     },
+    { .name = "delete",     .call = plt_delete__  },
+    { .name = "destroy",    .call = plt_destroy__ },
+    { .name = "create",     .call = plt_create__  },
+    { .name = "list",       .call = search_plt__  },
+    CMD_OPT_NULL,
+};
+
+static struct cmd_opt options_search[] = {
+    { .name = "database",   .call = search_db__     },
+    { .name = "get",        .call = search_get__    },
+    { .name = "plt",        .call = search_plt__    },
+    { .name = "count",      .call = search_count__  },
+    { .name = "queue",      .call = search_queue__  },
+    CMD_OPT_NULL,
+};
+
+static struct cmd_opt options_admin[] = {
+    { .name = "ping",       .call = ping__     },
+    { .name = "kill",       .call = kill__     },
+    { .name = "restart",    .call = restart__  },
+    { .name = "rescan",     .call = rescan__   },
+    { .name = "update",     .call = update__   },
+    { .name = "populate",   .call = populate__ },
+    { .name = "config",     .call = config__   },
+    CMD_OPT_NULL,
+};
+
+static struct cmd_opt options_stickers[] = {
+    { .name = "get",        .call = stickers_get__      },
+    { .name = "set",        .call = stickers_set__      },
+    { .name = "delete",     .call = stickers_delete__   },
+    { .name = "create",     .call = stickers_create__   },
+    { .name = "destroy",    .call = stickers_destroy__  },
+    CMD_OPT_NULL,
+};
+
+#define sub_command(name) /* Create sub-commands here */                            \
+    noreturn void name ## __(struct cmd_args *args) {                               \
+        fail_if(!args->argc, "Invalid command, specify a sub command for " #name);  \
+        cmd_parse(options_ ## name, args->argc, args->argv);                        \
+    }
+sub_command(stickers)
+sub_command(search)
+sub_command(plt)
+sub_command(admin)
+
+noreturn void
+queue__(struct cmd_args *args)
+{
+    if (args->argc == 0)
+        queue_list__(args);
+    cmd_parse(options_queue, args->argc, args->argv);
+}
+#undef sub_command
+
+static struct cmd_opt options_[] = {
+    { .name = "current",  .call = current__  },
+    { .name = "play",     .call = play__     },
+    { .name = "next",     .call = next__     },
+    { .name = "previous", .call = prev__     },
+    { .name = "queue",    .call = queue__    },
+    { .name = "shuffle",  .call = shuffle__  },
+    { .name = "status",   .call = status__   },
+    { .name = "stop",     .call = stop__     },
+    { .name = "plt",      .call = plt__      },
+    { .name = "search",   .call = search__   },
+    { .name = "admin",    .call = admin__    },
+    { .name = "stickers", .call = stickers__ },
+    CMD_OPT_NULL,
+};
+
+/* The sigpipe function, if SIGPIPE signal is sent. */
+
+static void
+sigpipe__(int sig)
+{
+    LOG_ERROR("GENERAL", "Exit because of signal sigpipe (%d)", sig);
+    exit(EXIT_FAILURE);
+}
+
+/* Functions declarations. */
+
+static void
+parse_args(args_t *args, int argc, const char **argv)
+{
+    int i, got = 0;
+    size_t len;
+
+    for (i = 1; i < argc && i < 3; ++i) {
+        len = strcspn(argv[i], "=");
+
+        if (STR_NMATCH("host", argv[i], len)) {
+            args->host = (argv[i] + len + 1);
+            ++got;
+        }
+
+        else if (STR_NMATCH("port", argv[i], len)) {
+            args->port = (argv[i] + len + 1);
+            ++got;
+        }
+
+        else if (STR_NMATCH("pwd", argv[i], len)) {
+            args->pwd = (argv[i] + len + 1);
+            ++got;
+        }
+    }
+
+    args->argv = &argv[got + 1];
+    args->argc = argc - (got + 1);
+}
+
+int
+main(int argc, const char **argv)
+{
+    __log_level = ERROR;
+    executable_name = "lkt";
+    assert(NULL != setlocale(LC_ALL, "en_US.UTF-8"));   /* BECAUSE! */
+    if (signal(SIGPIPE, sigpipe__)) {
+        // LOG_ERROR("SYS", "%s", "Failed to install handler for SIGPIPE signal");
+    }
+
+    args_t args = {
+        .host = "localhost",
+        .port = "6600",
+    };
+
+    parse_args(&args, argc, argv);
+
+    host     = args.host;
+    port     = args.port;
+    password = args.pwd;
+
+    /* Communication with lektor. */
+    cmd_parse(options_, args.argc, args.argv);
+}