lib/pb-config: Implement config_set
[petitboot] / ui / ncurses / nc-boot-editor.c
1 /*
2  *  Copyright (C) 2009 Sony Computer Entertainment Inc.
3  *  Copyright 2009 Sony Corp.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include "config.h"
20
21 #define _GNU_SOURCE
22
23 #include <assert.h>
24 #include <string.h>
25
26 #include "log/log.h"
27 #include "talloc/talloc.h"
28 #include "nc-boot-editor.h"
29
30 static struct boot_editor *boot_editor_from_scr(struct nc_scr *scr)
31 {
32         struct boot_editor *boot_editor;
33
34         assert(scr->sig == pb_boot_editor_sig);
35         boot_editor = (struct boot_editor *)
36                 ((char *)scr - (size_t)&((struct boot_editor *)0)->scr);
37         assert(boot_editor->scr.sig == pb_boot_editor_sig);
38         return boot_editor;
39 }
40
41 static struct boot_editor *boot_editor_from_arg(void *arg)
42 {
43         struct boot_editor *boot_editor = arg;
44
45         assert(boot_editor->scr.sig == pb_boot_editor_sig);
46         return boot_editor;
47 }
48
49 /**
50  * boot_editor_move_cursor - Move the cursor, setting correct attributes.
51  * @req: An ncurses request or char to send to form_driver().
52  */
53
54 static int boot_editor_move_cursor(struct boot_editor *boot_editor, int req)
55 {
56         int result;
57
58         wchgat(boot_editor->scr.sub_ncw, 1,
59                         boot_editor_attr_field_selected, 0, 0);
60         result = form_driver(boot_editor->ncf, req);
61         wchgat(boot_editor->scr.sub_ncw, 1, boot_editor->attr_cursor, 0, 0);
62         wrefresh(boot_editor->scr.main_ncw);
63         return result;
64 }
65
66 /**
67  * boot_editor_insert_mode_set - Set the insert mode.
68  */
69
70 static void boot_editor_insert_mode_set(struct boot_editor *boot_editor,
71                 int req)
72 {
73         switch (req) {
74         case REQ_INS_MODE:
75                 boot_editor->attr_cursor = boot_editor_attr_cursor_ins;
76                 break;
77         case REQ_OVL_MODE:
78                 boot_editor->attr_cursor = boot_editor_attr_cursor_ovl;
79                 break;
80         default:
81                 assert(0 && "bad req");
82                 break;
83         }
84         boot_editor_move_cursor(boot_editor, req);
85 }
86
87 /**
88  * boot_editor_insert_mode_tog - Toggle the insert mode.
89  */
90
91 static void boot_editor_insert_mode_tog(struct boot_editor *boot_editor)
92 {
93         if (boot_editor->attr_cursor == boot_editor_attr_cursor_ins)
94                 boot_editor_insert_mode_set(boot_editor, REQ_OVL_MODE);
95         else
96                 boot_editor_insert_mode_set(boot_editor, REQ_INS_MODE);
97 }
98
99 /**
100  * boot_editor_move_field - Move selected field, setting correct attributes.
101  * @req: An ncurses request to send to form_driver().
102  */
103
104 static int boot_editor_move_field(struct boot_editor *boot_editor, int req)
105 {
106         int result;
107
108         set_field_back(current_field(boot_editor->ncf),
109                         boot_editor_attr_field_normal);
110
111         result = form_driver(boot_editor->ncf, req);
112
113         set_field_back(current_field(boot_editor->ncf),
114                         boot_editor_attr_field_selected);
115
116         boot_editor_move_cursor(boot_editor, REQ_END_FIELD);
117         return result;
118 }
119
120 static int boot_editor_post(struct nc_scr *scr)
121 {
122         struct boot_editor *boot_editor = boot_editor_from_scr(scr);
123
124         post_form(boot_editor->ncf);
125
126         nc_scr_frame_draw(scr);
127         boot_editor_move_field(boot_editor, REQ_FIRST_FIELD);
128         boot_editor_move_field(boot_editor, REQ_END_FIELD);
129         boot_editor_insert_mode_set(boot_editor, REQ_INS_MODE);
130
131         redrawwin(boot_editor->scr.main_ncw);
132         wrefresh(boot_editor->scr.main_ncw);
133
134         return 0;
135 }
136
137 static int boot_editor_unpost(struct nc_scr *scr)
138 {
139         return unpost_form(boot_editor_from_scr(scr)->ncf);
140 }
141
142 static void boot_editor_resize(struct nc_scr *scr)
143 {
144         /* FIXME: forms can't be resized, need to recreate here */
145         boot_editor_unpost(scr);
146         boot_editor_post(scr);
147 }
148
149 /**
150  * boot_editor_chomp - Eat leading and trailing WS.
151  */
152
153 static char *boot_editor_chomp(char *s)
154 {
155         char *start;
156         char *end;
157         char *const s_end = s + strlen(s);
158
159         for (; s < s_end; s++)
160                 if (*s != ' ' && *s != '\t')
161                         break;
162
163         start = end = s;
164
165         for (; s < s_end; s++)
166                 if (*s != ' ' && *s != '\t')
167                         end = s;
168         *(end + 1) = 0;
169         return start;
170 }
171
172 static struct pb_boot_data *boot_editor_prepare_data(
173                 struct boot_editor *boot_editor)
174 {
175         struct pb_boot_data *bd;
176         char *s;
177
178         bd = talloc(boot_editor, struct pb_boot_data);
179
180         if (!bd)
181                 return NULL;
182
183         s = boot_editor_chomp(field_buffer(boot_editor->fields[0], 0));
184         bd->image = *s ? talloc_strdup(bd, s) : NULL;
185
186         s = boot_editor_chomp(field_buffer(boot_editor->fields[1], 0));
187         bd->initrd = *s ? talloc_strdup(bd, s) : NULL;
188
189         s = boot_editor_chomp(field_buffer(boot_editor->fields[2], 0));
190         bd->dtb = *s ? talloc_strdup(bd, s) : NULL;
191
192         s = boot_editor_chomp(field_buffer(boot_editor->fields[3], 0));
193         bd->args = *s ? talloc_strdup(bd, s) : NULL;
194
195         return bd;
196 }
197
198 /**
199  * boot_editor_process_key - Process a user keystroke.
200  *
201  * Called from the cui via the scr:process_key method.
202  */
203
204 static void boot_editor_process_key(struct nc_scr *scr, int key)
205 {
206         struct boot_editor *boot_editor = boot_editor_from_scr(scr);
207         enum boot_editor_result result;
208         struct pb_boot_data *bd;
209         FIELD *field;
210
211         field = current_field(boot_editor->ncf);
212
213         switch (key) {
214         default:
215                 boot_editor_move_cursor(boot_editor, key);
216                 break;
217
218         /* hot keys */
219         case 'x':
220                 if (field != boot_editor->button_cancel &&
221                                 field != boot_editor->button_ok) {
222                         boot_editor_move_cursor(boot_editor, key);
223                         break;
224                 }
225                 /* fall through */
226
227         case 27: /* ESC */
228                 boot_editor->on_exit(boot_editor, boot_editor_cancel, NULL);
229                 nc_flush_keys();
230                 return;
231         case '\n':
232         case '\r':
233                 if (field == boot_editor->button_cancel) {
234                         result = boot_editor_cancel;
235                         bd = NULL;
236                 } else if (field == boot_editor->button_ok) {
237                         result = boot_editor_update;
238                         form_driver(boot_editor->ncf, REQ_VALIDATION);
239                         bd = boot_editor_prepare_data(boot_editor);
240                 } else {
241                         boot_editor_move_field(boot_editor, REQ_NEXT_FIELD);
242                         break;
243                 }
244                 boot_editor->on_exit(boot_editor, result, bd);
245                 nc_flush_keys();
246                 return;
247
248         /* insert mode */
249         case KEY_IC:
250                 boot_editor_insert_mode_tog(boot_editor);
251                 break;
252
253         /* form nav */
254         case KEY_PPAGE:
255                 boot_editor_move_field(boot_editor, REQ_FIRST_FIELD);
256                 break;
257         case KEY_NPAGE:
258                 boot_editor_move_field(boot_editor, REQ_LAST_FIELD);
259                 break;
260         case KEY_DOWN:
261                 boot_editor_move_field(boot_editor, REQ_NEXT_FIELD);
262                 break;
263         case KEY_UP:
264                 boot_editor_move_field(boot_editor, REQ_PREV_FIELD);
265                 break;
266
267         /* field nav */
268         case KEY_HOME:
269                 boot_editor_move_cursor(boot_editor, REQ_BEG_FIELD);
270                 break;
271         case KEY_END:
272                 boot_editor_move_cursor(boot_editor, REQ_END_FIELD);
273                 break;
274         case KEY_LEFT:
275                 boot_editor_move_cursor(boot_editor, REQ_LEFT_CHAR);
276                 break;
277         case KEY_RIGHT:
278                 boot_editor_move_cursor(boot_editor, REQ_RIGHT_CHAR);
279                 break;
280         case KEY_BACKSPACE:
281                 if (boot_editor_move_cursor(boot_editor, REQ_LEFT_CHAR)
282                                 == E_OK)
283                         boot_editor_move_cursor(boot_editor,
284                                         REQ_DEL_CHAR);
285                 break;
286         case KEY_DC:
287                 boot_editor_move_cursor(boot_editor, REQ_DEL_CHAR);
288                 break;
289         }
290 }
291
292 /**
293  * boot_editor_destructor - The talloc destructor for a boot_editor.
294  */
295
296 static int boot_editor_destructor(void *arg)
297 {
298         struct boot_editor *boot_editor = boot_editor_from_arg(arg);
299         FIELD **f;
300
301         for (f = boot_editor->fields; *f; f++)
302                 free_field(*f);
303
304         free_form(boot_editor->ncf);
305         boot_editor->scr.sig = pb_removed_sig;
306
307         return 0;
308 }
309
310 static FIELD *boot_editor_setup_field(unsigned int y, unsigned int x, char *str)
311 {
312         FIELD *f;
313
314         f = new_field(1, COLS - 1 - x, y, x, 0, 0);
315         field_opts_off(f, O_STATIC | O_WRAP);
316         set_max_field(f, 256);
317         set_field_buffer(f, 0, str);
318         set_field_status(f, 0);
319         return f;
320 }
321
322 static FIELD *boot_editor_setup_label(unsigned int y, unsigned int x, char *str)
323 {
324         FIELD *f;
325
326         f = new_field(1, strlen(str), y, x, 0, 0);
327         field_opts_off(f, O_ACTIVE);
328         set_field_buffer(f, 0, str);
329         return f;
330 }
331
332 static FIELD *boot_editor_setup_button(unsigned int y, unsigned int x,
333                 char *str)
334 {
335         FIELD *f;
336
337         f = new_field(1, strlen(str), y, x, 0, 0);
338         field_opts_off(f, O_EDIT);
339         set_field_buffer(f, 0, str);
340         set_field_back(f, A_NORMAL);
341         set_field_fore(f, A_NORMAL);
342         return f;
343 }
344
345 struct boot_editor *boot_editor_init(struct pmenu *menu,
346                 const struct pb_boot_data *bd,
347                 void (*on_exit)(struct boot_editor *,
348                                 enum boot_editor_result,
349                                 struct pb_boot_data *))
350 {
351         char *image, *initrd, *dtb, *args;
352         struct boot_editor *boot_editor;
353
354         assert(on_exit);
355
356         boot_editor = talloc_zero(menu, struct boot_editor);
357
358         if (!boot_editor)
359                 return NULL;
360
361         talloc_set_destructor(boot_editor, boot_editor_destructor);
362         boot_editor->original_pmenu = menu;
363
364         nc_scr_init(&boot_editor->scr, pb_boot_editor_sig, 0,
365                         menu, boot_editor_process_key,
366                 boot_editor_post, boot_editor_unpost, boot_editor_resize);
367
368         boot_editor->scr.frame.ltitle = talloc_strdup(boot_editor,
369                         "Petitboot Option Editor");
370         boot_editor->scr.frame.rtitle = NULL;
371         boot_editor->scr.frame.help = talloc_strdup(boot_editor,
372                         "Enter=accept");
373
374         boot_editor->on_exit = on_exit;
375
376         boot_editor->fields = talloc_array(boot_editor, FIELD *, 11);
377
378         if (bd) {
379                 image = bd->image;
380                 initrd = bd->initrd;
381                 dtb = bd->dtb;
382                 args = bd->args;
383         } else {
384                 image = initrd = dtb = args = "";
385         }
386
387         boot_editor->fields[0] = boot_editor_setup_field(0, 9, image);
388         boot_editor->fields[1] = boot_editor_setup_field(1, 9, initrd);
389         boot_editor->fields[2] = boot_editor_setup_field(2, 9, dtb);
390         boot_editor->fields[3] = boot_editor_setup_field(3, 9, args);
391         boot_editor->fields[4] = boot_editor_setup_label(0, 1, "image:");
392         boot_editor->fields[5] = boot_editor_setup_label(1, 1, "initrd:");
393         boot_editor->fields[6] = boot_editor_setup_label(2, 1, "dtb:");
394         boot_editor->fields[7] = boot_editor_setup_label(3, 1, "args:");
395         boot_editor->fields[8] = boot_editor_setup_button(5, 9, "[  OK  ] ");
396         boot_editor->fields[9] = boot_editor_setup_button(5, 9 + 10,
397                                                         "[Cancel] ");
398         boot_editor->fields[10] = NULL;
399
400         boot_editor->button_ok = boot_editor->fields[8];
401         boot_editor->button_cancel = boot_editor->fields[9];
402
403         boot_editor->ncf = new_form(boot_editor->fields);
404
405         set_form_win(boot_editor->ncf, boot_editor->scr.main_ncw);
406         set_form_sub(boot_editor->ncf, boot_editor->scr.sub_ncw);
407
408         return boot_editor;
409 }