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