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
  50 extern 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 
  69 struct sigaction {
  70 };
  71 
  72 static inline int
  73 sigaction (int signum, const struct* sigaction, struct* sigaction)
  74 {
  75 }
  76 
  77 #  endif /* HAVE_SIGACTON */
  78 
  79 struct 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 
 157 char* prog_name = "GNU Parted " VERSION "\n";
 158 
 159 static char* banner_msg = N_(
 160 "Welcome to GNU Parted! Type 'help' to view a list of commands.\n");
 161 
 162 static 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 
 167 static 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 
 188 static StrList*     command_line;
 189 static Command**    commands;
 190 static StrList*     ex_opt_str [64];
 191 static StrList*     on_list;
 192 static StrList*     off_list;
 193 static StrList*     on_off_list;
 194 static StrList*     fs_type_list;
 195 static StrList*     disk_type_list;
 196 
 197 static struct {
 198         const StrList*    possibilities;
 199         const StrList*    cur_pos;
 200         int               in_readline;
 201         sigjmp_buf        jmp_state;
 202 } readline_state;
 203 
 204 static struct sigaction    sig_segv;
 205 static struct sigaction    sig_int;
 206 static struct sigaction    sig_fpe;
 207 static struct sigaction    sig_ill;
 208 
 209 volatile int got_ctrl_c = 0;    /* used in exception_handler */
 210 
 211 int
 212 screen_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 
 230 void
 231 wipe_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 */
 243 static char*
 244 command_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() */
 260 char**
 261 complete_function (char* text, int start, int end)
 262 {
 263         return rl_completion_matches (text,
 264                 (rl_compentry_func_t*) command_generator);
 265 }
 266 
 267 static 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 */
 278 static 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. */
 293 static inline void
 294 _dump_history (void)
 295 {
 296 }
 297 
 298 #endif /* HAVE_LIBREADLINE */
 299 
 300 static void
 301 mask_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  */
 315 static void
 316 reset_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'. */
 332 static void
 333 sa_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'. */
 343 static void
 344 s_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'. */
 352 static void
 353 sa_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'. */
 387 static void
 388 s_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'. */
 396 static void
 397 sa_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'. */
 460 static void
 461 s_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'. */
 469 static void
 470 sa_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'. */
 532 static void
 533 s_sigill_handler (int signum)
 534 {
 535         signal (SIGILL, &s_sigill_handler);
 536         mask_signal ();
 537         sa_sigill_handler (signum, NULL, NULL);
 538 }
 539 
 540 static 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 
 583 static PedExceptionOption
 584 option_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 
 600 static 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 
 620 static PedExceptionOption
 621 exception_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 
 651 void
 652 command_line_push_word (const char* word)
 653 {
 654         command_line = str_list_append (command_line, word);
 655 }
 656 
 657 char*
 658 command_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 
 673 void
 674 command_line_flush ()
 675 {
 676         str_list_destroy (command_line);
 677         command_line = NULL;
 678 }
 679 
 680 char*
 681 command_line_peek_word ()
 682 {
 683         if (command_line)
 684                 return str_list_convert_node (command_line);
 685         else
 686                 return NULL;
 687 }
 688 
 689 int
 690 command_line_get_word_count ()
 691 {
 692         return str_list_length (command_line);
 693 }
 694 
 695 static 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  */
 712 void
 713 command_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 
 759 static char*
 760 realloc_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 
 769 static 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 
 814 void
 815 command_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  */
 862 char*
 863 command_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 
 893 int
 894 command_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 
 910 int
 911 command_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 
 951 int
 952 command_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 
 973 int
 974 command_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 
 993 int
 994 command_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 
1010 int
1011 command_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 
1052 int
1053 command_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 
1080 int
1081 command_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 
1100 int
1101 command_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 
1129 static 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 
1142 static 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 
1157 static 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 
1166 int
1167 command_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 
1219 PedExceptionOption
1220 command_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 
1253 int
1254 command_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 
1276 int
1277 command_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 
1292 static int
1293 init_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 
1313 static void
1314 done_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 
1322 static int
1323 init_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 
1332 static void
1333 done_state_str ()
1334 {
1335         str_list_destroy (on_list);
1336         str_list_destroy (off_list);
1337         str_list_destroy (on_off_list);
1338 }
1339 
1340 static int
1341 init_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 
1358 static int
1359 init_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 
1376 int
1377 init_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 
1425 void
1426 done_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 
1435 void
1436 help_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 
1452 void
1453 print_using_dev (PedDevice* dev)
1454 {
1455         printf (_("Using %s\n"), dev->path);
1456 }
1457 
1458 int
1459 interactive_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 
1503 int
1504 non_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 
1540 error:
1541         return 0;
1542 }