View of xos/usr/xsh/xsh.c


XOS | Parent Directory | View | Download

/* Interpreteur de commandes pour XOS */
/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
/* Liens :
 * http://www.gnu.org/software/libtool/manual/libc/Implementing-a-Shell.html
 * http://www.opengroup.org/onlinepubs/000095399/utilities/xcu_chap02.html
 * http://www.opengroup.org/onlinepubs/000095399/utilities/sh.html
 * http://www.gnu.org/software/bash/manual/bashref.html
 * http://tldp.org/LDP/abs/html/
*/
 
#include "env.h"
#include "parse.h"
#include "execute_cmd.h"
#include "print_cmd.h"
#include "free_cmd.h"
#include "lex.h"
#include "jobs.h"
#include "getc.h"
#include "util.h"
#include "safe_malloc.h"
#include "error.h"
#include "vars.h"
 
#include <readline/readline.h>
#include <readline/history.h>
#include <getopt.h>
#include <error.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
 
static void __attribute__ ((noreturn)) die_bad_usage()
{
  fprintf(stderr, "Try `%s --help' for more information.\n", program_invocation_name);
  exit(EXIT_FAILURE);
}
 
static void print_help()
{
  printf("Usage: %s [OPTION]... [FILE]\n", program_invocation_name);
  puts("Read and execute commands.");
  putchar('\n');
  puts("  -c          read commands from the string operand");
  puts("  -i          specify that the shell is interactive");
  puts("  -s          read commands from the standard input");
  puts("  -h, --help  display this help and exit");
}
 
static void initialize_readline()
{
  rl_instream = stdin;
  rl_outstream = stderr;
}
 
/* Lit une commande. Si la commande est non nulle, la place dans l'historique.
 */
static int read_command()
{
  int rv;
 
  display_unreported_jobs();
  history_line = NULL;
  primary_prompt = safe_strdup(getenv("PS1"));
  secondary_prompt = safe_strdup(getenv("PS2"));
  rv = parse();
  free(primary_prompt);
  free(secondary_prompt);
  if (history_line) {
    if (*history_line)
      add_history(history_line);
    free(history_line);
  }
  return rv;
}
 
/* Execute la commande courante. */
static void execute_current_command()
{
  struct exec_env_desc_struct exec_env_desc;
  struct string_buffer_struct string_buffer;
 
  if (buffered_input_stream)
    sync_input_stream();
  string_buffer_init(&string_buffer);
  print_list(current_list, &exec_env_desc.line_number, &string_buffer);
  exec_env_desc.command = string_buffer_release(&string_buffer);
  exec_env_desc.subshell = 0;
  exec_env_desc.no_fork = full_str ? 1 : 0;
  exec_env_desc.builtin = 0;
  redirect_list_list_init(&exec_env_desc.redirect_list_list);
  execute_list(current_list, &exec_env_desc);
}
 
int main(int argc, char **argv)
{
  static const struct option longopts[] = {
    {"help", no_argument, NULL, 'h'},
    {NULL, 0, NULL, 0}
  };
 
  struct {
    unsigned string_op : 1;
    unsigned interactive : 1;
    unsigned stdin : 1;
  } options;
  int c;
 
  /* lecture des options de la ligne de commande */
  options.string_op = 0;
  options.interactive = 0;
  options.stdin = 0;
  while ((c = getopt_long(argc, argv, "cish", longopts, NULL)) != -1)
    switch (c) {
    case 'c':
      options.string_op = 1;
      break;
    case 'i':
      options.interactive = 1;
      break;
    case 's':
      options.stdin = 1;
      break;
    case 'h':
      print_help();
      return EXIT_SUCCESS;
    case '?':
      die_bad_usage();
    }
  if (optind < argc && !strcmp(argv[optind], "-"))
    optind++;
 
  if (options.string_op && optind == argc)
    error(EXIT_FAILURE, 0, "string expected after -c");
 
  /* initialisation */
  interactive = options.interactive || (!options.string_op && (options.stdin || optind == argc) && isatty(fileno(stdin)) && isatty(fileno(stderr)));
  script_name = !options.string_op && !options.stdin && optind < argc ? argv[optind] : NULL;
  line_number = 1;
  print_line_number_on_error = !interactive || script_name;
  current_builtin = NULL;
  last_command_status = EXIT_SUCCESS;
 
  pwd = get_working_directory();
  oldpwd = NULL;
 
  /* On force l'utilisation d'un tampon de ligne pour les sorties pour eviter
     que le type de tampon ne soit determine lors de l'appel d'une commande
     interne avec redirections. */
  setlinebuf(stdout);
  setlinebuf(stderr);
 
  /* selection de la source de lecture */
  buffered_input_stream = 0;
  if (options.string_op) {
    command_str = argv[optind];
    lexer_getc_func = string_getc;
  }
  else if (interactive && !script_name) {
    initialize_readline();
    lexer_getc_func = readline_getc;
  }
  else {
    if (script_name) {
      if ((input_stream_fd = open(script_name, O_RDONLY)) == -1)
        error(EXIT_FAILURE, errno, script_name);
    }
    else
      input_stream_fd = fileno(stdin);
    lexer_getc_func = stream_getc;
    buffered_input_stream = 1;
  }
 
  init_job_control();
 
  /* lecture et execution des commandes */
  if (interactive) {
    /* environnement */
    setup_environment();
 
    fputs("Welcome to xsh, the XOS shell :-)\n", stderr);
    fflush(stderr);
  }
  done = 0;
  while (!done)
    if (read_command() == 0) {
      if (parser_eof)
        done = 1;
      else if (current_list) {
        execute_current_command();
        free_list(current_list);
      }
    }
    else {
      last_command_status = EXIT_FAILURE;
      if (!interactive)
        done = 1;
    }
 
  end_job_control();
 
  if (script_name)
    close(input_stream_fd);
 
  return last_command_status;
}