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