]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/cfdisk.c
fdisk: add --wipe-partitions=auto|never|default
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
1 /*
2 * cfdisk.c - Display or manipulate a disk partition table.
3 *
4 * Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
5 * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
6 *
7 * The original cfdisk was inspired by the fdisk program
8 * by A. V. Le Blanc (leblanc@mcc.ac.uk.
9 *
10 * cfdisk is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <signal.h>
19 #include <ctype.h>
20 #include <getopt.h>
21 #include <assert.h>
22 #include <libsmartcols.h>
23 #include <sys/ioctl.h>
24 #include <libfdisk.h>
25
26 #ifdef HAVE_LIBBLKID
27 # include <blkid.h> /* keep it optional */
28 #endif
29
30 #ifdef HAVE_LIBMOUNT
31 # include <libmount.h> /* keep it optional for non-linux systems */
32 #endif
33
34 #ifdef HAVE_SLANG_H
35 # include <slang.h>
36 #elif defined(HAVE_SLANG_SLANG_H)
37 # include <slang/slang.h>
38 #endif
39
40 #ifdef HAVE_SLCURSES_H
41 # include <slcurses.h>
42 #elif defined(HAVE_SLANG_SLCURSES_H)
43 # include <slang/slcurses.h>
44 #elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
45 # include <ncursesw/ncurses.h>
46 #elif defined(HAVE_NCURSES_H)
47 # include <ncurses.h>
48 #elif defined(HAVE_NCURSES_NCURSES_H)
49 # include <ncurses/ncurses.h>
50 #endif
51
52 #ifdef HAVE_WIDECHAR
53 # include <wctype.h>
54 # include <wchar.h>
55 #endif
56
57 #include "c.h"
58 #include "closestream.h"
59 #include "nls.h"
60 #include "strutils.h"
61 #include "xalloc.h"
62 #include "mbsalign.h"
63 #include "colors.h"
64 #include "debug.h"
65 #include "list.h"
66
67 #ifdef __GNU__
68 # define DEFAULT_DEVICE "/dev/hd0"
69 # define ALTERNATE_DEVICE "/dev/sd0"
70 #elif defined(__FreeBSD__)
71 # define DEFAULT_DEVICE "/dev/ad0"
72 # define ALTERNATE_DEVICE "/dev/da0"
73 #else
74 # define DEFAULT_DEVICE "/dev/sda"
75 # define ALTERNATE_DEVICE "/dev/hda"
76 #endif
77
78 #define ARROW_CURSOR_STRING ">> "
79 #define ARROW_CURSOR_DUMMY " "
80 #define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
81
82 /* vertical menu */
83 #define MENU_V_SPADDING 1 /* space around menu item string */
84
85 /* horizontal menu */
86 #define MENU_H_SPADDING 0 /* space around menu item string */
87 #define MENU_H_BETWEEN 2 /* space between menu items */
88 #define MENU_H_PRESTR "["
89 #define MENU_H_POSTSTR "]"
90
91 #define MENU_TITLE_PADDING 3
92
93 #define MENU_H_PRESTR_SZ (sizeof(MENU_H_PRESTR) - 1)
94 #define MENU_H_POSTSTR_SZ (sizeof(MENU_H_POSTSTR) - 1)
95
96 #define TABLE_START_LINE 4
97 #define MENU_START_LINE (ui_lines - 4) /* The menu maybe use two lines */
98 #define INFO_LINE (ui_lines - 2)
99 #define WARN_LINE INFO_LINE
100 #define HINT_LINE (ui_lines - 1)
101
102 #define CFDISK_ERR_ESC 5000
103
104 #ifndef KEY_ESC
105 # define KEY_ESC '\033'
106 #endif
107 #ifndef KEY_DELETE
108 # define KEY_DELETE '\177'
109 #endif
110
111 /* colors */
112 enum {
113 CFDISK_CL_NONE = 0,
114 CFDISK_CL_WARNING,
115 CFDISK_CL_FREESPACE,
116 CFDISK_CL_INFO
117 };
118 static const int color_pairs[][2] = {
119 /* color foreground, background */
120 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
121 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 },
122 [CFDISK_CL_INFO] = { COLOR_BLUE, -1 }
123 };
124
125 struct cfdisk;
126
127 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
128 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
129 static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
130 static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
131 static void menu_refresh_size(struct cfdisk *cf);
132
133 static int ui_refresh(struct cfdisk *cf);
134 static void ui_warnx(const char *fmt, ...);
135 static void ui_warn(const char *fmt, ...);
136 static void ui_info(const char *fmt, ...);
137 static void ui_draw_menu(struct cfdisk *cf);
138 static int ui_menu_move(struct cfdisk *cf, int key);
139 static void ui_menu_resize(struct cfdisk *cf);
140
141 static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
142 uintmax_t low, uintmax_t up, int *expsize);
143
144 static int ui_enabled;
145 static int ui_resize;
146
147 /* ncurses LINES and COLS may be actual variables or *macros*, but we need
148 * something portable and writable */
149 size_t ui_lines;
150 size_t ui_cols;
151
152 /* menu item */
153 struct cfdisk_menuitem {
154 int key; /* keyboard shortcut */
155 const char *name; /* item name */
156 const char *desc; /* item description (hint) */
157 void *userdata;
158 };
159
160 /* menu */
161 struct cfdisk_menu {
162 char *title; /* optional menu title */
163 struct cfdisk_menuitem *items; /* array with menu items */
164 char *ignore;/* string with keys to ignore */
165 size_t width; /* maximal width of the menu item */
166 size_t nitems; /* number of the active menu items */
167 size_t page_sz;/* when menu longer than screen */
168 size_t idx; /* the current menu item */
169 int prefkey;/* preferred menu item */
170 struct cfdisk_menu *prev;
171
172 /* @ignore keys generator */
173 int (*ignore_cb) (struct cfdisk *, char *, size_t);
174
175 unsigned int vertical : 1; /* enable vertical mode */
176 };
177
178 /* main menu */
179 static struct cfdisk_menuitem main_menuitems[] = {
180 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
181 { 'd', N_("Delete"), N_("Delete the current partition") },
182 { 'n', N_("New"), N_("Create new partition from free space") },
183 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
184 { 't', N_("Type"), N_("Change the partition type") },
185 { 'h', N_("Help"), N_("Print help screen") },
186 { 's', N_("Sort"), N_("Fix partitions order") },
187 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
188 { 'u', N_("Dump"), N_("Dump partition table to sfdisk compatible script file") },
189 { 0, NULL, NULL }
190 };
191
192 /* extra partinfo in name:value pairs */
193 struct cfdisk_extra {
194 char *name;
195 char *data;
196
197 struct list_head exs;
198 };
199
200 /* line and extra partinfo list_head */
201 struct cfdisk_line {
202 char *data; /* line data */
203 struct libscols_table *extra; /* extra info ('X') */
204 WINDOW *w; /* window with extra info */
205 };
206
207 /* top level control struct */
208 struct cfdisk {
209 struct fdisk_context *cxt; /* libfdisk context */
210 struct fdisk_table *table; /* partition table */
211
212 struct cfdisk_menu *menu; /* the current menu */
213
214 int *fields; /* output columns IDs */
215 size_t nfields; /* number of columns IDs */
216
217 char *linesbuf; /* table as string */
218 size_t linesbufsz; /* size of the tb_buf */
219
220 struct cfdisk_line *lines; /* list of lines */
221
222 size_t nlines; /* number of lines */
223 size_t lines_idx; /* current line <0..N>, exclude header */
224 size_t page_sz;
225
226 unsigned int nwrites; /* fdisk_write_disklabel() counter */
227
228 WINDOW *act_win; /* the window currently on the screen */
229
230 #ifdef HAVE_LIBMOUNT
231 struct libmnt_table *mtab;
232 struct libmnt_table *fstab;
233 struct libmnt_cache *mntcache;
234 #endif
235 unsigned int wrong_order :1, /* PT not in right order */
236 zero_start :1, /* ignore existing partition table */
237 show_extra :1; /* show extra partinfo */
238 };
239
240
241 /*
242 * let's use include/debug.h stuff for cfdisk too
243 */
244 UL_DEBUG_DEFINE_MASK(cfdisk);
245 UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
246
247 #define CFDISK_DEBUG_INIT (1 << 1)
248 #define CFDISK_DEBUG_UI (1 << 2)
249 #define CFDISK_DEBUG_MENU (1 << 3)
250 #define CFDISK_DEBUG_MISC (1 << 4)
251 #define CFDISK_DEBUG_TABLE (1 << 5)
252 #define CFDISK_DEBUG_ALL 0xFFFF
253
254 #define DBG(m, x) __UL_DBG(cfdisk, CFDISK_DEBUG_, m, x)
255
256 static void cfdisk_init_debug(void)
257 {
258 __UL_INIT_DEBUG(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
259 }
260
261 /* Initialize output columns -- we follow libfdisk fields (usually specific
262 * to the label type.
263 */
264 static int cols_init(struct cfdisk *cf)
265 {
266 assert(cf);
267
268 free(cf->fields);
269 cf->fields = NULL;
270 cf->nfields = 0;
271
272 return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
273 }
274
275 static void resize(void)
276 {
277 struct winsize ws;
278
279 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1
280 && ws.ws_row && ws.ws_col) {
281 ui_lines = ws.ws_row;
282 ui_cols = ws.ws_col;
283 #if HAVE_RESIZETERM
284 resizeterm(ws.ws_row, ws.ws_col);
285 #endif
286 clearok(stdscr, TRUE);
287 }
288 touchwin(stdscr);
289
290 DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
291 ui_cols, ui_lines));
292 ui_resize = 0;
293 }
294
295 /* Reads partition in tree-like order from scols
296 */
297 static int partition_from_scols(struct fdisk_table *tb,
298 struct libscols_line *ln)
299 {
300 struct fdisk_partition *pa = scols_line_get_userdata(ln);
301
302 fdisk_table_add_partition(tb, pa);
303 fdisk_unref_partition(pa);
304
305 if (scols_line_has_children(ln)) {
306 struct libscols_line *chln;
307 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
308
309 if (!itr)
310 return -EINVAL;
311 while (scols_line_next_child(ln, itr, &chln) == 0)
312 partition_from_scols(tb, chln);
313 scols_free_iter(itr);
314 }
315 return 0;
316 }
317
318 static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
319 {
320 struct fdisk_partition *pa;
321 struct fdisk_label *lb;
322 struct fdisk_iter *itr = NULL;
323 struct libscols_table *table = NULL;
324 struct libscols_iter *s_itr = NULL;
325 char *res = NULL;
326 size_t i;
327 int tree = 0;
328 struct libscols_line *ln, *ln_cont = NULL;
329
330 DBG(TABLE, ul_debug("convert to string"));
331
332 assert(cf);
333 assert(cf->cxt);
334 assert(cf->fields);
335 assert(tb);
336
337 lb = fdisk_get_label(cf->cxt, NULL);
338 assert(lb);
339
340 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
341 if (!itr)
342 goto done;
343
344 /* get container (e.g. extended partition) */
345 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
346 if (fdisk_partition_is_nested(pa)) {
347 DBG(TABLE, ul_debug("nested detected, using tree"));
348 tree = SCOLS_FL_TREE;
349 break;
350 }
351 }
352
353 table = scols_new_table();
354 if (!table)
355 goto done;
356 scols_table_enable_maxout(table, 1);
357 scols_table_enable_nowrap(table, 1);
358
359 /* headers */
360 for (i = 0; i < cf->nfields; i++) {
361 int fl = 0;
362 const struct fdisk_field *field =
363 fdisk_label_get_field(lb, cf->fields[i]);
364 if (!field)
365 continue;
366
367 if (fdisk_field_is_number(field))
368 fl |= SCOLS_FL_RIGHT;
369 if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
370 fl |= SCOLS_FL_TRUNC;
371 if (tree && fdisk_field_get_id(field) == FDISK_FIELD_DEVICE)
372 fl |= SCOLS_FL_TREE;
373
374 if (!scols_table_new_column(table,
375 _(fdisk_field_get_name(field)),
376 fdisk_field_get_width(field), fl))
377 goto done;
378 }
379
380 /* data */
381 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
382
383 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
384 struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
385
386 ln = scols_table_new_line(table, parent);
387 if (!ln)
388 goto done;
389 for (i = 0; i < cf->nfields; i++) {
390 char *cdata = NULL;
391
392 if (fdisk_partition_to_string(pa, cf->cxt,
393 cf->fields[i], &cdata))
394 continue;
395 scols_line_refer_data(ln, i, cdata);
396 }
397 if (tree && fdisk_partition_is_container(pa))
398 ln_cont = ln;
399
400 scols_line_set_userdata(ln, (void *) pa);
401 fdisk_ref_partition(pa);
402 }
403
404 if (scols_table_is_empty(table))
405 goto done;
406
407 scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
408 scols_print_table_to_string(table, &res);
409
410 /* scols_* code might reorder lines, let's reorder @tb according to the
411 * final output (it's no problem because partitions are addressed by
412 * parno stored within struct fdisk_partition) */
413
414 /* remove all */
415 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
416 while (fdisk_table_next_partition(tb, itr, &pa) == 0)
417 fdisk_table_remove_partition(tb, pa);
418
419 s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
420 if (!s_itr)
421 goto done;
422
423 /* add all in the right order (don't forget the output is tree) */
424 while (scols_table_next_line(table, s_itr, &ln) == 0) {
425 if (scols_line_get_parent(ln))
426 continue;
427 if (partition_from_scols(tb, ln))
428 break;
429 }
430 done:
431 scols_unref_table(table);
432 scols_free_iter(s_itr);
433 fdisk_free_iter(itr);
434
435 return res;
436 }
437
438 static void cfdisk_free_lines(struct cfdisk *cf)
439 {
440 size_t i = 0;
441 while(i < cf->nlines) {
442 scols_unref_table(cf->lines[i].extra);
443
444 DBG(UI, ul_debug("delete window: %p",
445 cf->lines[i].w));
446
447 if (cf->lines[i].w)
448 delwin(cf->lines[i].w);
449 cf->lines[i].w = NULL;
450 ++i;
451 }
452 cf->act_win = NULL;
453 free(cf->lines);
454 cf->lines = NULL;
455 }
456 /*
457 * Read data about partitions from libfdisk and prepare output lines.
458 */
459 static int lines_refresh(struct cfdisk *cf)
460 {
461 int rc;
462 char *p;
463 size_t i;
464
465 assert(cf);
466
467 DBG(TABLE, ul_debug("refreshing buffer"));
468
469 free(cf->linesbuf);
470 cfdisk_free_lines(cf);
471 cf->linesbuf = NULL;
472 cf->linesbufsz = 0;
473 cf->lines = NULL;
474 cf->nlines = 0;
475
476 fdisk_unref_table(cf->table);
477 cf->table = NULL;
478
479 /* read partitions and free spaces into cf->table */
480 rc = fdisk_get_partitions(cf->cxt, &cf->table);
481 if (!rc)
482 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
483 if (rc)
484 return rc;
485
486 cf->linesbuf = table_to_string(cf, cf->table);
487 if (!cf->linesbuf)
488 return -ENOMEM;
489
490 cf->linesbufsz = strlen(cf->linesbuf);
491 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
492 cf->page_sz = 0;
493 cf->wrong_order = fdisk_table_wrong_order(cf->table) ? 1 : 0;
494
495 if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
496 cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
497
498 cf->lines = xcalloc(cf->nlines, sizeof(struct cfdisk_line));
499
500 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
501 cf->lines[i].data = p;
502 p = strchr(p, '\n');
503 if (p) {
504 *p = '\0';
505 p++;
506 }
507 cf->lines[i].extra = scols_new_table();
508 scols_table_enable_noheadings(cf->lines[i].extra, 1);
509 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_RIGHT);
510 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_TRUNC);
511 }
512
513 return 0;
514 }
515
516 static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
517 {
518 assert(cf);
519 assert(cf->table);
520
521 return fdisk_table_get_partition(cf->table, cf->lines_idx);
522 }
523
524 static int is_freespace(struct cfdisk *cf, size_t i)
525 {
526 struct fdisk_partition *pa;
527
528 assert(cf);
529 assert(cf->table);
530
531 pa = fdisk_table_get_partition(cf->table, i);
532 return fdisk_partition_is_freespace(pa);
533 }
534
535 /* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
536 * responseback to libfdisk
537 */
538 static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
539 {
540 struct cfdisk_menuitem *d, *cm;
541 int key;
542 size_t i = 0, nitems;
543 const char *name, *desc;
544
545 assert(ask);
546 assert(cf);
547
548 /* create cfdisk menu according to libfdisk ask-menu, note that the
549 * last cm[] item has to be empty -- so nitems + 1 */
550 nitems = fdisk_ask_menu_get_nitems(ask);
551 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
552
553 for (i = 0; i < nitems; i++) {
554 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
555 break;
556 cm[i].key = key;
557 cm[i].desc = desc;
558 cm[i].name = name;
559 }
560
561 /* make the new menu active */
562 menu_push(cf, cm);
563 ui_draw_menu(cf);
564 refresh();
565
566 /* wait for keys */
567 do {
568 key = getch();
569
570 if (ui_resize)
571 ui_menu_resize(cf);
572 if (ui_menu_move(cf, key) == 0)
573 continue;
574
575 switch (key) {
576 case KEY_ENTER:
577 case '\n':
578 case '\r':
579 d = menu_get_menuitem(cf, cf->menu->idx);
580 if (d)
581 fdisk_ask_menu_set_result(ask, d->key);
582 menu_pop(cf);
583 free(cm);
584 return 0;
585 }
586 } while (1);
587
588 menu_pop(cf);
589 free(cm);
590 return -1;
591 }
592
593 /* libfdisk callback
594 */
595 static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
596 struct fdisk_ask *ask,
597 void *data __attribute__((__unused__)))
598 {
599 int rc = 0;
600
601 assert(ask);
602
603 switch(fdisk_ask_get_type(ask)) {
604 case FDISK_ASKTYPE_INFO:
605 ui_info(fdisk_ask_print_get_mesg(ask));
606 break;
607 case FDISK_ASKTYPE_WARNX:
608 ui_warnx(fdisk_ask_print_get_mesg(ask));
609 break;
610 case FDISK_ASKTYPE_WARN:
611 ui_warn(fdisk_ask_print_get_mesg(ask));
612 break;
613 case FDISK_ASKTYPE_MENU:
614 ask_menu(ask, (struct cfdisk *) data);
615 break;
616 default:
617 ui_warnx(_("internal error: unsupported dialog type %d"),
618 fdisk_ask_get_type(ask));
619 return -EINVAL;
620 }
621 return rc;
622 }
623
624 static int ui_end(void)
625 {
626 if (!ui_enabled)
627 return -EINVAL;
628
629 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
630 SLsmg_gotorc(ui_lines - 1, 0);
631 SLsmg_refresh();
632 #else
633 mvcur(0, ui_cols - 1, ui_lines-1, 0);
634 #endif
635 curs_set(1);
636 nl();
637 endwin();
638 printf("\n");
639 ui_enabled = 0;
640 return 0;
641 }
642
643 static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
644 {
645 size_t width;
646 char *buf = NULL;
647
648 move(line, 0);
649 clrtoeol();
650
651 xvasprintf(&buf, fmt, ap);
652
653 width = mbs_safe_width(buf);
654 if (width > (size_t) ui_cols) {
655 char *p = strrchr(buf + ui_cols, ' ');
656 if (!p)
657 p = buf + ui_cols;
658 *p = '\0';
659 if (line + 1 >= ui_lines)
660 line--;
661 attron(attrs);
662 mvaddstr(line, 0, buf);
663 mvaddstr(line + 1, 0, p+1);
664 attroff(attrs);
665 } else {
666 attron(attrs);
667 mvaddstr(line, (ui_cols - width) / 2, buf);
668 attroff(attrs);
669 }
670 free(buf);
671 }
672
673 static void ui_center(size_t line, const char *fmt, ...)
674 {
675 va_list ap;
676 va_start(ap, fmt);
677 ui_vprint_center(line, 0, fmt, ap);
678 va_end(ap);
679 }
680
681 static void ui_warnx(const char *fmt, ...)
682 {
683 va_list ap;
684 va_start(ap, fmt);
685 if (ui_enabled)
686 ui_vprint_center(WARN_LINE,
687 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
688 fmt, ap);
689 else {
690 vfprintf(stderr, fmt, ap);
691 fputc('\n', stderr);
692 }
693 va_end(ap);
694 }
695
696 static void ui_warn(const char *fmt, ...)
697 {
698 char *fmt_m;
699 va_list ap;
700
701 xasprintf(&fmt_m, "%s: %m", fmt);
702
703 va_start(ap, fmt);
704 if (ui_enabled)
705 ui_vprint_center(WARN_LINE,
706 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
707 fmt_m, ap);
708 else {
709 vfprintf(stderr, fmt_m, ap);
710 fputc('\n', stderr);
711 }
712 va_end(ap);
713 free(fmt_m);
714 }
715
716 static void ui_clean_warn(void)
717 {
718 move(WARN_LINE, 0);
719 clrtoeol();
720 }
721
722 static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
723 {
724 va_list ap;
725 ui_end();
726
727 va_start(ap, fmt);
728 fprintf(stderr, "%s: ", program_invocation_short_name);
729 vfprintf(stderr, fmt, ap);
730 fputc('\n', stderr);
731 va_end(ap);
732
733 exit(rc);
734 }
735
736 static void ui_info(const char *fmt, ...)
737 {
738 va_list ap;
739 va_start(ap, fmt);
740 if (ui_enabled)
741 ui_vprint_center(INFO_LINE,
742 colors_wanted() ? COLOR_PAIR(CFDISK_CL_INFO) : 0,
743 fmt, ap);
744 else {
745 vfprintf(stdout, fmt, ap);
746 fputc('\n', stdout);
747 }
748 va_end(ap);
749 }
750
751 static void ui_clean_info(void)
752 {
753 move(INFO_LINE, 0);
754 clrtoeol();
755 }
756
757 static void ui_hint(const char *fmt, ...)
758 {
759 va_list ap;
760 va_start(ap, fmt);
761 if (ui_enabled)
762 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
763 else {
764 vfprintf(stdout, fmt, ap);
765 fputc('\n', stdout);
766 }
767 va_end(ap);
768 }
769
770 static void ui_clean_hint(void)
771 {
772 move(HINT_LINE, 0);
773 clrtoeol();
774 }
775
776 static void die_on_signal(int dummy __attribute__((__unused__)))
777 {
778 DBG(MISC, ul_debug("die on signal."));
779 ui_end();
780 exit(EXIT_FAILURE);
781 }
782
783 static void resize_on_signal(int dummy __attribute__((__unused__)))
784 {
785 DBG(MISC, ul_debug("resize on signal."));
786 ui_resize = 1;
787 }
788
789 static void menu_refresh_size(struct cfdisk *cf)
790 {
791 if (cf->menu && cf->menu->nitems)
792 cf->menu->page_sz = (cf->menu->nitems / (ui_lines - 4)) ? ui_lines - 4 : 0;
793 }
794
795 static void menu_update_ignore(struct cfdisk *cf)
796 {
797 char ignore[128] = { 0 };
798 int i = 0;
799 struct cfdisk_menu *m;
800 struct cfdisk_menuitem *d, *org = NULL;
801 size_t idx;
802
803 assert(cf);
804 assert(cf->menu);
805 assert(cf->menu->ignore_cb);
806
807 m = cf->menu;
808 DBG(MENU, ul_debug("update menu ignored keys"));
809
810 i = m->ignore_cb(cf, ignore, sizeof(ignore));
811 ignore[i] = '\0';
812
813 /* return if no change */
814 if ((!m->ignore && !*ignore)
815 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
816 return;
817 }
818
819 if (!m->prefkey)
820 org = menu_get_menuitem(cf, m->idx);
821
822 free(m->ignore);
823 m->ignore = xstrdup(ignore);
824 m->nitems = 0;
825
826 for (d = m->items; d->name; d++) {
827 if (m->ignore && strchr(m->ignore, d->key))
828 continue;
829 m->nitems++;
830 }
831
832 DBG(MENU, ul_debug("update menu preferred keys"));
833
834 /* refresh menu index to be at the same menuitem or go to the first */
835 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
836 m->idx = idx;
837 else if (m->prefkey && menu_get_menuitem_by_key(cf, m->prefkey, &idx))
838 m->idx = idx;
839 else
840 m->idx = 0;
841
842 menu_refresh_size(cf);
843 }
844
845 static struct cfdisk_menu *menu_push(
846 struct cfdisk *cf,
847 struct cfdisk_menuitem *items)
848 {
849 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
850 struct cfdisk_menuitem *d;
851
852 assert(cf);
853
854 DBG(MENU, ul_debug("new menu"));
855
856 m->prev = cf->menu;
857 m->items = items;
858
859 for (d = m->items; d->name; d++) {
860 const char *name = _(d->name);
861 size_t len = mbs_safe_width(name);
862 if (len > m->width)
863 m->width = len;
864 m->nitems++;
865 }
866
867 cf->menu = m;
868
869 menu_refresh_size(cf);
870 return m;
871 }
872
873 static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
874 {
875 struct cfdisk_menu *m = NULL;
876
877 assert(cf);
878
879 DBG(MENU, ul_debug("pop menu"));
880
881 if (cf->menu) {
882 m = cf->menu->prev;
883 free(cf->menu->ignore);
884 free(cf->menu->title);
885 free(cf->menu);
886 }
887 cf->menu = m;
888 return cf->menu;
889 }
890
891 static void menu_set_title(struct cfdisk_menu *m, const char *title)
892 {
893 char *str = NULL;
894
895 if (title) {
896 size_t len = mbs_safe_width(title);
897 if (len + MENU_TITLE_PADDING > m->width)
898 m->width = len + MENU_TITLE_PADDING;
899 str = xstrdup(title);
900 }
901 m->title = str;
902 }
903
904
905 static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
906 {
907 struct sigaction sa;
908
909 DBG(UI, ul_debug("init"));
910
911 /* setup SIGCHLD handler */
912 sigemptyset(&sa.sa_mask);
913 sa.sa_flags = 0;
914 sa.sa_handler = die_on_signal;
915 sigaction(SIGINT, &sa, NULL);
916 sigaction(SIGTERM, &sa, NULL);
917
918 sa.sa_handler = resize_on_signal;
919 sigaction(SIGWINCH, &sa, NULL);
920
921 ui_enabled = 1;
922 initscr();
923
924 #ifdef HAVE_USE_DEFAULT_COLORS
925 if (colors_wanted() && has_colors()) {
926 size_t i;
927
928 start_color();
929 use_default_colors();
930 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
931 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
932 }
933 #else
934 colors_off();
935 #endif
936
937 cbreak();
938 noecho();
939 nonl();
940 curs_set(0);
941 keypad(stdscr, TRUE);
942
943 return 0;
944 }
945
946 /* "[ string ]" */
947 #define MENU_H_ITEMWIDTH(m) ( MENU_H_PRESTR_SZ \
948 + MENU_H_SPADDING \
949 + (m)->width \
950 + MENU_H_SPADDING \
951 + MENU_H_POSTSTR_SZ)
952
953 #define MENU_V_ITEMWIDTH(m) (MENU_V_SPADDING + (m)->width + MENU_V_SPADDING)
954
955
956 static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
957 {
958 struct cfdisk_menu *m = cf->menu;
959
960 if (m->vertical) {
961 if (!m->page_sz) /* small menu */
962 return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
963 return (idx % m->page_sz) + 1;
964 } else {
965 size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
966 size_t items = ui_cols / len; /* items per line */
967
968 if (items == 0)
969 return 0;
970 return MENU_START_LINE + ((idx / items));
971 }
972 }
973
974 static int menuitem_get_column(struct cfdisk *cf, size_t idx)
975 {
976 if (cf->menu->vertical) {
977 size_t nc = MENU_V_ITEMWIDTH(cf->menu);
978 if ((size_t) ui_cols <= nc)
979 return 0;
980 return (ui_cols - nc) / 2;
981 } else {
982 size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
983 size_t items = ui_cols / len; /* items per line */
984 size_t extra = items < cf->menu->nitems ? /* extra space on line */
985 ui_cols % len : /* - multi-line menu */
986 ui_cols - (cf->menu->nitems * len); /* - one line menu */
987
988 if (items == 0)
989 return 0; /* hmm... no space */
990
991 extra += MENU_H_BETWEEN; /* add padding after last item to extra */
992
993 if (idx < items)
994 return (idx * len) + (extra / 2);
995 return ((idx % items) * len) + (extra / 2);
996 }
997 }
998
999 static int menuitem_on_page(struct cfdisk *cf, size_t idx)
1000 {
1001 struct cfdisk_menu *m = cf->menu;
1002
1003 if (m->page_sz == 0 ||
1004 m->idx / m->page_sz == idx / m->page_sz)
1005 return 1;
1006 return 0;
1007 }
1008
1009 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
1010 {
1011 struct cfdisk_menuitem *d;
1012 size_t i;
1013
1014 for (i = 0, d = cf->menu->items; d->name; d++) {
1015 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1016 continue;
1017 if (i++ == idx)
1018 return d;
1019 }
1020
1021 return NULL;
1022 }
1023
1024 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
1025 int key, size_t *idx)
1026 {
1027 struct cfdisk_menuitem *d;
1028
1029 for (*idx = 0, d = cf->menu->items; d->name; d++) {
1030 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1031 continue;
1032 if (key == d->key)
1033 return d;
1034 (*idx)++;
1035 }
1036
1037 return NULL;
1038 }
1039
1040 static void ui_draw_menuitem(struct cfdisk *cf,
1041 struct cfdisk_menuitem *d,
1042 size_t idx)
1043 {
1044 char buf[80 * MB_CUR_MAX], *ptr = buf;
1045 const char *name;
1046 size_t width;
1047 int ln, cl, vert = cf->menu->vertical;
1048
1049 if (!menuitem_on_page(cf, idx))
1050 return; /* no visible item */
1051 ln = menuitem_get_line(cf, idx);
1052 cl = menuitem_get_column(cf, idx);
1053
1054 /* string width */
1055 if (vert) {
1056 width = cf->menu->width + MENU_V_SPADDING;
1057 memset(ptr, ' ', MENU_V_SPADDING);
1058 ptr += MENU_V_SPADDING;
1059 } else
1060 width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
1061
1062 name = _(d->name);
1063 mbsalign(name, ptr, sizeof(buf), &width,
1064 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
1065 0);
1066
1067 DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
1068 cl, ln, buf));
1069
1070 if (vert) {
1071 mvaddch(ln, cl - 1, ACS_VLINE);
1072 mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
1073 }
1074
1075 if (cf->menu->idx == idx)
1076 standout();
1077
1078 if (vert)
1079 mvprintw(ln, cl, "%s", buf);
1080 else
1081 mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
1082
1083 if (cf->menu->idx == idx) {
1084 standend();
1085 if (d->desc)
1086 ui_hint(_(d->desc));
1087 }
1088 }
1089
1090 static void ui_clean_menu(struct cfdisk *cf)
1091 {
1092 size_t i;
1093 size_t lastline;
1094 struct cfdisk_menu *m = cf->menu;
1095 size_t ln = menuitem_get_line(cf, 0);
1096
1097 if (m->vertical)
1098 lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
1099 else
1100 lastline = menuitem_get_line(cf, m->nitems);
1101
1102 for (i = ln; i <= lastline; i++) {
1103 move(i, 0);
1104 clrtoeol();
1105 DBG(MENU, ul_debug("clean_menu: line %zu", i));
1106 }
1107 if (m->vertical) {
1108 move(ln - 1, 0);
1109 clrtoeol();
1110 }
1111 ui_clean_hint();
1112 }
1113
1114 static void ui_draw_menu(struct cfdisk *cf)
1115 {
1116 struct cfdisk_menuitem *d;
1117 struct cfdisk_menu *m;
1118 size_t i = 0;
1119 size_t ln = menuitem_get_line(cf, 0);
1120 size_t nlines;
1121
1122 assert(cf);
1123 assert(cf->menu);
1124
1125 DBG(MENU, ul_debug("draw start"));
1126
1127 ui_clean_menu(cf);
1128 m = cf->menu;
1129
1130 if (m->vertical)
1131 nlines = m->page_sz ? m->page_sz : m->nitems;
1132 else
1133 nlines = menuitem_get_line(cf, m->nitems);
1134
1135 if (m->ignore_cb)
1136 menu_update_ignore(cf);
1137 i = 0;
1138 while ((d = menu_get_menuitem(cf, i)))
1139 ui_draw_menuitem(cf, d, i++);
1140
1141 if (m->vertical) {
1142 size_t cl = menuitem_get_column(cf, 0);
1143 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
1144
1145 /* corners and horizontal lines */
1146 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
1147 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
1148
1149 for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
1150 mvaddch(ln - 1, cl + i, ACS_HLINE);
1151 mvaddch(ln + nlines, cl + i, ACS_HLINE);
1152 }
1153
1154 mvaddch(ln - 1, cl + i, ACS_URCORNER);
1155 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
1156
1157 /* draw also lines around empty lines on last page */
1158 if (m->page_sz &&
1159 m->nitems / m->page_sz == m->idx / m->page_sz) {
1160 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
1161 mvaddch(i, cl - 1, ACS_VLINE);
1162 mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
1163 }
1164 }
1165 if (m->title) {
1166 attron(A_BOLD);
1167 mvprintw(ln - 1, cl, " %s ", m->title);
1168 attroff(A_BOLD);
1169 }
1170 if (curpg != 0)
1171 mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
1172 if (m->page_sz && curpg < m->nitems / m->page_sz)
1173 mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
1174 }
1175
1176 DBG(MENU, ul_debug("draw end."));
1177 }
1178
1179 inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
1180 {
1181 struct libscols_line *lsl;
1182 int rc;
1183
1184 assert(l);
1185 assert(l->extra);
1186
1187 if (!data || !*data)
1188 return 0;
1189
1190 lsl = scols_table_new_line(l->extra, NULL);
1191 if (!lsl)
1192 return -ENOMEM;
1193
1194 rc = scols_line_set_data(lsl, 0, name);
1195 if (!rc)
1196 rc = scols_line_set_data(lsl, 1, data);
1197
1198 return rc;
1199 }
1200
1201 #ifdef HAVE_LIBMOUNT
1202 static char *get_mountpoint(struct cfdisk *cf, const char *uuid, const char *label)
1203 {
1204 struct libmnt_fs *fs = NULL;
1205 char *target = NULL;
1206 int mounted = 0;
1207
1208 assert(uuid || label);
1209
1210 DBG(UI, ul_debug("asking for mountpoint [uuid=%s, label=%s]", uuid, label));
1211
1212 if (!cf->mntcache)
1213 cf->mntcache = mnt_new_cache();
1214
1215 /* 1st try between mounted filesystems */
1216 if (!cf->mtab) {
1217 cf->mtab = mnt_new_table();
1218 if (cf->mtab) {
1219 mnt_table_set_cache(cf->mtab, cf->mntcache);
1220 mnt_table_parse_mtab(cf->mtab, NULL);
1221 }
1222 }
1223
1224 if (cf->mtab)
1225 fs = mnt_table_find_tag(cf->mtab,
1226 uuid ? "UUID" : "LABEL",
1227 uuid ? : label, MNT_ITER_FORWARD);
1228
1229 /* 2nd try fstab */
1230 if (!fs) {
1231 if (!cf->fstab) {
1232 cf->fstab = mnt_new_table();
1233 if (cf->fstab) {
1234 mnt_table_set_cache(cf->fstab, cf->mntcache);
1235 mnt_table_parse_fstab(cf->fstab, NULL);
1236 }
1237 }
1238 if (cf->fstab)
1239 fs = mnt_table_find_tag(cf->fstab,
1240 uuid ? "UUID" : "LABEL",
1241 uuid ? : label, MNT_ITER_FORWARD);
1242 } else
1243 mounted = 1;
1244
1245 if (fs) {
1246 if (mounted)
1247 xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
1248 else
1249 target = xstrdup(mnt_fs_get_target(fs));
1250 }
1251
1252 return target;
1253 }
1254 #endif /* HAVE_LIBMOUNT */
1255
1256 static void extra_prepare_data(struct cfdisk *cf)
1257 {
1258 struct fdisk_partition *pa = get_current_partition(cf);
1259 struct cfdisk_line *l = &cf->lines[cf->lines_idx];
1260 char *data = NULL;
1261 char *devuuid = NULL, *devlabel = NULL;
1262
1263 DBG(UI, ul_debug("preparing extra data"));
1264
1265 /* string data should not equal an empty string */
1266 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
1267 extra_insert_pair(l, _("Partition name:"), data);
1268 free(data);
1269 }
1270
1271 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
1272 extra_insert_pair(l, _("Partition UUID:"), data);
1273 free(data);
1274 }
1275
1276 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
1277 char *code = NULL, *type = NULL;
1278
1279 fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
1280 xasprintf(&type, "%s (%s)", data, code);
1281
1282 extra_insert_pair(l, _("Partition type:"), type);
1283 free(data);
1284 free(code);
1285 free(type);
1286 }
1287
1288 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
1289 extra_insert_pair(l, _("Attributes:"), data);
1290 free(data);
1291 }
1292
1293 /* for numeric data, only show non-zero rows */
1294 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
1295 if (atoi(data))
1296 extra_insert_pair(l, "BSIZE:", data);
1297 free(data);
1298 }
1299
1300 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
1301 if (atoi(data))
1302 extra_insert_pair(l, "CPG:", data);
1303 free(data);
1304 }
1305
1306 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
1307 if (atoi(data))
1308 extra_insert_pair(l, "FSIZE:", data);
1309 free(data);
1310 }
1311
1312 #ifdef HAVE_LIBBLKID
1313 if (fdisk_partition_has_start(pa) && fdisk_partition_has_size(pa)) {
1314 int fd;
1315 uintmax_t start, size;
1316 blkid_probe pr = blkid_new_probe();
1317
1318 if (!pr)
1319 goto done;
1320
1321 DBG(UI, ul_debug("blkid prober: %p", pr));
1322
1323 start = fdisk_partition_get_start(pa) * fdisk_get_sector_size(cf->cxt);
1324 size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
1325 fd = fdisk_get_devfd(cf->cxt);
1326
1327 if (blkid_probe_set_device(pr, fd, start, size) == 0 &&
1328 blkid_do_fullprobe(pr) == 0) {
1329 const char *bdata = NULL;
1330
1331 if (!blkid_probe_lookup_value(pr, "TYPE", &bdata, NULL))
1332 extra_insert_pair(l, _("Filesystem:"), bdata);
1333 if (!blkid_probe_lookup_value(pr, "LABEL", &bdata, NULL)) {
1334 extra_insert_pair(l, _("Filesystem label:"), bdata);
1335 devlabel = xstrdup(bdata);
1336 }
1337 if (!blkid_probe_lookup_value(pr, "UUID", &bdata, NULL)) {
1338 extra_insert_pair(l, _("Filesystem UUID:"), bdata);
1339 devuuid = xstrdup(bdata);
1340 }
1341 }
1342 blkid_free_probe(pr);
1343 }
1344 #endif /* HAVE_LIBBLKID */
1345
1346 #ifdef HAVE_LIBMOUNT
1347 if (devuuid || devlabel) {
1348 data = get_mountpoint(cf, devuuid, devlabel);
1349 if (data) {
1350 extra_insert_pair(l, _("Mountpoint:"), data);
1351 free(data);
1352 }
1353 }
1354 #endif /* HAVE_LIBMOUNT */
1355 done:
1356 free(devlabel);
1357 free(devuuid);
1358 }
1359
1360 static int ui_draw_extra(struct cfdisk *cf)
1361 {
1362 WINDOW *win_ex;
1363 int wline = 1;
1364 struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
1365 char *tbstr = NULL, *end;
1366 int win_ex_start_line, win_height, tblen;
1367 int ndatalines;
1368
1369 if (!cf->show_extra)
1370 return 0;
1371
1372 DBG(UI, ul_debug("draw extra"));
1373
1374 assert(ln->extra);
1375
1376 if (cf->act_win) {
1377 wclear(cf->act_win);
1378 touchwin(stdscr);
1379 }
1380
1381 if (scols_table_is_empty(ln->extra)) {
1382 extra_prepare_data(cf);
1383 if (scols_table_is_empty(ln->extra))
1384 return 0;
1385 }
1386
1387 ndatalines = fdisk_table_get_nents(cf->table) + 1;
1388
1389 /* nents + header + one free line */
1390 win_ex_start_line = TABLE_START_LINE + ndatalines;
1391 win_height = MENU_START_LINE - win_ex_start_line;
1392 tblen = scols_table_get_nlines(ln->extra);
1393
1394 /* we can't get a single line of data under the partlist*/
1395 if (win_height < 3)
1396 return 1;
1397
1398 /* number of data lines + 2 for top/bottom lines */
1399 win_height = win_height < tblen + 2 ? win_height : tblen + 2;
1400
1401 if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
1402 win_ex_start_line = MENU_START_LINE - win_height;
1403
1404 win_ex = subwin(stdscr, win_height, ui_cols - 2, win_ex_start_line, 1);
1405
1406 scols_table_reduce_termwidth(ln->extra, 4);
1407 scols_print_table_to_string(ln->extra, &tbstr);
1408
1409 end = tbstr;
1410 while ((end = strchr(end, '\n')))
1411 *end++ = '\0';
1412
1413 box(win_ex, 0, 0);
1414
1415 end = tbstr;
1416 while (--win_height > 1) {
1417 mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
1418 tbstr += strlen(tbstr) + 1;
1419 }
1420 free(end);
1421
1422 if (ln->w)
1423 delwin(ln->w);
1424
1425 DBG(UI, ul_debug("draw window: %p", win_ex));
1426 touchwin(stdscr);
1427 wrefresh(win_ex);
1428
1429 cf->act_win = ln->w = win_ex;
1430 return 0;
1431 }
1432
1433 static void ui_menu_goto(struct cfdisk *cf, int where)
1434 {
1435 struct cfdisk_menuitem *d;
1436 size_t old;
1437
1438 /* stop and begin/end for vertical menus */
1439 if (cf->menu->vertical) {
1440 if (where < 0)
1441 where = 0;
1442 else if (where > (int) cf->menu->nitems - 1)
1443 where = cf->menu->nitems - 1;
1444 } else {
1445 /* continue from begin/end */
1446 if (where < 0)
1447 where = cf->menu->nitems - 1;
1448 else if ((size_t) where > cf->menu->nitems - 1)
1449 where = 0;
1450 }
1451 if ((size_t) where == cf->menu->idx)
1452 return;
1453
1454 ui_clean_info();
1455
1456 old = cf->menu->idx;
1457 cf->menu->idx = where;
1458
1459 if (!menuitem_on_page(cf, old)) {
1460 ui_draw_menu(cf);
1461 return;
1462 }
1463
1464 d = menu_get_menuitem(cf, old);
1465 ui_draw_menuitem(cf, d, old);
1466
1467 d = menu_get_menuitem(cf, where);
1468 ui_draw_menuitem(cf, d, where);
1469
1470 }
1471
1472 static int ui_menu_move(struct cfdisk *cf, int key)
1473 {
1474 struct cfdisk_menu *m;
1475
1476 assert(cf);
1477 assert(cf->menu);
1478
1479 if (key == ERR)
1480 return 0; /* ignore errors */
1481
1482 m = cf->menu;
1483
1484 DBG(MENU, ul_debug("menu move key >%c<.", key));
1485
1486 if (m->vertical)
1487 {
1488 switch (key) {
1489 case KEY_DOWN:
1490 case '\016': /* ^N */
1491 case 'j': /* Vi-like alternative */
1492 ui_menu_goto(cf, m->idx + 1);
1493 return 0;
1494 case KEY_UP:
1495 case '\020': /* ^P */
1496 case 'k': /* Vi-like alternative */
1497 ui_menu_goto(cf, (int) m->idx - 1);
1498 return 0;
1499 case KEY_PPAGE:
1500 if (m->page_sz) {
1501 ui_menu_goto(cf, (int) m->idx - m->page_sz);
1502 return 0;
1503 }
1504 /* fallthrough */
1505 case KEY_HOME:
1506 ui_menu_goto(cf, 0);
1507 return 0;
1508 case KEY_NPAGE:
1509 if (m->page_sz) {
1510 ui_menu_goto(cf, m->idx + m->page_sz);
1511 return 0;
1512 }
1513 /* fallthrough */
1514 case KEY_END:
1515 ui_menu_goto(cf, m->nitems);
1516 return 0;
1517 }
1518 } else {
1519 switch (key) {
1520 case KEY_RIGHT:
1521 case '\t':
1522 ui_menu_goto(cf, m->idx + 1);
1523 return 0;
1524 case KEY_LEFT:
1525 #ifdef KEY_BTAB
1526 case KEY_BTAB:
1527 #endif
1528 ui_menu_goto(cf, (int) m->idx - 1);
1529 return 0;
1530 }
1531 }
1532
1533 return 1; /* key irrelevant for menu move */
1534 }
1535
1536 /* but don't call me from ui_run(), this is for pop-up menus only */
1537 static void ui_menu_resize(struct cfdisk *cf)
1538 {
1539 resize();
1540 ui_clean_menu(cf);
1541 menu_refresh_size(cf);
1542 ui_draw_menu(cf);
1543 refresh();
1544 }
1545
1546 static int partition_on_page(struct cfdisk *cf, size_t i)
1547 {
1548 if (cf->page_sz == 0 ||
1549 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1550 return 1;
1551 return 0;
1552 }
1553
1554 static void ui_draw_partition(struct cfdisk *cf, size_t i)
1555 {
1556 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1557 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
1558 int cur = cf->lines_idx == i;
1559 size_t curpg = 0;
1560
1561 if (cf->page_sz) {
1562 if (!partition_on_page(cf, i))
1563 return;
1564 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1565 curpg = cf->lines_idx / cf->page_sz;
1566 }
1567
1568 DBG(UI, ul_debug(
1569 "draw partition %zu [page_sz=%zu, "
1570 "line=%d, idx=%zu]",
1571 i, cf->page_sz, ln, cf->lines_idx));
1572
1573 if (cur) {
1574 attron(A_REVERSE);
1575 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1576 mvaddstr(ln, cl, cf->lines[i + 1].data);
1577 attroff(A_REVERSE);
1578 } else {
1579 int at = 0;
1580
1581 if (colors_wanted() && is_freespace(cf, i)) {
1582 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1583 at = 1;
1584 }
1585 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1586 mvaddstr(ln, cl, cf->lines[i + 1].data);
1587 if (at)
1588 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
1589 }
1590
1591 if ((size_t) ln == MENU_START_LINE - 1 &&
1592 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1593 if (cur)
1594 attron(A_REVERSE);
1595 mvaddch(ln, ui_cols - 1, ACS_DARROW);
1596 mvaddch(ln, 0, ACS_DARROW);
1597 if (cur)
1598 attroff(A_REVERSE);
1599 }
1600
1601 }
1602
1603 static int ui_draw_table(struct cfdisk *cf)
1604 {
1605 int cl = ARROW_CURSOR_WIDTH;
1606 size_t i, nparts = fdisk_table_get_nents(cf->table);
1607 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
1608
1609 DBG(UI, ul_debug("draw table"));
1610
1611 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1612 move(i, 0);
1613 clrtoeol();
1614 }
1615
1616 if (nparts == 0 || (size_t) cf->lines_idx > nparts - 1)
1617 cf->lines_idx = nparts ? nparts - 1 : 0;
1618
1619 /* print header */
1620 attron(A_BOLD);
1621 mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
1622 attroff(A_BOLD);
1623
1624 /* print partitions */
1625 for (i = 0; i < nparts; i++)
1626 ui_draw_partition(cf, i);
1627
1628 if (curpg != 0) {
1629 mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
1630 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1631 }
1632 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1633 mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
1634 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1635 }
1636 return 0;
1637 }
1638
1639 static int ui_table_goto(struct cfdisk *cf, int where)
1640 {
1641 size_t old;
1642 size_t nparts = fdisk_table_get_nents(cf->table);
1643
1644 DBG(UI, ul_debug("goto table %d", where));
1645
1646 if (where < 0)
1647 where = 0;
1648 else if ((size_t) where > nparts - 1)
1649 where = nparts - 1;
1650
1651 if ((size_t) where == cf->lines_idx)
1652 return 0;
1653
1654 old = cf->lines_idx;
1655 cf->lines_idx = where;
1656
1657 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1658 ui_draw_table(cf);
1659 else {
1660 ui_draw_partition(cf, old); /* cleanup old */
1661 ui_draw_partition(cf, where); /* draw new */
1662 }
1663 ui_clean_info();
1664 ui_draw_menu(cf);
1665 ui_draw_extra(cf);
1666 refresh();
1667
1668 return 0;
1669 }
1670
1671 static int ui_refresh(struct cfdisk *cf)
1672 {
1673 struct fdisk_label *lb;
1674 char *id = NULL;
1675 uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
1676 char *strsz;
1677
1678 if (!ui_enabled)
1679 return -EINVAL;
1680
1681 strsz = size_to_human_string(SIZE_SUFFIX_SPACE
1682 | SIZE_SUFFIX_3LETTER, bytes);
1683
1684 lb = fdisk_get_label(cf->cxt, NULL);
1685 assert(lb);
1686
1687 clear();
1688
1689 /* header */
1690 attron(A_BOLD);
1691 ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
1692 attroff(A_BOLD);
1693 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
1694 strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
1695 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1696 ui_center(2, _("Label: %s, identifier: %s"),
1697 fdisk_label_get_name(lb), id);
1698 else
1699 ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
1700 free(strsz);
1701
1702 ui_draw_table(cf);
1703 ui_draw_menu(cf);
1704 refresh();
1705 return 0;
1706 }
1707
1708 static ssize_t ui_get_string(const char *prompt,
1709 const char *hint, char *buf, size_t len)
1710 {
1711 size_t cells = 0;
1712 ssize_t i = 0, rc = -1;
1713 int ln = MENU_START_LINE, cl = 1;
1714
1715 assert(buf);
1716 assert(len);
1717
1718 move(ln, 0);
1719 clrtoeol();
1720
1721 move(ln + 1, 0);
1722 clrtoeol();
1723
1724 if (prompt) {
1725 mvaddstr(ln, cl, (char *) prompt);
1726 cl += mbs_safe_width(prompt);
1727 }
1728
1729 /* default value */
1730 if (*buf) {
1731 i = strlen(buf);
1732 cells = mbs_safe_width(buf);
1733 mvaddstr(ln, cl, buf);
1734 }
1735
1736 if (hint)
1737 ui_hint(hint);
1738 else
1739 ui_clean_hint();
1740
1741 move(ln, cl + cells);
1742 curs_set(1);
1743 refresh();
1744
1745 while (1) {
1746 #if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1747 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1748 wint_t c;
1749 if (get_wch(&c) == ERR) {
1750 #else
1751 int c;
1752 if ((c = getch()) == ERR) {
1753 #endif
1754 if (ui_resize) {
1755 resize();
1756 continue;
1757 }
1758 if (!isatty(STDIN_FILENO))
1759 exit(2);
1760 else
1761 goto done;
1762 }
1763 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1764 break;
1765
1766 switch (c) {
1767 case KEY_ESC:
1768 rc = -CFDISK_ERR_ESC;
1769 goto done;
1770 case KEY_LEFT: /* TODO: implement full buffer editor */
1771 case KEY_RIGHT:
1772 case KEY_END:
1773 case KEY_HOME:
1774 case KEY_UP:
1775 case KEY_DOWN:
1776 beep();
1777 break;
1778 case KEY_DELETE:
1779 case '\b':
1780 case KEY_BACKSPACE:
1781 if (i > 0) {
1782 cells--;
1783 i = mbs_truncate(buf, &cells);
1784 if (i < 0)
1785 goto done;
1786 mvaddch(ln, cl + cells, ' ');
1787 move(ln, cl + cells);
1788 } else
1789 beep();
1790 break;
1791
1792 default:
1793 #if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1794 if (i + 1 < (ssize_t) len && iswprint(c)) {
1795 wchar_t wc = (wchar_t) c;
1796 char s[MB_CUR_MAX + 1];
1797 int sz = wctomb(s, wc);
1798
1799 if (sz > 0 && sz + i < (ssize_t) len) {
1800 s[sz] = '\0';
1801 mvaddnstr(ln, cl + cells, s, sz);
1802 memcpy(buf + i, s, sz);
1803 i += sz;
1804 buf[i] = '\0';
1805 cells += wcwidth(wc);
1806 } else
1807 beep();
1808 }
1809 #else
1810 if (i + 1 < (ssize_t) len && isprint(c)) {
1811 mvaddch(ln, cl + cells, c);
1812 buf[i++] = c;
1813 buf[i] = '\0';
1814 cells++;
1815 }
1816 #endif
1817 else
1818 beep();
1819 }
1820 refresh();
1821 }
1822
1823 rc = i; /* success */
1824 done:
1825 move(ln, 0);
1826 clrtoeol();
1827 curs_set(0);
1828 refresh();
1829
1830 return rc;
1831 }
1832
1833 /* @res is default value as well as result in bytes */
1834 static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
1835 uintmax_t low, uintmax_t up, int *expsize)
1836 {
1837 char buf[128];
1838 uintmax_t user = 0;
1839 ssize_t rc;
1840 char *dflt = size_to_human_string(0, *res);
1841
1842 DBG(UI, ul_debug("get_size (default=%ju)", *res));
1843
1844 ui_clean_info();
1845
1846 snprintf(buf, sizeof(buf), "%s", dflt);
1847
1848 do {
1849 int pwr = 0, insec = 0;
1850
1851 rc = ui_get_string(prompt,
1852 _("May be followed by M for MiB, G for GiB, "
1853 "T for TiB, or S for sectors."),
1854 buf, sizeof(buf));
1855 ui_clean_warn();
1856
1857 if (rc == 0) {
1858 ui_warnx(_("Please, specify size."));
1859 continue; /* nothing specified */
1860 } else if (rc == -CFDISK_ERR_ESC)
1861 break; /* cancel dialog */
1862
1863 if (strcmp(buf, dflt) == 0)
1864 user = *res, rc = 0; /* no change, use default */
1865 else {
1866 size_t len = strlen(buf);
1867 if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
1868 insec = 1;
1869 buf[len - 1] = '\0';
1870 }
1871 rc = parse_size(buf, &user, &pwr); /* parse */
1872 }
1873
1874 if (rc == 0) {
1875 DBG(UI, ul_debug("get_size user=%ju, power=%d, sectors=%s",
1876 user, pwr, insec ? "yes" : "no"));
1877 if (insec)
1878 user *= fdisk_get_sector_size(cf->cxt);
1879 if (user < low) {
1880 ui_warnx(_("Minimum size is %ju bytes."), low);
1881 rc = -ERANGE;
1882 }
1883 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1884 /* ignore when the user specified size overflow
1885 * with in range specified by suffix (e.g. MiB) */
1886 user = up;
1887
1888 if (user > up) {
1889 ui_warnx(_("Maximum size is %ju bytes."), up);
1890 rc = -ERANGE;
1891 }
1892 if (rc == 0 && insec && expsize)
1893 *expsize = 1;
1894
1895 } else
1896 ui_warnx(_("Failed to parse size."));
1897 } while (rc != 0);
1898
1899 if (rc == 0)
1900 *res = user;
1901 free(dflt);
1902
1903 DBG(UI, ul_debug("get_size (result=%ju, rc=%zd)", *res, rc));
1904 return rc;
1905 }
1906
1907 static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1908 struct fdisk_parttype *cur)
1909 {
1910 struct cfdisk_menuitem *d, *cm;
1911 size_t i = 0, nitems, idx = 0;
1912 struct fdisk_parttype *t = NULL;
1913 struct fdisk_label *lb;
1914 int codetypes = 0;
1915
1916 DBG(UI, ul_debug("asking for parttype."));
1917
1918 lb = fdisk_get_label(cf->cxt, NULL);
1919
1920 /* create cfdisk menu according to label types, note that the
1921 * last cm[] item has to be empty -- so nitems + 1 */
1922 nitems = fdisk_label_get_nparttypes(lb);
1923 if (!nitems)
1924 return NULL;
1925
1926 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
1927 if (!cm)
1928 return NULL;
1929
1930 codetypes = fdisk_label_has_code_parttypes(lb);
1931
1932 for (i = 0; i < nitems; i++) {
1933 const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
1934 char *name;
1935
1936 cm[i].userdata = (void *) x;
1937 if (codetypes)
1938 xasprintf(&name, "%2x %s",
1939 fdisk_parttype_get_code(x),
1940 _(fdisk_parttype_get_name(x)));
1941 else {
1942 name = (char *) _(fdisk_parttype_get_name(x));
1943 cm[i].desc = fdisk_parttype_get_string(x);
1944 }
1945 cm[i].name = name;
1946 if (x == cur)
1947 idx = i;
1948 }
1949
1950 /* make the new menu active */
1951 menu_push(cf, cm);
1952 cf->menu->vertical = 1;
1953 cf->menu->idx = idx;
1954 menu_set_title(cf->menu, _("Select partition type"));
1955 ui_draw_menu(cf);
1956 refresh();
1957
1958 do {
1959 int key = getch();
1960
1961 if (ui_resize)
1962 ui_menu_resize(cf);
1963 if (ui_menu_move(cf, key) == 0)
1964 continue;
1965
1966 switch (key) {
1967 case KEY_ENTER:
1968 case '\n':
1969 case '\r':
1970 d = menu_get_menuitem(cf, cf->menu->idx);
1971 if (d)
1972 t = (struct fdisk_parttype *) d->userdata;
1973 goto done;
1974 case KEY_ESC:
1975 case 'q':
1976 case 'Q':
1977 goto done;
1978 }
1979 } while (1);
1980
1981 done:
1982 menu_pop(cf);
1983 if (codetypes) {
1984 for (i = 0; i < nitems; i++)
1985 free((char *) cm[i].name);
1986 }
1987 free(cm);
1988 DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
1989 fdisk_parttype_get_name(t) : NULL));
1990 return t;
1991 }
1992
1993 static int ui_script_read(struct cfdisk *cf)
1994 {
1995 struct fdisk_script *sc = NULL;
1996 char buf[PATH_MAX] = { 0 };
1997 int rc;
1998
1999 erase();
2000 rc = ui_get_string( _("Enter script file name: "),
2001 _("The script file will be applied to in-memory partition table."),
2002 buf, sizeof(buf));
2003 if (rc <= 0)
2004 return rc;
2005
2006 rc = -1;
2007 errno = 0;
2008 sc = fdisk_new_script_from_file(cf->cxt, buf);
2009 if (!sc && errno)
2010 ui_warn(_("Cannot open %s"), buf);
2011 else if (!sc)
2012 ui_warnx(_("Failed to parse script file %s"), buf);
2013 else if (fdisk_apply_script(cf->cxt, sc) != 0)
2014 ui_warnx(_("Failed to apply script %s"), buf);
2015 else
2016 rc = 0;
2017
2018 ui_clean_hint();
2019 fdisk_unref_script(sc);
2020 return rc;
2021 }
2022
2023 static int ui_script_write(struct cfdisk *cf)
2024 {
2025 struct fdisk_script *sc = NULL;
2026 char buf[PATH_MAX] = { 0 };
2027 FILE *f = NULL;
2028 int rc;
2029
2030 rc = ui_get_string( _("Enter script file name: "),
2031 _("The current in-memory partition table will be dumped to the file."),
2032 buf, sizeof(buf));
2033 if (rc <= 0)
2034 return rc;
2035
2036 rc = 0;
2037 sc = fdisk_new_script(cf->cxt);
2038 if (!sc) {
2039 ui_warn(_("Failed to allocate script handler"));
2040 goto done;
2041 }
2042
2043 rc = fdisk_script_read_context(sc, NULL);
2044 if (rc) {
2045 ui_warnx(_("Failed to read disk layout into script."));
2046 goto done;
2047 }
2048
2049 DBG(UI, ul_debug("writing dump into: '%s'", buf));
2050 f = fopen(buf, "w");
2051 if (!f) {
2052 ui_warn(_("Cannot open %s"), buf);
2053 rc = -errno;
2054 goto done;
2055 }
2056
2057 rc = fdisk_script_write_file(sc, f);
2058 if (!rc)
2059 ui_info(_("Disk layout successfully dumped."));
2060 done:
2061 if (rc)
2062 ui_warn(_("Failed to write script %s"), buf);
2063 if (f)
2064 fclose(f);
2065 fdisk_unref_script(sc);
2066 return rc;
2067 }
2068
2069 /* prints menu with libfdisk labels and waits for users response */
2070 static int ui_create_label(struct cfdisk *cf)
2071 {
2072 struct cfdisk_menuitem *d, *cm;
2073 int rc = 1, refresh_menu = 1;
2074 size_t i = 0, nitems;
2075 struct fdisk_label *lb = NULL;
2076
2077 assert(cf);
2078
2079 DBG(UI, ul_debug("asking for new disklabe."));
2080
2081 /* create cfdisk menu according to libfdisk labels, note that the
2082 * last cm[] item has to be empty -- so nitems + 1 */
2083 nitems = fdisk_get_nlabels(cf->cxt);
2084 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
2085
2086 while (fdisk_next_label(cf->cxt, &lb) == 0) {
2087 if (fdisk_label_is_disabled(lb) ||
2088 fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
2089 continue;
2090 cm[i++].name = fdisk_label_get_name(lb);
2091 }
2092
2093 erase();
2094
2095 /* make the new menu active */
2096 menu_push(cf, cm);
2097 cf->menu->vertical = 1;
2098 menu_set_title(cf->menu, _("Select label type"));
2099
2100 if (!cf->zero_start)
2101 ui_info(_("Device does not contain a recognized partition table."));
2102
2103
2104 do {
2105 if (refresh_menu) {
2106 ui_draw_menu(cf);
2107 ui_hint(_("Select a type to create a new label or press 'L' to load script file."));
2108 refresh();
2109 refresh_menu = 0;
2110 }
2111
2112 int key = getch();
2113
2114 if (ui_resize)
2115 ui_menu_resize(cf);
2116 if (ui_menu_move(cf, key) == 0)
2117 continue;
2118 switch (key) {
2119 case KEY_ENTER:
2120 case '\n':
2121 case '\r':
2122 d = menu_get_menuitem(cf, cf->menu->idx);
2123 if (d)
2124 rc = fdisk_create_disklabel(cf->cxt, d->name);
2125 goto done;
2126 case KEY_ESC:
2127 case 'q':
2128 case 'Q':
2129 goto done;
2130 case 'l':
2131 case 'L':
2132 rc = ui_script_read(cf);
2133 if (rc == 0)
2134 goto done;
2135 refresh_menu = 1;
2136 break;
2137 }
2138 } while (1);
2139
2140 done:
2141 menu_pop(cf);
2142 free(cm);
2143 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
2144 return rc;
2145 }
2146
2147
2148 static int ui_help(void)
2149 {
2150 size_t i;
2151 static const char *help[] = {
2152 N_("This is cfdisk, a curses-based disk partitioning program."),
2153 N_("It lets you create, delete, and modify partitions on a block device."),
2154 " ",
2155 N_("Command Meaning"),
2156 N_("------- -------"),
2157 N_(" b Toggle bootable flag of the current partition"),
2158 N_(" d Delete the current partition"),
2159 N_(" h Print this screen"),
2160 N_(" n Create new partition from free space"),
2161 N_(" q Quit program without writing partition table"),
2162 N_(" s Fix partitions order (only when in disarray)"),
2163 N_(" t Change the partition type"),
2164 N_(" u Dump disk layout to sfdisk compatible script file"),
2165 N_(" W Write partition table to disk (you must enter uppercase W);"),
2166 N_(" since this might destroy data on the disk, you must either"),
2167 N_(" confirm or deny the write by entering 'yes' or 'no'"),
2168 N_(" x Display/hide extra information about a partition"),
2169 N_("Up Arrow Move cursor to the previous partition"),
2170 N_("Down Arrow Move cursor to the next partition"),
2171 N_("Left Arrow Move cursor to the previous menu item"),
2172 N_("Right Arrow Move cursor to the next menu item"),
2173 " ",
2174 N_("Note: All of the commands can be entered with either upper or lower"),
2175 N_("case letters (except for Write)."),
2176 " ",
2177 N_("Use lsblk(8) or partx(8) to see more details about the device.")
2178 };
2179
2180 erase();
2181 for (i = 0; i < ARRAY_SIZE(help); i++)
2182 mvaddstr(i, 1, _(help[i]));
2183
2184 ui_info(_("Press a key to continue."));
2185
2186 getch();
2187 return 0;
2188 }
2189
2190 /* TODO: use @sz, now 128bytes */
2191 static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
2192 size_t sz __attribute__((__unused__)))
2193 {
2194 struct fdisk_partition *pa = get_current_partition(cf);
2195 size_t i = 0;
2196
2197 if (!pa)
2198 return 0;
2199 if (fdisk_partition_is_freespace(pa)) {
2200 ignore[i++] = 'd'; /* delete */
2201 ignore[i++] = 't'; /* set type */
2202 ignore[i++] = 'b'; /* set bootable */
2203 cf->menu->prefkey = 'n';
2204 } else {
2205 cf->menu->prefkey = 'q';
2206 ignore[i++] = 'n';
2207 if (!fdisk_is_label(cf->cxt, DOS) &&
2208 !fdisk_is_label(cf->cxt, SGI))
2209 ignore[i++] = 'b';
2210 }
2211
2212 if (!cf->wrong_order)
2213 ignore[i++] = 's';
2214
2215 if (fdisk_is_readonly(cf->cxt))
2216 ignore[i++] = 'W';
2217
2218 return i;
2219 }
2220
2221
2222 /* returns: error: < 0, success: 0, quit: 1 */
2223 static int main_menu_action(struct cfdisk *cf, int key)
2224 {
2225 size_t n;
2226 int ref = 0, rc, org_order = cf->wrong_order;
2227 const char *info = NULL, *warn = NULL;
2228 struct fdisk_partition *pa;
2229
2230 assert(cf);
2231 assert(cf->cxt);
2232 assert(cf->menu);
2233
2234 if (key == 0) {
2235 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
2236 if (!d)
2237 return 0;
2238 key = d->key;
2239
2240 } else if (key != 'w' && key != 'W')
2241 key = tolower(key); /* case insensitive except 'W'rite */
2242
2243 DBG(MENU, ul_debug("main menu action: key=%c", key));
2244
2245 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
2246 DBG(MENU, ul_debug(" ignore '%c'", key));
2247 return 0;
2248 }
2249
2250 pa = get_current_partition(cf);
2251 if (!pa)
2252 return -EINVAL;
2253 n = fdisk_partition_get_partno(pa);
2254
2255 DBG(MENU, ul_debug("menu action on %p", pa));
2256 ui_clean_hint();
2257 ui_clean_info();
2258
2259 switch (key) {
2260 case 'b': /* Bootable flag */
2261 {
2262 int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
2263 fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
2264
2265 if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
2266 warn = _("Could not toggle the flag.");
2267 else if (fl)
2268 ref = 1;
2269 break;
2270 }
2271 #ifdef KEY_DC
2272 case KEY_DC:
2273 #endif
2274 case 'd': /* Delete */
2275 if (fdisk_delete_partition(cf->cxt, n) != 0)
2276 warn = _("Could not delete partition %zu.");
2277 else
2278 info = _("Partition %zu has been deleted.");
2279 ref = 1;
2280 break;
2281 case 'h': /* Help */
2282 case '?':
2283 ui_help();
2284 ref = 1;
2285 break;
2286 case 'n': /* New */
2287 {
2288 uint64_t start, size, dflt_size, secs;
2289 struct fdisk_partition *npa; /* the new partition */
2290 int expsize = 0; /* size specified explicitly in sectors */
2291
2292 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2293 return -EINVAL;
2294
2295 /* free space range */
2296 start = fdisk_partition_get_start(pa);
2297 size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
2298
2299 if (ui_get_size(cf, _("Partition size: "), &size,
2300 fdisk_get_sector_size(cf->cxt),
2301 size, &expsize) == -CFDISK_ERR_ESC)
2302 break;
2303
2304 secs = size / fdisk_get_sector_size(cf->cxt);
2305
2306 npa = fdisk_new_partition();
2307 if (!npa)
2308 return -ENOMEM;
2309
2310 if (dflt_size == size) /* default is to fillin all free space */
2311 fdisk_partition_end_follow_default(npa, 1);
2312 else
2313 fdisk_partition_set_size(npa, secs);
2314
2315 if (expsize)
2316 fdisk_partition_size_explicit(pa, 1);
2317
2318 fdisk_partition_set_start(npa, start);
2319 fdisk_partition_partno_follow_default(npa, 1);
2320 /* add to disk label -- libfdisk will ask for missing details */
2321 rc = fdisk_add_partition(cf->cxt, npa, NULL);
2322 fdisk_unref_partition(npa);
2323 if (rc == 0)
2324 ref = 1;
2325 break;
2326 }
2327 case 'q': /* Quit */
2328 return 1;
2329 case 't': /* Type */
2330 {
2331 struct fdisk_parttype *t;
2332
2333 if (fdisk_partition_is_freespace(pa))
2334 return -EINVAL;
2335 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
2336 t = ui_get_parttype(cf, t);
2337 ref = 1;
2338
2339 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
2340 info = _("Changed type of partition %zu.");
2341 else
2342 info = _("The type of partition %zu is unchanged.");
2343 break;
2344 }
2345 case 's': /* Sort */
2346 if (cf->wrong_order) {
2347 fdisk_reorder_partitions(cf->cxt);
2348 ref = 1;
2349 }
2350 break;
2351 case 'u': /* dUmp */
2352 ui_script_write(cf);
2353 break;
2354 case 'W': /* Write */
2355 {
2356 char buf[64] = { 0 };
2357
2358 if (fdisk_is_readonly(cf->cxt)) {
2359 warn = _("Device is open in read-only mode.");
2360 break;
2361 }
2362
2363 rc = ui_get_string(
2364 _("Are you sure you want to write the partition "
2365 "table to disk? "),
2366 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
2367 buf, sizeof(buf));
2368
2369 ref = 1;
2370 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2371 strcasecmp(buf, _("yes")) != 0)) {
2372 info = _("Did not write partition table to disk.");
2373 break;
2374 }
2375 rc = fdisk_write_disklabel(cf->cxt);
2376 if (rc)
2377 warn = _("Failed to write disklabel.");
2378 else {
2379 fdisk_reread_partition_table(cf->cxt);
2380 info = _("The partition table has been altered.");
2381 }
2382 cf->nwrites++;
2383 break;
2384 }
2385 default:
2386 break;
2387 }
2388
2389 if (ref) {
2390 lines_refresh(cf);
2391 ui_refresh(cf);
2392 ui_draw_extra(cf);
2393 } else
2394 ui_draw_menu(cf);
2395
2396 ui_clean_hint();
2397
2398 if (warn)
2399 ui_warnx(warn, n + 1);
2400 else if (info)
2401 ui_info(info, n + 1);
2402 else if (key == 'n' && cf->wrong_order && org_order == 0)
2403 ui_info(_("Note that partition table entries are not in disk order now."));
2404
2405 return 0;
2406 }
2407
2408 static void ui_resize_refresh(struct cfdisk *cf)
2409 {
2410 resize();
2411 menu_refresh_size(cf);
2412 lines_refresh(cf);
2413 ui_refresh(cf);
2414 ui_draw_extra(cf);
2415 }
2416
2417 static void toggle_show_extra(struct cfdisk *cf)
2418 {
2419 if (cf->show_extra && cf->act_win) {
2420 wclear(cf->act_win);
2421 touchwin(stdscr);
2422 }
2423 cf->show_extra = cf->show_extra ? 0 : 1;
2424
2425 if (cf->show_extra)
2426 ui_draw_extra(cf);
2427 DBG(MENU, ul_debug("extra: %s", cf->show_extra ? "ENABLED" : "DISABLED" ));
2428 }
2429
2430 static int ui_run(struct cfdisk *cf)
2431 {
2432 int rc = 0;
2433
2434 ui_lines = LINES;
2435 ui_cols = COLS;
2436 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
2437
2438 if (fdisk_get_collision(cf->cxt)) {
2439 ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
2440 fdisk_get_collision(cf->cxt));
2441 fdisk_enable_wipe(cf->cxt, 1);
2442 ui_hint(_("Press a key to continue."));
2443 getch();
2444 }
2445
2446 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
2447 rc = ui_create_label(cf);
2448 if (rc < 0)
2449 ui_errx(EXIT_FAILURE,
2450 _("failed to create a new disklabel"));
2451 if (rc)
2452 return rc;
2453 }
2454
2455 cols_init(cf);
2456 rc = lines_refresh(cf);
2457 if (rc)
2458 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
2459
2460 menu_push(cf, main_menuitems);
2461 cf->menu->ignore_cb = main_menu_ignore_keys;
2462
2463 rc = ui_refresh(cf);
2464 if (rc)
2465 return rc;
2466
2467 cf->show_extra = 1;
2468 ui_draw_extra(cf);
2469
2470 if (fdisk_is_readonly(cf->cxt))
2471 ui_warnx(_("Device is open in read-only mode."));
2472 else if (cf->wrong_order)
2473 ui_info(_("Note that partition table entries are not in disk order now."));
2474
2475 do {
2476 int key = getch();
2477
2478 rc = 0;
2479 if (ui_resize)
2480 /* Note that ncurses getch() returns ERR when interrupted
2481 * by signal, but SLang does not interrupt at all. */
2482 ui_resize_refresh(cf);
2483 if (key == ERR)
2484 continue;
2485 if (ui_menu_move(cf, key) == 0)
2486 continue;
2487
2488 DBG(UI, ul_debug("main action key >%c<.", key));
2489
2490 switch (key) {
2491 case KEY_DOWN:
2492 case '\016': /* ^N */
2493 case 'j': /* Vi-like alternative */
2494 ui_table_goto(cf, cf->lines_idx + 1);
2495 break;
2496 case KEY_UP:
2497 case '\020': /* ^P */
2498 case 'k': /* Vi-like alternative */
2499 ui_table_goto(cf, (int) cf->lines_idx - 1);
2500 break;
2501 case KEY_PPAGE:
2502 if (cf->page_sz) {
2503 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2504 break;
2505 }
2506 case KEY_HOME:
2507 ui_table_goto(cf, 0);
2508 break;
2509 case KEY_NPAGE:
2510 if (cf->page_sz) {
2511 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2512 break;
2513 }
2514 case KEY_END:
2515 ui_table_goto(cf, (int) cf->nlines - 1);
2516 break;
2517 case KEY_ENTER:
2518 case '\n':
2519 case '\r':
2520 rc = main_menu_action(cf, 0);
2521 break;
2522 case 'X':
2523 case 'x': /* Extra */
2524 toggle_show_extra(cf);
2525 break;
2526 default:
2527 rc = main_menu_action(cf, key);
2528 if (rc < 0)
2529 beep();
2530 break;
2531 }
2532
2533 if (rc == 1)
2534 break; /* quit */
2535 } while (1);
2536
2537 menu_pop(cf);
2538
2539 DBG(UI, ul_debug("end"));
2540 return 0;
2541 }
2542
2543 static void __attribute__ ((__noreturn__)) usage(FILE *out)
2544 {
2545 fputs(USAGE_HEADER, out);
2546 fprintf(out,
2547 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2548
2549 fputs(USAGE_SEPARATOR, out);
2550 fputs(_("Display or manipulate a disk partition table.\n"), out);
2551
2552 fputs(USAGE_OPTIONS, out);
2553 fputs(_(" -L, --color[=<when>] colorize output (auto, always or never)\n"), out);
2554 fprintf(out,
2555 " %s\n", USAGE_COLORS_DEFAULT);
2556 fputs(_(" -z, --zero start with zeroed partition table\n"), out);
2557
2558 fputs(USAGE_SEPARATOR, out);
2559 fputs(USAGE_HELP, out);
2560 fputs(USAGE_VERSION, out);
2561
2562 fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
2563 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
2564 }
2565
2566 int main(int argc, char *argv[])
2567 {
2568 const char *diskpath;
2569 int rc, c, colormode = UL_COLORMODE_UNDEF;
2570 struct cfdisk _cf = { .lines_idx = 0 },
2571 *cf = &_cf;
2572
2573 static const struct option longopts[] = {
2574 { "color", optional_argument, NULL, 'L' },
2575 { "help", no_argument, NULL, 'h' },
2576 { "version", no_argument, NULL, 'V' },
2577 { "zero", no_argument, NULL, 'z' },
2578 { NULL, 0, 0, 0 },
2579 };
2580
2581 setlocale(LC_ALL, "");
2582 bindtextdomain(PACKAGE, LOCALEDIR);
2583 textdomain(PACKAGE);
2584 atexit(close_stdout);
2585
2586 while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
2587 switch(c) {
2588 case 'h':
2589 usage(stdout);
2590 break;
2591 case 'L':
2592 colormode = UL_COLORMODE_AUTO;
2593 if (optarg)
2594 colormode = colormode_or_err(optarg,
2595 _("unsupported color mode"));
2596 break;
2597 case 'V':
2598 printf(UTIL_LINUX_VERSION);
2599 return EXIT_SUCCESS;
2600 case 'z':
2601 cf->zero_start = 1;
2602 break;
2603 }
2604 }
2605
2606 colors_init(colormode, "cfdisk");
2607
2608 fdisk_init_debug(0);
2609 scols_init_debug(0);
2610 cfdisk_init_debug();
2611 cf->cxt = fdisk_new_context();
2612 if (!cf->cxt)
2613 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
2614
2615 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
2616
2617 if (optind == argc)
2618 diskpath = access(DEFAULT_DEVICE, F_OK) == 0 ?
2619 DEFAULT_DEVICE : ALTERNATE_DEVICE;
2620 else
2621 diskpath = argv[optind];
2622
2623 rc = fdisk_assign_device(cf->cxt, diskpath, 0);
2624 if (rc == -EACCES)
2625 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
2626 if (rc != 0)
2627 err(EXIT_FAILURE, _("cannot open %s"),
2628 optind == argc ? DEFAULT_DEVICE : diskpath);
2629
2630 /* Don't use err(), warn() from this point */
2631 ui_init(cf);
2632 ui_run(cf);
2633 ui_end();
2634
2635 cfdisk_free_lines(cf);
2636 free(cf->linesbuf);
2637
2638 fdisk_unref_table(cf->table);
2639 #ifdef HAVE_LIBMOUNT
2640 mnt_unref_table(cf->fstab);
2641 mnt_unref_table(cf->mtab);
2642 mnt_unref_cache(cf->mntcache);
2643 #endif
2644 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
2645 fdisk_unref_context(cf->cxt);
2646 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
2647 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2648 }