]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/cfdisk.c
Merge branch 'libeconf' of https://github.com/thkukuk/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 }
990
991 {
992 size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
993 size_t items = ui_cols / len; /* items per line */
994
995 if (items == 0)
996 return 0;
997 return MENU_START_LINE + ((idx / items));
998 }
999 }
1000
1001 static int menuitem_get_column(struct cfdisk *cf, size_t idx)
1002 {
1003 if (cf->menu->vertical) {
1004 size_t nc = MENU_V_ITEMWIDTH(cf->menu);
1005 if ((size_t) ui_cols <= nc)
1006 return 0;
1007 return (ui_cols - nc) / 2;
1008 }
1009
1010 {
1011 size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
1012 size_t items = ui_cols / len; /* items per line */
1013 size_t extra = items < cf->menu->nitems ? /* extra space on line */
1014 ui_cols % len : /* - multi-line menu */
1015 ui_cols - (cf->menu->nitems * len); /* - one line menu */
1016
1017 if (items == 0)
1018 return 0; /* hmm... no space */
1019
1020 extra += MENU_H_BETWEEN; /* add padding after last item to extra */
1021
1022 if (idx < items)
1023 return (idx * len) + (extra / 2);
1024 return ((idx % items) * len) + (extra / 2);
1025 }
1026 }
1027
1028 static int menuitem_on_page(struct cfdisk *cf, size_t idx)
1029 {
1030 struct cfdisk_menu *m = cf->menu;
1031
1032 if (m->page_sz == 0 ||
1033 m->idx / m->page_sz == idx / m->page_sz)
1034 return 1;
1035 return 0;
1036 }
1037
1038 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
1039 {
1040 struct cfdisk_menuitem *d;
1041 size_t i;
1042
1043 for (i = 0, d = cf->menu->items; d->name; d++) {
1044 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1045 continue;
1046 if (i++ == idx)
1047 return d;
1048 }
1049
1050 return NULL;
1051 }
1052
1053 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
1054 int key, size_t *idx)
1055 {
1056 struct cfdisk_menuitem *d;
1057
1058 for (*idx = 0, d = cf->menu->items; d->name; d++) {
1059 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1060 continue;
1061 if (key == d->key)
1062 return d;
1063 (*idx)++;
1064 }
1065
1066 return NULL;
1067 }
1068
1069 static void ui_draw_menuitem(struct cfdisk *cf,
1070 struct cfdisk_menuitem *d,
1071 size_t idx)
1072 {
1073 char *buf, *ptr;
1074 const char *name;
1075 size_t width;
1076 const size_t buf_sz = 80 * MB_CUR_MAX;
1077 int ln, cl, vert = cf->menu->vertical;
1078
1079 if (!menuitem_on_page(cf, idx))
1080 return; /* no visible item */
1081 ln = menuitem_get_line(cf, idx);
1082 cl = menuitem_get_column(cf, idx);
1083
1084 ptr = buf = xmalloc(buf_sz);
1085 /* string width */
1086 if (vert) {
1087 width = cf->menu->width + MENU_V_SPADDING;
1088 memset(ptr, ' ', MENU_V_SPADDING);
1089 ptr += MENU_V_SPADDING;
1090 } else
1091 width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
1092
1093 name = _(d->name);
1094 mbsalign(name, ptr, buf_sz, &width,
1095 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
1096 0);
1097
1098 DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
1099 cl, ln, buf));
1100
1101 if (vert) {
1102 mvaddch(ln, cl - 1, ACS_VLINE);
1103 mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
1104 }
1105
1106 if (cf->menu->idx == idx)
1107 standout();
1108
1109 if (vert)
1110 mvprintw(ln, cl, "%s", buf);
1111 else
1112 mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
1113 free(buf);
1114
1115 if (cf->menu->idx == idx) {
1116 standend();
1117 if (d->desc)
1118 ui_hint(_(d->desc));
1119 }
1120 }
1121
1122 static void ui_clean_menu(struct cfdisk *cf)
1123 {
1124 size_t i;
1125 size_t lastline;
1126 struct cfdisk_menu *m = cf->menu;
1127 size_t ln = menuitem_get_line(cf, 0);
1128
1129 if (m->vertical)
1130 lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
1131 else
1132 lastline = menuitem_get_line(cf, m->nitems);
1133
1134 for (i = ln; i <= lastline; i++) {
1135 move(i, 0);
1136 clrtoeol();
1137 DBG(MENU, ul_debug("clean_menu: line %zu", i));
1138 }
1139 if (m->vertical) {
1140 move(ln - 1, 0);
1141 clrtoeol();
1142 }
1143 ui_clean_hint();
1144 }
1145
1146 static void ui_draw_menu(struct cfdisk *cf)
1147 {
1148 struct cfdisk_menuitem *d;
1149 struct cfdisk_menu *m;
1150 size_t i = 0;
1151 size_t ln = menuitem_get_line(cf, 0);
1152 size_t nlines;
1153
1154 assert(cf);
1155 assert(cf->menu);
1156
1157 DBG(MENU, ul_debug("draw start"));
1158
1159 ui_clean_menu(cf);
1160 m = cf->menu;
1161
1162 if (m->vertical)
1163 nlines = m->page_sz ? m->page_sz : m->nitems;
1164 else
1165 nlines = menuitem_get_line(cf, m->nitems);
1166
1167 if (m->ignore_cb)
1168 menu_update_ignore(cf);
1169 i = 0;
1170 while ((d = menu_get_menuitem(cf, i)))
1171 ui_draw_menuitem(cf, d, i++);
1172
1173 if (m->vertical) {
1174 size_t cl = menuitem_get_column(cf, 0);
1175 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
1176
1177 /* corners and horizontal lines */
1178 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
1179 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
1180
1181 for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
1182 mvaddch(ln - 1, cl + i, ACS_HLINE);
1183 mvaddch(ln + nlines, cl + i, ACS_HLINE);
1184 }
1185
1186 mvaddch(ln - 1, cl + i, ACS_URCORNER);
1187 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
1188
1189 /* draw also lines around empty lines on last page */
1190 if (m->page_sz &&
1191 m->nitems / m->page_sz == m->idx / m->page_sz) {
1192 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
1193 mvaddch(i, cl - 1, ACS_VLINE);
1194 mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
1195 }
1196 }
1197 if (m->title) {
1198 attron(A_BOLD);
1199 mvprintw(ln - 1, cl, " %s ", m->title);
1200 attroff(A_BOLD);
1201 }
1202 if (curpg != 0)
1203 mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
1204 if (m->page_sz && curpg < m->nitems / m->page_sz)
1205 mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
1206 }
1207
1208 DBG(MENU, ul_debug("draw end."));
1209 }
1210
1211 inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
1212 {
1213 struct libscols_line *lsl;
1214 int rc;
1215
1216 assert(l);
1217 assert(l->extra);
1218
1219 if (!data || !*data)
1220 return 0;
1221
1222 lsl = scols_table_new_line(l->extra, NULL);
1223 if (!lsl)
1224 return -ENOMEM;
1225
1226 rc = scols_line_set_data(lsl, 0, name);
1227 if (!rc)
1228 rc = scols_line_set_data(lsl, 1, data);
1229
1230 return rc;
1231 }
1232
1233 #ifndef HAVE_LIBMOUNT
1234 static char *get_mountpoint( struct cfdisk *cf __attribute__((unused)),
1235 const char *tagname __attribute__((unused)),
1236 const char *tagdata __attribute__((unused)))
1237 {
1238 return NULL;
1239 }
1240 #else
1241 static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *tagdata)
1242 {
1243 struct libmnt_fs *fs = NULL;
1244 char *target = NULL;
1245 int mounted = 0;
1246
1247 assert(tagname);
1248 assert(tagdata);
1249
1250 DBG(UI, ul_debug("asking for mountpoint [%s=%s]", tagname, tagdata));
1251
1252 if (!cf->mntcache)
1253 cf->mntcache = mnt_new_cache();
1254
1255 /* 1st try between mounted filesystems */
1256 if (!cf->mtab) {
1257 cf->mtab = mnt_new_table();
1258 if (cf->mtab) {
1259 mnt_table_set_cache(cf->mtab, cf->mntcache);
1260 mnt_table_parse_mtab(cf->mtab, NULL);
1261 }
1262 }
1263
1264 if (cf->mtab)
1265 fs = mnt_table_find_tag(cf->mtab, tagname, tagdata, MNT_ITER_FORWARD);
1266
1267 /* 2nd try fstab */
1268 if (!fs) {
1269 if (!cf->fstab) {
1270 cf->fstab = mnt_new_table();
1271 if (cf->fstab) {
1272 mnt_table_set_cache(cf->fstab, cf->mntcache);
1273 if (mnt_table_parse_fstab(cf->fstab, NULL) != 0) {
1274 mnt_unref_table(cf->fstab);
1275 cf->fstab = NULL;
1276 }
1277 }
1278 }
1279 if (cf->fstab)
1280 fs = mnt_table_find_tag(cf->fstab, tagname, tagdata, MNT_ITER_FORWARD);
1281 } else
1282 mounted = 1;
1283
1284 if (fs) {
1285 if (mounted)
1286 xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
1287 else
1288 target = xstrdup(mnt_fs_get_target(fs));
1289 }
1290
1291 return target;
1292 }
1293 #endif /* HAVE_LIBMOUNT */
1294
1295 static void extra_prepare_data(struct cfdisk *cf)
1296 {
1297 struct fdisk_partition *pa = get_current_partition(cf);
1298 struct cfdisk_line *l = &cf->lines[cf->lines_idx];
1299 char *data = NULL;
1300 char *mountpoint = NULL;
1301
1302 DBG(UI, ul_debug("preparing extra data"));
1303
1304 /* string data should not equal an empty string */
1305 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
1306 extra_insert_pair(l, _("Partition name:"), data);
1307 if (!mountpoint)
1308 mountpoint = get_mountpoint(cf, "PARTLABEL", data);
1309 free(data);
1310 }
1311
1312 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
1313 extra_insert_pair(l, _("Partition UUID:"), data);
1314 if (!mountpoint)
1315 mountpoint = get_mountpoint(cf, "PARTUUID", data);
1316 free(data);
1317 }
1318
1319 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
1320 char *code = NULL, *type = NULL;
1321
1322 fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
1323 xasprintf(&type, "%s (%s)", data, code);
1324
1325 extra_insert_pair(l, _("Partition type:"), type);
1326 free(data);
1327 free(code);
1328 free(type);
1329 }
1330
1331 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
1332 extra_insert_pair(l, _("Attributes:"), data);
1333 free(data);
1334 }
1335
1336 /* for numeric data, only show non-zero rows */
1337 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
1338 if (atoi(data))
1339 extra_insert_pair(l, "BSIZE:", data);
1340 free(data);
1341 }
1342
1343 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
1344 if (atoi(data))
1345 extra_insert_pair(l, "CPG:", data);
1346 free(data);
1347 }
1348
1349 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
1350 if (atoi(data))
1351 extra_insert_pair(l, "FSIZE:", data);
1352 free(data);
1353 }
1354
1355 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSUUID, &data) && data) {
1356 extra_insert_pair(l, _("Filesystem UUID:"), data);
1357 if (!mountpoint)
1358 mountpoint = get_mountpoint(cf, "UUID", data);
1359 free(data);
1360 }
1361
1362 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSLABEL, &data) && data) {
1363 extra_insert_pair(l, _("Filesystem LABEL:"), data);
1364 if (!mountpoint)
1365 mountpoint = get_mountpoint(cf, "LABEL", data);
1366 free(data);
1367 }
1368 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSTYPE, &data) && data) {
1369 extra_insert_pair(l, _("Filesystem:"), data);
1370 free(data);
1371 }
1372
1373 if (mountpoint) {
1374 extra_insert_pair(l, _("Mountpoint:"), mountpoint);
1375 free(mountpoint);
1376 }
1377 }
1378
1379 static int ui_draw_extra(struct cfdisk *cf)
1380 {
1381 WINDOW *win_ex;
1382 int wline = 1;
1383 struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
1384 char *tbstr = NULL, *end;
1385 int win_ex_start_line, win_height, tblen;
1386 int ndatalines;
1387
1388 if (!cf->show_extra)
1389 return 0;
1390
1391 DBG(UI, ul_debug("draw extra"));
1392
1393 assert(ln->extra);
1394
1395 if (cf->act_win) {
1396 wclear(cf->act_win);
1397 touchwin(stdscr);
1398 }
1399
1400 if (scols_table_is_empty(ln->extra)) {
1401 extra_prepare_data(cf);
1402 if (scols_table_is_empty(ln->extra))
1403 return 0;
1404 }
1405
1406 ndatalines = fdisk_table_get_nents(cf->table) + 1;
1407
1408 /* nents + header + one free line */
1409 win_ex_start_line = TABLE_START_LINE + ndatalines;
1410 win_height = MENU_START_LINE - win_ex_start_line;
1411 tblen = scols_table_get_nlines(ln->extra);
1412
1413 /* we can't get a single line of data under the partlist*/
1414 if (win_height < 3)
1415 return 1;
1416
1417 /* number of data lines + 2 for top/bottom lines */
1418 win_height = win_height < tblen + 2 ? win_height : tblen + 2;
1419
1420 if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
1421 win_ex_start_line = MENU_START_LINE - win_height;
1422
1423 win_ex = subwin(stdscr, win_height, ui_cols - 2, win_ex_start_line, 1);
1424
1425 scols_table_reduce_termwidth(ln->extra, 4);
1426 scols_print_table_to_string(ln->extra, &tbstr);
1427
1428 end = tbstr;
1429 while ((end = strchr(end, '\n')))
1430 *end++ = '\0';
1431
1432 box(win_ex, 0, 0);
1433
1434 end = tbstr;
1435 while (--win_height > 1) {
1436 mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
1437 tbstr += strlen(tbstr) + 1;
1438 }
1439 free(end);
1440
1441 if (ln->w)
1442 delwin(ln->w);
1443
1444 DBG(UI, ul_debug("draw window: %p", win_ex));
1445 touchwin(stdscr);
1446 wrefresh(win_ex);
1447
1448 cf->act_win = ln->w = win_ex;
1449 return 0;
1450 }
1451
1452 static void ui_menu_goto(struct cfdisk *cf, int where)
1453 {
1454 struct cfdisk_menuitem *d;
1455 size_t old;
1456
1457 /* stop and begin/end for vertical menus */
1458 if (cf->menu->vertical) {
1459 if (where < 0)
1460 where = 0;
1461 else if (where > (int) cf->menu->nitems - 1)
1462 where = cf->menu->nitems - 1;
1463 } else {
1464 /* continue from begin/end */
1465 if (where < 0)
1466 where = cf->menu->nitems - 1;
1467 else if ((size_t) where > cf->menu->nitems - 1)
1468 where = 0;
1469 }
1470 if ((size_t) where == cf->menu->idx)
1471 return;
1472
1473 ui_clean_info();
1474
1475 old = cf->menu->idx;
1476 cf->menu->idx = where;
1477
1478 if (!menuitem_on_page(cf, old)) {
1479 ui_draw_menu(cf);
1480 return;
1481 }
1482
1483 d = menu_get_menuitem(cf, old);
1484 ui_draw_menuitem(cf, d, old);
1485
1486 d = menu_get_menuitem(cf, where);
1487 ui_draw_menuitem(cf, d, where);
1488
1489 }
1490
1491 static int ui_menu_move(struct cfdisk *cf, int key)
1492 {
1493 struct cfdisk_menu *m;
1494
1495 assert(cf);
1496 assert(cf->menu);
1497
1498 if (key == (int) ERR)
1499 return 0; /* ignore errors */
1500
1501 m = cf->menu;
1502
1503 DBG(MENU, ul_debug("menu move key >%c<.", key));
1504
1505 if (m->vertical)
1506 {
1507 switch (key) {
1508 case KEY_DOWN:
1509 case '\016': /* ^N */
1510 case 'j': /* Vi-like alternative */
1511 ui_menu_goto(cf, m->idx + 1);
1512 return 0;
1513 case KEY_UP:
1514 case '\020': /* ^P */
1515 case 'k': /* Vi-like alternative */
1516 ui_menu_goto(cf, (int) m->idx - 1);
1517 return 0;
1518 case KEY_PPAGE:
1519 if (m->page_sz) {
1520 ui_menu_goto(cf, (int) m->idx - m->page_sz);
1521 return 0;
1522 }
1523 /* fallthrough */
1524 case KEY_HOME:
1525 ui_menu_goto(cf, 0);
1526 return 0;
1527 case KEY_NPAGE:
1528 if (m->page_sz) {
1529 ui_menu_goto(cf, m->idx + m->page_sz);
1530 return 0;
1531 }
1532 /* fallthrough */
1533 case KEY_END:
1534 ui_menu_goto(cf, m->nitems);
1535 return 0;
1536 }
1537 } else {
1538 switch (key) {
1539 case KEY_RIGHT:
1540 case '\t':
1541 ui_menu_goto(cf, m->idx + 1);
1542 return 0;
1543 case KEY_LEFT:
1544 #ifdef KEY_BTAB
1545 case KEY_BTAB:
1546 #endif
1547 ui_menu_goto(cf, (int) m->idx - 1);
1548 return 0;
1549 }
1550 }
1551
1552 if (key == '\014') { /* ^L refresh */
1553 ui_menu_resize(cf);
1554 return 0;
1555 }
1556
1557 DBG(MENU, ul_debug(" no menu move key"));
1558 return 1;
1559 }
1560
1561 /* but don't call me from ui_run(), this is for pop-up menus only */
1562 static void ui_menu_resize(struct cfdisk *cf)
1563 {
1564 DBG(MENU, ul_debug("menu resize/refresh"));
1565 resize();
1566 ui_clean_menu(cf);
1567 menu_refresh_size(cf);
1568 ui_draw_menu(cf);
1569 refresh();
1570 }
1571
1572 static int partition_on_page(struct cfdisk *cf, size_t i)
1573 {
1574 if (cf->page_sz == 0 ||
1575 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1576 return 1;
1577 return 0;
1578 }
1579
1580 static void ui_draw_partition(struct cfdisk *cf, size_t i)
1581 {
1582 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1583 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
1584 int cur = cf->lines_idx == i;
1585 size_t curpg = 0;
1586
1587 if (cf->page_sz) {
1588 if (!partition_on_page(cf, i))
1589 return;
1590 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1591 curpg = cf->lines_idx / cf->page_sz;
1592 }
1593
1594 DBG(UI, ul_debug(
1595 "draw partition %zu [page_sz=%zu, "
1596 "line=%d, idx=%zu]",
1597 i, cf->page_sz, ln, cf->lines_idx));
1598
1599 if (cur) {
1600 attron(A_REVERSE);
1601 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1602 mvaddstr(ln, cl, cf->lines[i + 1].data);
1603 attroff(A_REVERSE);
1604 } else {
1605 int at = 0;
1606
1607 if (colors_wanted() && is_freespace(cf, i)) {
1608 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1609 at = 1;
1610 }
1611 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1612 mvaddstr(ln, cl, cf->lines[i + 1].data);
1613 if (at)
1614 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
1615 }
1616
1617 if ((size_t) ln == MENU_START_LINE - 1 &&
1618 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1619 if (cur)
1620 attron(A_REVERSE);
1621 mvaddch(ln, ui_cols - 1, ACS_DARROW);
1622 mvaddch(ln, 0, ACS_DARROW);
1623 if (cur)
1624 attroff(A_REVERSE);
1625 }
1626
1627 }
1628
1629 static int ui_draw_table(struct cfdisk *cf)
1630 {
1631 int cl = ARROW_CURSOR_WIDTH;
1632 size_t i, nparts = fdisk_table_get_nents(cf->table);
1633 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
1634
1635 DBG(UI, ul_debug("draw table"));
1636
1637 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1638 move(i, 0);
1639 clrtoeol();
1640 }
1641
1642 if (nparts == 0 || (size_t) cf->lines_idx > nparts - 1)
1643 cf->lines_idx = nparts ? nparts - 1 : 0;
1644
1645 /* print header */
1646 attron(A_BOLD);
1647 mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
1648 attroff(A_BOLD);
1649
1650 /* print partitions */
1651 for (i = 0; i < nparts; i++)
1652 ui_draw_partition(cf, i);
1653
1654 if (curpg != 0) {
1655 mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
1656 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1657 }
1658 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1659 mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
1660 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1661 }
1662 return 0;
1663 }
1664
1665 static int ui_table_goto(struct cfdisk *cf, int where)
1666 {
1667 size_t old;
1668 size_t nparts = fdisk_table_get_nents(cf->table);
1669
1670 DBG(UI, ul_debug("goto table %d", where));
1671
1672 if (where < 0)
1673 where = 0;
1674 else if ((size_t) where > nparts - 1)
1675 where = nparts - 1;
1676
1677 if ((size_t) where == cf->lines_idx)
1678 return 0;
1679
1680 old = cf->lines_idx;
1681 cf->lines_idx = where;
1682
1683 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1684 ui_draw_table(cf);
1685 else {
1686 ui_draw_partition(cf, old); /* cleanup old */
1687 ui_draw_partition(cf, where); /* draw new */
1688 }
1689 ui_clean_info();
1690 ui_draw_menu(cf);
1691 ui_draw_extra(cf);
1692 refresh();
1693
1694 return 0;
1695 }
1696
1697 static int ui_refresh(struct cfdisk *cf)
1698 {
1699 struct fdisk_label *lb;
1700 char *id = NULL;
1701 uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
1702 char *strsz;
1703
1704 if (!ui_enabled)
1705 return -EINVAL;
1706
1707 strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
1708 | SIZE_SUFFIX_SPACE
1709 | SIZE_SUFFIX_3LETTER, bytes);
1710
1711 lb = fdisk_get_label(cf->cxt, NULL);
1712 assert(lb);
1713
1714 clear();
1715
1716 /* header */
1717 attron(A_BOLD);
1718 ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
1719 attroff(A_BOLD);
1720 ui_center(1, _("Size: %s, %"PRIu64" bytes, %ju sectors"),
1721 strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
1722 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1723 ui_center(2, _("Label: %s, identifier: %s"),
1724 fdisk_label_get_name(lb), id);
1725 else
1726 ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
1727 free(strsz);
1728
1729 ui_draw_table(cf);
1730 ui_draw_menu(cf);
1731 refresh();
1732 return 0;
1733 }
1734
1735 static ssize_t ui_get_string(const char *prompt,
1736 const char *hint, char *buf, size_t len)
1737 {
1738 int ln = MENU_START_LINE, cl = 1;
1739 ssize_t rc = -1;
1740 struct mbs_editor *edit;
1741
1742 DBG(UI, ul_debug("ui get string"));
1743
1744 assert(buf);
1745 assert(len);
1746
1747 move(ln, 0);
1748 clrtoeol();
1749
1750 move(ln + 1, 0);
1751 clrtoeol();
1752
1753 if (prompt) {
1754 mvaddstr(ln, cl, prompt);
1755 cl += mbs_safe_width(prompt);
1756 }
1757
1758 edit = mbs_new_edit(buf, len, ui_cols - cl);
1759 if (!edit)
1760 goto done;
1761
1762 mbs_edit_goto(edit, MBS_EDIT_END);
1763
1764 if (hint)
1765 ui_hint(hint);
1766 else
1767 ui_clean_hint();
1768
1769 curs_set(1);
1770
1771 while (!sig_die) {
1772 wint_t c; /* we have fallback in widechar.h */
1773
1774 move(ln, cl);
1775 clrtoeol();
1776 mvaddstr(ln, cl, edit->buf);
1777 move(ln, cl + edit->cursor_cells);
1778 refresh();
1779
1780 #if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1781 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1782 if (get_wch(&c) == ERR) {
1783 #else
1784 if ((c = getch()) == (wint_t) ERR) {
1785 #endif
1786 if (sig_die)
1787 break;
1788 if (sig_resize) {
1789 resize();
1790 continue;
1791 }
1792 if (!isatty(STDIN_FILENO))
1793 exit(2);
1794 else
1795 goto done;
1796 }
1797
1798 DBG(UI, ul_debug("ui get string: key=%lc", c));
1799
1800 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1801 break;
1802
1803 rc = 1;
1804
1805 switch (c) {
1806 case KEY_ESC:
1807 rc = -CFDISK_ERR_ESC;
1808 goto done;
1809 case KEY_LEFT:
1810 rc = mbs_edit_goto(edit, MBS_EDIT_LEFT);
1811 break;
1812 case KEY_RIGHT:
1813 rc = mbs_edit_goto(edit, MBS_EDIT_RIGHT);
1814 break;
1815 case KEY_END:
1816 rc = mbs_edit_goto(edit, MBS_EDIT_END);
1817 break;
1818 case KEY_HOME:
1819 rc = mbs_edit_goto(edit, MBS_EDIT_HOME);
1820 break;
1821 case KEY_UP:
1822 case KEY_DOWN:
1823 break;
1824 case KEY_DC:
1825 rc = mbs_edit_delete(edit);
1826 break;
1827 case '\b':
1828 case KEY_DELETE:
1829 case KEY_BACKSPACE:
1830 rc = mbs_edit_backspace(edit);
1831 break;
1832 default:
1833 rc = mbs_edit_insert(edit, c);
1834 break;
1835 }
1836 if (rc == 1)
1837 beep();
1838 }
1839
1840 if (sig_die)
1841 die_on_signal();
1842
1843 rc = strlen(edit->buf); /* success */
1844 done:
1845 move(ln, 0);
1846 clrtoeol();
1847 curs_set(0);
1848 refresh();
1849 mbs_free_edit(edit);
1850
1851 return rc;
1852 }
1853
1854 static int ui_get_size(struct cfdisk *cf, /* context */
1855 const char *prompt, /* UI dialog string */
1856 uint64_t *res, /* result in bytes */
1857 uint64_t low, /* minimal size */
1858 uint64_t up, /* maximal size */
1859 int *expsize) /* explicitly specified size */
1860 {
1861 char buf[128];
1862 uint64_t user = 0;
1863 ssize_t rc;
1864 char *dflt = size_to_human_string(0, *res);
1865
1866 DBG(UI, ul_debug("get_size (default=%"PRIu64")", *res));
1867
1868 ui_clean_info();
1869
1870 snprintf(buf, sizeof(buf), "%s", dflt);
1871
1872 do {
1873 int pwr = 0, insec = 0;
1874
1875 rc = ui_get_string(prompt,
1876 _("May be followed by M for MiB, G for GiB, "
1877 "T for TiB, or S for sectors."),
1878 buf, sizeof(buf));
1879 ui_clean_warn();
1880
1881 if (rc == 0) {
1882 ui_warnx(_("Please, specify size."));
1883 continue; /* nothing specified */
1884 } if (rc == -CFDISK_ERR_ESC)
1885 break; /* cancel dialog */
1886
1887 if (strcmp(buf, dflt) == 0)
1888 user = *res, rc = 0; /* no change, use default */
1889 else {
1890 size_t len = strlen(buf);
1891 if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
1892 insec = 1;
1893 buf[len - 1] = '\0';
1894 }
1895 rc = parse_size(buf, (uintmax_t *)&user, &pwr); /* parse */
1896 }
1897
1898 if (rc == 0) {
1899 DBG(UI, ul_debug("get_size user=%"PRIu64", power=%d, in-sectors=%s",
1900 user, pwr, insec ? "yes" : "no"));
1901 if (insec)
1902 user *= fdisk_get_sector_size(cf->cxt);
1903 if (user < low) {
1904 ui_warnx(_("Minimum size is %"PRIu64" bytes."), low);
1905 rc = -ERANGE;
1906 }
1907 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1908 /* ignore when the user specified size overflow
1909 * with in range specified by suffix (e.g. MiB) */
1910 user = up;
1911
1912 if (user > up) {
1913 ui_warnx(_("Maximum size is %"PRIu64" bytes."), up);
1914 rc = -ERANGE;
1915 }
1916 if (rc == 0 && insec && expsize)
1917 *expsize = 1;
1918
1919 } else
1920 ui_warnx(_("Failed to parse size."));
1921 } while (rc != 0);
1922
1923 if (rc == 0)
1924 *res = user;
1925 free(dflt);
1926
1927 DBG(UI, ul_debug("get_size (result=%"PRIu64", rc=%zd)", *res, rc));
1928 return rc;
1929 }
1930
1931 static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1932 struct fdisk_parttype *cur)
1933 {
1934 struct cfdisk_menuitem *d, *cm;
1935 size_t i = 0, nitems, idx = 0;
1936 struct fdisk_parttype *t = NULL;
1937 struct fdisk_label *lb;
1938 int codetypes = 0;
1939
1940 DBG(UI, ul_debug("asking for parttype."));
1941
1942 lb = fdisk_get_label(cf->cxt, NULL);
1943
1944 /* create cfdisk menu according to label types, note that the
1945 * last cm[] item has to be empty -- so nitems + 1 */
1946 nitems = fdisk_label_get_nparttypes(lb);
1947 if (!nitems)
1948 return NULL;
1949
1950 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
1951 if (!cm)
1952 return NULL;
1953
1954 codetypes = fdisk_label_has_code_parttypes(lb);
1955
1956 for (i = 0; i < nitems; i++) {
1957 const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
1958 char *name;
1959
1960 cm[i].userdata = (void *) x;
1961 if (codetypes)
1962 xasprintf(&name, "%2x %s",
1963 fdisk_parttype_get_code(x),
1964 _(fdisk_parttype_get_name(x)));
1965 else {
1966 name = (char *) _(fdisk_parttype_get_name(x));
1967 cm[i].desc = fdisk_parttype_get_string(x);
1968 }
1969 cm[i].name = name;
1970 if (x == cur)
1971 idx = i;
1972 }
1973
1974 /* make the new menu active */
1975 menu_push(cf, cm);
1976 cf->menu->vertical = 1;
1977 cf->menu->idx = idx;
1978 menu_set_title(cf->menu, _("Select partition type"));
1979 ui_draw_menu(cf);
1980 refresh();
1981
1982 while (!sig_die) {
1983 int key = getch();
1984
1985 if (sig_die)
1986 break;
1987 if (sig_resize)
1988 ui_menu_resize(cf);
1989 if (ui_menu_move(cf, key) == 0)
1990 continue;
1991
1992 switch (key) {
1993 case KEY_ENTER:
1994 case '\n':
1995 case '\r':
1996 d = menu_get_menuitem(cf, cf->menu->idx);
1997 if (d)
1998 t = (struct fdisk_parttype *) d->userdata;
1999 goto done;
2000 case KEY_ESC:
2001 case 'q':
2002 case 'Q':
2003 goto done;
2004 }
2005 }
2006
2007 if (sig_die)
2008 die_on_signal();
2009 done:
2010 menu_pop(cf);
2011 if (codetypes) {
2012 for (i = 0; i < nitems; i++)
2013 free((char *) cm[i].name);
2014 }
2015 free(cm);
2016 DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
2017 fdisk_parttype_get_name(t) : NULL));
2018 return t;
2019 }
2020
2021 static int ui_script_read(struct cfdisk *cf)
2022 {
2023 struct fdisk_script *sc = NULL;
2024 char buf[PATH_MAX] = { 0 };
2025 int rc;
2026
2027 erase();
2028 rc = ui_get_string( _("Enter script file name: "),
2029 _("The script file will be applied to in-memory partition table."),
2030 buf, sizeof(buf));
2031 if (rc <= 0)
2032 return rc;
2033
2034 rc = -1;
2035 errno = 0;
2036 sc = fdisk_new_script_from_file(cf->cxt, buf);
2037 if (!sc && errno)
2038 ui_warn(_("Cannot open %s"), buf);
2039 else if (!sc)
2040 ui_warnx(_("Failed to parse script file %s"), buf);
2041 else if (fdisk_apply_script(cf->cxt, sc) != 0)
2042 ui_warnx(_("Failed to apply script %s"), buf);
2043 else
2044 rc = 0;
2045
2046 ui_clean_hint();
2047 fdisk_unref_script(sc);
2048 return rc;
2049 }
2050
2051 static int ui_script_write(struct cfdisk *cf)
2052 {
2053 struct fdisk_script *sc = NULL;
2054 char buf[PATH_MAX] = { 0 };
2055 FILE *f = NULL;
2056 int rc;
2057
2058 rc = ui_get_string( _("Enter script file name: "),
2059 _("The current in-memory partition table will be dumped to the file."),
2060 buf, sizeof(buf));
2061 if (rc <= 0)
2062 return rc;
2063
2064 rc = 0;
2065 sc = fdisk_new_script(cf->cxt);
2066 if (!sc) {
2067 ui_warn(_("Failed to allocate script handler"));
2068 goto done;
2069 }
2070
2071 rc = fdisk_script_read_context(sc, NULL);
2072 if (rc) {
2073 ui_warnx(_("Failed to read disk layout into script."));
2074 goto done;
2075 }
2076
2077 DBG(UI, ul_debug("writing dump into: '%s'", buf));
2078 f = fopen(buf, "w");
2079 if (!f) {
2080 ui_warn(_("Cannot open %s"), buf);
2081 rc = -errno;
2082 goto done;
2083 }
2084
2085 rc = fdisk_script_write_file(sc, f);
2086 if (!rc)
2087 ui_info(_("Disk layout successfully dumped."));
2088 done:
2089 if (rc)
2090 ui_warn(_("Failed to write script %s"), buf);
2091 if (f)
2092 fclose(f);
2093 fdisk_unref_script(sc);
2094 return rc;
2095 }
2096
2097 /* prints menu with libfdisk labels and waits for users response */
2098 static int ui_create_label(struct cfdisk *cf)
2099 {
2100 struct cfdisk_menuitem *d, *cm;
2101 int rc = 1, refresh_menu = 1;
2102 size_t i = 0, nitems;
2103 struct fdisk_label *lb = NULL;
2104
2105 assert(cf);
2106
2107 DBG(UI, ul_debug("asking for new disklabe."));
2108
2109 /* create cfdisk menu according to libfdisk labels, note that the
2110 * last cm[] item has to be empty -- so nitems + 1 */
2111 nitems = fdisk_get_nlabels(cf->cxt);
2112 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
2113
2114 while (fdisk_next_label(cf->cxt, &lb) == 0) {
2115 if (fdisk_label_is_disabled(lb) ||
2116 fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
2117 continue;
2118 cm[i++].name = fdisk_label_get_name(lb);
2119 }
2120
2121 erase();
2122
2123 /* make the new menu active */
2124 menu_push(cf, cm);
2125 cf->menu->vertical = 1;
2126 menu_set_title(cf->menu, _("Select label type"));
2127
2128 if (!cf->zero_start)
2129 ui_info(_("Device does not contain a recognized partition table."));
2130
2131
2132 while (!sig_die) {
2133 int key;
2134
2135 if (refresh_menu) {
2136 ui_draw_menu(cf);
2137 ui_hint(_("Select a type to create a new label or press 'L' to load script file."));
2138 refresh();
2139 refresh_menu = 0;
2140 }
2141
2142 key = getch();
2143
2144 if (sig_die)
2145 break;
2146 if (sig_resize)
2147 ui_menu_resize(cf);
2148 if (ui_menu_move(cf, key) == 0)
2149 continue;
2150 switch (key) {
2151 case KEY_ENTER:
2152 case '\n':
2153 case '\r':
2154 d = menu_get_menuitem(cf, cf->menu->idx);
2155 if (d)
2156 rc = fdisk_create_disklabel(cf->cxt, d->name);
2157 goto done;
2158 case KEY_ESC:
2159 case 'q':
2160 case 'Q':
2161 goto done;
2162 case 'l':
2163 case 'L':
2164 rc = ui_script_read(cf);
2165 if (rc == 0)
2166 goto done;
2167 refresh_menu = 1;
2168 break;
2169 }
2170 }
2171
2172 if (sig_die)
2173 die_on_signal();
2174 done:
2175 menu_pop(cf);
2176 free(cm);
2177 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
2178 return rc;
2179 }
2180
2181
2182 static int ui_help(void)
2183 {
2184 size_t i;
2185 static const char *help[] = {
2186 N_("This is cfdisk, a curses-based disk partitioning program."),
2187 N_("It lets you create, delete, and modify partitions on a block device."),
2188 " ",
2189 N_("Command Meaning"),
2190 N_("------- -------"),
2191 N_(" b Toggle bootable flag of the current partition"),
2192 N_(" d Delete the current partition"),
2193 N_(" h Print this screen"),
2194 N_(" n Create new partition from free space"),
2195 N_(" q Quit program without writing partition table"),
2196 N_(" s Fix partitions order (only when in disarray)"),
2197 N_(" t Change the partition type"),
2198 N_(" u Dump disk layout to sfdisk compatible script file"),
2199 N_(" W Write partition table to disk (you must enter uppercase W);"),
2200 N_(" since this might destroy data on the disk, you must either"),
2201 N_(" confirm or deny the write by entering 'yes' or 'no'"),
2202 N_(" x Display/hide extra information about a partition"),
2203 N_("Up Arrow Move cursor to the previous partition"),
2204 N_("Down Arrow Move cursor to the next partition"),
2205 N_("Left Arrow Move cursor to the previous menu item"),
2206 N_("Right Arrow Move cursor to the next menu item"),
2207 " ",
2208 N_("Note: All of the commands can be entered with either upper or lower"),
2209 N_("case letters (except for Write)."),
2210 " ",
2211 N_("Use lsblk(8) or partx(8) to see more details about the device."),
2212 " ",
2213 " ",
2214 "Copyright (C) 2014-2017 Karel Zak <kzak@redhat.com>"
2215 };
2216
2217 erase();
2218 for (i = 0; i < ARRAY_SIZE(help); i++)
2219 mvaddstr(i, 1, _(help[i]));
2220
2221 ui_info(_("Press a key to continue."));
2222
2223 getch();
2224
2225 if (sig_die)
2226 die_on_signal();
2227 return 0;
2228 }
2229
2230 /* TODO: use @sz, now 128bytes */
2231 static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
2232 size_t sz __attribute__((__unused__)))
2233 {
2234 struct fdisk_partition *pa = get_current_partition(cf);
2235 size_t i = 0;
2236
2237 if (!pa)
2238 return 0;
2239 if (fdisk_partition_is_freespace(pa)) {
2240 ignore[i++] = 'd'; /* delete */
2241 ignore[i++] = 't'; /* set type */
2242 ignore[i++] = 'b'; /* set bootable */
2243 ignore[i++] = 'r'; /* resize */
2244 cf->menu->prefkey = 'n';
2245 } else {
2246 cf->menu->prefkey = 'q';
2247 ignore[i++] = 'n';
2248 if (!fdisk_is_label(cf->cxt, DOS) &&
2249 !fdisk_is_label(cf->cxt, SGI))
2250 ignore[i++] = 'b';
2251 }
2252
2253 if (!cf->wrong_order)
2254 ignore[i++] = 's';
2255
2256 if (fdisk_is_readonly(cf->cxt))
2257 ignore[i++] = 'W';
2258
2259 return i;
2260 }
2261
2262
2263 /* returns: error: < 0, success: 0, quit: 1 */
2264 static int main_menu_action(struct cfdisk *cf, int key)
2265 {
2266 size_t n;
2267 int ref = 0, rc, org_order = cf->wrong_order;
2268 const char *info = NULL, *warn = NULL;
2269 struct fdisk_partition *pa;
2270
2271 assert(cf);
2272 assert(cf->cxt);
2273 assert(cf->menu);
2274
2275 if (key == 0) {
2276 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
2277 if (!d)
2278 return 0;
2279 key = d->key;
2280
2281 } else if (key != 'w' && key != 'W')
2282 key = tolower(key); /* case insensitive except 'W'rite */
2283
2284 DBG(MENU, ul_debug("main menu action: key=%c", key));
2285
2286 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
2287 DBG(MENU, ul_debug(" ignore '%c'", key));
2288 return 0;
2289 }
2290
2291 pa = get_current_partition(cf);
2292 if (!pa)
2293 return -EINVAL;
2294 n = fdisk_partition_get_partno(pa);
2295
2296 DBG(MENU, ul_debug("menu action on %p", pa));
2297 ui_clean_hint();
2298 ui_clean_info();
2299
2300 switch (key) {
2301 case 'b': /* Bootable flag */
2302 {
2303 int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
2304 fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
2305
2306 if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
2307 warn = _("Could not toggle the flag.");
2308 else if (fl)
2309 ref = 1;
2310 break;
2311 }
2312 #ifdef KEY_DC
2313 case KEY_DC:
2314 #endif
2315 case 'd': /* Delete */
2316 if (fdisk_delete_partition(cf->cxt, n) != 0)
2317 warn = _("Could not delete partition %zu.");
2318 else
2319 info = _("Partition %zu has been deleted.");
2320 ref = 1;
2321 break;
2322 case 'h': /* Help */
2323 case '?':
2324 ui_help();
2325 ref = 1;
2326 break;
2327 case 'n': /* New */
2328 {
2329 uint64_t start, size, dflt_size, secs, max_size;
2330 struct fdisk_partition *npa; /* the new partition */
2331 int expsize = 0; /* size specified explicitly in sectors */
2332
2333 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2334 return -EINVAL;
2335
2336 /* free space range */
2337 start = fdisk_partition_get_start(pa);
2338 size = max_size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
2339
2340 if (ui_get_size(cf, _("Partition size: "), &size,
2341 fdisk_get_sector_size(cf->cxt),
2342 max_size, &expsize) == -CFDISK_ERR_ESC)
2343 break;
2344
2345 secs = size / fdisk_get_sector_size(cf->cxt);
2346
2347 npa = fdisk_new_partition();
2348 if (!npa)
2349 return -ENOMEM;
2350
2351 if (dflt_size == size) /* default is to fillin all free space */
2352 fdisk_partition_end_follow_default(npa, 1);
2353 else
2354 fdisk_partition_set_size(npa, secs);
2355
2356 if (expsize)
2357 fdisk_partition_size_explicit(pa, 1);
2358
2359 fdisk_partition_set_start(npa, start);
2360 fdisk_partition_partno_follow_default(npa, 1);
2361 /* add to disk label -- libfdisk will ask for missing details */
2362 rc = fdisk_add_partition(cf->cxt, npa, NULL);
2363 fdisk_unref_partition(npa);
2364 if (rc == 0)
2365 ref = 1;
2366 break;
2367 }
2368 case 'q': /* Quit */
2369 return 1;
2370 case 't': /* Type */
2371 {
2372 struct fdisk_parttype *t;
2373
2374 if (fdisk_partition_is_freespace(pa))
2375 return -EINVAL;
2376 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
2377 t = ui_get_parttype(cf, t);
2378 ref = 1;
2379
2380 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
2381 info = _("Changed type of partition %zu.");
2382 else
2383 info = _("The type of partition %zu is unchanged.");
2384 break;
2385 }
2386 case 'r': /* resize */
2387 {
2388 struct fdisk_partition *npa, *next;
2389 uint64_t size, max_size, secs;
2390
2391 if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2392 return -EINVAL;
2393
2394 size = fdisk_partition_get_size(pa);
2395
2396 /* is the next freespace? */
2397 next = fdisk_table_get_partition(cf->table, cf->lines_idx + 1);
2398 if (next && fdisk_partition_is_freespace(next))
2399 size += fdisk_partition_get_size(next);
2400
2401 size *= fdisk_get_sector_size(cf->cxt);
2402 max_size = size;
2403
2404 if (ui_get_size(cf, _("New size: "), &size,
2405 fdisk_get_sector_size(cf->cxt),
2406 max_size, NULL) == -CFDISK_ERR_ESC)
2407 break;
2408 secs = size / fdisk_get_sector_size(cf->cxt);
2409 npa = fdisk_new_partition();
2410 if (!npa)
2411 return -ENOMEM;
2412
2413 fdisk_partition_set_size(npa, secs);
2414
2415 rc = fdisk_set_partition(cf->cxt, n, npa);
2416 fdisk_unref_partition(npa);
2417 if (rc == 0) {
2418 ref = 1;
2419 info = _("Partition %zu resized.");
2420 }
2421 break;
2422 }
2423 case 's': /* Sort */
2424 if (cf->wrong_order) {
2425 fdisk_reorder_partitions(cf->cxt);
2426 ref = 1;
2427 }
2428 break;
2429 case 'u': /* dUmp */
2430 ui_script_write(cf);
2431 break;
2432 case 'W': /* Write */
2433 {
2434 char buf[64] = { 0 };
2435
2436 if (fdisk_is_readonly(cf->cxt)) {
2437 warn = _("Device is open in read-only mode.");
2438 break;
2439 }
2440
2441 rc = ui_get_string(
2442 _("Are you sure you want to write the partition "
2443 "table to disk? "),
2444 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
2445 buf, sizeof(buf));
2446
2447 ref = 1;
2448 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2449 strcasecmp(buf, _("yes")) != 0)) {
2450 info = _("Did not write partition table to disk.");
2451 break;
2452 }
2453 rc = fdisk_write_disklabel(cf->cxt);
2454 if (rc)
2455 warn = _("Failed to write disklabel.");
2456 else {
2457 if (cf->device_is_used)
2458 fdisk_reread_changes(cf->cxt, cf->original_layout);
2459 else
2460 fdisk_reread_partition_table(cf->cxt);
2461 info = _("The partition table has been altered.");
2462 }
2463 cf->nwrites++;
2464 break;
2465 }
2466 default:
2467 break;
2468 }
2469
2470 if (ref) {
2471 lines_refresh(cf);
2472 ui_refresh(cf);
2473 ui_draw_extra(cf);
2474 } else
2475 ui_draw_menu(cf);
2476
2477 ui_clean_hint();
2478
2479 if (warn)
2480 ui_warnx(warn, n + 1);
2481 else if (info)
2482 ui_info(info, n + 1);
2483 else if (key == 'n' && cf->wrong_order && org_order == 0)
2484 ui_info(_("Note that partition table entries are not in disk order now."));
2485
2486 return 0;
2487 }
2488
2489 static void ui_resize_refresh(struct cfdisk *cf)
2490 {
2491 DBG(UI, ul_debug("ui resize/refresh"));
2492 resize();
2493 menu_refresh_size(cf);
2494 lines_refresh(cf);
2495 ui_refresh(cf);
2496 ui_draw_extra(cf);
2497 }
2498
2499 static void toggle_show_extra(struct cfdisk *cf)
2500 {
2501 if (cf->show_extra && cf->act_win) {
2502 wclear(cf->act_win);
2503 touchwin(stdscr);
2504 }
2505 cf->show_extra = cf->show_extra ? 0 : 1;
2506
2507 if (cf->show_extra)
2508 ui_draw_extra(cf);
2509 DBG(MENU, ul_debug("extra: %s", cf->show_extra ? "ENABLED" : "DISABLED" ));
2510 }
2511
2512 static int ui_run(struct cfdisk *cf)
2513 {
2514 int rc = 0;
2515
2516 ui_lines = LINES;
2517 ui_cols = COLS;
2518 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
2519
2520 if (fdisk_get_collision(cf->cxt)) {
2521 ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
2522 fdisk_get_collision(cf->cxt));
2523 fdisk_enable_wipe(cf->cxt, 1);
2524 ui_hint(_("Press a key to continue."));
2525 getch();
2526 }
2527
2528 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
2529 rc = ui_create_label(cf);
2530 if (rc < 0)
2531 ui_errx(EXIT_FAILURE,
2532 _("failed to create a new disklabel"));
2533 if (rc)
2534 return rc;
2535 }
2536
2537 cols_init(cf);
2538 rc = lines_refresh(cf);
2539 if (rc)
2540 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
2541
2542 menu_push(cf, main_menuitems);
2543 cf->menu->ignore_cb = main_menu_ignore_keys;
2544
2545 rc = ui_refresh(cf);
2546 if (rc)
2547 return rc;
2548
2549 cf->show_extra = 1;
2550 ui_draw_extra(cf);
2551
2552 if (fdisk_is_readonly(cf->cxt))
2553 ui_warnx(_("Device is open in read-only mode."));
2554 else if (cf->wrong_order)
2555 ui_info(_("Note that partition table entries are not in disk order now."));
2556
2557 while (!sig_die) {
2558 int key = getch();
2559
2560 rc = 0;
2561
2562 if (sig_die)
2563 break;
2564 if (sig_resize)
2565 /* Note that ncurses getch() returns ERR when interrupted
2566 * by signal, but SLang does not interrupt at all. */
2567 ui_resize_refresh(cf);
2568 if (key == ERR)
2569 continue;
2570 if (key == '\014') { /* ^L refresh */
2571 ui_resize_refresh(cf);
2572 continue;
2573 }
2574 if (ui_menu_move(cf, key) == 0)
2575 continue;
2576
2577 DBG(UI, ul_debug("main action key >%1$c< [\\0%1$o].", key));
2578
2579 switch (key) {
2580 case KEY_DOWN:
2581 case '\016': /* ^N */
2582 case 'j': /* Vi-like alternative */
2583 ui_table_goto(cf, cf->lines_idx + 1);
2584 break;
2585 case KEY_UP:
2586 case '\020': /* ^P */
2587 case 'k': /* Vi-like alternative */
2588 ui_table_goto(cf, (int) cf->lines_idx - 1);
2589 break;
2590 case KEY_PPAGE:
2591 if (cf->page_sz) {
2592 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2593 break;
2594 }
2595 /* fallthrough */
2596 case KEY_HOME:
2597 ui_table_goto(cf, 0);
2598 break;
2599 case KEY_NPAGE:
2600 if (cf->page_sz) {
2601 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2602 break;
2603 }
2604 /* fallthrough */
2605 case KEY_END:
2606 ui_table_goto(cf, (int) cf->nlines - 1);
2607 break;
2608 case KEY_ENTER:
2609 case '\n':
2610 case '\r':
2611 rc = main_menu_action(cf, 0);
2612 break;
2613 case 'X':
2614 case 'x': /* Extra */
2615 toggle_show_extra(cf);
2616 break;
2617 default:
2618 rc = main_menu_action(cf, key);
2619 if (rc < 0)
2620 beep();
2621 break;
2622 }
2623
2624 if (rc == 1)
2625 break; /* quit */
2626 }
2627
2628 menu_pop(cf);
2629
2630 DBG(UI, ul_debug("end"));
2631 return 0;
2632 }
2633
2634 static void __attribute__((__noreturn__)) usage(void)
2635 {
2636 FILE *out = stdout;
2637 fputs(USAGE_HEADER, out);
2638 fprintf(out,
2639 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2640
2641 fputs(USAGE_SEPARATOR, out);
2642 fputs(_("Display or manipulate a disk partition table.\n"), out);
2643
2644 fputs(USAGE_OPTIONS, out);
2645 fprintf(out,
2646 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
2647 fprintf(out,
2648 " %s\n", USAGE_COLORS_DEFAULT);
2649 fputs(_(" -z, --zero start with zeroed partition table\n"), out);
2650
2651 fputs(USAGE_SEPARATOR, out);
2652 printf(USAGE_HELP_OPTIONS(26));
2653
2654 printf(USAGE_MAN_TAIL("cfdisk(8)"));
2655 exit(EXIT_SUCCESS);
2656 }
2657
2658 int main(int argc, char *argv[])
2659 {
2660 const char *diskpath = NULL;
2661 int rc, c, colormode = UL_COLORMODE_UNDEF;
2662 struct cfdisk _cf = { .lines_idx = 0 },
2663 *cf = &_cf;
2664
2665 static const struct option longopts[] = {
2666 { "color", optional_argument, NULL, 'L' },
2667 { "help", no_argument, NULL, 'h' },
2668 { "version", no_argument, NULL, 'V' },
2669 { "zero", no_argument, NULL, 'z' },
2670 { NULL, 0, NULL, 0 },
2671 };
2672
2673 setlocale(LC_ALL, "");
2674 bindtextdomain(PACKAGE, LOCALEDIR);
2675 textdomain(PACKAGE);
2676 close_stdout_atexit();
2677
2678 while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
2679 switch(c) {
2680 case 'h':
2681 usage();
2682 break;
2683 case 'L':
2684 colormode = UL_COLORMODE_AUTO;
2685 if (optarg)
2686 colormode = colormode_or_err(optarg,
2687 _("unsupported color mode"));
2688 break;
2689 case 'V':
2690 print_version(EXIT_SUCCESS);
2691 case 'z':
2692 cf->zero_start = 1;
2693 break;
2694 default:
2695 errtryhelp(EXIT_FAILURE);
2696 }
2697 }
2698
2699 colors_init(colormode, "cfdisk");
2700
2701 fdisk_init_debug(0);
2702 scols_init_debug(0);
2703 cfdisk_init_debug();
2704 cf->cxt = fdisk_new_context();
2705 if (!cf->cxt)
2706 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
2707
2708 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
2709
2710 if (optind == argc) {
2711 size_t i;
2712
2713 for (i = 0; i < ARRAY_SIZE(default_disks); i++) {
2714 if (access(default_disks[i], F_OK) == 0) {
2715 diskpath = default_disks[i];
2716 break;
2717 }
2718 }
2719 if (!diskpath)
2720 diskpath = default_disks[0]; /* default, used for "cannot open" */
2721 } else
2722 diskpath = argv[optind];
2723
2724 rc = fdisk_assign_device(cf->cxt, diskpath, 0);
2725 if (rc == -EACCES)
2726 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
2727 if (rc != 0)
2728 err(EXIT_FAILURE, _("cannot open %s"), diskpath);
2729
2730 if (!fdisk_is_readonly(cf->cxt)) {
2731 cf->device_is_used = fdisk_device_is_used(cf->cxt);
2732 fdisk_get_partitions(cf->cxt, &cf->original_layout);
2733 }
2734
2735 /* Don't use err(), warn() from this point */
2736 ui_init(cf);
2737 ui_run(cf);
2738 ui_end();
2739
2740 cfdisk_free_lines(cf);
2741 free(cf->linesbuf);
2742 free(cf->fields);
2743
2744 fdisk_unref_table(cf->table);
2745 #ifdef HAVE_LIBMOUNT
2746 mnt_unref_table(cf->fstab);
2747 mnt_unref_table(cf->mtab);
2748 mnt_unref_cache(cf->mntcache);
2749 #endif
2750 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
2751 fdisk_unref_context(cf->cxt);
2752 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
2753 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2754 }