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