Add \brief command to file header to make doxygen happy.
[adu.git] / interactive.c
1 /*
2 * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 #include <ctype.h> /* isspace() */
8
9 #include "adu.h"
10 #include "format.h"
11 #include "user.h"
12 #include "string.h"
13 #include "select.cmdline.h"
14 #include "select.h"
15 #include "error.h"
16
17 /**
18 * Describes one valid command for interactive mode.
19 *
20 * When invoked in interactive mode, adu reads commands from stdin. There's a
21 * static array of all such commands.
22 */
23 struct interactive_command {
24 /** The name of the command. */
25 const char *name;
26 /** Pointer to The function that is being executed. */
27 int (*handler)(char *);
28 /** Help text. */
29 const char *desc;
30 };
31
32 static struct uid_range *admissible_uids;
33 static struct format_info *fi;
34
35 #define INTERACTIVE_COMMANDS \
36 INTERACTIVE_COMMAND(set, "change the current configuration") \
37 INTERACTIVE_COMMAND(reset, "reset configuration to defaults") \
38 INTERACTIVE_COMMAND(help, "show list of commands and one-line descriptions") \
39 INTERACTIVE_COMMAND(run, "start the query according to the current configuration") \
40 INTERACTIVE_COMMAND(source, "read and execute interactive commands from a file")
41
42
43 #define INTERACTIVE_COMMAND(name, desc) \
44 static int icom_ ## name (char *line);
45
46 INTERACTIVE_COMMANDS
47
48 #undef INTERACTIVE_COMMAND
49
50 #define INTERACTIVE_COMMAND(_name, _desc) \
51 { \
52 .name = #_name, \
53 .handler = icom_ ## _name, \
54 .desc = _desc \
55 },
56
57 struct interactive_command icmds[] = {
58 INTERACTIVE_COMMANDS
59 {.name = NULL}
60 };
61
62 #define FOR_EACH_COMMAND(c) for (c = icmds; c->name; c++)
63
64 static int read_input_line(char *line, size_t size)
65 {
66 return fgets(line, size, stdin)? 1 : -1;
67 }
68
69 static int icom_run(__a_unused char *line)
70 {
71 return run_select_query(admissible_uids, fi);
72 }
73
74 static int icom_help(__a_unused char *line)
75 {
76 struct interactive_command *c;
77
78 FOR_EACH_COMMAND(c)
79 fprintf(stdout, "%s\t%s\n", c->name, c->desc);
80 return 1;
81 }
82
83 void print_interactive_help(void)
84 {
85 struct interactive_command *c;
86 FOR_EACH_COMMAND(c)
87 fprintf(stdout, "\t%s\t%s\n", c->name, c->desc);
88 }
89
90 static int icom_reset(__a_unused char *line)
91 {
92 NOTICE_LOG("resetting configuration to default\n");
93 free_format_info(fi);
94 fi = NULL;
95 free(admissible_uids);
96 admissible_uids = NULL;
97 select_cmdline_parser_init(&select_conf);
98 return parse_select_options(NULL, NULL, &admissible_uids, &fi);
99 }
100
101 static int icom_set(char *line)
102 {
103 int ret;
104 struct select_cmdline_parser_params params = {
105 .override = 1,
106 .initialize = 0,
107 .check_required = 1,
108 .check_ambiguity = 0,
109 .print_errors = 1
110 };
111 if (!line) {
112 select_cmdline_parser_dump(stdout, &select_conf);
113 return 1;
114 }
115
116 free_format_info(fi);
117 fi = NULL;
118 free(admissible_uids);
119 admissible_uids = NULL;
120 ret = parse_select_options(line, &params, &admissible_uids, &fi);
121 if (ret >= 0)
122 return ret;
123 return icom_reset(NULL);
124 }
125
126 static int exec_interactive_command(char *line)
127 {
128 const char const *delim = "\t\n\f\r\v ";
129 int i;
130 char *cmd, *args;
131 int ret = -E_SYNTAX;
132 size_t len;
133
134 if (!line || !*line)
135 return 1;
136 len = strlen(line);
137
138 while (len && isspace(line[len - 1])) {
139 line[len - 1] = '\0';
140 len--;
141 }
142 if (!len)
143 return 1;
144 line += strspn(line, delim); /* skip initial whitespace */
145 if (!*line)
146 return 1;
147 /* OK, we have a non-empty line */
148 if (*line == '#')
149 return 1;
150 cmd = adu_strdup(line);
151 args = cmd + strcspn(cmd, delim);
152 if (!*args)
153 args = NULL;
154 else {
155 *args = '\0';
156 args++;
157 /* let args point to the next non-whitespace char */
158 args += strspn(args, delim);
159 if (!*args)
160 args = NULL;
161 }
162 DEBUG_LOG("name: %s, args: %s.\n", cmd, args);
163 for (i = 0; icmds[i].name; i++) {
164 if (strcmp(icmds[i].name, cmd))
165 continue;
166 INFO_LOG("exec cmd: %s, args: %s\n", cmd, args);
167 ret = icmds[i].handler(args);
168 break;
169 }
170 free(cmd);
171 return ret;
172 }
173
174 static int icom_source(char *args)
175 {
176 char line[255];
177 FILE *src = fopen(args, "r");
178 int ret;
179
180 if (!src)
181 return -ERRNO_TO_ERROR(errno);
182 while (fgets(line, sizeof(line), src)) {
183 ret = exec_interactive_command(line);
184 if (ret < 0)
185 goto out;
186 }
187 ret = 1;
188 out:
189 fclose(src);
190 return ret;
191 }
192
193 int com_interactive(void)
194 {
195 char line[255];
196 int ret = 1;
197
198 select_cmdline_parser_init(&select_conf);
199 ret = parse_select_options(NULL, NULL, &admissible_uids, &fi);
200 if (ret< 0)
201 return ret;
202 ret = read_uid_file();
203 if (ret < 0)
204 return ret;
205 while (read_input_line(line, sizeof(line)) >= 0) {
206 ret = exec_interactive_command(line);
207 if (ret < 0)
208 printf("%s\n", adu_strerror(-ret));
209 }
210 return ret;
211 }