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