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