filter: Use lsu_merge_config_file_options().
[paraslash.git] / client.c
index 88b1e93..bc5132b 100644 (file)
--- a/client.c
+++ b/client.c
@@ -1,19 +1,16 @@
-/*
- * Copyright (C) 1997-2014 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
 
 /** \file client.c The client program used to connect to para_server. */
 
 #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"
@@ -22,7 +19,8 @@
 #include "error.h"
 #include "version.h"
 
-INIT_CLIENT_ERRLISTS;
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
 
 static struct sched sched;
 static struct client_task *ct;
@@ -35,8 +33,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;
@@ -45,18 +42,18 @@ struct exec_task {
        size_t result_size;
 };
 
-static void exec_pre_select(struct sched *s, struct task *t)
+static void exec_pre_select(struct sched *s, void *context)
 {
-       struct exec_task *et = task_context(t);
+       struct exec_task *et = context;
        int ret = btr_node_status(et->btrn, 0, BTR_NT_LEAF);
 
        if (ret != 0)
                sched_min_delay(s);
 }
 
-static int exec_post_select(__a_unused struct sched *s, struct task *t)
+static int exec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct exec_task *et = task_context(t);
+       struct exec_task *et = context;
        struct btr_node *btrn = et->btrn;
        char *buf;
        size_t sz;
@@ -77,17 +74,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;
@@ -96,9 +128,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"));
@@ -113,6 +147,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;
@@ -173,7 +209,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);
@@ -204,24 +240,36 @@ I9E_DUMMY_COMPLETER(pause);
 I9E_DUMMY_COMPLETER(play);
 I9E_DUMMY_COMPLETER(si);
 I9E_DUMMY_COMPLETER(term);
-I9E_DUMMY_COMPLETER(version);
 I9E_DUMMY_COMPLETER(stop);
 I9E_DUMMY_COMPLETER(addatt);
 I9E_DUMMY_COMPLETER(init);
+I9E_DUMMY_COMPLETER(tasks);
 
 static struct i9e_completer completers[];
 
 static void help_completer(struct i9e_completion_info *ci,
-               struct i9e_completion_result *result)
+               struct i9e_completion_result *cr)
 {
-       result->matches = i9e_complete_commands(ci->word, completers);
+       char *opts[] = {LSG_SERVER_CMD_HELP_OPTS, NULL};
+
+       if (ci->word[0] == '-') {
+               i9e_complete_option(opts, ci, cr);
+               return;
+       }
+       cr->matches = i9e_complete_commands(ci->word, completers);
 }
 
 static void stat_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_STAT_OPTS, NULL};
+       i9e_complete_option(opts, ci, cr);
+}
+
+static void version_completer(struct i9e_completion_info *ci,
+               struct i9e_completion_result *cr)
+{
+       char *opts[] = {LSG_SERVER_CMD_VERSION_OPTS, NULL};
        i9e_complete_option(opts, ci, cr);
 }
 
@@ -258,7 +306,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);
@@ -268,11 +316,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", "-ls", "-ll", "-lv", "-lp", "-lm", "-lc", "-p",
-               "-a", "-r", "-d", "-sp", "-sl", "-ss", "-sn", "-sf", "-sc",
-               "-si", "-sy", "-sb", "-sd", "-sa", NULL
-       };
+       char *opts[] = {LSG_SERVER_CMD_LS_OPTS, NULL};
        if (ci->word[0] == '-')
                i9e_complete_option(opts, ci, cr);
        cr->filename_completion_desired = true;
@@ -315,12 +359,8 @@ out:
 static void lsatt_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
 {
-       char *opts[] = {"-i", "-l", "-r", NULL};
-
-       if (ci->word[0] == '-')
-               i9e_complete_option(opts, ci, cr);
-       else
-               complete_attributes(ci->word, &cr->matches);
+       char *opts[] = {LSG_SERVER_CMD_LSATT_OPTS, NULL};
+       i9e_complete_option(opts, ci, cr);
 }
 
 static void mvatt_completer(struct i9e_completion_info *ci,
@@ -338,14 +378,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);
@@ -357,7 +397,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);
@@ -367,7 +407,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);
@@ -443,9 +483,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);
@@ -456,15 +500,16 @@ static int client_i9e_line_handler(char *line)
 }
 
 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},
@@ -475,16 +520,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;
@@ -508,7 +552,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);
 }
 
@@ -533,12 +579,13 @@ struct supervisor_task {
        struct task *task;
 };
 
-static int supervisor_post_select(struct sched *s, struct task *t)
+static int supervisor_post_select(struct sched *s, void *context)
 {
-       struct supervisor_task *svt = task_context(t);
+       struct supervisor_task *svt = context;
+       int ret = task_status(ct->task);
 
-       if (ct->task->error < 0)
-               return ct->task->error;
+       if (ret < 0)
+               return ret;
        if (!svt->stdout_task_started && ct->status == CL_EXECUTING) {
                stdout_task_register(&sot, s);
                svt->stdout_task_started = true;
@@ -570,7 +617,8 @@ static struct supervisor_task supervisor_task;
  *
  * \return EXIT_SUCCESS or EXIT_FAILURE
  *
- * \sa client_open(), stdin.c, stdout.c, para_client(1), para_server(1)
+ * \sa \ref client_open(), \ref stdin.c, \ref stdout.c, para_client(1),
+ * para_server(1).
  */
 int main(int argc, char *argv[])
 {
@@ -582,7 +630,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 */
@@ -606,16 +654,19 @@ int main(int argc, char *argv[])
        }, &sched);
 
        ret = schedule(&sched);
-       if (ret >= 0 && ct->task->error < 0) {
-               switch (ct->task->error) {
-               /* these are not errors */
-               case -E_SERVER_CMD_SUCCESS:
-               case -E_EOF:
-               case -E_SERVER_EOF:
-               case -E_BTR_EOF:
-                       ret = 0;
-                       break;
-               default: ret = -E_SERVER_CMD_FAILURE;
+       if (ret >= 0) {
+               ret = task_status(ct->task);
+               if (ret < 0) {
+                       switch (ret) {
+                       /* these are not errors */
+                       case -E_SERVER_CMD_SUCCESS:
+                       case -E_EOF:
+                       case -E_SERVER_EOF:
+                       case -E_BTR_EOF:
+                               ret = 0;
+                               break;
+                       default: ret = -E_SERVER_CMD_FAILURE;
+                       }
                }
        }
        sched_shutdown(&sched);