ui.c revision 9663:ace9a2ac3683
1/*
2    parted - a frontend to libparted
3    Copyright (C) 1999, 2000, 2001, 2002, 2006, 2007
4    Free Software Foundation, Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include <parted/parted.h>
21#include <parted/debug.h>
22
23#include <ctype.h>
24#include <signal.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <setjmp.h>
29
30#include <config.h>
31#include "command.h"
32#include "strlist.h"
33#include "ui.h"
34#include "error.h"
35
36#define N_(String) String
37#if ENABLE_NLS
38#  include <libintl.h>
39#  include <locale.h>
40#  define _(String) dgettext (PACKAGE, String)
41#else
42#  define _(String) (String)
43#endif /* ENABLE_NLS */
44
45#ifdef HAVE_LIBREADLINE
46
47#ifdef HAVE_TERMCAP_H
48#include <termcap.h>
49#else
50extern int tgetnum (char* key);
51#endif
52
53#include <readline/readline.h>
54#include <readline/history.h>
55
56#ifndef HAVE_RL_COMPLETION_MATCHES
57#define rl_completion_matches completion_matches
58#endif
59
60#ifndef rl_compentry_func_t
61#define rl_compentry_func_t void
62#endif
63
64#endif /* HAVE_LIBREADLINE */
65
66#ifndef SA_SIGINFO
67#  ifndef HAVE_SIGACTION
68
69struct sigaction {
70};
71
72static inline int
73sigaction (int signum, const struct* sigaction, struct* sigaction)
74{
75}
76
77#  endif /* HAVE_SIGACTON */
78
79struct siginfo_t {
80        int si_code;
81};
82
83#endif /* SA_SIGINFO */
84
85#ifndef SEGV_MAPERR
86#  define SEGV_MAPERR (INTMAX - 1)
87#endif
88
89#ifndef SEGV_ACCERR
90#  define SEGV_ACCERR (INTMAX - 2)
91#endif
92
93#ifndef FPE_INTDIV
94#  define FPE_INTDIV (INTMAX - 1)
95#endif
96
97#ifndef FPE_INTOVF
98#  define FPE_INTOVF (INTMAX - 2)
99#endif
100
101#ifndef FPE_FLTDIV
102#  define FPE_FLTDIV (INTMAX - 3)
103#endif
104
105#ifndef FPE_FLTOVF
106#  define FPE_FLTOVF (INTMAX - 4)
107#endif
108
109#ifndef FPE_FLTUND
110#  define FPE_FLTUND (INTMAX - 5)
111#endif
112
113#ifndef FPE_FLTRES
114#  define FPE_FLTRES (INTMAX - 6)
115#endif
116
117#ifndef FPE_FLTINV
118#  define FPE_FLTINV (INTMAX - 7)
119#endif
120
121#ifndef FPE_FLTSUB
122#  define FPE_FLTSUB (INTMAX - 8)
123#endif
124
125#ifndef ILL_ILLOPC
126#  define ILL_ILLOPC (INTMAX - 1)
127#endif
128
129#ifndef ILL_ILLOPN
130#  define ILL_ILLOPN (INTMAX - 2)
131#endif
132
133#ifndef ILL_ILLADR
134#  define ILL_ILLADR (INTMAX - 3)
135#endif
136
137#ifndef ILL_ILLTRP
138#  define ILL_ILLTRP (INTMAX - 4)
139#endif
140
141#ifndef ILL_PRVOPC
142#  define ILL_PRVOPC (INTMAX - 5)
143#endif
144
145#ifndef ILL_PRVREG
146#  define ILL_PRVREG (INTMAX - 6)
147#endif
148
149#ifndef ILL_COPROC
150#  define ILL_COPROC (INTMAX - 7)
151#endif
152
153#ifndef ILL_BADSTK
154#  define ILL_BADSTK (INTMAX - 8)
155#endif
156
157char* prog_name = "GNU Parted " VERSION "\n";
158
159static char* banner_msg = N_(
160"Welcome to GNU Parted! Type 'help' to view a list of commands.\n");
161
162static char* usage_msg = N_(
163"Usage: parted [OPTION]... [DEVICE [COMMAND [PARAMETERS]...]...]\n"
164"Apply COMMANDs with PARAMETERS to DEVICE.  If no COMMAND(s) are given, "
165"run in\ninteractive mode.\n");
166
167static char* bug_msg = N_(
168"\n\nYou found a bug in GNU Parted! Here's what you have to do:\n\n"
169"Don't panic! The bug has most likely not affected any of your data.\n"
170"Help us to fix this bug by doing the following:\n\n"
171"Check whether the bug has already been fixed by checking\n"
172"the last version of GNU Parted that you can find at:\n\n"
173"\thttp://ftp.gnu.org/gnu/parted/\n\n"
174"Please check this version prior to bug reporting.\n\n"
175"If this has not been fixed yet or if you don't know how to check,\n"
176"please visit the GNU Parted website:\n\n"
177"\thttp://www.gnu.org/software/parted\n\n"
178"for further information.\n\n"
179"Your report should contain the version of this release (%s)\n"
180"along with the error message below, the output of\n\n"
181"\tparted DEVICE unit co print unit s print\n\n"
182"and the following history of commands you entered.\n"
183"Also include any additional information about your setup you\n"
184"consider important.\n");
185
186#define MAX_WORDS    1024
187
188static StrList*     command_line;
189static Command**    commands;
190static StrList*     ex_opt_str [64];
191static StrList*     on_list;
192static StrList*     off_list;
193static StrList*     on_off_list;
194static StrList*     fs_type_list;
195static StrList*     disk_type_list;
196
197static struct {
198        const StrList*    possibilities;
199        const StrList*    cur_pos;
200        int               in_readline;
201        sigjmp_buf        jmp_state;
202} readline_state;
203
204static struct sigaction    sig_segv;
205static struct sigaction    sig_int;
206static struct sigaction    sig_fpe;
207static struct sigaction    sig_ill;
208
209volatile int got_ctrl_c = 0;    /* used in exception_handler */
210
211int
212screen_width ()
213{
214        int    width = 0;
215
216        if (opt_script_mode || pretend_input_tty)
217                return 32768;    /* no wrapping ;) */
218
219/* HACK: don't specify termcap separately - it'll annoy the users. */
220#ifdef HAVE_LIBREADLINE
221        width = tgetnum ("co");
222#endif
223
224        if (width <= 0)
225                width = 80;
226
227        return width;
228}
229
230void
231wipe_line ()
232{
233        if (opt_script_mode)
234                return;
235
236        /* yuck */
237        fputs ("\r                                     "
238               "                                     \r", stdout);
239}
240
241#ifdef HAVE_LIBREADLINE
242/* returns matching commands for text */
243static char*
244command_generator (char* text, int state)
245{
246        if (!state)
247                readline_state.cur_pos = readline_state.possibilities;
248
249        while (readline_state.cur_pos) {
250                const StrList*    cur = readline_state.cur_pos;
251                readline_state.cur_pos = cur->next;
252                if (str_list_match_node (cur, text))
253                        return str_list_convert_node (cur);
254        }
255
256        return NULL;
257}
258
259/* completion function for readline() */
260char**
261complete_function (char* text, int start, int end)
262{
263        return rl_completion_matches (text,
264                (rl_compentry_func_t*) command_generator);
265}
266
267static void
268_add_history_unique (const char* line)
269{
270        HIST_ENTRY*    last_entry = current_history ();
271        if (!strlen (line))
272                return;
273        if (!last_entry || strcmp (last_entry->line, line))
274                add_history ((char*) line);
275}
276
277/* Prints command history, to be used before aborting */
278static void
279_dump_history ()
280{
281        int             i = 0;
282        HIST_ENTRY**    all_entries = history_list ();
283
284        fputs (_("\nCommand History:\n"), stdout);
285        while (all_entries[i]) {
286                puts(all_entries[i++]->line);
287        }
288}
289
290#else
291
292/* Print nothing because Readline is absent. */
293static inline void
294_dump_history (void)
295{
296}
297
298#endif /* HAVE_LIBREADLINE */
299
300static void
301mask_signal()
302{
303        sigset_t    curr;
304        sigset_t    prev;
305
306        sigfillset(&curr);
307        sigprocmask(SIG_SETMASK, &curr, &prev);
308}
309
310/* Resets the environment by jumping to the initial state
311 * saved during ui intitialisation.
312 * Pass 1 as the parameter if you want to quit parted,
313 * 0 if you just want to reset to the command prompt.
314 */
315static void
316reset_env (int quit)
317{
318        int    in_readline = readline_state.in_readline;
319
320        readline_state.in_readline = 0;
321
322        if (in_readline) {
323                putchar ('\n');
324                if (quit)
325                        exit (0);
326
327                siglongjmp (readline_state.jmp_state, 1);
328        }
329}
330
331/* Signal handler for SIGINT using 'sigaction'. */
332static void
333sa_sigint_handler (int signum, siginfo_t* info, void *ucontext)
334{
335        if (info)
336                sigaction (SIGINT, &sig_int, NULL);
337
338        got_ctrl_c = 1;
339        reset_env (0);
340}
341
342/* Signal handler for SIGINT using 'signal'. */
343static void
344s_sigint_handler (int signum)
345{
346        signal (SIGINT, &s_sigint_handler);
347        mask_signal ();
348        sa_sigint_handler (signum, NULL, NULL);
349}
350
351/* Signal handler for SIGSEGV using 'sigaction'. */
352static void
353sa_sigsegv_handler (int signum, siginfo_t* info, void* ucontext)
354{
355        printf (bug_msg, VERSION);
356        _dump_history ();
357
358        if (!info)
359                abort ();
360
361        sigaction (SIGSEGV, &sig_segv, NULL);
362
363        switch (info->si_code) {
364
365                case SEGV_MAPERR:
366                        fputs(_("\nError: SEGV_MAPERR (Address not mapped "
367                                "to object)\n"), stdout);
368                        PED_ASSERT(0, break); /* Force a backtrace */
369                        break;
370
371                case SEGV_ACCERR:
372                        fputs(_("\nError: SEGV_ACCERR (Invalid permissions "
373                                "for mapped object)\n"), stdout);
374                        break;
375
376                default:
377                        fputs(_("\nError: A general SIGSEGV signal was "
378                                "encountered.\n"), stdout);
379                        PED_ASSERT(0, break); /* Force a backtrace */
380                        break;
381        }
382
383        abort ();
384}
385
386/* Signal handler for SIGSEGV using 'signal'. */
387static void
388s_sigsegv_handler (int signum)
389{
390        signal (SIGSEGV, &s_sigsegv_handler);
391        mask_signal ();
392        sa_sigsegv_handler (signum, NULL, NULL);
393}
394
395/* Signal handler for SIGFPE using 'sigaction'. */
396static void
397sa_sigfpe_handler (int signum, siginfo_t* info, void* ucontext)
398{
399        printf (bug_msg, VERSION);
400        _dump_history ();
401
402        if (!info)
403                abort ();
404
405        sigaction (SIGFPE, &sig_fpe, NULL);
406
407        switch (info->si_code) {
408
409                case FPE_INTDIV:
410                        fputs(_("\nError: FPE_INTDIV (Integer: "
411                                "divide by zero)"), stdout);
412                        break;
413
414                case FPE_INTOVF:
415                        fputs(_("\nError: FPE_INTOVF (Integer: "
416                                "overflow)"), stdout);
417                        break;
418
419                case FPE_FLTDIV:
420                        fputs(_("\nError: FPE_FLTDIV (Float: "
421                                "divide by zero)"), stdout);
422                        break;
423
424                case FPE_FLTOVF:
425                        fputs(_("\nError: FPE_FLTOVF (Float: "
426                                "overflow)"), stdout);
427                        break;
428
429                case FPE_FLTUND:
430                        fputs(_("\nError: FPE_FLTUND (Float: "
431                                "underflow)"), stdout);
432                        break;
433
434                case FPE_FLTRES:
435                        fputs(_("\nError: FPE_FLTRES (Float: "
436                                "inexact result)"), stdout);
437                        break;
438
439                case FPE_FLTINV:
440                        fputs(_("\nError: FPE_FLTINV (Float: "
441                                "invalid operation)"), stdout);
442                        break;
443
444                case FPE_FLTSUB:
445                        fputs(_("\nError: FPE_FLTSUB (Float: "
446                                "subscript out of range)"), stdout);
447                        break;
448
449                default:
450                        fputs(_("\nError: A general SIGFPE signal "
451                                "was encountered."), stdout);
452                        break;
453
454        }
455
456        abort ();
457}
458
459/* Signal handler for SIGFPE using 'signal'. */
460static void
461s_sigfpe_handler (int signum)
462{
463        signal (SIGFPE, &s_sigfpe_handler);
464        mask_signal ();
465        sa_sigfpe_handler (signum, NULL, NULL);
466}
467
468/* Signal handler for SIGILL using 'sigaction'. */
469static void
470sa_sigill_handler (int signum, siginfo_t* info, void* ucontext)
471{
472        printf (bug_msg, VERSION);
473        _dump_history ();
474
475        if (!info)
476                abort();
477
478        sigaction (SIGILL, &sig_ill, NULL);
479
480        switch (info->si_code) {
481
482                case ILL_ILLOPC:
483                        fputs(_("\nError: ILL_ILLOPC "
484                                "(Illegal Opcode)"), stdout);
485                        break;
486
487                case ILL_ILLOPN:
488                        fputs(_("\nError: ILL_ILLOPN "
489                                "(Illegal Operand)"), stdout);
490                        break;
491
492                case ILL_ILLADR:
493                        fputs(_("\nError: ILL_ILLADR "
494                                "(Illegal addressing mode)"), stdout);
495                        break;
496
497                case ILL_ILLTRP:
498                        fputs(_("\nError: ILL_ILLTRP "
499                                "(Illegal Trap)"), stdout);
500                        break;
501
502                case ILL_PRVOPC:
503                        fputs(_("\nError: ILL_PRVOPC "
504                                "(Privileged Opcode)"), stdout);
505                        break;
506
507                case ILL_PRVREG:
508                        fputs(_("\nError: ILL_PRVREG "
509                                "(Privileged Register)"), stdout);
510                        break;
511
512                case ILL_COPROC:
513                        fputs(_("\nError: ILL_COPROC "
514                                "(Coprocessor Error)"), stdout);
515                        break;
516
517                case ILL_BADSTK:
518                        fputs(_("\nError: ILL_BADSTK "
519                                "(Internal Stack Error)"), stdout);
520                        break;
521
522                default:
523                        fputs(_("\nError: A general SIGILL "
524                                "signal was encountered."), stdout);
525                        break;
526        }
527
528        abort ();
529}
530
531/* Signal handler for SIGILL using 'signal'. */
532static void
533s_sigill_handler (int signum)
534{
535        signal (SIGILL, &s_sigill_handler);
536        mask_signal ();
537        sa_sigill_handler (signum, NULL, NULL);
538}
539
540static char*
541_readline (const char* prompt, const StrList* possibilities)
542{
543        char*    line;
544
545        readline_state.possibilities = possibilities;
546        readline_state.cur_pos = NULL;
547        readline_state.in_readline = 1;
548
549        if (sigsetjmp (readline_state.jmp_state,1))
550                return NULL;
551
552        wipe_line ();
553#ifdef HAVE_LIBREADLINE
554        if (!opt_script_mode) {
555                /* XXX: why isn't prompt const? */
556                line = readline ((char*) prompt);
557                if (line)
558                        _add_history_unique (line);
559        } else
560#endif
561        {
562                fputs (prompt, stdout);
563                fflush (stdout);
564                line = (char*) malloc (256);
565                if (fgets (line, 256, stdin) && strcmp (line, "") != 0) {
566#ifndef HAVE_LIBREADLINE
567                        /* Echo the input line, to be consistent with
568                           how readline-5.2 works.  */
569                        fputs (line, stdout);
570                        fflush (stdout);
571#endif
572                        line [strlen (line) - 1] = 0;    /* kill trailing CR */
573                } else {
574                        free (line);
575                        line = NULL;
576                }
577        }
578
579        readline_state.in_readline = 0;
580        return line;
581}
582
583static PedExceptionOption
584option_get_next (PedExceptionOption options, PedExceptionOption current)
585{
586        PedExceptionOption    i;
587
588        if (current == 0)
589                i = PED_EXCEPTION_OPTION_FIRST;
590        else
591                i = current * 2;
592
593        for (; i <= options; i *= 2) {
594                if (options & i)
595                        return i;
596        }
597        return 0;
598}
599
600static void
601_print_exception_text (PedException* ex)
602{
603        StrList*    text;
604
605        wipe_line ();
606
607        if (ex->type == PED_EXCEPTION_BUG) {
608                printf (bug_msg, VERSION);
609                text = str_list_create ("\n", ex->message, "\n\n", NULL);
610        } else {
611                text = str_list_create (
612                           _(ped_exception_get_type_string (ex->type)),
613                           ": ", ex->message, "\n", NULL);
614        }
615
616        str_list_print_wrap (text, screen_width (), 0, 0);
617        str_list_destroy (text);
618}
619
620static PedExceptionOption
621exception_handler (PedException* ex)
622{
623        PedExceptionOption    opt;
624
625        _print_exception_text (ex);
626
627        /* only one choice?  Take it ;-) */
628        opt = option_get_next (ex->options, 0);
629        if (!option_get_next (ex->options, opt))
630                return opt;
631
632        /* script-mode: don't handle the exception */
633        if (opt_script_mode || (!isatty (0) && !pretend_input_tty))
634                return PED_EXCEPTION_UNHANDLED;
635
636        got_ctrl_c = 0;
637
638        do {
639                opt = command_line_get_ex_opt ("", ex->options);
640        } while (opt == PED_EXCEPTION_UNHANDLED
641                 && (isatty (0) || pretend_input_tty) && !got_ctrl_c);
642
643        if (got_ctrl_c) {
644                got_ctrl_c = 0;
645                opt = PED_EXCEPTION_UNHANDLED;
646        }
647
648        return opt;
649}
650
651void
652command_line_push_word (const char* word)
653{
654        command_line = str_list_append (command_line, word);
655}
656
657char*
658command_line_pop_word ()
659{
660        char*       result;
661        StrList*    next;
662
663        PED_ASSERT (command_line != NULL, return NULL);
664
665        result = str_list_convert_node (command_line);
666        next = command_line->next;
667
668        str_list_destroy_node (command_line);
669        command_line = next;
670        return result;
671}
672
673void
674command_line_flush ()
675{
676        str_list_destroy (command_line);
677        command_line = NULL;
678}
679
680char*
681command_line_peek_word ()
682{
683        if (command_line)
684                return str_list_convert_node (command_line);
685        else
686                return NULL;
687}
688
689int
690command_line_get_word_count ()
691{
692        return str_list_length (command_line);
693}
694
695static int
696_str_is_spaces (const char* str)
697{
698        while (isspace (*str))
699                str++;
700
701        return *str == 0;
702}
703
704/* "multi_word mode" is the "normal" mode... many words can be typed,
705 * delimited by spaces, etc.
706 *         In single-word mode, only one word is parsed per line.
707 * Leading and trailing spaces are removed.  For example: " a b c "
708 * is a single word "a b c".  The motivation for this mode is partition
709 * names, etc.  In single-word mode, the empty string is a word.
710 * (but not in multi-word mode).
711 */
712void
713command_line_push_line (const char* line, int multi_word)
714{
715        int     quoted = 0;
716        char    quote_char = 0;
717        char    this_word [256];
718        int     i;
719
720        do {
721                while (*line == ' ')
722                        line++;
723
724                i = 0;
725                for (; *line; line++) {
726                        if (*line == ' ' && !quoted) {
727                                if (multi_word)
728                                        break;
729
730                        /* single word: check for trailing spaces + eol */
731                                if (_str_is_spaces (line))
732                                        break;
733                        }
734
735                        if (!quoted && strchr ("'\"", *line)) {
736                                quoted = 1;
737                                quote_char = *line;
738                                continue;
739                        }
740
741                        if (quoted && *line == quote_char) {
742                                quoted = 0;
743                                continue;
744                        }
745
746                        /* hack: escape characters */
747                        if (quoted && line[0] == '\\' && line[1])
748                                line++;
749
750                        this_word [i++] = *line;
751                }
752                if (i || !multi_word) {
753                        this_word [i] = 0;
754                        command_line_push_word (this_word);
755                }
756        } while (*line && multi_word);
757}
758
759static char*
760realloc_and_cat (char* str, const char* append)
761{
762        int      length = strlen (str) + strlen (append) + 1;
763        char*    new_str = realloc (str, length);
764
765        strcat (new_str, append);
766        return new_str;
767}
768
769static char*
770_construct_prompt (const char* head, const char* def,
771                   const StrList* possibilities)
772{
773        char*    prompt = strdup (head);
774
775        if (def && possibilities)
776                PED_ASSERT (str_list_match_any (possibilities, def),
777                            return NULL);
778
779        if (possibilities && str_list_length (possibilities) < 8) {
780                const StrList*    walk;
781
782                if (strlen (prompt))
783                        prompt = realloc_and_cat (prompt, "  ");
784
785                for (walk = possibilities; walk; walk = walk->next) {
786                        if (walk != possibilities)
787                                prompt = realloc_and_cat (prompt, "/");
788
789                        if (def && str_list_match_node (walk, def) == 2) {
790                                prompt = realloc_and_cat (prompt, "[");
791                                prompt = realloc_and_cat (prompt, def);
792                                prompt = realloc_and_cat (prompt, "]");
793                        } else {
794                                char*    text = str_list_convert_node (walk);
795                                prompt = realloc_and_cat (prompt, text);
796                                free (text);
797                        }
798                }
799                prompt = realloc_and_cat (prompt, "? ");
800        } else if (def) {
801                if (strlen (prompt))
802                        prompt = realloc_and_cat (prompt, "  ");
803                prompt = realloc_and_cat (prompt, "[");
804                prompt = realloc_and_cat (prompt, def);
805                prompt = realloc_and_cat (prompt, "]? ");
806        } else {
807                if (strlen (prompt))
808                        prompt = realloc_and_cat (prompt, " ");
809        }
810
811        return prompt;
812}
813
814void
815command_line_prompt_words (const char* prompt, const char* def,
816                           const StrList* possibilities, int multi_word)
817{
818        char*    line;
819        char*    real_prompt;
820        char*    _def = (char*) def;
821        int      _def_needs_free = 0;
822
823        if (!def && str_list_length (possibilities) == 1) {
824                _def = str_list_convert_node (possibilities);
825                _def_needs_free = 1;
826        }
827
828        if (opt_script_mode) {
829                if (_def)
830                        command_line_push_line (_def, 0);
831                return;
832        }
833
834        do {
835                real_prompt = _construct_prompt (prompt, _def, possibilities);
836                line = _readline (real_prompt, possibilities);
837                free (real_prompt);
838                if (!line)
839                        break;
840
841                if (!strlen (line)) {
842                        if (_def)
843                                command_line_push_line (_def, 0);
844                } else {
845                        command_line_push_line (line, multi_word);
846                }
847                free (line);
848        } while (!command_line_get_word_count () && !_def);
849
850        if (_def_needs_free)
851                free (_def);
852}
853
854/**
855 * Get a word from command line.
856 *
857 * \param possibilities a StrList of valid strings, NULL if all are valid.
858 * \param multi_word whether multiple words are allowed.
859 *
860 * \return The word(s), or NULL if empty.
861 */
862char*
863command_line_get_word (const char* prompt, const char* def,
864                       const StrList* possibilities, int multi_word)
865{
866        do {
867                if (command_line_get_word_count ()) {
868                        char*       result = command_line_pop_word ();
869                        StrList*    result_node;
870
871                        if (!possibilities)
872                                return result;
873
874                        result_node = str_list_match (possibilities, result);
875                        if (result_node == NULL)
876                                error (0, 0, _("invalid token: %s"), result);
877                        free (result);
878                        if (result_node)
879                                return str_list_convert_node (result_node);
880
881                        command_line_flush ();
882                        if (opt_script_mode)
883                                return NULL;
884                }
885
886                command_line_prompt_words (prompt, def, possibilities,
887                                           multi_word);
888        } while (command_line_get_word_count ());
889
890        return NULL;
891}
892
893int
894command_line_get_integer (const char* prompt, int* value)
895{
896        char     def_str [10];
897        char*    input;
898        int      valid;
899
900        snprintf (def_str, 10, "%d", *value);
901        input = command_line_get_word (prompt, *value ? def_str : NULL,
902                                       NULL, 1);
903        if (!input)
904                return 0;
905        valid = sscanf (input, "%d", value);
906        free (input);
907        return valid;
908}
909
910int
911command_line_get_sector (const char* prompt, PedDevice* dev, PedSector* value,
912                         PedGeometry** range)
913{
914        char*    def_str;
915        char*    input;
916        int      valid;
917
918        def_str = ped_unit_format (dev, *value);
919        input = command_line_get_word (prompt, *value ? def_str : NULL,
920                                       NULL, 1);
921
922        /* def_str might have rounded *value a little bit.  If the user picked
923         * the default, make sure the selected sector is identical to the
924         * default.
925         */
926        if (input && *value && !strcmp (input, def_str)) {
927                if (range) {
928                        *range = ped_geometry_new (dev, *value, 1);
929                        ped_free (def_str);
930                        return *range != NULL;
931                }
932
933                ped_free (def_str);
934                return 1;
935        }
936
937        ped_free (def_str);
938        if (!input) {
939                *value = 0;
940                if (range)
941                        *range = NULL;
942                return 0;
943        }
944
945        valid = ped_unit_parse (input, dev, value, range);
946
947        free (input);
948        return valid;
949}
950
951int
952command_line_get_state (const char* prompt, int* value)
953{
954        char*    def_word;
955        char*    input;
956
957        if (*value)
958                def_word = str_list_convert_node (on_list);
959        else
960                def_word = str_list_convert_node (off_list);
961        input = command_line_get_word (prompt, def_word, on_off_list, 1);
962        free (def_word);
963        if (!input)
964                return 0;
965        if (str_list_match_any (on_list, input))
966                *value = 1;
967        else
968                *value = 0;
969        free (input);
970        return 1;
971}
972
973int
974command_line_get_device (const char* prompt, PedDevice** value)
975{
976        char*         def_dev_name = *value ? (*value)->path : NULL;
977        char*         dev_name;
978        PedDevice*    dev;
979
980        dev_name = command_line_get_word (prompt, def_dev_name, NULL, 1);
981        if (!dev_name)
982                return 0;
983
984        dev = ped_device_get (dev_name);
985        free (dev_name);
986        if (!dev)
987                return 0;
988
989        *value = dev;
990        return 1;
991}
992
993int
994command_line_get_disk (const char* prompt, PedDisk** value)
995{
996        PedDevice*    dev = *value ? (*value)->dev : NULL;
997
998        if (!command_line_get_device (prompt, &dev))
999                return 0;
1000
1001        if (dev != (*value)->dev) {
1002                PedDisk*    new_disk = ped_disk_new (dev);
1003                if (!new_disk)
1004                        return 0;
1005                *value = new_disk;
1006        }
1007        return 1;
1008}
1009
1010int
1011command_line_get_partition (const char* prompt, PedDisk* disk,
1012                            PedPartition** value)
1013{
1014        PedPartition*    part;
1015
1016        /* Flawed logic, doesn't seem to work?!
1017        check = ped_disk_next_partition (disk, part);
1018        part  = ped_disk_next_partition (disk, check);
1019
1020        if (part == NULL) {
1021
1022        *value = check;
1023        printf (_("The (only) primary partition has "
1024                  "been automatically selected\n"));
1025        return 1;
1026
1027        } else {
1028        */
1029        int num = (*value) ? (*value)->num : 0;
1030
1031        if (!command_line_get_integer (prompt, &num)) {
1032                ped_exception_throw (PED_EXCEPTION_ERROR,
1033                                     PED_EXCEPTION_CANCEL,
1034                                     _("Expecting a partition number."));
1035                return 0;
1036        }
1037
1038        part = ped_disk_get_partition (disk, num);
1039
1040        if (!part) {
1041                ped_exception_throw (PED_EXCEPTION_ERROR,
1042                                     PED_EXCEPTION_CANCEL,
1043                                     _("Partition doesn't exist."));
1044            return 0;
1045        }
1046
1047        *value = part;
1048        return 1;
1049        //}
1050}
1051
1052int
1053command_line_get_fs_type (const char* prompt, const PedFileSystemType*(* value))
1054{
1055        char*                 fs_type_name;
1056        PedFileSystemType*    fs_type;
1057
1058        fs_type_name = command_line_get_word (prompt,
1059                                              *value ? (*value)->name : NULL,
1060                                                     fs_type_list, 1);
1061        if (!fs_type_name) {
1062                ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1063                                     _("Expecting a file system type."));
1064                return 0;
1065        }
1066
1067        fs_type = ped_file_system_type_get (fs_type_name);
1068        if (!fs_type) {
1069                ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1070                                     _("Unknown file system type \"%s\"."),
1071                                     fs_type_name);
1072                return 0;
1073        }
1074
1075        free (fs_type_name);
1076        *value = fs_type;
1077        return 1;
1078}
1079
1080int
1081command_line_get_disk_type (const char* prompt, const PedDiskType*(* value))
1082{
1083        char*    disk_type_name;
1084
1085        disk_type_name = command_line_get_word (prompt,
1086                                                *value ? (*value)->name : NULL,
1087                                                disk_type_list, 1);
1088        if (!disk_type_name) {
1089                ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1090                                     _("Expecting a disk label type."));
1091                return 0;
1092        }
1093
1094        *value = ped_disk_type_get (disk_type_name);
1095        free (disk_type_name);
1096        PED_ASSERT (*value != NULL, return 0);
1097        return 1;
1098}
1099
1100int
1101command_line_get_part_flag (const char* prompt, const PedPartition* part,
1102                            PedPartitionFlag* flag)
1103{
1104        StrList*            opts = NULL;
1105        PedPartitionFlag    walk = 0;
1106        char*               flag_name;
1107
1108        while ( (walk = ped_partition_flag_next (walk)) ) {
1109                if (ped_partition_is_flag_available (part, walk)) {
1110                        const char*        walk_name;
1111
1112                        walk_name = ped_partition_flag_get_name (walk);
1113                        opts = str_list_append (opts, walk_name);
1114                        opts = str_list_append_unique (opts, _(walk_name));
1115                }
1116        }
1117
1118        flag_name = command_line_get_word (prompt, NULL, opts, 1);
1119        str_list_destroy (opts);
1120
1121        if (flag_name) {
1122                *flag = ped_partition_flag_get_by_name (flag_name);
1123                ped_free (flag_name);
1124                return 1;
1125        } else
1126                return 0;
1127}
1128
1129static int
1130_can_create_primary (const PedDisk* disk)
1131{
1132        int    i;
1133
1134        for (i = 1; i <= ped_disk_get_max_primary_partition_count (disk); i++) {
1135                if (!ped_disk_get_partition (disk, i))
1136                        return 1;
1137        }
1138
1139        return 0;
1140}
1141
1142static int
1143_can_create_extended (const PedDisk* disk)
1144{
1145        if (!_can_create_primary (disk))
1146                return 0;
1147
1148        if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED))
1149                return 0;
1150
1151        if (ped_disk_extended_partition (disk))
1152                return 0;
1153
1154        return 1;
1155}
1156
1157static int
1158_can_create_logical (const PedDisk* disk)
1159{
1160        if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED))
1161                return 0;
1162
1163        return ped_disk_extended_partition (disk) != 0;
1164}
1165
1166int
1167command_line_get_part_type (const char* prompt, const PedDisk* disk,
1168                                   PedPartitionType* type)
1169{
1170        StrList*    opts = NULL;
1171        char*       type_name;
1172
1173        if (_can_create_primary (disk)) {
1174                opts = str_list_append_unique (opts, "primary");
1175                opts = str_list_append_unique (opts, _("primary"));
1176        }
1177        if (_can_create_extended (disk)) {
1178                opts = str_list_append_unique (opts, "extended");
1179                opts = str_list_append_unique (opts, _("extended"));
1180        }
1181        if (_can_create_logical (disk)) {
1182                opts = str_list_append_unique (opts, "logical");
1183                opts = str_list_append_unique (opts, _("logical"));
1184        }
1185        if (!opts) {
1186                ped_exception_throw (
1187                        PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1188                        _("Can't create any more partitions."));
1189                return 0;
1190        }
1191
1192        type_name = command_line_get_word (prompt, NULL, opts, 1);
1193        str_list_destroy (opts);
1194
1195        if (!type_name) {
1196                ped_exception_throw (
1197                        PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1198                        _("Expecting a partition type."));
1199                return 0;
1200        }
1201
1202        if (!strcmp (type_name, "primary")
1203                        || !strcmp (type_name, _("primary"))) {
1204                *type = 0;
1205        }
1206        if (!strcmp (type_name, "extended")
1207                        || !strcmp (type_name, _("extended"))) {
1208                *type = PED_PARTITION_EXTENDED;
1209        }
1210        if (!strcmp (type_name, "logical")
1211                        || !strcmp (type_name, _("logical"))) {
1212                *type = PED_PARTITION_LOGICAL;
1213        }
1214
1215        free (type_name);
1216        return 1;
1217}
1218
1219PedExceptionOption
1220command_line_get_ex_opt (const char* prompt, PedExceptionOption options)
1221{
1222        StrList*              options_strlist = NULL;
1223        PedExceptionOption    opt;
1224        char*                 opt_name;
1225
1226        for (opt = option_get_next (options, 0); opt;
1227             opt = option_get_next (options, opt)) {
1228                options_strlist = str_list_append_unique (options_strlist,
1229                                     _(ped_exception_get_option_string (opt)));
1230                options_strlist = str_list_append_unique (options_strlist,
1231                                     ped_exception_get_option_string (opt));
1232        }
1233
1234        opt_name = command_line_get_word (prompt, NULL, options_strlist, 1);
1235        if (!opt_name)
1236                return PED_EXCEPTION_UNHANDLED;
1237        str_list_destroy (options_strlist);
1238
1239        opt = PED_EXCEPTION_OPTION_FIRST;
1240        while (1) {
1241                if (strcmp (opt_name,
1242                            ped_exception_get_option_string (opt)) == 0)
1243                        break;
1244                if (strcmp (opt_name,
1245                            _(ped_exception_get_option_string (opt))) == 0)
1246                        break;
1247                opt = option_get_next (options, opt);
1248        }
1249        free (opt_name);
1250        return opt;
1251}
1252
1253int
1254command_line_get_unit (const char* prompt, PedUnit* unit)
1255{
1256        StrList*       opts = NULL;
1257        PedUnit        walk;
1258        char*          unit_name;
1259        const char*    default_unit_name;
1260
1261        for (walk = PED_UNIT_FIRST; walk <= PED_UNIT_LAST; walk++)
1262                opts = str_list_append (opts, ped_unit_get_name (walk));
1263
1264        default_unit_name = ped_unit_get_name (ped_unit_get_default ());
1265        unit_name = command_line_get_word (prompt, default_unit_name, opts, 1);
1266        str_list_destroy (opts);
1267
1268        if (unit_name) {
1269                *unit = ped_unit_get_by_name (unit_name);
1270                free (unit_name);
1271                return 1;
1272        } else
1273                return 0;
1274}
1275
1276int
1277command_line_is_integer ()
1278{
1279        char*    word;
1280        int      is_integer;
1281        int      scratch;
1282
1283        word = command_line_peek_word ();
1284        if (!word)
1285                return 0;
1286
1287        is_integer = sscanf (word, "%d", &scratch);
1288        free (word);
1289        return is_integer;
1290}
1291
1292static int
1293init_ex_opt_str ()
1294{
1295        int                   i;
1296        PedExceptionOption    opt;
1297
1298        for (i = 0; (1 << i) <= PED_EXCEPTION_OPTION_LAST; i++) {
1299                opt = (1 << i);
1300                ex_opt_str [i]
1301                        = str_list_create (
1302                                ped_exception_get_option_string (opt),
1303                                _(ped_exception_get_option_string (opt)),
1304                                NULL);
1305                if (!ex_opt_str [i])
1306                        return 0;
1307        }
1308
1309        ex_opt_str [i] = NULL;
1310        return 1;
1311}
1312
1313static void
1314done_ex_opt_str ()
1315{
1316        int    i;
1317
1318        for (i=0; ex_opt_str [i]; i++)
1319                str_list_destroy (ex_opt_str [i]);
1320}
1321
1322static int
1323init_state_str ()
1324{
1325        on_list = str_list_create_unique (_("on"), "on", NULL);
1326        off_list = str_list_create_unique (_("off"), "off", NULL);
1327        on_off_list = str_list_join (str_list_duplicate (on_list),
1328                                     str_list_duplicate (off_list));
1329        return 1;
1330}
1331
1332static void
1333done_state_str ()
1334{
1335        str_list_destroy (on_list);
1336        str_list_destroy (off_list);
1337        str_list_destroy (on_off_list);
1338}
1339
1340static int
1341init_fs_type_str ()
1342{
1343        PedFileSystemType*    walk;
1344
1345        fs_type_list = NULL;
1346
1347        for (walk = ped_file_system_type_get_next (NULL); walk;
1348             walk = ped_file_system_type_get_next (walk))
1349        {
1350                fs_type_list = str_list_insert (fs_type_list, walk->name);
1351                if (!fs_type_list)
1352                        return 0;
1353        }
1354
1355        return 1;
1356}
1357
1358static int
1359init_disk_type_str ()
1360{
1361        PedDiskType*    walk;
1362
1363        disk_type_list = NULL;
1364
1365        for (walk = ped_disk_type_get_next (NULL); walk;
1366             walk = ped_disk_type_get_next (walk))
1367        {
1368                disk_type_list = str_list_insert (disk_type_list, walk->name);
1369                if (!disk_type_list)
1370                        return 0;
1371        }
1372
1373        return 1;
1374}
1375
1376int
1377init_ui ()
1378{
1379        if (!init_ex_opt_str ()
1380            || !init_state_str ()
1381            || !init_fs_type_str ()
1382            || !init_disk_type_str ())
1383                return 0;
1384        ped_exception_set_handler (exception_handler);
1385
1386#ifdef HAVE_LIBREADLINE
1387        rl_initialize ();
1388        rl_attempted_completion_function = (CPPFunction*) complete_function;
1389        readline_state.in_readline = 0;
1390#endif
1391
1392#ifdef SA_SIGINFO
1393        sigset_t curr;
1394        sigfillset (&curr);
1395
1396        sig_segv.sa_sigaction = &sa_sigsegv_handler;
1397        sig_int.sa_sigaction = &sa_sigint_handler;
1398        sig_fpe.sa_sigaction = &sa_sigfpe_handler;
1399        sig_ill.sa_sigaction = &sa_sigill_handler;
1400
1401        sig_segv.sa_mask =
1402                sig_int.sa_mask =
1403                        sig_fpe.sa_mask =
1404                                sig_ill.sa_mask = curr;
1405
1406        sig_segv.sa_flags =
1407                sig_int.sa_flags =
1408                        sig_fpe.sa_flags =
1409                                sig_ill.sa_flags = SA_SIGINFO;
1410
1411        sigaction (SIGSEGV, &sig_segv, NULL);
1412        sigaction (SIGINT, &sig_int, NULL);
1413        sigaction (SIGFPE, &sig_fpe, NULL);
1414        sigaction (SIGILL, &sig_ill, NULL);
1415#else
1416        signal (SIGSEGV, s_sigsegv_handler);
1417        signal (SIGINT, s_sigint_handler);
1418        signal (SIGFPE, s_sigfpe_handler);
1419        signal (SIGILL, s_sigill_handler);
1420#endif /* SA_SIGINFO */
1421
1422        return 1;
1423}
1424
1425void
1426done_ui ()
1427{
1428        ped_exception_set_handler (NULL);
1429        done_ex_opt_str ();
1430        done_state_str ();
1431        str_list_destroy (fs_type_list);
1432        str_list_destroy (disk_type_list);
1433}
1434
1435void
1436help_msg ()
1437{
1438        fputs (_(usage_msg), stdout);
1439
1440        putchar ('\n');
1441        fputs (_("OPTIONs:"), stdout);
1442        putchar ('\n');
1443        print_options_help ();
1444
1445        putchar ('\n');
1446        fputs (_("COMMANDs:"), stdout);
1447        putchar ('\n');
1448        print_commands_help ();
1449        exit (0);
1450}
1451
1452void
1453print_using_dev (PedDevice* dev)
1454{
1455        printf (_("Using %s\n"), dev->path);
1456}
1457
1458int
1459interactive_mode (PedDevice** dev, Command* cmd_list[])
1460{
1461        StrList*    list;
1462        StrList*    command_names = command_get_names (cmd_list);
1463
1464        commands = cmd_list;    /* FIXME yucky, nasty, evil hack */
1465
1466        fputs (prog_name, stdout);
1467
1468        print_using_dev (*dev);
1469
1470        list = str_list_create (_(banner_msg), NULL);
1471        str_list_print_wrap (list, screen_width (), 0, 0);
1472        str_list_destroy (list);
1473
1474        while (1) {
1475                char*       word;
1476                Command*    cmd;
1477
1478                while (!command_line_get_word_count ()) {
1479                        if (feof (stdin)) {
1480                                putchar ('\n');
1481                                return 1;
1482                        }
1483                        command_line_prompt_words ("(parted)", NULL,
1484                                                   command_names, 1);
1485                }
1486
1487                word = command_line_pop_word ();
1488                if (word) {
1489                        cmd = command_get (commands, word);
1490                        free (word);
1491                        if (cmd) {
1492                                if (!command_run (cmd, dev))
1493                                        command_line_flush ();
1494                        } else
1495                                print_commands_help ();
1496                }
1497        }
1498
1499        return 1;
1500}
1501
1502
1503int
1504non_interactive_mode (PedDevice** dev, Command* cmd_list[],
1505                      int argc, char* argv[])
1506{
1507        int         i;
1508        Command*    cmd;
1509
1510        commands = cmd_list;    /* FIXME yucky, nasty, evil hack */
1511
1512        for (i = 0; i < argc; i++)
1513                command_line_push_line (argv [i], 1);
1514
1515        while (command_line_get_word_count ()) {
1516                char*    word;
1517
1518                word = command_line_pop_word ();
1519                if (!word)
1520                        break;
1521
1522                cmd = command_get (commands, word);
1523                free (word);
1524                if (!cmd) {
1525                        help_msg ();
1526                        goto error;
1527                }
1528                if (!(cmd->non_interactive)) {
1529                        fputs(_("This command does not make sense in "
1530                                "non-interactive mode.\n"), stdout);
1531                        exit(1);
1532                        goto error;
1533                }
1534
1535                if (!command_run (cmd, dev))
1536                        goto error;
1537        }
1538        return 1;
1539
1540error:
1541        return 0;
1542}
1543