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