AmendHub

Download

jcs

/

subtext

/

settings.c

 

(View History)

jcs   *: Minor cleanups, remove dead variables, fix some error paths Latest amendment: 580 on 2024-01-24

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdio.h>
18 #include <string.h>
19
20 #include "ansi.h"
21 #include "subtext.h"
22 #include "db.h"
23 #include "focusable.h"
24 #include "main_menu.h"
25 #include "settings.h"
26 #include "tcp.h" /* for long2ip/ip2long */
27 #include "util.h"
28
29 short
30 struct_editor(struct session *s, const struct struct_field *fields,
31 const size_t nfields, const struct session_menu_option *extra_opts,
32 size_t nextra_opts, void *data, size_t dsize, void **result,
33 char *title, char *prompt)
34 {
35 static const struct session_menu_option opts[] = {
36 { '#', "", "Edit setting [#]" },
37 { 's', "Ss", "Save and return to main menu" },
38 { 'q', "QqXx", "Discard changes and return to main menu" },
39 { '?', "?", "List menu options" },
40 };
41 struct session_menu_option *dopts = NULL;
42 const struct struct_field *sf;
43 long lval;
44 char initial[20];
45 char *input = NULL, *new_data;
46 size_t nopts;
47 short n, i, sval, on, ret;
48 bool any_changes = false, done, show_list;
49 unsigned short c;
50
51 new_data = xmalloc(dsize);
52 if (new_data == NULL)
53 goto done;
54 memcpy(new_data, data, dsize);
55
56 nopts = nitems(opts) + nextra_opts;
57 dopts = xcalloc(sizeof(struct session_menu_option), nopts);
58 if (dopts == NULL)
59 goto done;
60 if (extra_opts != NULL)
61 memcpy(dopts, extra_opts,
62 sizeof(struct session_menu_option) * nextra_opts);
63 memcpy(dopts + nextra_opts, opts, sizeof(opts));
64
65 session_printf(s, "{{B}}Editor: %s{{/B}}\r\n", title);
66 session_flush(s);
67
68 any_changes = false;
69 show_list = true;
70 done = false;
71 ret = -1;
72
73 while (!done && !s->ending) {
74 if (show_list) {
75 for (n = 0; n < nfields; n++) {
76 if ((n + 1) % (s->terminal_lines - 2) == 0) {
77 session_printf(s, "-- More --");
78 session_flush(s);
79 sval = session_input_char(s);
80 if (s->vt100)
81 session_printf(s, "\r%s",
82 ansi(s, ANSI_ERASE_LINE, ANSI_END));
83 else
84 session_output(s, "\r", 1);
85
86 if (sval == 'q' || sval == 'Q') {
87 /* abort pagination, like less */
88 show_list = false;
89 break;
90 }
91 }
92
93 sf = &fields[n];
94
95 session_printf(s, "{{B}}%d{{/B}}: %s [", n + 1, sf->name);
96
97 switch (sf->type) {
98 case CONFIG_TYPE_STRING:
99 session_printf(s, "%s", new_data + sf->off);
100 break;
101 case CONFIG_TYPE_PASSWORD:
102 i = strlen(new_data + sf->off);
103 while (i) {
104 session_output(s, "*", 1);
105 i--;
106 }
107 break;
108 case CONFIG_TYPE_SHORT:
109 sval = CHARS_TO_SHORT(new_data[sf->off],
110 new_data[sf->off + 1]);
111 session_printf(s, "%d", sval);
112 break;
113 case CONFIG_TYPE_LONG:
114 lval = CHARS_TO_LONG(new_data[sf->off],
115 new_data[sf->off + 1], new_data[sf->off + 2],
116 new_data[sf->off + 3]);
117 session_printf(s, "%ld", lval);
118 break;
119 case CONFIG_TYPE_BOOLEAN:
120 session_printf(s, "%s",
121 new_data[sf->off] == 0 ? "false" : "true");
122 break;
123 case CONFIG_TYPE_IP: {
124 char ip_str[16];
125 lval = CHARS_TO_LONG(new_data[sf->off],
126 new_data[sf->off + 1], new_data[sf->off + 2],
127 new_data[sf->off + 3]);
128 if (lval == 0)
129 session_printf(s, "0.0.0.0");
130 else {
131 long2ip(lval, ip_str);
132 session_printf(s, "%s", ip_str);
133 }
134 break;
135 }
136 }
137
138 session_printf(s, "]\r\n");
139 }
140 }
141
142 c = session_menu(s, NULL, prompt, NULL, dopts, nopts, show_list,
143 "Option #", &on);
144 show_list = false;
145
146 switch (c) {
147 case 's':
148 goto done;
149 case 'q':
150 any_changes = false;
151 goto done;
152 case '?':
153 show_list = true;
154 break;
155 case '#':
156 if (on < 1 || on > nfields) {
157 session_printf(s, "Invalid option\r\n");
158 session_flush(s);
159 break;
160 }
161
162 sf = &fields[on - 1];
163
164 get_input:
165 session_printf(s, "{{B}}%s:{{/B}} ", sf->name);
166 session_flush(s);
167
168 switch (sf->type) {
169 case CONFIG_TYPE_STRING:
170 case CONFIG_TYPE_PASSWORD:
171 input = session_field_input(s, sf->max, 50,
172 new_data + sf->off, false,
173 (sf->type == CONFIG_TYPE_PASSWORD ? '*' : 0));
174 session_output(s, "\r\n", 2);
175 session_flush(s);
176 if (input == NULL || s->ending)
177 break;
178
179 if (strlen(input) >= sf->max) {
180 session_printf(s,
181 "%s is too long (%d max, ^C to cancel)", sf->name,
182 sf->max - 1);
183 xfree(&input);
184 goto get_input;
185 }
186 strlcpy(new_data + sf->off, input, sf->max);
187 any_changes = true;
188 break;
189 case CONFIG_TYPE_SHORT:
190 case CONFIG_TYPE_LONG:
191 if (sf->type == CONFIG_TYPE_LONG) {
192 lval = CHARS_TO_LONG(new_data[sf->off],
193 new_data[sf->off + 1], new_data[sf->off + 2],
194 new_data[sf->off + 3]);
195 snprintf(initial, sizeof(initial), "%ld", lval);
196 } else {
197 sval = CHARS_TO_SHORT(new_data[sf->off],
198 new_data[sf->off + 1]);
199 snprintf(initial, sizeof(initial), "%d", sval);
200 }
201 input = session_field_input(s, 10, 10, initial, false, 0);
202 session_output(s, "\r\n", 2);
203 session_flush(s);
204 if (input == NULL || s->ending)
205 break;
206
207 lval = atol(input);
208 if (lval < sf->min) {
209 session_printf(s,
210 "%s must be %ld or higher (^C to cancel)\r\n",
211 sf->name, sf->min);
212 xfree(&input);
213 goto get_input;
214 }
215 if (lval > sf->max) {
216 session_printf(s,
217 "%s must be %ld or less (^C to cancel)\r\n",
218 sf->name, sf->max);
219 xfree(&input);
220 goto get_input;
221 }
222 if (sf->type == CONFIG_TYPE_LONG) {
223 new_data[sf->off] = (lval >> 24) & 0xff;
224 new_data[sf->off + 1] = (lval >> 16) & 0xff;
225 new_data[sf->off + 2] = (lval >> 8) & 0xff;
226 new_data[sf->off + 3] = lval & 0xff;
227 } else {
228 sval = lval;
229 new_data[sf->off] = (sval >> 8) & 0xff;
230 new_data[sf->off + 1] = sval & 0xff;
231 }
232 any_changes = true;
233 break;
234 case CONFIG_TYPE_BOOLEAN:
235 if (new_data[sf->off])
236 session_printf(s, "[Y/n] ");
237 else
238 session_printf(s, "[y/N] ");
239 session_flush(s);
240
241 for (;;) {
242 c = session_input_char(s);
243 if (s->ending)
244 break;
245 if (c == '\r' && new_data[sf->off])
246 c = 'y';
247 else if (c == '\r' && new_data[sf->off] == 0)
248 c = 'n';
249 if (c == 'y' || c == 'Y') {
250 new_data[sf->off] = 1;
251 any_changes = true;
252 session_printf(s, "%c\r\n", c);
253 break;
254 }
255 if (c == 'n' || c == 'N') {
256 new_data[sf->off] = 0;
257 any_changes = true;
258 session_printf(s, "%c\r\n", c);
259 break;
260 }
261 }
262 break;
263 case CONFIG_TYPE_IP: {
264 char ip_str[16];
265 lval = CHARS_TO_LONG(new_data[sf->off],
266 new_data[sf->off + 1], new_data[sf->off + 2],
267 new_data[sf->off + 3]);
268 if (lval == 0)
269 snprintf(initial, sizeof(initial), "0.0.0.0");
270 else {
271 long2ip(lval, ip_str);
272 snprintf(initial, sizeof(initial), "%s", ip_str);
273 }
274
275 input = session_field_input(s, 16, 16, initial, false, 0);
276 session_output(s, "\r\n", 2);
277 session_flush(s);
278 if (input == NULL || s->ending)
279 break;
280
281 if (input[0] == '\0') {
282 lval = 0;
283 xfree(&input);
284 } else {
285 lval = ip2long(input);
286 xfree(&input);
287 if (lval == 0) {
288 session_printf(s,
289 "Invalid IP address (^C to cancel)\r\n");
290 goto get_input;
291 }
292 }
293 new_data[sf->off] = (lval >> 24) & 0xff;
294 new_data[sf->off + 1] = (lval >> 16) & 0xff;
295 new_data[sf->off + 2] = (lval >> 8) & 0xff;
296 new_data[sf->off + 3] = lval & 0xff;
297 any_changes = true;
298 break;
299 }
300 }
301 break;
302 default:
303 ret = c;
304 done = true;
305 break;
306 }
307
308 if (s->ending) {
309 any_changes = false;
310 ret = -1;
311 goto done;
312 }
313 }
314
315 done:
316 xfree(&dopts);
317
318 if (any_changes) {
319 *result = new_data;
320 return 0;
321 } else {
322 if (new_data)
323 xfree(&new_data);
324 *result = NULL;
325 return ret;
326 }
327 }