]> git.ipfire.org Git - thirdparty/util-linux.git/blob - fdisks/fdisk-menu.c
fdisk: move geometry commands to separate menu
[thirdparty/util-linux.git] / fdisks / fdisk-menu.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <stdint.h>
7
8 #include "c.h"
9 #include "fdisk.h"
10
11 struct menu_entry {
12 const char key;
13 const char *title;
14 unsigned int normal : 1,
15 expert : 1,
16 hidden : 1;
17
18 enum fdisk_labeltype exclude;
19 };
20
21 #define IS_MENU_SEP(e) ((e)->key == '-')
22 #define IS_MENU_HID(e) ((e)->hidden)
23
24 struct menu {
25 enum fdisk_labeltype label; /* only for this label */
26 enum fdisk_labeltype exclude; /* all labels except this */
27
28 int (*callback)(struct fdisk_context *,
29 const struct menu *,
30 const struct menu_entry *);
31
32 struct menu_entry entries[]; /* NULL terminated array */
33 };
34
35 struct menu_context {
36 size_t menu_idx; /* the current menu */
37 size_t entry_idx; /* index with in the current menu */
38 };
39
40 #define MENU_CXT_EMPTY { 0, 0 }
41
42 static int gpt_menu_cb(struct fdisk_context *cxt,
43 const struct menu *menu,
44 const struct menu_entry *ent);
45
46 /*
47 * Menu entry macros:
48 * MENU_X* expert mode only
49 * MENU_B* both -- expert + normal mode
50 *
51 * *_E exclude
52 * *_H hidden
53 */
54
55 /* separator */
56 #define MENU_SEP(t) { .title = t, .key = '-', .normal = 1 }
57 #define MENU_XSEP(t) { .title = t, .key = '-', .expert = 1 }
58 #define MENU_BSEP(t) { .title = t, .key = '-', .expert = 1, .normal = 1 }
59
60 /* entry */
61 #define MENU_ENT(k, t) { .title = t, .key = k, .normal = 1 }
62 #define MENU_ENT_E(k, t, l) { .title = t, .key = k, .normal = 1, .exclude = l }
63
64 #define MENU_XENT(k, t) { .title = t, .key = k, .expert = 1 }
65 #define MENU_XENT_H(k, t) { .title = t, .key = k, .expert = 1, .hidden = 1 }
66
67 #define MENU_BENT(k, t) { .title = t, .key = k, .expert = 1, .normal = 1 }
68
69
70 /* Generic menu */
71 struct menu menu_generic = {
72 /* .callback = generic_menu_cb,*/
73 .entries = {
74 MENU_BSEP(N_("Generic")),
75 MENU_ENT ('d', N_("delete a partition")),
76 MENU_ENT ('l', N_("list known partition types")),
77 MENU_ENT ('n', N_("add a new partition")),
78 MENU_BENT ('p', N_("print the partition table")),
79 MENU_ENT ('t', N_("change a partition type")),
80 MENU_ENT ('v', N_("verify the partition table")),
81
82 MENU_XENT('d', N_("print the raw data of the first sector")),
83
84 MENU_SEP(N_("Misc")),
85 MENU_BENT ('m', N_("print this menu")),
86 MENU_ENT_E('u', N_("change display/entry units"), FDISK_DISKLABEL_GPT),
87 MENU_ENT ('x', N_("extra functionality (experts only)")),
88
89 MENU_BSEP(N_("Save & Exit")),
90 MENU_ENT_E('w', N_("write table to disk and exit"), FDISK_DISKLABEL_OSF),
91 MENU_BENT ('q', N_("quit without saving changes")),
92 MENU_XENT ('r', N_("return to main menu")),
93
94 { 0, NULL }
95 }
96 };
97
98 struct menu menu_createlabel = {
99 /* .callback = createlabel_menu_cb, */
100 .exclude = FDISK_DISKLABEL_OSF,
101 .entries = {
102 MENU_SEP(N_("Create a new label")),
103 MENU_ENT('g', N_("create a new empty GPT partition table")),
104 MENU_ENT('G', N_("create a new empty SGI (IRIX) partition table")),
105 MENU_ENT('o', N_("create a new empty DOS partition table")),
106 MENU_ENT('s', N_("create a new empty Sun partition table")),
107
108 /* backward compatibility -- be sensitive to 'g', but don't
109 * print it in the expert menu */
110 MENU_XENT_H('g', N_("create an IRIX (SGI) partition table")),
111 { 0, NULL }
112 }
113 };
114
115 struct menu menu_geo = {
116 /* .callback = geo_menu_cb, */
117 .exclude = FDISK_DISKLABEL_GPT,
118 .entries = {
119 MENU_XSEP(N_("Geometry")),
120 MENU_XENT('c', N_("change number of cylinders")),
121 MENU_XENT('h', N_("change number of heads")),
122 MENU_XENT('s', N_("change number of sectors/track")),
123 { 0, NULL }
124 }
125 };
126
127 struct menu menu_gpt = {
128 .callback = gpt_menu_cb,
129 .label = FDISK_DISKLABEL_GPT,
130 .entries = {
131 MENU_XSEP(N_("GPT")),
132 MENU_XENT('u', N_("change partition UUID")),
133 MENU_XENT('n', N_("change partition name")),
134 { 0, NULL }
135 }
136 };
137
138 struct menu menu_sun = {
139 /* .callback = sun_menu_cb, */
140 .label = FDISK_DISKLABEL_SUN,
141 .entries = {
142 MENU_BSEP(N_("Sun")),
143 MENU_ENT('a', N_("toggle a read only flag")),
144 MENU_ENT('c', N_("toggle the mountable flag")),
145
146 MENU_XENT('a', N_("change number of alternate cylinders")),
147 MENU_XENT('e', N_("change number of extra sectors per cylinder")),
148 MENU_XENT('i', N_("change interleave factor")),
149 MENU_XENT('o', N_("change rotation speed (rpm)")),
150 MENU_XENT('y', N_("change number of physical cylinders")),
151 { 0, NULL }
152 }
153 };
154
155 struct menu menu_sgi = {
156 /* .callback = sgi_menu_cb, */
157 .label = FDISK_DISKLABEL_SGI,
158 .entries = {
159 MENU_SEP(N_("SGI")),
160 MENU_ENT('a', N_("select bootable partition")),
161 MENU_ENT('b', N_("edit bootfile entry")),
162 MENU_ENT('c', N_("select sgi swap partition")),
163 { 0, NULL }
164 }
165 };
166
167 struct menu menu_dos = {
168 /* .callback = dos_menu_cb, */
169 .label = FDISK_DISKLABEL_DOS,
170 .entries = {
171 MENU_BSEP(N_("DOS (MBR)")),
172 MENU_ENT('a', N_("toggle a bootable flag")),
173 MENU_ENT('b', N_("edit nested BSD disklabel")),
174 MENU_ENT('c', N_("toggle the dos compatibility flag")),
175
176 MENU_XENT('b', N_("move beginning of data in a partition")),
177 MENU_XENT('e', N_("list extended partitions")),
178 MENU_XENT('f', N_("fix partition order")),
179 MENU_XENT('i', N_("change the disk identifier")),
180 { 0, NULL }
181 }
182 };
183
184 struct menu menu_bsd = {
185 /* .callback = bsd_menu_cb, */
186 .label = FDISK_DISKLABEL_OSF,
187 .entries = {
188 MENU_SEP(N_("BSD")),
189 MENU_ENT('e', N_("edit drive data")),
190 MENU_ENT('i', N_("install bootstrap")),
191 MENU_ENT('s', N_("show complete disklabel")),
192 MENU_ENT('w', N_("write disklabel to disk")),
193 #if !defined (__alpha__)
194 MENU_ENT('x', N_("link BSD partition to non-BSD partition")),
195 #endif
196 { 0, NULL }
197 }
198 };
199
200 static const struct menu *menus[] = {
201 &menu_gpt,
202 &menu_sun,
203 &menu_sgi,
204 &menu_dos,
205 &menu_bsd,
206 &menu_geo,
207 &menu_generic,
208 &menu_createlabel,
209 };
210
211 static const struct menu_entry *next_menu_entry(
212 struct fdisk_context *cxt,
213 struct menu_context *mc)
214 {
215 while (mc->menu_idx < ARRAY_SIZE(menus)) {
216 const struct menu *m = menus[mc->menu_idx];
217 const struct menu_entry *e = &(m->entries[mc->entry_idx]);
218
219 /* no more entries */
220 if (e->title == NULL ||
221 /* menu wanted for specified labels only */
222 (m->label && cxt->label && !(m->label & cxt->label->id)) ||
223 /* menu excluded for specified labels */
224 (m->exclude && cxt->label && (m->exclude & cxt->label->id))) {
225 mc->menu_idx++;
226 mc->entry_idx = 0;
227 continue;
228 }
229
230 /* is the entry excluded for the current label? */
231 if ((e->exclude && cxt->label &&
232 e->exclude & cxt->label->id) ||
233 /* exclude non-expert entries in expect mode */
234 (e->expert == 0 && fdisk_context_display_details(cxt)) ||
235 /* exclude non-normal entries in normal mode */
236 (e->normal == 0 && !fdisk_context_display_details(cxt))) {
237
238 mc->entry_idx++;
239 continue;
240 }
241 mc->entry_idx++;
242 return e;
243
244 }
245 return NULL;
246 }
247
248 /* returns @menu and menu entry for then @key */
249 static const struct menu_entry *get_fdisk_menu_entry(
250 struct fdisk_context *cxt,
251 int key,
252 const struct menu **menu)
253 {
254 struct menu_context mc = MENU_CXT_EMPTY;
255 const struct menu_entry *e;
256
257 while ((e = next_menu_entry(cxt, &mc))) {
258 if (IS_MENU_SEP(e) || e->key != key)
259 continue;
260
261 if (menu)
262 *menu = menus[mc.menu_idx];
263 return e;
264 }
265
266 return NULL;
267 }
268
269 static int menu_detect_collisions(struct fdisk_context *cxt)
270 {
271 struct menu_context mc = MENU_CXT_EMPTY;
272 const struct menu_entry *e, *r;
273
274 while ((e = next_menu_entry(cxt, &mc))) {
275 if (IS_MENU_SEP(e))
276 continue;
277
278 r = get_fdisk_menu_entry(cxt, e->key, NULL);
279 if (!r) {
280 DBG(FRONTEND, dbgprint("warning: not found "
281 "entry for %c", e->key));
282 return -1;
283 }
284 if (r != e) {
285 DBG(FRONTEND, dbgprint("warning: duplicate key '%c'",
286 e->key));
287 DBG(FRONTEND, dbgprint(" %s", e->title));
288 DBG(FRONTEND, dbgprint(" %s", r->title));
289 abort();
290 }
291 }
292
293 return 0;
294 }
295
296 int print_fdisk_menu(struct fdisk_context *cxt)
297 {
298 struct menu_context mc = MENU_CXT_EMPTY;
299 const struct menu_entry *e;
300
301 ON_DBG(FRONTEND, menu_detect_collisions(cxt));
302
303 if (fdisk_context_display_details(cxt))
304 printf(_("\nHelp (expert commands):\n"));
305 else
306 printf(_("\nHelp:\n"));
307
308 while ((e = next_menu_entry(cxt, &mc))) {
309 if (IS_MENU_HID(e))
310 continue; /* hidden entry */
311 if (IS_MENU_SEP(e))
312 printf("\n %s\n", _(e->title));
313 else
314 printf(" %c %s\n", e->key, _(e->title));
315 }
316 fputc('\n', stdout);
317
318 return 0;
319 }
320
321 /* Asks for command, verify the key and perform the command or
322 * returns the command key if no callback for the command is
323 * implemented.
324 *
325 * Returns: <0 on error
326 * 0 on success (the command performed)
327 * >0 if no callback (then returns the key)
328 */
329 int process_fdisk_menu(struct fdisk_context *cxt)
330 {
331 const struct menu_entry *ent;
332 const struct menu *menu;
333 int key, rc;
334 const char *prompt;
335 char buf[BUFSIZ];
336
337 if (fdisk_context_display_details(cxt))
338 prompt = _("Expert command (m for help): ");
339 else
340 prompt = _("Command (m for help): ");
341
342 fputc('\n',stdout);
343 rc = get_user_reply(cxt, prompt, buf, sizeof(buf));
344 if (rc)
345 return rc;
346
347 key = buf[0];
348 ent = get_fdisk_menu_entry(cxt, key, &menu);
349 if (!ent) {
350 fdisk_warnx(cxt, _("%c: unknown command"), key);
351 return -EINVAL;
352 }
353
354 DBG(FRONTEND, dbgprint("selected: key=%c, entry='%s'",
355 key, ent->title));
356 /* hardcoded help */
357 if (key == 'm') {
358 print_fdisk_menu(cxt);
359 return 0;
360
361 /* menu has implemented callback, use it */
362 } else if (menu->callback)
363 return menu->callback(cxt, menu, ent);
364
365 /* no callback, return the key */
366 return key;
367 }
368
369 /*
370 * This is fdisk frontend for GPT specific libfdisk functions that
371 * are not expported by generic libfdisk API.
372 */
373 static int gpt_menu_cb(struct fdisk_context *cxt,
374 const struct menu *menu __attribute__((__unused__)),
375 const struct menu_entry *ent)
376 {
377 size_t n;
378 int rc;
379
380 assert(cxt);
381 assert(ent);
382 assert(fdisk_is_disklabel(cxt, GPT));
383
384 DBG(FRONTEND, dbgprint("enter GPT menu"));
385
386 rc = fdisk_ask_partnum(cxt, &n, FALSE);
387 if (rc)
388 return rc;
389
390 switch(ent->key) {
391 case 'u':
392 rc = fdisk_gpt_partition_set_uuid(cxt, n);
393 break;
394 case 'n':
395 rc = fdisk_gpt_partition_set_name(cxt, n);
396 break;
397 }
398 return rc;
399 }
400
401 #ifdef TEST_PROGRAM
402 struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
403 struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
404 struct fdisk_label *fdisk_new_mac_label(struct fdisk_context *cxt) { return NULL; }
405 struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt) { return NULL; }
406
407 int main(int argc, char *argv[])
408 {
409 struct fdisk_context *cxt;
410 int idx = 1;
411
412 fdisk_init_debug(0);
413 cxt = fdisk_new_context();
414
415 if (argc > idx && strcmp(argv[idx], "--expert") == 0) {
416 fdisk_context_enable_details(cxt, 1);
417 idx++;
418 }
419 fdisk_context_switch_label(cxt, argc > idx ? argv[idx] : "gpt");
420
421 print_fdisk_menu(cxt);
422 return 0;
423 }
424 #endif