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