4579b1a3852c7e4552d205b7c1ecdf666f9c8547
[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 #define _GNU_SOURCE
20
21 #include <assert.h>
22 #include <string.h>
23
24 #include "log/log.h"
25 #include "talloc/talloc.h"
26 #include "nc-boot-editor.h"
27
28 static struct boot_editor *boot_editor_from_scr(struct nc_scr *scr)
29 {
30         struct boot_editor *boot_editor;
31
32         assert(scr->sig == pb_boot_editor_sig);
33         boot_editor = (struct boot_editor *)
34                 ((char *)scr - (size_t)&((struct boot_editor *)0)->scr);
35         assert(boot_editor->scr.sig == pb_boot_editor_sig);
36         return boot_editor;
37 }
38
39 static struct boot_editor *boot_editor_from_arg(void *arg)
40 {
41         struct boot_editor *boot_editor = arg;
42
43         assert(boot_editor->scr.sig == pb_boot_editor_sig);
44         return boot_editor;
45 }
46
47 /**
48  * boot_editor_move_cursor - Move the cursor, setting correct attributes.
49  * @req: An ncurses request or char to send to form_driver().
50  */
51
52 static int boot_editor_move_cursor(struct boot_editor *boot_editor, int req)
53 {
54         int result;
55
56         wchgat(boot_editor->scr.sub_ncw, 1,
57                         boot_editor_attr_field_selected, 0, 0);
58         result = form_driver(boot_editor->ncf, req);
59         wchgat(boot_editor->scr.sub_ncw, 1, boot_editor->attr_cursor, 0, 0);
60         wrefresh(boot_editor->scr.main_ncw);
61         return result;
62 }
63
64 /**
65  * boot_editor_insert_mode_set - Set the insert mode.
66  */
67
68 static void boot_editor_insert_mode_set(struct boot_editor *boot_editor,
69                 int req)
70 {
71         switch (req) {
72         case REQ_INS_MODE:
73                 boot_editor->attr_cursor = boot_editor_attr_cursor_ins;
74                 break;
75         case REQ_OVL_MODE:
76                 boot_editor->attr_cursor = boot_editor_attr_cursor_ovl;
77                 break;
78         default:
79                 assert(0 && "bad req");
80                 break;
81         }
82         boot_editor_move_cursor(boot_editor, req);
83 }
84
85 /**
86  * boot_editor_insert_mode_tog - Toggle the insert mode.
87  */
88
89 static void boot_editor_insert_mode_tog(struct boot_editor *boot_editor)
90 {
91         if (boot_editor->attr_cursor == boot_editor_attr_cursor_ins)
92                 boot_editor_insert_mode_set(boot_editor, REQ_OVL_MODE);
93         else
94                 boot_editor_insert_mode_set(boot_editor, REQ_INS_MODE);
95 }
96
97 /**
98  * boot_editor_move_field - Move selected field, setting correct attributes.
99  * @req: An ncurses request to send to form_driver().
100  */
101
102 static int boot_editor_move_field(struct boot_editor *boot_editor, int req)
103 {
104         int result;
105
106         set_field_back(current_field(boot_editor->ncf),
107                         boot_editor_attr_field_normal);
108
109         result = form_driver(boot_editor->ncf, req);
110
111         set_field_back(current_field(boot_editor->ncf),
112                         boot_editor_attr_field_selected);
113
114         boot_editor_move_cursor(boot_editor, REQ_END_FIELD);
115         return result;
116 }
117
118 static int boot_editor_post(struct nc_scr *scr)
119 {
120         struct boot_editor *boot_editor = boot_editor_from_scr(scr);
121
122         post_form(boot_editor->ncf);
123
124         nc_scr_frame_draw(scr);
125         boot_editor_move_field(boot_editor, REQ_FIRST_FIELD);
126         boot_editor_move_field(boot_editor, REQ_END_FIELD);
127         boot_editor_insert_mode_set(boot_editor, REQ_INS_MODE);
128
129         redrawwin(boot_editor->scr.main_ncw);
130         wrefresh(boot_editor->scr.main_ncw);
131
132         return 0;
133 }
134
135 static int boot_editor_unpost(struct nc_scr *scr)
136 {
137         return unpost_form(boot_editor_from_scr(scr)->ncf);
138 }
139
140 static void boot_editor_resize(struct nc_scr *scr)
141 {
142         /* FIXME: forms can't be resized, need to recreate here */
143         boot_editor_unpost(scr);
144         boot_editor_post(scr);
145 }
146
147 /**
148  * boot_editor_chomp - Eat leading and trailing WS.
149  */
150
151 static char *boot_editor_chomp(char *s)
152 {
153         char *start;
154         char *end;
155         char *const s_end = s + strlen(s);
156
157         for (; s < s_end; s++)
158                 if (*s != ' ' && *s != '\t')
159                         break;
160
161         start = end = s;
162
163         for (; s < s_end; s++)
164                 if (*s != ' ' && *s != '\t')
165                         end = s;
166         *(end + 1) = 0;
167         return start;
168 }
169
170 static struct pb_boot_data *boot_editor_prepare_data(
171                 struct boot_editor *boot_editor)
172 {
173         struct pb_boot_data *bd;
174         char *s;
175
176         bd = talloc(boot_editor, struct pb_boot_data);
177
178         if (!bd)
179                 return NULL;
180
181         s = boot_editor_chomp(field_buffer(boot_editor->fields[0], 0));
182         bd->image = *s ? talloc_strdup(bd, s) : NULL;
183
184         s = boot_editor_chomp(field_buffer(boot_editor->fields[1], 0));
185         bd->initrd = *s ? talloc_strdup(bd, s) : NULL;
186
187         s = boot_editor_chomp(field_buffer(boot_editor->fields[2], 0));
188         bd->dtb = *s ? talloc_strdup(bd, s) : NULL;
189
190         s = boot_editor_chomp(field_buffer(boot_editor->fields[3], 0));
191         bd->args = *s ? talloc_strdup(bd, s) : NULL;
192
193         return bd;
194 }
195
196 /**
197  * boot_editor_process_key - Process a user keystroke.
198  *
199  * Called from the cui via the scr:process_key method.
200  */
201
202 static void boot_editor_process_key(struct nc_scr *scr, int key)
203 {
204         struct boot_editor *boot_editor = boot_editor_from_scr(scr);
205         struct pb_boot_data *bd;
206
207         switch (key) {
208         default:
209                 boot_editor_move_cursor(boot_editor, key);
210                 break;
211
212         /* hot keys */
213         case 27: /* ESC */
214                 boot_editor->on_exit(boot_editor,
215                                 boot_editor_cancel, NULL);
216                 nc_flush_keys();
217                 return;
218         case '\n':
219         case '\r':
220                 form_driver(boot_editor->ncf, REQ_VALIDATION);
221                 bd = boot_editor_prepare_data(boot_editor);
222                 boot_editor->on_exit(boot_editor,
223                                 boot_editor_update, bd);
224                 nc_flush_keys();
225                 return;
226
227         /* insert mode */
228         case KEY_IC:
229                 boot_editor_insert_mode_tog(boot_editor);
230                 break;
231
232         /* form nav */
233         case KEY_PPAGE:
234                 boot_editor_move_field(boot_editor, REQ_FIRST_FIELD);
235                 break;
236         case KEY_NPAGE:
237                 boot_editor_move_field(boot_editor, REQ_LAST_FIELD);
238                 break;
239         case KEY_DOWN:
240                 boot_editor_move_field(boot_editor, REQ_NEXT_FIELD);
241                 break;
242         case KEY_UP:
243                 boot_editor_move_field(boot_editor, REQ_PREV_FIELD);
244                 break;
245
246         /* field nav */
247         case KEY_HOME:
248                 boot_editor_move_cursor(boot_editor, REQ_BEG_FIELD);
249                 break;
250         case KEY_END:
251                 boot_editor_move_cursor(boot_editor, REQ_END_FIELD);
252                 break;
253         case KEY_LEFT:
254                 boot_editor_move_cursor(boot_editor, REQ_LEFT_CHAR);
255                 break;
256         case KEY_RIGHT:
257                 boot_editor_move_cursor(boot_editor, REQ_RIGHT_CHAR);
258                 break;
259         case KEY_BACKSPACE:
260                 if (boot_editor_move_cursor(boot_editor, REQ_LEFT_CHAR)
261                                 == E_OK)
262                         boot_editor_move_cursor(boot_editor,
263                                         REQ_DEL_CHAR);
264                 break;
265         case KEY_DC:
266                 boot_editor_move_cursor(boot_editor, REQ_DEL_CHAR);
267                 break;
268         }
269 }
270
271 /**
272  * boot_editor_destructor - The talloc destructor for a boot_editor.
273  */
274
275 static int boot_editor_destructor(void *arg)
276 {
277         struct boot_editor *boot_editor = boot_editor_from_arg(arg);
278         FIELD **f;
279
280         for (f = boot_editor->fields; *f; f++)
281                 free_field(*f);
282
283         free_form(boot_editor->ncf);
284         boot_editor->scr.sig = pb_removed_sig;
285
286         return 0;
287 }
288
289 static FIELD *boot_editor_setup_field(unsigned int y, unsigned int x, char *str)
290 {
291         FIELD *f;
292
293         f = new_field(1, COLS - 1 - x, y, x, 0, 0);
294         field_opts_off(f, O_STATIC | O_WRAP);
295         set_max_field(f, 256);
296         set_field_buffer(f, 0, str);
297         set_field_status(f, 0);
298         return f;
299 }
300
301 static FIELD *boot_editor_setup_label(unsigned int y, unsigned int x, char *str)
302 {
303         FIELD *f;
304
305         f = new_field(1, strlen(str), y, x, 0, 0);
306         field_opts_off(f, O_ACTIVE);
307         set_field_buffer(f, 0, str);
308         return f;
309 }
310
311 struct boot_editor *boot_editor_init(void *ui_ctx,
312                 const struct pb_boot_data *bd,
313                 void (*on_exit)(struct boot_editor *,
314                                 enum boot_editor_result,
315                                 struct pb_boot_data *))
316 {
317         struct boot_editor *boot_editor;
318
319         pb_log("%s: image:  '%s'\n", __func__, bd->image);
320         pb_log("%s: initrd: '%s'\n", __func__, bd->initrd);
321         pb_log("%s: dtb:    '%s'\n", __func__, bd->dtb);
322         pb_log("%s: args:   '%s'\n", __func__, bd->args);
323
324         assert(on_exit);
325
326         boot_editor = talloc_zero(ui_ctx, struct boot_editor);
327
328         if (!boot_editor)
329                 return NULL;
330
331         talloc_set_destructor(boot_editor, boot_editor_destructor);
332
333         nc_scr_init(&boot_editor->scr, pb_boot_editor_sig, 0,
334                         ui_ctx, boot_editor_process_key,
335                 boot_editor_post, boot_editor_unpost, boot_editor_resize);
336
337         boot_editor->scr.frame.title = talloc_strdup(boot_editor,
338                         "Petitboot Option Editor");
339         boot_editor->scr.frame.help = talloc_strdup(boot_editor,
340                         "ESC=cancel, Enter=accept");
341
342         boot_editor->on_exit = on_exit;
343
344         boot_editor->fields = talloc_array(boot_editor, FIELD *, 9);
345
346         boot_editor->fields[0] = boot_editor_setup_field(0, 9, bd->image);
347         boot_editor->fields[1] = boot_editor_setup_field(1, 9, bd->initrd);
348         boot_editor->fields[2] = boot_editor_setup_field(2, 9, bd->dtb);
349         boot_editor->fields[3] = boot_editor_setup_field(3, 9, bd->args);
350         boot_editor->fields[4] = boot_editor_setup_label(0, 1, "image:");
351         boot_editor->fields[5] = boot_editor_setup_label(1, 1, "initrd:");
352         boot_editor->fields[6] = boot_editor_setup_label(2, 1, "dtb:");
353         boot_editor->fields[7] = boot_editor_setup_label(3, 1, "args:");
354         boot_editor->fields[8] = NULL;
355
356         boot_editor->ncf = new_form(boot_editor->fields);
357
358         set_form_win(boot_editor->ncf, boot_editor->scr.main_ncw);
359         set_form_sub(boot_editor->ncf, boot_editor->scr.sub_ncw);
360
361         return boot_editor;
362 }