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