]> git.ipfire.org Git - thirdparty/util-linux.git/blob - fdisks/fdisk-menu.c
libfdisk: clean up sun geometry stuff
[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_gpt = {
116 .callback = gpt_menu_cb,
117 .label = FDISK_DISKLABEL_GPT,
118 .entries = {
119 MENU_XSEP(N_("GPT")),
120 MENU_XENT('u', N_("change partition UUID")),
121 MENU_XENT('n', N_("change partition name")),
122 { 0, NULL }
123 }
124 };
125
126 struct menu menu_sun = {
127 /* .callback = sun_menu_cb, */
128 .label = FDISK_DISKLABEL_SUN,
129 .entries = {
130 MENU_BSEP(N_("Sun")),
131 MENU_ENT('a', N_("toggle a read only flag")),
132 MENU_ENT('c', N_("toggle the mountable flag")),
133
134 MENU_XENT('a', N_("change number of alternate cylinders")),
135 MENU_XENT('c', N_("change number of cylinders")),
136 MENU_XENT('e', N_("change number of extra sectors per cylinder")),
137 MENU_XENT('h', N_("change number of heads")),
138 MENU_XENT('i', N_("change interleave factor")),
139 MENU_XENT('o', N_("change rotation speed (rpm)")),
140 MENU_XENT('s', N_("change number of sectors/track")),
141 MENU_XENT('y', N_("change number of physical cylinders")),
142 { 0, NULL }
143 }
144 };
145
146 struct menu menu_sgi = {
147 /* .callback = sgi_menu_cb, */
148 .label = FDISK_DISKLABEL_SGI,
149 .entries = {
150 MENU_SEP(N_("SGI")),
151 MENU_ENT('a', N_("select bootable partition")),
152 MENU_ENT('b', N_("edit bootfile entry")),
153 MENU_ENT('c', N_("select sgi swap partition")),
154 { 0, NULL }
155 }
156 };
157
158 struct menu menu_dos = {
159 /* .callback = dos_menu_cb, */
160 .label = FDISK_DISKLABEL_DOS,
161 .entries = {
162 MENU_BSEP(N_("DOS (MBR)")),
163 MENU_ENT('a', N_("toggle a bootable flag")),
164 MENU_ENT('b', N_("edit nested BSD disklabel")),
165 MENU_ENT('c', N_("toggle the dos compatibility flag")),
166
167 MENU_XENT('b', N_("move beginning of data in a partition")),
168 MENU_XENT('c', N_("change number of cylinders")), MENU_XENT('e', N_("list extended partitions")),
169 MENU_XENT('f', N_("fix partition order")),
170 MENU_XENT('h', N_("change number of heads")),
171 MENU_XENT('i', N_("change the disk identifier")),
172 MENU_XENT('s', N_("change number of sectors/track")),
173 { 0, NULL }
174 }
175 };
176
177 struct menu menu_bsd = {
178 /* .callback = bsd_menu_cb, */
179 .label = FDISK_DISKLABEL_OSF,
180 .entries = {
181 MENU_SEP(N_("BSD")),
182 MENU_ENT('e', N_("edit drive data")),
183 MENU_ENT('i', N_("install bootstrap")),
184 MENU_ENT('s', N_("show complete disklabel")),
185 MENU_ENT('w', N_("write disklabel to disk")),
186 #if !defined (__alpha__)
187 MENU_ENT('x', N_("link BSD partition to non-BSD partition")),
188 #endif
189 { 0, NULL }
190 }
191 };
192
193 static const struct menu *menus[] = {
194 &menu_gpt,
195 &menu_sun,
196 &menu_sgi,
197 &menu_dos,
198 &menu_bsd,
199 &menu_generic,
200 &menu_createlabel,
201 };
202
203 static const struct menu_entry *next_menu_entry(
204 struct fdisk_context *cxt,
205 struct menu_context *mc)
206 {
207 while (mc->menu_idx < ARRAY_SIZE(menus)) {
208 const struct menu *m = menus[mc->menu_idx];
209 const struct menu_entry *e = &(m->entries[mc->entry_idx]);
210
211 /* move to the next submenu if there is no more entries */
212 if (e->title == NULL ||
213 (m->label && cxt->label && !(m->label & cxt->label->id))) {
214 mc->menu_idx++;
215 mc->entry_idx = 0;
216 continue;
217 }
218
219 /* is the entry excluded for the current label? */
220 if ((e->exclude && cxt->label &&
221 e->exclude & cxt->label->id) ||
222 /* exclude non-expert entries in expect mode */
223 (e->expert == 0 && fdisk_context_display_details(cxt)) ||
224 /* exclude non-normal entries in normal mode */
225 (e->normal == 0 && !fdisk_context_display_details(cxt))) {
226
227 mc->entry_idx++;
228 continue;
229 }
230 mc->entry_idx++;
231 return e;
232
233 }
234 return NULL;
235 }
236
237 /* returns @menu and menu entry for then @key */
238 static const struct menu_entry *get_fdisk_menu_entry(
239 struct fdisk_context *cxt,
240 int key,
241 const struct menu **menu)
242 {
243 struct menu_context mc = MENU_CXT_EMPTY;
244 const struct menu_entry *e;
245
246 while ((e = next_menu_entry(cxt, &mc))) {
247 if (IS_MENU_SEP(e) || e->key != key)
248 continue;
249
250 if (menu)
251 *menu = menus[mc.menu_idx];
252 return e;
253 }
254
255 return NULL;
256 }
257
258 static int menu_detect_collisions(struct fdisk_context *cxt)
259 {
260 struct menu_context mc = MENU_CXT_EMPTY;
261 const struct menu_entry *e, *r;
262
263 while ((e = next_menu_entry(cxt, &mc))) {
264 if (IS_MENU_SEP(e))
265 continue;
266
267 r = get_fdisk_menu_entry(cxt, e->key, NULL);
268 if (!r) {
269 DBG(FRONTEND, dbgprint("warning: not found "
270 "entry for %c", e->key));
271 return -1;
272 }
273 if (r != e) {
274 DBG(FRONTEND, dbgprint("warning: duplicate key '%c'",
275 e->key));
276 DBG(FRONTEND, dbgprint(" %s", e->title));
277 DBG(FRONTEND, dbgprint(" %s", r->title));
278 abort();
279 }
280 }
281
282 return 0;
283 }
284
285 int print_fdisk_menu(struct fdisk_context *cxt)
286 {
287 struct menu_context mc = MENU_CXT_EMPTY;
288 const struct menu_entry *e;
289
290 ON_DBG(FRONTEND, menu_detect_collisions(cxt));
291
292 if (fdisk_context_display_details(cxt))
293 printf(_("\nHelp (expert commands):\n"));
294 else
295 printf(_("\nHelp:\n"));
296
297 while ((e = next_menu_entry(cxt, &mc))) {
298 if (IS_MENU_HID(e))
299 continue; /* hidden entry */
300 if (IS_MENU_SEP(e))
301 printf("\n %s\n", _(e->title));
302 else
303 printf(" %c %s\n", e->key, _(e->title));
304 }
305 fputc('\n', stdout);
306
307 return 0;
308 }
309
310 /* Asks for command, verify the key and perform the command or
311 * returns the command key if no callback for the command is
312 * implemented.
313 *
314 * Returns: <0 on error
315 * 0 on success (the command performed)
316 * >0 if no callback (then returns the key)
317 */
318 int process_fdisk_menu(struct fdisk_context *cxt)
319 {
320 const struct menu_entry *ent;
321 const struct menu *menu;
322 int key, rc;
323 const char *prompt;
324 char buf[BUFSIZ];
325
326 if (fdisk_context_display_details(cxt))
327 prompt = _("Expert command (m for help): ");
328 else
329 prompt = _("Command (m for help): ");
330
331 fputc('\n',stdout);
332 rc = get_user_reply(cxt, prompt, buf, sizeof(buf));
333 if (rc)
334 return rc;
335
336 key = buf[0];
337 ent = get_fdisk_menu_entry(cxt, key, &menu);
338 if (!ent) {
339 fdisk_warnx(cxt, _("%c: unknown command"), key);
340 return -EINVAL;
341 }
342
343 DBG(FRONTEND, dbgprint("selected: key=%c, entry='%s'",
344 key, ent->title));
345 /* hardcoded help */
346 if (key == 'm') {
347 print_fdisk_menu(cxt);
348 return 0;
349
350 /* menu has implemented callback, use it */
351 } else if (menu->callback)
352 return menu->callback(cxt, menu, ent);
353
354 /* no callback, return the key */
355 return key;
356 }
357
358 /*
359 * This is fdisk frontend for GPT specific libfdisk functions that
360 * are not expported by generic libfdisk API.
361 */
362 static int gpt_menu_cb(struct fdisk_context *cxt,
363 const struct menu *menu __attribute__((__unused__)),
364 const struct menu_entry *ent)
365 {
366 size_t n;
367 int rc;
368
369 assert(cxt);
370 assert(ent);
371 assert(fdisk_is_disklabel(cxt, GPT));
372
373 DBG(FRONTEND, dbgprint("enter GPT menu"));
374
375 rc = fdisk_ask_partnum(cxt, &n, FALSE);
376 if (rc)
377 return rc;
378
379 switch(ent->key) {
380 case 'u':
381 rc = fdisk_gpt_partition_set_uuid(cxt, n);
382 break;
383 case 'n':
384 rc = fdisk_gpt_partition_set_name(cxt, n);
385 break;
386 }
387 return rc;
388 }
389
390
391 #ifdef TEST_PROGRAM
392 struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
393 struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
394 struct fdisk_label *fdisk_new_mac_label(struct fdisk_context *cxt) { return NULL; }
395 struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt) { return NULL; }
396
397 int main(int argc, char *argv[])
398 {
399 struct fdisk_context *cxt;
400 int idx = 1;
401
402 fdisk_init_debug(0);
403 cxt = fdisk_new_context();
404
405 if (argc > idx && strcmp(argv[idx], "--expert") == 0) {
406 fdisk_context_enable_details(cxt, 1);
407 idx++;
408 }
409 fdisk_context_switch_label(cxt, argc > idx ? argv[idx] : "gpt");
410
411 print_fdisk_menu(cxt);
412 return 0;
413 }
414 #endif