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