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