]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/cfdisk.c
8658ff25af857c2fdcd19b1b162a523fde9a63c3
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
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 */
103 enum {
104 CFDISK_CL_NONE = 0,
105 CFDISK_CL_WARNING,
106 CFDISK_CL_FREESPACE,
107 };
108 static 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
114 struct cfdisk;
115
116 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
117 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
118 static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
119 static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
120 static void menu_refresh_size(struct cfdisk *cf);
121
122 static int ui_refresh(struct cfdisk *cf);
123 static void ui_warnx(const char *fmt, ...);
124 static void ui_warn(const char *fmt, ...);
125 static void ui_info(const char *fmt, ...);
126 static void ui_draw_menu(struct cfdisk *cf);
127 static int ui_menu_move(struct cfdisk *cf, int key);
128 static void ui_menu_resize(struct cfdisk *cf);
129
130 static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
131 uintmax_t low, uintmax_t up, int *expsize);
132
133 static int ui_enabled;
134 static int ui_resize;
135
136 /* ncurses LINES and COLS may be actual variables or *macros*, but we need
137 * something portable and writable */
138 size_t ui_lines;
139 size_t ui_cols;
140
141 /* menu item */
142 struct 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 */
150 struct 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 */
167 static 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 */
181 struct 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 */
207 UL_DEBUG_DEFINE_MASK(cfdisk);
208 UL_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
219 static 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 */
227 static 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
238 static 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 */
260 static 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
281 static 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 }
392 done:
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 */
403 static 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
456 static 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
464 static 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 */
478 static 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 */
535 static 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
564 static 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
583 static 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
613 static 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
621 static 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
636 static 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
656 static 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
670 static 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
683 static void ui_clean_info(void)
684 {
685 move(INFO_LINE, 0);
686 clrtoeol();
687 }
688
689 static 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
702 static void ui_clean_hint(void)
703 {
704 move(HINT_LINE, 0);
705 clrtoeol();
706 }
707
708 static 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
715 static void resize_on_signal(int dummy __attribute__((__unused__)))
716 {
717 DBG(MISC, ul_debug("resize on signal."));
718 ui_resize = 1;
719 }
720
721 static 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
727 static 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
772 static 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
799 static 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
817 static 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
831 static 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
882 static 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
900 static 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
925 static 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
935 static 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
950 static 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
966 static 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
1016 static 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
1040 static 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
1105 static 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
1143 static 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 */
1208 static 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
1217 static 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
1225 static 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
1273 static 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
1309 static 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
1339 static 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
1375 static 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 */
1483 done:
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 */
1493 static 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
1563 static 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
1637 done:
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
1649 static 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
1677 static 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."));
1714 done:
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 */
1724 static 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
1790 done:
1791 menu_pop(cf);
1792 free(cm);
1793 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
1794 return rc;
1795 }
1796
1797
1798 static 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 */
1840 static 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 */
1868 static 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 case '?':
1928 ui_help();
1929 ref = 1;
1930 break;
1931 case 'n': /* New */
1932 {
1933 uint64_t start, size, dflt_size, secs;
1934 struct fdisk_partition *npa; /* the new partition */
1935 int expsize = 0; /* size specified explicitly in sectors */
1936
1937 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
1938 return -EINVAL;
1939
1940 /* free space range */
1941 start = fdisk_partition_get_start(pa);
1942 size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
1943
1944 if (ui_get_size(cf, _("Partition size: "), &size, 1, size, &expsize)
1945 == -CFDISK_ERR_ESC)
1946 break;
1947
1948 secs = size / fdisk_get_sector_size(cf->cxt);
1949 if (size && secs < 1) {
1950 warn = _("Too small partition size specified.");
1951 break;
1952 }
1953
1954 npa = fdisk_new_partition();
1955 if (!npa)
1956 return -ENOMEM;
1957
1958 if (dflt_size == size) /* default is to fillin all free space */
1959 fdisk_partition_end_follow_default(npa, 1);
1960 else
1961 fdisk_partition_set_size(npa, secs);
1962
1963 if (expsize)
1964 fdisk_partition_size_explicit(pa, 1);
1965
1966 fdisk_partition_set_start(npa, start);
1967 fdisk_partition_partno_follow_default(npa, 1);
1968 /* add to disk label -- libfdisk will ask for missing details */
1969 rc = fdisk_add_partition(cf->cxt, npa, NULL);
1970 fdisk_unref_partition(npa);
1971 if (rc == 0)
1972 ref = 1;
1973 break;
1974 }
1975 case 'q': /* Quit */
1976 return 1;
1977 case 't': /* Type */
1978 {
1979 struct fdisk_parttype *t;
1980
1981 if (fdisk_partition_is_freespace(pa))
1982 return -EINVAL;
1983 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
1984 t = ui_get_parttype(cf, t);
1985 ref = 1;
1986
1987 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
1988 info = _("Changed type of partition %zu.");
1989 else
1990 info = _("The type of partition %zu is unchanged.");
1991 break;
1992 }
1993 case 's': /* Sort */
1994 if (cf->wrong_order) {
1995 fdisk_reorder_partitions(cf->cxt);
1996 ref = 1;
1997 }
1998 break;
1999 case 'u': /* dUmp */
2000 ui_script_write(cf);
2001 break;
2002 case 'W': /* Write */
2003 {
2004 char buf[64] = { 0 };
2005
2006 if (fdisk_is_readonly(cf->cxt)) {
2007 warn = _("Device is open in read-only mode.");
2008 break;
2009 }
2010
2011 rc = ui_get_string(cf,
2012 _("Are you sure you want to write the partition "
2013 "table to disk? "),
2014 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
2015 buf, sizeof(buf));
2016
2017 ref = 1;
2018 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2019 strcasecmp(buf, _("yes")) != 0)) {
2020 info = _("Did not write partition table to disk.");
2021 break;
2022 }
2023 rc = fdisk_write_disklabel(cf->cxt);
2024 if (rc)
2025 warn = _("Failed to write disklabel.");
2026 else {
2027 fdisk_reread_partition_table(cf->cxt);
2028 info = _("The partition table has been altered.");
2029 }
2030 cf->nwrites++;
2031 break;
2032 }
2033 default:
2034 break;
2035 }
2036
2037 if (ref) {
2038 lines_refresh(cf);
2039 ui_refresh(cf);
2040 } else
2041 ui_draw_menu(cf);
2042
2043 ui_clean_hint();
2044
2045 if (warn)
2046 ui_warnx(warn, n + 1);
2047 else if (info)
2048 ui_info(info, n + 1);
2049 else if (key == 'n' && cf->wrong_order && org_order == 0)
2050 ui_info(_("Note that partition table entries are not in disk order now."));
2051
2052 return 0;
2053 }
2054
2055 static void ui_resize_refresh(struct cfdisk *cf)
2056 {
2057 resize();
2058 menu_refresh_size(cf);
2059 lines_refresh(cf);
2060 ui_refresh(cf);
2061 }
2062
2063 static int ui_run(struct cfdisk *cf)
2064 {
2065 int rc = 0;
2066
2067 ui_lines = LINES;
2068 ui_cols = COLS;
2069 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
2070
2071 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
2072 rc = ui_create_label(cf);
2073 if (rc < 0)
2074 ui_errx(EXIT_FAILURE,
2075 _("failed to create a new disklabel"));
2076 if (rc)
2077 return rc;
2078 }
2079
2080 cols_init(cf);
2081 rc = lines_refresh(cf);
2082 if (rc)
2083 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
2084
2085 menu_push(cf, main_menuitems);
2086 cf->menu->ignore_cb = main_menu_ignore_keys;
2087
2088 rc = ui_refresh(cf);
2089 if (rc)
2090 return rc;
2091
2092 if (fdisk_is_readonly(cf->cxt))
2093 ui_warnx(_("Device open in read-only mode."));
2094
2095 do {
2096 int key = getch();
2097
2098 rc = 0;
2099 if (ui_resize)
2100 /* Note that ncurses getch() returns ERR when interrupted
2101 * by signal, but SLang does not interrupt at all. */
2102 ui_resize_refresh(cf);
2103 if (key == ERR)
2104 continue;
2105 if (ui_menu_move(cf, key) == 0)
2106 continue;
2107
2108 DBG(UI, ul_debug("main action key >%c<.", key));
2109
2110 switch (key) {
2111 case KEY_DOWN:
2112 case '\016': /* ^N */
2113 case 'j': /* Vi-like alternative */
2114 ui_table_goto(cf, cf->lines_idx + 1);
2115 break;
2116 case KEY_UP:
2117 case '\020': /* ^P */
2118 case 'k': /* Vi-like alternative */
2119 ui_table_goto(cf, (int) cf->lines_idx - 1);
2120 break;
2121 case KEY_PPAGE:
2122 if (cf->page_sz) {
2123 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2124 break;
2125 }
2126 case KEY_HOME:
2127 ui_table_goto(cf, 0);
2128 break;
2129 case KEY_NPAGE:
2130 if (cf->page_sz) {
2131 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2132 break;
2133 }
2134 case KEY_END:
2135 ui_table_goto(cf, (int) cf->nlines - 1);
2136 break;
2137 case KEY_ENTER:
2138 case '\n':
2139 case '\r':
2140 rc = main_menu_action(cf, 0);
2141 break;
2142 default:
2143 rc = main_menu_action(cf, key);
2144 if (rc < 0)
2145 beep();
2146 break;
2147 }
2148
2149 if (rc == 1)
2150 break; /* quit */
2151 } while (1);
2152
2153 menu_pop(cf);
2154
2155 DBG(UI, ul_debug("end"));
2156 return 0;
2157 }
2158
2159 static void __attribute__ ((__noreturn__)) usage(FILE *out)
2160 {
2161 fputs(USAGE_HEADER, out);
2162 fprintf(out,
2163 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2164
2165 fputs(USAGE_SEPARATOR, out);
2166 fputs(_("Display or manipulate a disk partition table.\n"), out);
2167
2168 fputs(USAGE_OPTIONS, out);
2169 fputs(_(" -L --color[=<when>] colorize output (auto, always or never)\n"), out);
2170 fputs(_(" -z --zero start with zeroed partition table\n"), out);
2171
2172 fputs(USAGE_SEPARATOR, out);
2173 fputs(USAGE_HELP, out);
2174 fputs(USAGE_VERSION, out);
2175
2176 fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
2177 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
2178 }
2179
2180 int main(int argc, char *argv[])
2181 {
2182 const char *diskpath;
2183 int rc, c, colormode = UL_COLORMODE_UNDEF;
2184 struct cfdisk _cf = { .lines_idx = 0 },
2185 *cf = &_cf;
2186
2187 static const struct option longopts[] = {
2188 { "color", optional_argument, NULL, 'L' },
2189 { "help", no_argument, NULL, 'h' },
2190 { "version", no_argument, NULL, 'V' },
2191 { "zero", no_argument, NULL, 'z' },
2192 { NULL, 0, 0, 0 },
2193 };
2194
2195 setlocale(LC_ALL, "");
2196 bindtextdomain(PACKAGE, LOCALEDIR);
2197 textdomain(PACKAGE);
2198 atexit(close_stdout);
2199
2200 while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
2201 switch(c) {
2202 case 'h':
2203 usage(stdout);
2204 break;
2205 case 'L':
2206 colormode = UL_COLORMODE_AUTO;
2207 if (optarg)
2208 colormode = colormode_or_err(optarg,
2209 _("unsupported color mode"));
2210 break;
2211 case 'V':
2212 printf(UTIL_LINUX_VERSION);
2213 return EXIT_SUCCESS;
2214 case 'z':
2215 cf->zero_start = 1;
2216 break;
2217 }
2218 }
2219
2220 colors_init(colormode, "cfdisk");
2221
2222 fdisk_init_debug(0);
2223 scols_init_debug(0);
2224 cfdisk_init_debug();
2225 cf->cxt = fdisk_new_context();
2226 if (!cf->cxt)
2227 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
2228
2229 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
2230
2231 if (optind == argc)
2232 diskpath = access(DEFAULT_DEVICE, F_OK) == 0 ?
2233 DEFAULT_DEVICE : ALTERNATE_DEVICE;
2234 else
2235 diskpath = argv[optind];
2236
2237 rc = fdisk_assign_device(cf->cxt, diskpath, 0);
2238 if (rc == -EACCES)
2239 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
2240 if (rc != 0)
2241 err(EXIT_FAILURE, _("cannot open %s"),
2242 optind == argc ? DEFAULT_DEVICE : diskpath);
2243
2244 /* Don't use err(), warn() from this point */
2245 ui_init(cf);
2246 ui_run(cf);
2247 ui_end();
2248
2249 free(cf->lines);
2250 free(cf->linesbuf);
2251 fdisk_unref_table(cf->table);
2252
2253 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
2254 fdisk_unref_context(cf->cxt);
2255 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
2256 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2257 }