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