]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/cfdisk.c
cfdisk: add create lebel UI
[thirdparty/util-linux.git] / fdisks / cfdisk.c
CommitLineData
6dbe3af9
KZ
1#include <stdlib.h>
2#include <stdio.h>
6dbe3af9 3#include <errno.h>
8c3a5a44 4#include <signal.h>
00b4f26a 5#include <ctype.h>
541e6934
KZ
6
7#ifdef HAVE_SLANG_H
8#include <slang.h>
9#elif defined(HAVE_SLANG_SLANG_H)
10#include <slang/slang.h>
11#endif
12
48d7b13a
KZ
13#ifdef HAVE_SLCURSES_H
14#include <slcurses.h>
15#elif defined(HAVE_SLANG_SLCURSES_H)
16#include <slang/slcurses.h>
30c97bb8
MF
17#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
18#include <ncursesw/ncurses.h>
48d7b13a
KZ
19#elif defined(HAVE_NCURSES_H)
20#include <ncurses.h>
21#elif defined(HAVE_NCURSES_NCURSES_H)
22#include <ncurses/ncurses.h>
2b6fc908 23#endif
541e6934 24
5f94ca33
KZ
25#ifdef HAVE_WIDECHAR
26#include <wctype.h>
27#endif
28
8c3a5a44 29#include "c.h"
b2d28533 30#include "closestream.h"
7eda085c 31#include "nls.h"
8abcf290 32#include "strutils.h"
8c3a5a44 33#include "xalloc.h"
5f94ca33 34#include "mbsalign.h"
6dbe3af9 35
8c3a5a44
KZ
36#include "fdiskP.h"
37
38#define ARROW_CURSOR_STRING ">>> "
39#define ARROW_CURSOR_DUMMY " "
40#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
41
42#define MENU_PADDING 2
43#define TABLE_START_LINE 4
44#define MENU_START_LINE (LINES - 5)
1af8003b 45#define INFO_LINE (LINES - 2)
b1f58330
KZ
46#define HINT_LINE (LINES - 1)
47
48#define CFDISK_ERR_ESC 5000
49
50#ifndef KEY_ESC
51# define KEY_ESC '\033'
52#endif
53#ifndef KEY_DELETE
54# define KEY_DELETE '\177'
55#endif
1af8003b
KZ
56
57/* colors */
58enum {
59 CFDISK_CL_NONE = 0,
45333e9d
KZ
60 CFDISK_CL_WARNING,
61 CFDISK_CL_FREESPACE,
1af8003b
KZ
62};
63static const int color_pairs[][2] = {
64 /* color foreground, background */
45333e9d
KZ
65 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
66 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 }
1af8003b 67};
8c3a5a44 68
8460875d
KZ
69struct cfdisk;
70typedef int (menu_callback_t)(struct cfdisk *, int);
71
72static int menu_cb_main(struct cfdisk *cf, int key);
83fa0f80
KZ
73static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx);
74static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
b1f58330
KZ
75static struct cfdisk_menu *menu_push(struct cfdisk *cf, size_t id, struct cfdisk_menudesc *desc);
76static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
83fa0f80 77
1af8003b 78static int ui_refresh(struct cfdisk *cf);
83fa0f80
KZ
79static void ui_warnx(const char *fmt, ...);
80static void ui_warn(const char *fmt, ...);
81static void ui_info(const char *fmt, ...);
b1f58330 82static void ui_draw_menu(struct cfdisk *cf);
ac27ea5c
KZ
83static int ui_menu_move(struct cfdisk *cf, int key);
84
b1f58330
KZ
85static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
86 uintmax_t low, uintmax_t up);
8460875d 87
83fa0f80 88static int ui_enabled;
8c3a5a44
KZ
89
90struct cfdisk_menudesc {
91 int key; /* keyboard shortcut */
92 const char *name; /* item name */
93 const char *desc; /* item description */
749af4b6
KZ
94};
95
8c3a5a44 96struct cfdisk_menu {
8a726114 97 char *title;
8c3a5a44
KZ
98 struct cfdisk_menudesc *desc;
99 char *ignore;
100 size_t id;
101 size_t width;
102 size_t nitems;
8460875d
KZ
103
104 menu_callback_t *callback;
105
8c3a5a44 106 struct cfdisk_menu *prev;
8a726114
KZ
107
108 unsigned int vertical : 1;
749af4b6
KZ
109};
110
8c3a5a44
KZ
111static struct cfdisk_menudesc menu_main[] = {
112 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
113 { 'd', N_("Delete"), N_("Delete the current partition") },
114// { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
115// { 'h', N_("Help"), N_("Print help screen") },
116// { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
117 { 'n', N_("New"), N_("Create new partition from free space") },
118// { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
119 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
120 { 't', N_("Type"), N_("Change the partition type") },
121// { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
122 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
123 { 0, NULL, NULL }
124};
6dbe3af9 125
8c3a5a44 126enum {
b1f58330
KZ
127 CFDISK_MENU_GENERATED = -1, /* used in libfdisk callback */
128
129 /* built-in menus */
8c3a5a44 130 CFDISK_MENU_MAIN = 0,
b1f58330 131
5c36a0eb
KZ
132};
133
8c3a5a44
KZ
134static struct cfdisk_menudesc *menus[] = {
135 [CFDISK_MENU_MAIN] = menu_main
136};
7eda085c 137
8460875d
KZ
138static menu_callback_t *menu_callbacks[] = {
139 [CFDISK_MENU_MAIN] = menu_cb_main
140};
141
142
8c3a5a44
KZ
143struct cfdisk {
144 struct fdisk_context *cxt; /* libfdisk context */
145 struct fdisk_table *table; /* partition table */
7eda085c 146
8c3a5a44
KZ
147 struct cfdisk_menu *menu; /* the current menu */
148 size_t menu_idx;
7eda085c 149
8c3a5a44
KZ
150 int *cols; /* output columns */
151 size_t ncols; /* number of columns */
7eda085c 152
8c3a5a44
KZ
153 char *linesbuf; /* table as string */
154 size_t linesbufsz; /* size of the tb_buf */
7eda085c 155
8c3a5a44
KZ
156 char **lines; /* array with lines */
157 size_t nlines; /* number of lines */
158 size_t lines_idx; /* current line <0..N>, exclude header */
8c3a5a44 159};
5c36a0eb 160
8c3a5a44
KZ
161static int cols_init(struct cfdisk *cf)
162{
163 assert(cf);
5c36a0eb 164
8c3a5a44
KZ
165 free(cf->cols);
166 cf->cols = NULL;
167 cf->ncols = 0;
5c36a0eb 168
8c3a5a44 169 return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols);
5c36a0eb
KZ
170}
171
8c3a5a44
KZ
172/* It would be possible to use fdisk_table_to_string(), but we want some
173 * extension to the output format, so let's do it without libfdisk
174 */
175static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
176{
8c3a5a44 177 const struct fdisk_column *col;
8903f7df 178 struct fdisk_partition *pa;
8c3a5a44
KZ
179 struct fdisk_label *lb;
180 struct fdisk_iter *itr = NULL;
181 struct tt *tt = NULL;
182 char *res = NULL;
8903f7df 183 size_t i;
45333e9d 184 int tree = 0;
d051ea93 185 struct tt_line *ln, *ln_cont = NULL;
8c3a5a44
KZ
186
187 DBG(FRONTEND, dbgprint("table: convert to string"));
188
189 assert(cf);
190 assert(cf->cxt);
191 assert(cf->cols);
192 assert(tb);
193
194 lb = fdisk_context_get_label(cf->cxt, NULL);
195 assert(lb);
196
8c3a5a44
KZ
197 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
198 if (!itr)
199 goto done;
5c36a0eb 200
45333e9d
KZ
201 /* get container (e.g. extended partition) */
202 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
203 if (fdisk_partition_is_nested(pa)) {
204 DBG(FRONTEND, dbgprint("table: nested detected, using tree"));
45333e9d
KZ
205 tree = TT_FL_TREE;
206 break;
207 }
208 }
45333e9d
KZ
209
210 tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX | tree);
211 if (!tt)
212 goto done;
213
8c3a5a44
KZ
214 /* headers */
215 for (i = 0; i < cf->ncols; i++) {
216 col = fdisk_label_get_column(lb, cf->cols[i]);
45333e9d
KZ
217 if (col) {
218 int fl = col->tt_flags;
45333e9d
KZ
219 if (tree && col->id == FDISK_COL_DEVICE)
220 fl |= TT_FL_TREE;
221 tt_define_column(tt, col->name, col->width, fl);
222 }
8c3a5a44
KZ
223 }
224
225 /* data */
8903f7df
KZ
226 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
227
8c3a5a44 228 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
8903f7df
KZ
229 struct tt_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
230
231 ln = tt_add_line(tt, parent);
8c3a5a44
KZ
232 if (!ln)
233 goto done;
234 for (i = 0; i < cf->ncols; i++) {
235 char *cdata = NULL;
8c3a5a44
KZ
236 col = fdisk_label_get_column(lb, cf->cols[i]);
237 if (!col)
238 continue;
239 if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata))
240 continue;
241 tt_line_set_data(ln, i, cdata);
242 }
8903f7df 243 if (tree && fdisk_partition_is_container(pa))
45333e9d 244 ln_cont = ln;
d051ea93
KZ
245
246 tt_line_set_userdata(ln, (void *) pa);
247 fdisk_ref_partition(pa);
8c3a5a44 248 }
6dbe3af9 249
d051ea93
KZ
250 if (tt_is_empty(tt))
251 goto done;
252
253 tt_set_termreduce(tt, ARROW_CURSOR_WIDTH);
254 tt_print_table_to_string(tt, &res);
255
256 /* tt_* code might to reorder lines, let's reorder @tb according to the
257 * final output (it's no problem because partitions are addressed by
258 * parno stored within struct fdisk_partition) */
259
260 /* remove all */
261 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
262 while (fdisk_table_next_partition(tb, itr, &pa) == 0)
263 fdisk_table_remove_partition(tb, pa);
264
265 /* add all in the right order */
266 i = 0;
267 while (tt_get_output_line(tt, i++, &ln) == 0) {
268 struct fdisk_partition *pa = tt_line_get_userdata(ln);
269
270 fdisk_table_add_partition(tb, pa);
271 fdisk_unref_partition(pa);
8c3a5a44
KZ
272 }
273done:
274 tt_free_table(tt);
275 fdisk_free_iter(itr);
6dbe3af9 276
8c3a5a44 277 return res;
2b6fc908
KZ
278}
279
8460875d 280static int lines_refresh(struct cfdisk *cf)
8c3a5a44
KZ
281{
282 int rc;
283 char *p;
284 size_t i;
7eda085c 285
8c3a5a44 286 assert(cf);
2b6fc908 287
1af8003b 288 DBG(FRONTEND, dbgprint("refreshing buffer"));
c64061c9 289
8c3a5a44
KZ
290 free(cf->linesbuf);
291 free(cf->lines);
292 cf->linesbuf = NULL;
293 cf->linesbufsz = 0;
294 cf->lines = NULL;
295 cf->nlines = 0;
6dbe3af9 296
8c3a5a44 297 fdisk_unref_table(cf->table);
1af8003b 298 cf->table = NULL;
6dbe3af9 299
2cec7949
KZ
300 /* read partitions and free spaces into cf->table */
301 rc = fdisk_get_partitions(cf->cxt, &cf->table);
302 if (!rc)
303 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
8c3a5a44
KZ
304 if (rc)
305 return rc;
0d8589c5 306
8c3a5a44
KZ
307 cf->linesbuf = table_to_string(cf, cf->table);
308 if (!cf->linesbuf)
309 return -ENOMEM;
6dbe3af9 310
8c3a5a44
KZ
311 cf->linesbufsz = strlen(cf->linesbuf);
312 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
6dbe3af9 313
8c3a5a44
KZ
314 cf->lines = calloc(cf->nlines, sizeof(char *));
315 if (!cf->lines)
316 return -ENOMEM;
6dbe3af9 317
8c3a5a44
KZ
318 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
319 cf->lines[i] = p;
320 p = strchr(p, '\n');
321 if (p) {
322 *p = '\0';
323 p++;
324 }
325 }
6dbe3af9 326
8c3a5a44 327 return 0;
6dbe3af9
KZ
328}
329
00b4f26a
KZ
330static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
331{
332 assert(cf);
333 assert(cf->table);
334
335 return fdisk_table_get_partition(cf->table, cf->lines_idx);
336}
337
45333e9d
KZ
338static int is_freespace(struct cfdisk *cf, size_t i)
339{
340 struct fdisk_partition *pa;
341
342 assert(cf);
343 assert(cf->table);
344
345 pa = fdisk_table_get_partition(cf->table, i);
346 return fdisk_partition_is_freespace(pa);
347}
348
b1f58330
KZ
349/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
350 * responseback to libfdisk
351 */
352static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
353{
354 struct cfdisk_menudesc *d, *cm;
355 int key;
356 size_t i = 0, nitems;
357 const char *name, *desc;
358
359 assert(ask);
360 assert(cf);
361
362 /* create cfdisk menu according to libfdisk ask-menu, note that the
363 * last cm[] item has to be empty -- so nitems + 1 */
364 nitems = fdisk_ask_menu_get_nitems(ask);
365 cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
366 if (!cm)
367 return -ENOMEM;
368
369 for (i = 0; i < nitems; i++) {
370 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
371 break;
372 cm[i].key = key;
373 cm[i].desc = desc;
374 cm[i].name = name;
375 }
376
377 /* make the new menu active */
378 menu_push(cf, CFDISK_MENU_GENERATED, cm);
379 ui_draw_menu(cf);
380 refresh();
381
382 /* wait for keys */
383 do {
ac27ea5c
KZ
384 int key = getch();
385
386 if (ui_menu_move(cf, key) == 0)
387 continue;
388
389 switch (key) {
b1f58330
KZ
390 case KEY_ENTER:
391 case '\n':
392 case '\r':
393 d = menu_get_menuitem(cf, cf->menu_idx);
394 if (d)
395 fdisk_ask_menu_set_result(ask, d->key);
396 menu_pop(cf);
397 free(cm);
398 return 0;
399 }
400 } while (1);
401
402 menu_pop(cf);
403 free(cm);
404 return -1;
405}
406
407
8c3a5a44
KZ
408static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
409 void *data __attribute__((__unused__)))
410{
411 int rc = 0;
6dbe3af9 412
8c3a5a44
KZ
413 assert(cxt);
414 assert(ask);
6dbe3af9 415
8c3a5a44
KZ
416 switch(fdisk_ask_get_type(ask)) {
417 case FDISK_ASKTYPE_INFO:
83fa0f80 418 ui_info(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
419 break;
420 case FDISK_ASKTYPE_WARNX:
83fa0f80 421 ui_warnx(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
422 break;
423 case FDISK_ASKTYPE_WARN:
83fa0f80 424 ui_warn(fdisk_ask_print_get_mesg(ask));
8c3a5a44 425 break;
b1f58330
KZ
426 case FDISK_ASKTYPE_MENU:
427 ask_menu(ask, (struct cfdisk *) data);
428 break;
8c3a5a44 429 default:
83fa0f80
KZ
430 ui_warnx(_("internal error: unsupported dialog type %d"),
431 fdisk_ask_get_type(ask));
8c3a5a44
KZ
432 return -EINVAL;
433 }
434 return rc;
fd6b7a7f 435}
6dbe3af9 436
7aa0d529 437static int ui_end(void)
8c3a5a44 438{
7aa0d529 439 if (!ui_enabled)
8c3a5a44
KZ
440 return -EINVAL;
441
48d7b13a 442#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
8c3a5a44
KZ
443 SLsmg_gotorc(LINES - 1, 0);
444 SLsmg_refresh();
2b6fc908 445#else
8c3a5a44 446 mvcur(0, COLS - 1, LINES-1, 0);
2b6fc908 447#endif
8c3a5a44
KZ
448 nl();
449 endwin();
450 printf("\n");
7aa0d529 451 ui_enabled = 0;
8c3a5a44 452 return 0;
6dbe3af9
KZ
453}
454
1af8003b 455static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap)
f609e92e 456{
8c3a5a44 457 size_t width;
8c3a5a44 458 char *buf = NULL;
f609e92e 459
8c3a5a44
KZ
460 move(line, 0);
461 clrtoeol();
f609e92e 462
8c3a5a44 463 xvasprintf(&buf, fmt, ap);
fd6b7a7f 464
b1f58330 465 width = mbs_safe_width(buf);
91ba41ca
KZ
466 if (width > (size_t) COLS) {
467 char *p = strrchr(buf + COLS, ' ');
468 if (!p)
469 p = buf + COLS;
470 *p = '\0';
471 if (line + 1 >= LINES)
472 line--;
473 attron(attrs);
474 mvaddstr(line, 0, buf);
475 mvaddstr(line + 1, 0, p+1);
476 attroff(attrs);
477 } else {
478 attron(attrs);
479 mvaddstr(line, (COLS - width) / 2, buf);
480 attroff(attrs);
481 }
8c3a5a44 482 free(buf);
6dbe3af9
KZ
483}
484
1af8003b
KZ
485static void ui_center(int line, const char *fmt, ...)
486{
487 va_list ap;
488 va_start(ap, fmt);
489 ui_vprint_center(line, 0, fmt, ap);
490 va_end(ap);
491}
492
83fa0f80
KZ
493static void ui_warnx(const char *fmt, ...)
494{
495 va_list ap;
496 va_start(ap, fmt);
497 if (ui_enabled)
498 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt, ap);
499 else
500 vfprintf(stderr, fmt, ap);
501 va_end(ap);
502}
503
504static void ui_warn(const char *fmt, ...)
1af8003b 505{
83fa0f80 506 char *fmt_m;
1af8003b 507 va_list ap;
83fa0f80
KZ
508
509 xasprintf(&fmt_m, "%s: %m", fmt);
510
1af8003b 511 va_start(ap, fmt);
83fa0f80
KZ
512 if (ui_enabled)
513 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt_m, ap);
514 else
515 vfprintf(stderr, fmt_m, ap);
1af8003b 516 va_end(ap);
83fa0f80 517 free(fmt_m);
1af8003b
KZ
518}
519
7aa0d529
KZ
520static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
521 {
522 va_list ap;
523 ui_end();
524
525 va_start(ap, fmt);
526 fprintf(stderr, "%s: ", program_invocation_short_name);
527 vfprintf(stderr, fmt, ap);
528 va_end(ap);
529
530 exit(rc);
531}
532
1af8003b
KZ
533static void ui_info(const char *fmt, ...)
534{
535 va_list ap;
536 va_start(ap, fmt);
83fa0f80
KZ
537 if (ui_enabled)
538 ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap);
539 else
540 vfprintf(stdout, fmt, ap);
1af8003b
KZ
541 va_end(ap);
542}
543
544static void ui_clean_info(void)
545{
546 move(INFO_LINE, 0);
547 clrtoeol();
548}
6dbe3af9 549
b1f58330
KZ
550static void ui_hint(const char *fmt, ...)
551{
552 va_list ap;
553 va_start(ap, fmt);
554 if (ui_enabled)
555 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
556 else
557 vfprintf(stdout, fmt, ap);
558 va_end(ap);
559}
560
561static void ui_clean_hint(void)
562{
563 move(HINT_LINE, 0);
564 clrtoeol();
565}
566
8c3a5a44
KZ
567static void die_on_signal(int dummy __attribute__((__unused__)))
568{
7aa0d529
KZ
569 DBG(FRONTEND, dbgprint("die on signal."));
570 ui_end();
8c3a5a44 571 exit(EXIT_FAILURE);
6dbe3af9
KZ
572}
573
8c3a5a44
KZ
574static void menu_update_ignore(struct cfdisk *cf)
575{
00b4f26a
KZ
576 char ignore[128] = { 0 };
577 int i = 0;
578 struct fdisk_partition *pa;
8c3a5a44 579 struct cfdisk_menu *m;
83fa0f80
KZ
580 struct cfdisk_menudesc *d, *org;
581 size_t idx;
6dbe3af9 582
8c3a5a44 583 assert(cf);
6dbe3af9 584
8c3a5a44 585 m = cf->menu;
83fa0f80
KZ
586 org = menu_get_menuitem(cf, cf->menu_idx);
587
8c3a5a44 588 DBG(FRONTEND, dbgprint("menu: update menu ignored keys"));
6dbe3af9 589
8c3a5a44
KZ
590 switch (m->id) {
591 case CFDISK_MENU_MAIN:
00b4f26a
KZ
592 pa = get_current_partition(cf);
593 if (!pa)
594 break;
595 if (fdisk_partition_is_freespace(pa)) {
596 ignore[i++] = 'd'; /* delete */
597 ignore[i++] = 't'; /* set type */
598 ignore[i++] = 'b'; /* set bootable */
83fa0f80 599 } else {
00b4f26a 600 ignore[i++] = 'n';
83fa0f80
KZ
601 if (!fdisk_is_disklabel(cf->cxt, DOS) &&
602 !fdisk_is_disklabel(cf->cxt, SGI))
603 ignore[i++] = 'b';
604 }
00b4f26a 605
8c3a5a44 606 break;
fd6b7a7f 607 }
6dbe3af9 608
00b4f26a
KZ
609 ignore[i] = '\0';
610
8c3a5a44 611 /* return if no change */
00b4f26a
KZ
612 if ( (!m->ignore && !*ignore)
613 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
8c3a5a44 614 return;
fd6b7a7f 615 }
6dbe3af9 616
8c3a5a44 617 free(m->ignore);
8460875d 618 m->ignore = xstrdup(ignore);
8c3a5a44 619 m->nitems = 0;
6dbe3af9 620
8c3a5a44
KZ
621 for (d = m->desc; d->name; d++) {
622 if (m->ignore && strchr(m->ignore, d->key))
8460875d
KZ
623 continue;
624 m->nitems++;
8c3a5a44 625 }
83fa0f80
KZ
626
627 /* refresh menu index to be at the same menuitem or go to the first */
628 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
629 cf->menu_idx = idx;
630 else
631 cf->menu_idx = 0;
6dbe3af9
KZ
632}
633
b1f58330
KZ
634static struct cfdisk_menu *menu_push(
635 struct cfdisk *cf,
636 size_t id,
637 struct cfdisk_menudesc *desc)
8c3a5a44
KZ
638{
639 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
640 struct cfdisk_menudesc *d;
6dbe3af9 641
8c3a5a44 642 assert(cf);
6dbe3af9 643
8c3a5a44 644 DBG(FRONTEND, dbgprint("menu: new menu"));
6dbe3af9 645
8c3a5a44
KZ
646 m->prev = cf->menu;
647 m->id = id;
b1f58330 648 m->desc = desc ? desc : menus[id];
8460875d 649 m->callback = menu_callbacks[id];
6dbe3af9 650
8c3a5a44
KZ
651 for (d = m->desc; d->name; d++) {
652 const char *name = _(d->name);
b1f58330 653 size_t len = mbs_safe_width(name);
8c3a5a44
KZ
654 if (len > m->width)
655 m->width = len;
656 m->nitems++;
657 }
6dbe3af9 658
8c3a5a44
KZ
659 cf->menu = m;
660 return m;
6dbe3af9
KZ
661}
662
8c3a5a44 663static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
6dbe3af9 664{
8c3a5a44 665 struct cfdisk_menu *m = NULL;
6dbe3af9 666
8c3a5a44 667 assert(cf);
7eda085c 668
8c3a5a44 669 DBG(FRONTEND, dbgprint("menu: rem menu"));
7eda085c 670
8c3a5a44
KZ
671 if (cf->menu) {
672 m = cf->menu->prev;
673 free(cf->menu->ignore);
8a726114 674 free(cf->menu->title);
8c3a5a44 675 free(cf->menu);
c07ebfa1 676 }
8c3a5a44
KZ
677 cf->menu = m;
678 return cf->menu;
6dbe3af9
KZ
679}
680
8a726114
KZ
681static void menu_set_title(struct cfdisk_menu *m, const char *title)
682{
683 char *str = NULL;
684
685 if (title) {
686 size_t len = mbs_safe_width(title);
687 if (len + 3 > m->width)
688 m->width = len + 3;
689 str = xstrdup(title);
690 }
691 m->title = str;
692}
693
694
8460875d
KZ
695/* returns: error: < 0, success: 0, quit: 1 */
696static int menu_cb_main(struct cfdisk *cf, int key)
697{
83fa0f80 698 size_t n;
b1f58330 699 int ref = 0, rc;
83fa0f80 700 const char *info = NULL, *warn = NULL;
b1f58330 701 struct fdisk_partition *pa;
1af8003b
KZ
702
703 assert(cf);
704 assert(cf->cxt);
705 assert(key);
706
b1f58330 707 pa = get_current_partition(cf);
d80921ac 708 n = fdisk_partition_get_partno(pa);
83fa0f80 709
5139eca7
KZ
710 DBG(FRONTEND, dbgprint("menu action on %p", pa));
711
8460875d 712 switch (key) {
83fa0f80
KZ
713 case 'b': /* Bootable flag */
714 {
715 int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
716 fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
717
718 if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl))
719 warn = _("Could not toggle the flag.");
720 else if (fl)
721 ref = 1;
722 break;
723 }
8460875d 724 case 'd': /* Delete */
83fa0f80
KZ
725 if (fdisk_delete_partition(cf->cxt, n) != 0)
726 warn = _("Could not delete partition %zu.");
1af8003b 727 else
83fa0f80
KZ
728 info = _("Partition %zu has been deleted.");
729 ref = 1;
8460875d
KZ
730 break;
731 case 'n': /* New */
b1f58330 732 {
872d2389 733 uint64_t start, size, dflt_size;
b1f58330
KZ
734 struct fdisk_partition *npa; /* the new partition */
735
736 if (!pa || !fdisk_partition_is_freespace(pa))
737 return -EINVAL;
738 npa = fdisk_new_partition();
739 if (!npa)
740 return -ENOMEM;
741 /* free space range */
742 start = fdisk_partition_get_start(pa);
872d2389 743 size = dflt_size = fdisk_partition_get_size(pa) * cf->cxt->sector_size;
b1f58330
KZ
744
745 if (ui_get_size(cf, _("Partition size: "), &size, 1, size)
746 == -CFDISK_ERR_ESC)
747 break;
872d2389
KZ
748
749 if (dflt_size == size) /* default is to fillin all free space */
750 fdisk_partition_end_follow_default(npa, 1);
751 else /* set relative size of the partition */
752 fdisk_partition_set_size(npa, size / cf->cxt->sector_size);
753
b1f58330 754 fdisk_partition_set_start(npa, start);
872d2389 755 fdisk_partition_partno_follow_default(npa, 1);
b1f58330
KZ
756 /* add to disk label -- libfdisk will ask for missing details */
757 rc = fdisk_add_partition(cf->cxt, npa);
758 fdisk_unref_partition(npa);
759 if (rc == 0)
760 ref = 1;
8460875d 761 break;
b1f58330 762 }
8460875d
KZ
763 case 'q': /* Quit */
764 return 1;
765 case 't': /* Type */
766 break;
767 case 'W': /* Write */
768 break;
8460875d 769 }
83fa0f80
KZ
770
771 if (ref) {
772 lines_refresh(cf);
773 ui_refresh(cf);
774 }
775 if (warn)
776 ui_warnx(warn, n);
777 else if (info)
778 ui_info(info, n);
779
8460875d
KZ
780 return 0;
781}
7eda085c 782
b1f58330 783static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
8c3a5a44
KZ
784{
785 struct sigaction sa;
6dbe3af9 786
8c3a5a44 787 DBG(FRONTEND, dbgprint("ui: init"));
6dbe3af9 788
8c3a5a44
KZ
789 /* setup SIGCHLD handler */
790 sigemptyset(&sa.sa_mask);
791 sa.sa_flags = 0;
792 sa.sa_handler = die_on_signal;
793 sigaction(SIGINT, &sa, NULL);
794 sigaction(SIGTERM, &sa, NULL);
6dbe3af9 795
83fa0f80 796 ui_enabled = 1;
8c3a5a44 797 initscr();
6dbe3af9 798
1af8003b
KZ
799 if (has_colors()) {
800 size_t i;
801
802 start_color();
803 use_default_colors();
804
805 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
806 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
807 }
808
8c3a5a44
KZ
809 cbreak();
810 noecho();
811 nonl();
812 curs_set(0);
813 keypad(stdscr, TRUE);
6dbe3af9 814
8c3a5a44 815 return 0;
7eda085c
KZ
816}
817
8c3a5a44
KZ
818static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
819{
8a726114 820 if (cf->menu->vertical) {
7aa0d529
KZ
821 size_t ni = cf->menu->nitems + 1;
822 if ((size_t) LINES < ni)
823 return 0;
824 return (LINES - ni) / 2 + idx;
8a726114
KZ
825 } else {
826 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
827 size_t items = COLS / len; /* items per line */
828
829 return MENU_START_LINE + ((idx / items));
830 }
7eda085c
KZ
831}
832
8c3a5a44
KZ
833static int menuitem_get_column(struct cfdisk *cf, size_t idx)
834{
8a726114
KZ
835 if (cf->menu->vertical) {
836 size_t nc = cf->menu->width + MENU_PADDING;
7aa0d529
KZ
837 if ((size_t) COLS <= nc)
838 return 0;
839 return (COLS - nc) / 2;
8a726114
KZ
840 } else {
841 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
842 size_t items = COLS / len; /* items per line */
843 size_t extra = items < cf->menu->nitems ? /* extra space on line */
844 COLS % len : /* - multi-line menu */
845 COLS - (cf->menu->nitems * len); /* - one line menu */
846
847 extra += MENU_PADDING; /* add padding after last item to extra */
e66ac5d3 848
8a726114
KZ
849 if (idx < items)
850 return (idx * len) + (extra / 2);
851 return ((idx % items) * len) + (extra / 2);
852 }
df1dddf9
KZ
853}
854
8c3a5a44
KZ
855static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx)
856{
857 struct cfdisk_menudesc *d;
858 size_t i;
7eda085c 859
8c3a5a44
KZ
860 for (i = 0, d = cf->menu->desc; d->name; d++) {
861 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
862 continue;
863 if (i++ == idx)
864 return d;
d26aa358 865 }
7eda085c 866
8c3a5a44 867 return NULL;
6dbe3af9
KZ
868}
869
83fa0f80
KZ
870static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf,
871 int key, size_t *idx)
872{
873 struct cfdisk_menudesc *d;
874
875 for (*idx = 0, d = cf->menu->desc; d->name; d++) {
876 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
877 continue;
878 if (key == d->key)
879 return d;
880 (*idx)++;
881 }
882
883 return NULL;
884}
885
8c3a5a44
KZ
886static void ui_draw_menuitem(struct cfdisk *cf,
887 struct cfdisk_menudesc *d,
888 size_t idx)
889{
890 char buf[80 * MB_CUR_MAX];
891 const char *name;
892 size_t width = cf->menu->width + 2; /* 2 = blank around string */
8a726114 893 int ln, cl, vert = cf->menu->vertical;
8c3a5a44
KZ
894
895 name = _(d->name);
8a726114
KZ
896 mbsalign(name, buf, sizeof(buf), &width,
897 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
898 0);
8c3a5a44
KZ
899
900 ln = menuitem_get_line(cf, idx);
901 cl = menuitem_get_column(cf, idx);
902
903 DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
904 cl, ln, buf));
905
8a726114
KZ
906 if (vert) {
907 mvaddch(ln, cl - 1, ACS_VLINE);
908 mvaddch(ln, cl + cf->menu->width + 4, ACS_VLINE);
909 }
910
8c3a5a44
KZ
911 if (cf->menu_idx == idx) {
912 standout();
8a726114 913 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
8c3a5a44
KZ
914 standend();
915 if (d->desc)
b1f58330 916 ui_hint(d->desc);
8c3a5a44 917 } else
8a726114 918 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
6dbe3af9
KZ
919}
920
8c3a5a44
KZ
921static void ui_draw_menu(struct cfdisk *cf)
922{
923 struct cfdisk_menudesc *d;
8a726114 924 struct cfdisk_menu *m;
7aa0d529 925 size_t i = 0, maxi;
7eda085c 926
8c3a5a44
KZ
927 assert(cf);
928 assert(cf->menu);
fd6b7a7f 929
8c3a5a44 930 DBG(FRONTEND, dbgprint("ui: menu: draw start"));
6dbe3af9 931
8a726114 932 m = cf->menu;
7aa0d529 933 maxi = menuitem_get_line(cf, m->nitems);
8a726114 934
7aa0d529 935 for (i = menuitem_get_line(cf, 0); i <= maxi; i++) {
00b4f26a
KZ
936 move(i, 0);
937 clrtoeol();
938 }
939
8c3a5a44 940 menu_update_ignore(cf);
6dbe3af9 941
00b4f26a 942 i = 0;
8c3a5a44
KZ
943 while ((d = menu_get_menuitem(cf, i)))
944 ui_draw_menuitem(cf, d, i++);
6dbe3af9 945
8a726114
KZ
946 if (m->vertical) {
947 size_t ln = menuitem_get_line(cf, 0);
948 size_t cl = menuitem_get_column(cf, 0);
949 size_t nlines = i;
950
951 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
952 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
953
954 for (i = 0; i < m->width + 4; i++) {
955 mvaddch(ln - 1, cl + i, ACS_HLINE);
956 mvaddch(ln + nlines, cl + i, ACS_HLINE);
957 }
958 mvaddch(ln - 1, cl + i, ACS_URCORNER);
959 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
960
961 if (m->title) {
962 attron(A_BOLD);
963 mvprintw(ln - 1, cl, " %s ", m->title);
964 attroff(A_BOLD);
965 }
966
967 }
968
8c3a5a44 969 DBG(FRONTEND, dbgprint("ui: menu: draw end."));
6dbe3af9
KZ
970}
971
8c3a5a44
KZ
972static void ui_menu_goto(struct cfdisk *cf, int where)
973{
974 struct cfdisk_menudesc *d;
975 size_t old;
976
977 if (where < 0)
978 where = cf->menu->nitems - 1;
979 else if ((size_t) where > cf->menu->nitems - 1)
980 where = 0;
981 if ((size_t) where == cf->menu_idx)
982 return;
6dbe3af9 983
1af8003b
KZ
984 ui_clean_info();
985
8c3a5a44
KZ
986 old = cf->menu_idx;
987 cf->menu_idx = where;
6dbe3af9 988
8c3a5a44
KZ
989 d = menu_get_menuitem(cf, old);
990 ui_draw_menuitem(cf, d, old);
6dbe3af9 991
8c3a5a44
KZ
992 d = menu_get_menuitem(cf, where);
993 ui_draw_menuitem(cf, d, where);
6dbe3af9
KZ
994}
995
ac27ea5c
KZ
996static int ui_menu_move(struct cfdisk *cf, int key)
997{
998 assert(cf);
999 assert(cf->menu);
1000
1001 if (cf->menu->vertical)
1002 {
1003 switch (key) {
1004 case KEY_DOWN:
1005 case '\016': /* ^N */
1006 case 'j': /* Vi-like alternative */
1007 ui_menu_goto(cf, cf->menu_idx + 1);
1008 return 0;
1009 case KEY_UP:
1010 case '\020': /* ^P */
1011 case 'k': /* Vi-like alternative */
1012 ui_menu_goto(cf, cf->menu_idx - 1);
1013 return 0;
1014 case KEY_HOME:
1015 ui_menu_goto(cf, 0);
1016 return 0;
1017 case KEY_END:
1018 ui_menu_goto(cf, cf->menu->nitems);
1019 return 0;
1020 }
1021 } else {
1022 switch (key) {
1023 case KEY_RIGHT:
1024 case '\t':
1025 ui_menu_goto(cf, cf->menu_idx + 1);
1026 return 0;
1027 case KEY_LEFT:
1028#ifdef KEY_BTAB
1029 case KEY_BTAB:
1030#endif
1031 ui_menu_goto(cf, cf->menu_idx - 1);
1032 return 0;
1033 }
1034 }
1035
1036 return 1; /* key irrelevant for menu move */
1037}
1038
8460875d 1039/* returns: error: < 0, success: 0, quit: 1 */
8c3a5a44
KZ
1040static int ui_menu_action(struct cfdisk *cf, int key)
1041{
8460875d
KZ
1042 assert(cf);
1043 assert(cf->menu);
1044 assert(cf->menu->callback);
1045
1046 if (key == 0) {
1047 struct cfdisk_menudesc *d = menu_get_menuitem(cf, cf->menu_idx);
1048 if (!d)
1049 return 0;
1050 key = d->key;
00b4f26a
KZ
1051
1052 } else if (key != 'w' && key != 'W')
1053 key = tolower(key); /* case insensitive except 'W'rite */
8460875d
KZ
1054
1055 DBG(FRONTEND, dbgprint("ui: menu action: key=%c", key));
1056
1057 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
1058 DBG(FRONTEND, dbgprint(" ignore '%c'", key));
1059 return 0;
1060 }
1061
1062 return cf->menu->callback(cf, key);
6dbe3af9
KZ
1063}
1064
8c3a5a44
KZ
1065static void ui_draw_partition(struct cfdisk *cf, size_t i)
1066{
1067 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1068 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
6dbe3af9 1069
8c3a5a44 1070 DBG(FRONTEND, dbgprint("ui: draw partition %zu", i));
6dbe3af9 1071
8c3a5a44
KZ
1072 if (cf->lines_idx == i) {
1073 standout();
1074 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1075 mvaddstr(ln, cl, cf->lines[i + 1]);
1076 standend();
6dbe3af9 1077 } else {
45333e9d
KZ
1078 int at = 0;
1079
1080 if (is_freespace(cf, i)) {
1081 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1082 at = 1;
1083 }
8c3a5a44
KZ
1084 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1085 mvaddstr(ln, cl, cf->lines[i + 1]);
45333e9d
KZ
1086 if (at)
1087 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
6dbe3af9
KZ
1088 }
1089
6dbe3af9
KZ
1090}
1091
8c3a5a44
KZ
1092static int ui_draw_table(struct cfdisk *cf)
1093{
1094 int cl = ARROW_CURSOR_WIDTH;
1095 size_t i, nparts = fdisk_table_get_nents(cf->table);
7eda085c 1096
8c3a5a44 1097 DBG(FRONTEND, dbgprint("ui: draw table"));
6dbe3af9 1098
8c3a5a44
KZ
1099 if (cf->nlines - 2 < cf->lines_idx)
1100 cf->lines_idx = cf->nlines - 2; /* don't count header */
6dbe3af9 1101
8c3a5a44
KZ
1102 /* print header */
1103 attron(A_BOLD);
1104 mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
1105 attroff(A_BOLD);
6dbe3af9 1106
8c3a5a44
KZ
1107 /* print partitions */
1108 for (i = 0; i < nparts; i++)
1109 ui_draw_partition(cf, i);
6dbe3af9 1110
8c3a5a44 1111 return 0;
6dbe3af9
KZ
1112}
1113
8c3a5a44
KZ
1114static int ui_table_goto(struct cfdisk *cf, int where)
1115{
1116 size_t old;
1117 size_t nparts = fdisk_table_get_nents(cf->table);
6dbe3af9 1118
8c3a5a44 1119 DBG(FRONTEND, dbgprint("ui: goto table %d", where));
6dbe3af9 1120
8c3a5a44
KZ
1121 if (where < 0)
1122 where = 0;
1123 else if ((size_t) where > nparts - 1)
1124 where = nparts - 1;
6dbe3af9 1125
8c3a5a44
KZ
1126 if ((size_t) where == cf->lines_idx)
1127 return 0;
6dbe3af9 1128
8c3a5a44
KZ
1129 old = cf->lines_idx;
1130 cf->lines_idx = where;
6dbe3af9 1131
8c3a5a44
KZ
1132 ui_draw_partition(cf, old); /* cleanup old */
1133 ui_draw_partition(cf, where); /* draw new */
1af8003b 1134 ui_clean_info();
8c3a5a44
KZ
1135 ui_draw_menu(cf);
1136 refresh();
1137 return 0;
6dbe3af9
KZ
1138}
1139
8c3a5a44
KZ
1140static int ui_refresh(struct cfdisk *cf)
1141{
1142 char *id = NULL;
1143 uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size;
1144 char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
1145 | SIZE_SUFFIX_3LETTER, bytes);
1146 erase();
1147
83fa0f80 1148 if (!ui_enabled)
8c3a5a44
KZ
1149 return -EINVAL;
1150
1151 /* header */
1152 attron(A_BOLD);
1af8003b 1153 ui_center(0, _("Disk: %s"), cf->cxt->dev_path);
8c3a5a44 1154 attroff(A_BOLD);
1af8003b 1155 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
8c3a5a44
KZ
1156 strsz, bytes, (uintmax_t) cf->cxt->total_sectors);
1157 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1af8003b 1158 ui_center(2, _("Label: %s, identifier: %s"),
8c3a5a44 1159 cf->cxt->label->name, id);
7eda085c 1160 else
1af8003b 1161 ui_center(2, _("Label: %s"));
8c3a5a44 1162 free(strsz);
6dbe3af9 1163
8c3a5a44
KZ
1164 ui_draw_table(cf);
1165 ui_draw_menu(cf);
6dbe3af9 1166 refresh();
8c3a5a44 1167 return 0;
6dbe3af9
KZ
1168}
1169
b1f58330
KZ
1170static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
1171 const char *hint, char *buf, size_t len)
1172{
1173 size_t cells = 0;
1174 ssize_t i = 0, rc = -1;
1175 wint_t c;
1176 int ln = MENU_START_LINE, cl = 1;
1177
1178 assert(cf);
1179 assert(buf);
1180 assert(len);
1181
1182 move(ln, 0);
1183 clrtoeol();
1184
1185 if (prompt) {
1186 mvaddstr(ln, cl, prompt);
1187 cl += mbs_safe_width(prompt);
1188 }
1189
1190 /* default value */
1191 if (*buf) {
1192 i = strlen(buf);
1193 cells = mbs_safe_width(buf);
1194 mvaddstr(ln, cl, buf);
1195 }
1196
1197 if (hint)
1198 ui_hint(hint);
1199 else
1200 ui_clean_hint();
1201
1202 move(ln, cl + cells);
1203 curs_set(1);
1204 refresh();
1205
1206 while (1) {
1207#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1208 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1209 if (get_wch(&c) == ERR) {
1210#else
1211 if ((c = getch()) == ERR) {
1212#endif
1213 if (!isatty(STDIN_FILENO))
1214 exit(2);
1215 else
1216 goto done;
1217 }
1218 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1219 break;
1220
1221 switch (c) {
1222 case KEY_ESC:
1223 rc = -CFDISK_ERR_ESC;
1224 goto done;
1225 case KEY_DELETE:
1226 case '\b':
1227 case KEY_BACKSPACE:
1228 if (i > 0) {
1229 cells--;
1230 i = mbs_truncate(buf, &cells);
1231 if (i < 0)
1232 goto done;
1233 mvaddch(ln, cl + cells, ' ');
1234 move(ln, cl + cells);
1235 } else
1236 beep();
1237 break;
1238 default:
1239#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1240 if (i + 1 < (ssize_t) len && iswprint(c)) {
1241 wchar_t wc = (wchar_t) c;
1242 char s[MB_CUR_MAX + 1];
1243 int sz = wctomb(s, wc);
1244
1245 if (sz > 0 && sz + i < (ssize_t) len) {
1246 s[sz] = '\0';
1247 mvaddnstr(ln, cl + cells, s, sz);
1248 memcpy(buf + i, s, sz);
1249 i += sz;
1250 buf[i] = '\0';
1251 cells += wcwidth(wc);
1252 } else
1253 beep();
1254 }
1255#else
1256 if (i + 1 < (ssize_t) len && isprint(c)) {
1257 mvaddch(ln, cl + cells, c);
1258 str[i++] = c;
1259 str[i] = '\0';
1260 cells++;
1261 }
1262#endif
1263 else
1264 beep();
1265 }
1266 refresh();
1267 }
1268
1269 rc = i; /* success */
1270done:
1271 move(ln, 0);
1272 clrtoeol();
1273 curs_set(0);
1274 refresh();
1275
1276 return rc;
1277}
1278
1279/* @res is default value as well as result in bytes */
1280static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
1281 uintmax_t low, uintmax_t up)
1282{
1283 char buf[128];
1284 uintmax_t user = 0;
1285 ssize_t rc;
1286 char *dflt = size_to_human_string(0, *res);
1287
1288 DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res));
1289
1290 ui_clean_info();
1291
1292 do {
91ba41ca 1293 int pwr = 0, insec = 0;
b1f58330
KZ
1294
1295 snprintf(buf, sizeof(buf), "%s", dflt);
1296 rc = ui_get_string(cf, prompt,
5139eca7 1297 _("May be followed by {M,B,G,T}iB "
91ba41ca 1298 "(the \"iB\" is optional) or S for sectors."),
b1f58330
KZ
1299 buf, sizeof(buf));
1300 if (rc == 0) {
1301 ui_warnx(_("Please, specify size."));
1302 continue; /* nothing specified */
1303 } else if (rc == -CFDISK_ERR_ESC)
1304 break; /* cancel dialog */
1305
91ba41ca
KZ
1306 if (strcmp(buf, dflt) == 0)
1307 user = *res, rc = 0; /* no change, use default */
1308 else {
1309 size_t len = strlen(buf);
1310 if (buf[len - 1] == 'S') {
1311 insec = 1;
1312 buf[len - 1] = '\0';
1313 }
1314 rc = parse_size(buf, &user, &pwr); /* parse */
1315 }
1316
b1f58330 1317 if (rc == 0) {
91ba41ca
KZ
1318 DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d, sectors=%s",
1319 user, pwr, insec ? "yes" : "no"));
1320 if (insec)
1321 user *= cf->cxt->sector_size;
b1f58330
KZ
1322 if (user < low) {
1323 ui_warnx(_("Minimal size is %ju"), low);
1324 rc = -ERANGE;
1325 }
1326 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1327 /* ignore when the user specified size overflow
1328 * with in range specified by suffix (e.g. MiB) */
1329 user = up;
1330
1331 if (user > up) {
1332 ui_warnx(_("Maximal size is %ju bytes."), up);
1333 rc = -ERANGE;
1334 }
2cec7949
KZ
1335 } else
1336 ui_warnx(_("Failed to parse size."));
b1f58330
KZ
1337 } while (rc != 0);
1338
1339 if (rc == 0)
1340 *res = user;
1341 free(dflt);
1342
1343 DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc));
1344 return rc;
1345}
1346
7aa0d529
KZ
1347/* prints menu with libfdisk labels and waits for users response */
1348static int ui_create_label(struct cfdisk *cf)
1349{
1350 struct cfdisk_menudesc *d, *cm;
1351 int rc = 1;
1352 size_t i = 0, nitems;
1353 struct fdisk_label *lb = NULL;
1354
1355 assert(cf);
1356
1357 DBG(FRONTEND, dbgprint("ui: asking for new disklabe."));
1358
1359 /* create cfdisk menu according to libfdisk labels, note that the
1360 * last cm[] item has to be empty -- so nitems + 1 */
1361 nitems = fdisk_context_get_nlabels(cf->cxt);
1362 cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
1363 if (!cm)
1364 return -ENOMEM;
1365
1366 for (i = 0; i < nitems; i++) {
1367 if (fdisk_context_next_label(cf->cxt, &lb))
1368 break;
1369 cm[i].name = lb->name;
1370 }
1371
1372 erase();
1373 ui_center(LINES - 4,
1374 _("Device does not contain a recognized partition table."));
1375 ui_center(LINES - 3,
1376 _("Please, select a type to create a new disk label."));
1377
1378 /* make the new menu active */
1379 menu_push(cf, CFDISK_MENU_GENERATED, cm);
1380 cf->menu->vertical = 1;
1381 menu_set_title(cf->menu, _("Select label type"));
1382 ui_draw_menu(cf);
1383 refresh();
1384
1385 do {
1386 int key = getch();
1387 if (ui_menu_move(cf, key) == 0)
1388 continue;
1389 switch (key) {
1390 case KEY_ENTER:
1391 case '\n':
1392 case '\r':
1393 d = menu_get_menuitem(cf, cf->menu_idx);
1394 if (d)
1395 rc = fdisk_create_disklabel(cf->cxt, d->name);
1396 goto done;
1397 case 'q':
1398 case 'Q':
1399 goto done;
1400 }
1401 } while (1);
1402
1403done:
1404 menu_pop(cf);
1405 free(cm);
1406 DBG(FRONTEND, dbgprint("ui: create label done [rc=%d] ", rc));
1407 return rc;
1408}
1409
8c3a5a44
KZ
1410static int ui_run(struct cfdisk *cf)
1411{
7aa0d529 1412 int rc = 0;
6dbe3af9 1413
8c3a5a44 1414 DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES));
6dbe3af9 1415
7aa0d529
KZ
1416 if (!fdisk_dev_has_disklabel(cf->cxt)) {
1417 rc = ui_create_label(cf);
1418 if (rc < 0)
1419 ui_errx(EXIT_FAILURE,
1420 _("failed to create a new disklabel"));
1421 if (rc)
1422 return rc;
1423 }
1424
1425 cols_init(cf);
1426 rc = lines_refresh(cf);
1427 if (rc)
1428 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
6dbe3af9 1429
7aa0d529 1430 menu_push(cf, CFDISK_MENU_MAIN, NULL);
8c3a5a44
KZ
1431 rc = ui_refresh(cf);
1432 if (rc)
1433 return rc;
6dbe3af9 1434
8c3a5a44 1435 do {
8460875d 1436 int rc = 0, key = getch();
6dbe3af9 1437
ac27ea5c
KZ
1438 if (ui_menu_move(cf, key) == 0)
1439 continue;
1440
8c3a5a44
KZ
1441 switch (key) {
1442 case KEY_DOWN:
1443 case '\016': /* ^N */
1444 case 'j': /* Vi-like alternative */
1445 ui_table_goto(cf, cf->lines_idx + 1);
1446 break;
1447 case KEY_UP:
1448 case '\020': /* ^P */
1449 case 'k': /* Vi-like alternative */
1450 ui_table_goto(cf, cf->lines_idx - 1);
1451 break;
1452 case KEY_HOME:
1453 ui_table_goto(cf, 0);
1454 break;
1455 case KEY_END:
1456 ui_table_goto(cf, cf->nlines - 1);
1457 break;
1458 ui_menu_action(cf, 0);
1459 break;
8c3a5a44
KZ
1460 case KEY_ENTER:
1461 case '\n':
1462 case '\r':
8460875d 1463 rc = ui_menu_action(cf, 0);
8c3a5a44
KZ
1464 break;
1465 default:
8460875d
KZ
1466 rc = ui_menu_action(cf, key);
1467 if (rc < 0)
8c3a5a44
KZ
1468 beep();
1469 break;
1470 }
8460875d
KZ
1471
1472 if (rc == 1)
1473 break; /* quit */
8c3a5a44 1474 } while (1);
6dbe3af9 1475
8c3a5a44 1476 menu_pop(cf);
6dbe3af9 1477
8c3a5a44
KZ
1478 DBG(FRONTEND, dbgprint("ui: end"));
1479
1480 return 0;
1481}
6dbe3af9 1482
8c3a5a44
KZ
1483int main(int argc, char *argv[])
1484{
1485 struct cfdisk _cf = { .lines_idx = 0 },
1486 *cf = &_cf;
6dbe3af9 1487
8c3a5a44
KZ
1488 setlocale(LC_ALL, "");
1489 bindtextdomain(PACKAGE, LOCALEDIR);
1490 textdomain(PACKAGE);
1491 atexit(close_stdout);
6dbe3af9 1492
8c3a5a44
KZ
1493 fdisk_init_debug(0);
1494 cf->cxt = fdisk_new_context();
1495 if (!cf->cxt)
1496 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
6dbe3af9 1497
8c3a5a44 1498 fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf);
6dbe3af9 1499
8c3a5a44
KZ
1500 if (argc != 2)
1501 err(EXIT_FAILURE, "usage: %s <device>", argv[0]);
6dbe3af9 1502
8c3a5a44
KZ
1503 if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0)
1504 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
6dbe3af9 1505
8c3a5a44
KZ
1506 /* Don't use err(), warn() from this point */
1507 ui_init(cf);
1508 ui_run(cf);
7aa0d529 1509 ui_end();
6dbe3af9 1510
8c3a5a44
KZ
1511 free(cf->lines);
1512 free(cf->linesbuf);
1513 fdisk_unref_table(cf->table);
1514 fdisk_free_context(cf->cxt);
7aa0d529 1515 DBG(FRONTEND, dbgprint("bye!"));
8c3a5a44 1516 return EXIT_SUCCESS;
6dbe3af9 1517}