SiSh

Standard

Description

Simple shell for Linux.


Source

/*
 * "sish.c" (C) Davide Francesco "HdS619" Merico ( hds619@gmail.com )
 *
 * This 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 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
*/

#define _BSD_SOURCE
#define _GNU_SOURCE
#define SISH_IN     5
#define SISH_SIN    3
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <getopt.h>

struct user_info {
       char *username;
       char hostname[64];
       char *cpath;
       char **paths;
} *user_info = NULL;

typedef enum { false, true } bool;

void get_and_do_command (char *);
char **getpaths (char *);
char **depure_command_string (char *, int *);
bool search_program (char **);
bool internal_command (char **);
bool check_internal_command (char *);
bool check_is_env_var (char *);
bool adjust_env_var (char **);
void load_config_file (char *);
char *cmds_join (char **, int, int);
char *remove_facoltative_space (char *);
void logging_command (char **);
int pipe_action (char **, int *);
void sish_exec (char ***);
void redirect_input (char ***, int);
void redirect_output (char ***, int);
void process_or (char ***, int);
void process_and (char ***, int);

static unsigned int quiet = 1;
static struct logging {
              char *filename;
              bool logging;
       } *log;
static struct alias {
              char *command;
              char *alias;
       } *alias;
static char *internal[SISH_IN] = { "quit", "exit",
                                   "sish",
                                   "cd", "alias"
                                 };
static char *s_internal[SISH_SIN] = { "info",
                                      "load_config",
                                      "logging"
                                    };

void sish_err (const char *fmtstr, ...)
{
 va_list list;

 va_start (list, fmtstr);
 vfprintf (stderr, fmtstr, list);
 va_end (list);
}

void SISH_SIG_IGN (int num)
{
 return;
}

void SISH_EXIT (int num)
{
 sish_err ("\nSegnale Ricevuto. Uscita con codice: %d\n", num);
 exit (num);
}

int main (int argc, char *argv[])
{
 int opt, opt_index;
 char *config_file = NULL, *command = NULL;
 bool chk_config = true;
 struct option sish_long_option[] = {
        { "command", required_argument, 0, 'c' },
        { "profile", optional_argument, 0, 'P' },
        { "logging", optional_argument, 0, 'L' },
        { "noprofile", no_argument, 0, 'p' },
        { 0, 0, 0, 0 }
 };


 user_info = (struct user_info *) malloc (sizeof (struct user_info));
 user_info->username = getenv ("USER");
 gethostname (user_info->hostname, 64);
 user_info->cpath = getcwd (NULL, 0);
 user_info->paths = getpaths (getenv ("PATH"));

 alias = (struct alias *) malloc (sizeof (struct alias));
 alias->command = NULL;
 alias->alias = NULL;

 log = (struct logging *) malloc (sizeof (struct logging));
 log->logging = false;
 log->filename = NULL;

 signal (SIGINT, SISH_SIG_IGN);
 signal (SIGPIPE, SISH_SIG_IGN);
 signal (SIGCHLD, SISH_SIG_IGN);
 signal (SIGQUIT, SISH_EXIT);

 for ( opt_index = 0; (opt = getopt_long (argc, argv, "plc:P:",
                             sish_long_option, &opt_index)) != -1; opt_index = 0 ) {
       switch ( opt ) {
                case 'P': if ( optarg )
                               config_file = strdup (optarg);
                          break;
                case 'L': log->logging = true;
                          if ( optarg )
                               log->filename = strdup (optarg);
                          break;
                case 'c': if ( optarg )
                               command = strdup (optarg);
                          break;
                case 'p': chk_config = false;
                          break;
       }
 }

 if ( chk_config )
      load_config_file (config_file);
 if ( config_file )
      free (config_file);

 if ( command ) {
      get_and_do_command (command);
      free (command);
      return 0;
 }

 while ( quiet ) {
         fprintf (stdout, "%s@%s:%s%c ", user_info->username, user_info->hostname,
                          user_info->cpath, geteuid () ? '$' : '#');
         get_and_do_command (NULL);
         user_info->cpath = getcwd (NULL, 0);
 }

 return 0;
}

char **getpaths (char *str)
{
 char *ptr;
 char **paths = NULL;
 int mem = 1;

 paths = (char **) malloc (mem * sizeof (char *));

 if ( !(ptr = strtok (str, ":")) ) {
      paths[mem - 1] = NULL;
      return paths;
 }
 paths[mem - 1] = strdup (ptr);
 paths = (char **) realloc (paths, ++mem * sizeof (char *));

 while ((ptr = strtok (NULL, ":"))) {
        paths[mem - 1] = strdup (ptr);
        paths = (char **) realloc (paths, ++mem * sizeof (char *));
 }
 paths[mem - 1] = NULL;

 return paths;
}

void get_and_do_command (char *o_cmd)
{
 char *cmd = NULL, **commands;
 int mem = 1, i, ncmd, c, status;
 bool chk = false;
 pid_t pid;

 if ( !o_cmd ) {
      cmd = (char *) malloc (mem * sizeof (char));
      while ((cmd[mem - 1] = getchar ())) {
              if ( cmd[mem - 1] == 0x0A && !chk )
                   break;

              if ( cmd[mem - 1] == '"' || cmd[mem - 1] == '\'' )
                   chk = !chk;

              cmd = (char *) realloc (cmd, ++mem * sizeof (char));

              if ( cmd[mem - 2] == 0x0A )
                   sish_err ("> ");
      }
      cmd[mem-1] = 0x00;

      commands = depure_command_string (cmd, &ncmd);
      free (cmd);
 }
 else commands = depure_command_string (o_cmd, &ncmd);

 if ( !*commands || !**commands) {
      free (commands);
      return;
 }

 for ( i = 0; alias[i].command; i++ ) {
       if ( !strcmp (alias[i].command, *commands) ) {
            free (*commands);
            *commands = strdup (alias[i].alias);
            cmd = cmds_join (commands, -1, -1);
            for ( c = 0; commands[c]; c++ )
                  free (commands[c]);
            free (commands);
            commands = depure_command_string (cmd, &ncmd);
            free (cmd);
       }
 }

 if ( !*commands || !**commands) {
      free (commands);
      return;
 }

 if ( internal_command (commands) ) {
      for ( i = 0; commands[i]; i++ )
            free (commands[i]);
      free (commands);
      return;
 }

 if ( !search_program (&*commands) ) {
      sish_err ("sish: %s: comando non trovato\n", *commands);
      for ( i = 0; commands[i]; i++ )
            free (commands[i]);
      free (commands);
      return;
 }

 if ( access (*commands, X_OK) ) {
      sish_err ("sish: %s: file non eseguibile\n", *commands);
      for ( i = 0; commands[i]; i++ )
            free (commands[i]);
      free (commands);
      return;
 }

 if ( (pid = fork ()) < 0 ) {
      sish_err ("sish: impossibile eseguire il programma.\n");
      for ( i = 0; commands[i]; i++ )
            free (commands[i]);
      free (commands);
      return;
 }

 if ( !pid ) {
      sish_exec (&commands);
      exit (0);
 }
 else {
       if ( log->logging )
            logging_command (commands);
       waitpid (pid, &status, 0);
 }

 for ( i = 0; commands[i]; i++ )
       free (commands[i]);
 free (commands);
}

char **depure_command_string (char *_cstring, int *cnum)
{
 char **cmds = NULL, *cstring;
 int mem = 1, len, i = 0;
 bool chk = false;

 cstring = remove_facoltative_space (_cstring);
 cmds = (char **) malloc ((*cnum = 1) * sizeof (char *));
 len = strlen (cstring);

 if ( !*cstring ) {
      *cmds = NULL;
      *cnum = 0;
      return cmds;
 }

 do {
       if ( (cstring[i] == 0x20 || cstring[i] == 0x00 || cstring[i] == 0x0A) && !chk ) {
            if ( check_is_env_var (cmds[*cnum - 1]) ) {
                 if ( !adjust_env_var (&cmds[*cnum - 1]) ) {
                      free (cmds[*cnum - 1]);
                      cmds = (char **) realloc (cmds, --(*cnum) * sizeof (char *));
                 }
            }

            cmds = (char **) realloc (cmds, ++(*cnum) * sizeof (char *));
            mem = 1;

            continue;
       }

       if ( (cstring[i] == '"' || cstring[i] == '\'') ) {
            chk = !chk;
            continue;
       }

       if ( mem == 1 )
            cmds[*cnum - 1] = (char *) malloc (mem * sizeof (char));

       cmds[*cnum - 1][mem - 1] = cstring[i];
       cmds[*cnum - 1] = (char *) realloc (cmds[*cnum - 1], ++mem * sizeof (char));
       cmds[*cnum - 1][mem - 1] = 0x00;
 } while ( i++ < len );

 cmds[*cnum - 1] = NULL;
 (*cnum)--;
 free (cstring);

 return cmds;
}

bool search_program (char **prg)
{
 int i, len = strlen (*prg), plen;
 char *tmp;

 for ( i = 0; user_info->paths[i]; i++ ) {
       plen = 2 + len + strlen (user_info->paths[i]);
       tmp = (char *) malloc (plen * sizeof (char));
       snprintf (tmp, plen, "%s/%s", user_info->paths[i], *prg);

       if ( !access (tmp, F_OK) ) {
            free (*prg);
            *prg = strdup (tmp);
            free (tmp);
            return true;
       }
       free (tmp);
 }

 if ( !access (*prg, F_OK) )
      return true;

 return false;
}

bool check_internal_command (char *cmd)
{
 int i;

 for ( i = 0; i < SISH_IN; i++ )
       if ( !strcmp (cmd, internal[i]) )
            return true;

 return false;
}

bool internal_command (char **cmds)
{
 int i;

 for ( i = 0; i < SISH_IN; i++ ) {
       if ( !strcmp (*cmds, internal[i]) )
            break;

       if ( i == (SISH_IN - 1) ) {
            i = -1;
            break;
       }
 }

 if ( i < 0 )
      return false;

 switch ( i ) {
          case 0:
          case 1:
            sish_err ("exit.\n");
            exit (0);
            break;
          case 2:
            if ( !cmds[1] ) {
                 sish_err ("sish: argomento mancante.\n");
                 break;
            }

            for ( i = 0; i < SISH_SIN; i++ ) {
                  if ( !strcmp (cmds[1], s_internal[i]) )
                       break;

                  if ( i == (SISH_SIN - 1) ) {
                       i = -1;
                       break;
                  }
            }

            if ( i < 0 ) {
                 sish_err ("sish: argomento non valido.\n");
                 break;
            }

            switch ( i ) {
                     case 0:
                       printf ("SiSh ( Simple Shell ) è una piccolissima shell "
                               "creata per puro divertimento e piccolo esercizio "
                               "di HdS619. Può essere usata per piccoli compiti ma "
                               "di certo il suo obiettivo non è quello di diventare "
                               "una shell alternativa ad altre già esistenti.\n"
                               "Divertitevi ;)\n");
                       break;
                     case 1:
                         load_config_file (cmds[2]);
                     break;
                     case 2:
                       if ( !cmds[2] ) {
                            sish_err ("sish: logging: argomento mancante.\n");
                            break;
                       }

                       if ( !strcmp (cmds[2], "on") ) {
                            log->logging = true;
                            if ( cmds[3] )
                                 log->filename = strdup (cmds[3]);
                            else {
                                 if ( log->filename )
                                      free (log->filename);
                                 log->filename = NULL;
                            }
                       }
                       else if ( !strcmp (cmds[2], "off") ) {
                                 log->logging = false;
                                 if ( log->filename )
                                      free (log->filename);
                                 log->filename = NULL;
                       }

                       break;
            }
            break;
          case 3:
            if ( !cmds[1] ) {
                 sish_err ("sish: cd: argomento mancante.\n");
                 break;
            }

            if ( chdir (cmds[1]) )
                 sish_err ("sish: cd: posizione non valida.\n");

            break;
          case 4:
            if ( !cmds[1] ) {
                 sish_err ("sish: alias: argomenti mancanti.\n");
                 break;
            }

            for ( i = 0; alias[i].command; i++ ) {
                  if ( !strcmp (alias[i].command, cmds[1]) ) {
                       free (alias[i].alias);
                       alias[i].alias = strdup (cmds[2] ? cmds[2] : " ");
                       i = -1;
                       break;
                  }
            }
            if ( i < 0 )
                 break;

            alias[i].command = strdup (cmds[1]);
            alias[i].alias = strdup (cmds[2] ? cmds[2] : " ");
            alias = (struct alias *) realloc (alias, (i + 2) * sizeof (struct alias));
            alias[i+1].command = NULL;
            alias[i+1].alias = NULL;

            break;
 }

 return true;
}

bool check_is_env_var (char *str)
{
 int i, len;
 bool chk = false;

 if ( !str )
      return false;

 len = strlen (str);
 for ( i = 0; i < len; i++ ) {
       if ( str[i] == '=' && i > 0 && !chk)
            chk = true;

       if ( !isalnum (str[i]) && i > 0 && !chk && str[i] != '_' )
            return false;
 }

 if ( (!chk && *str != '$') || ( *str == '$' && len < 2 ) )
      return false;

 return true;
}

bool adjust_env_var (char **env_var)
{
 char *evn, *evv;
 int mem = 1, len = strlen (*env_var), i;

 evn = (char *) malloc (mem * sizeof (char));

 if ( **env_var == '$' ) {
      for ( i = 1; i < len && ( isalnum((*env_var)[i]) || (*env_var)[i] == '_' ); i++ ) {
            evn[mem - 1] = (*env_var)[i];
            evn = (char *) realloc (evn, ++mem * sizeof (char));
      }
      evn[mem - 1] = 0x00;
      free (*env_var);
      *env_var = strdup (getenv (evn) ? getenv (evn) : "\0");
      free (evn);

      return **env_var ? true : false;
 }

 for ( i = 0; i < len && (*env_var)[i] != '='; i++ ) {
       evn[mem - 1] = (*env_var)[i];
       evn = (char *) realloc (evn, ++mem * sizeof (char));
 }
 evn[mem - 1] = 0x00;

 evv = (char *) malloc ((mem = 1) * sizeof (char));
 for ( i++; i < len; i++ ) {
       evv[mem - 1] = (*env_var)[i];
       evv = (char *) realloc (evv, ++mem * sizeof (char));
 }
 evv[mem - 1] = 0x00;

 if ( !(*evv) )
      unsetenv (evn);
 else setenv (evn, evv, 1);

 free (evv);
 free (evn);

 return false;
}

void load_config_file (char *file)
{
 unsigned char *data;
 char *filename, *t;
 int len, i, c;
 bool chk = false;
 FILE *of;

 if ( !(t = getenv ("HOME")) ) {
      sish_err ("sish: impossibile caricare il file di configurazione.\n");
      return;
 }

 if ( !file ) {
      len = strlen (t) + 14;
      filename = (char *) malloc (len * sizeof (char));
      snprintf (filename, len, "%s/.sish-config", t);
 }
 else filename = strdup (file);

 if ( access (filename, F_OK | R_OK) ) {
      sish_err ("sish: impossibile caricare il file di configurazione.\n");
      return;
 }

 if ( !(of = fopen (filename, "rb")) ) {
      sish_err ("sish: impossibile caricare il file di configurazione.\n");
      return;
 }
 free (filename);

 fseek (of, 0, SEEK_END);
 data = (unsigned char *) malloc ((len = ftell (of)) * sizeof (unsigned char));
 rewind (of);
 fread (data, sizeof (unsigned char), len, of);
 fclose (of);

 for ( i = c = 0; i < len; i++ ) {
       if ( data[i] == '"' || data[i] == '\'' )
            chk = !chk;

       if ( (data[i] == 0x0A || i == (len - 1)) && !chk ) {
            t = strndup ((char *) data + c, i - c);
            c = i + 1;
            if ( *t != 0x0A )
                 get_and_do_command (t);
            free (t);
       }
 }

 if ( chk )
      sish_err ("sish: file di configurazione caricato non correttamente: "
                "mancata chiusura di \" o '.\n");

 free (data);
}

char *cmds_join (char **cmds, int start, int end)
{
 int i, len = 0, n;
 char *str;

 str = (char *) malloc ((len = strlen (cmds[(start > 0) ? start : 0])) + 1);
 strncpy (str, cmds[(start > 0) ? start : 0], len);
 str[len] = ' ';

 for ( n = (start > 0) ? start + 1 : 1, i = len + 1; cmds[n]; n++, i += len ) {
       if ( end > 0 && !(n < end) )
            break;

       str = (char *) realloc (str, (len = strlen (cmds[n])) + i + 1);
       strncpy (str + i, cmds[n], len);
       str[i + len] = ' ';
       i++;
 }
 str[i - 1] = 0x00;

 return str;
}

char *remove_facoltative_space (char *str)
{
 int i, len = strlen (str ? str : ""), mem = 1;
 bool chk = false, s = false;
 char *l_str;

 l_str = (char *) malloc (mem * sizeof (char));
 for ( i = 0; i < len; i++ ) {
       if ( str[i] == '"' || str[i] == '\'' ) {
            chk = true;
            s = false;
       }

       if ( str[i] == 0x20 && !i )
            s = true;

       if ( str[i] == 0x20 && !s ) {
            l_str[mem - 1] = str[i];
            l_str = (char *) realloc (l_str, ++mem * sizeof (char));
            s = true;
       }

       if ( str[i] != 0x20 ) {
            l_str[mem - 1] = str[i];
            l_str = (char *) realloc (l_str, ++mem * sizeof (char));
            s = false;
       }
 }
 l_str [mem - ((l_str[mem - 2] == 0x20) ? 2 : 1)] = 0x00;

 return l_str;
}

void logging_command (char **commands)
{
 char *t;
 int len;
 FILE *of;

 if ( !log->filename ) {
      t = getenv ("HOME");
      len = strlen (t ? t : "") + 15;
      log->filename = (char *) malloc (len * sizeof (char));
      snprintf (log->filename, len, "%s/.sish-history", t);
 }

 if ( !(of = fopen(log->filename, "a")) ) {
      sish_err ("sish: logging: impossibile aprire il file `%s' di log.\n",
                log->filename);
      return;
 }

 for (len = 0; commands[len]; len++ )
      fprintf (of, "%s ", commands[len]);
 putc (0x0A, of);
 fclose (of);

 return;
}

void sish_exec (char ***commands)
{
 int i, chk = 0;

 i = pipe_action (*commands, &chk);
 switch ( chk ) {
          case 0: execv (**commands, *commands);
                  break;
          case 1: process_or (commands, i);
                  break;
          case 2: process_and (commands, i);
                  break;
          case 3: redirect_output (commands, i);
                  break;
          case 4: redirect_input (commands, i);
                  break;
 }
}

int pipe_action (char **commands, int *chk)
{
 int i;

 *chk = 0;
 for ( i = 0; commands[i]; i++ ) {
       if ( !strcmp (commands[i], "|") )
            *chk = 1;
       if ( !strcmp (commands[i], "&") )
            *chk = 2;
       if ( !strcmp (commands[i], ">") )
            *chk = 3;
       if ( !strcmp (commands[i], "<") )
            *chk = 4;
       if ( *chk )
            break;
 }

 return i;
}

void redirect_input (char ***commands, int _i)
{
 int len, c, i = _i;
 char *t;
 unsigned char *data = NULL;
 FILE *in, *out;

 if ( !(*commands)[i + 1] ) {
        sish_err ("sish: redirect dell'input non valido: "
                  "nessun input specificato.\n");
        return;
 }

 if ( access ((*commands)[i + 1], F_OK) ) {
      sish_err ("sish: il file `%s' passato come input non esiste.\n",
                (*commands)[i + 1]);
      return;
 }

 if ( !(in = fopen ((*commands)[i + 1], "rb")) ) {
        sish_err ("sish: impossibile aprire il file `%s' per la lettura.\n",
                  (*commands)[i + 1]);
        return;
 }
 fseek (in, 0, SEEK_END);
 data = (unsigned char *) malloc ((len = ftell (in)) * sizeof (unsigned char));
 rewind (in);
 fread (data, sizeof (unsigned char), len, in);
 fclose (in);

 free ((*commands)[i]);
 (*commands)[i] = NULL;
 free ((*commands)[i + 1]);
 (*commands)[i + 1] = NULL;
 for ( c = i + 2; (*commands)[c]; c++ ) {
       (*commands)[i++] = strdup ((*commands)[c]);
       free ((*commands)[c]);
       (*commands)[c] = NULL;
 }

 t = cmds_join (*commands, -1, -1);
 if ( !(out = popen (t, "w")) ) {
        sish_err ("sish: impossibile aprire la pipe in scrittura.\n");
        free (t);
        free (data);
        return;
 }
 free (t);

 for ( i = 0; i < len; i++ )
       putc ((int) data[i], out);
 pclose (out);
 free (data);
}

void redirect_output (char ***commands, int _i)
{
 char *t;
 int chk, c, i = _i;
 FILE *in, *out;

 if ( !(*commands)[i + 1] ) {
      sish_err ("sish: redirect dell'output non valido: "
                "nessun output specificato.\n");
      return;
 }

 if ( !access ((*commands)[i + 1], F_OK) ) {
      sish_err ("sish: il file scelto in cui reindirizzare l'output esiste "
                "già . Quale azione eseguire [(A)NNULLA/(S)ovrascrivi] ? ");
      chk = getchar ();
      while (getchar () != 0x0A);
      if ( chk != 'S' && chk != 's' )
           return;
 }

 if ( !(out = fopen ((*commands)[i + 1], "w")) ) {
      sish_err ("sish: impossibile aprire il file `%s' per la scrittura.\n",
                (*commands)[i + 1]);
      return;
 }

 free ((*commands)[i]);
 (*commands)[i] = NULL;
 free ((*commands)[i + 1]);
 (*commands)[i + 1] = NULL;
 for ( c = i + 2; (*commands)[c]; c++ ) {
       (*commands)[i++] = strdup ((*commands)[c]);
       free ((*commands)[c]);
       (*commands)[c] = NULL;
 }

 t = cmds_join (*commands, -1, -1);
 if ( !(in = popen (t, "r")) ) {
      sish_err ("sish: impossibile aprire la pipe in lettura.\n");
      free (t);
      fclose (out);
      return;
 }
 free (t);

 while ((c = getc (in)) != EOF)
         putc (c, out);
 pclose (in);
 fclose (out);
}

void process_or (char ***commands, int _i)
{
 int c, r, s, i = _i;
 char *t, **cmds;
 unsigned char *data;
 FILE *in, *out;

 t = cmds_join (*commands, -1, i);
 if ( !(in = popen (t, "r")) ) {
      sish_err ("sish: impossibile aprire la pipe in lettura.\n");
      free (t);
      return;
 }
 free (t);
 data = (unsigned char *) malloc ((c = 1) * sizeof (unsigned char));
 while ((r = getc (in)) != EOF) {
         data[c - 1] = (unsigned char) r;
         data = (unsigned char *) realloc (data, ++c * sizeof (unsigned char));
 }
 data[c - 1] = 0x00;
 pclose (in);

 if ( !(*commands)[i + 1] ) {
      sish_err ("sish: pipe rotta.\n");
      free (data);
      return;
 }

 for ( r = 0; alias[r].command; r++ ) {
       if ( !strcmp (alias[r].command, (*commands)[i + 1]) ) {
            free ((*commands)[i + 1]);
            (*commands)[i + 1] = strdup (alias[r].alias);
            t = cmds_join (*commands, i + 1, -1);
            for ( s = i + 1; (*commands)[s]; s++ )
                  free ((*commands)[s]);
            cmds = depure_command_string (t, &s);
            free (t);
            (*commands) = (char **) realloc (*commands, (s + i + 1) * sizeof (char *));
            for ( s = 0; cmds[s]; s++ ) {
                  (*commands)[s + i + 1] = strdup (cmds[s]);
                  free (cmds[s]);
            }
            free (cmds);
       }
 }

 if ( check_internal_command ((*commands)[i + 1]) ) {
      sish_err ("sish: impossibile aprire una pipe con un comando interno.\n");
      free (data);
      return;
 }

 if ( !search_program (&((*commands)[i + 1])) ) {
      sish_err ("sish: %s: comando non trovato\n", (*commands)[i + 1]);
      free (data);
      return;
 }

 t = cmds_join ((*commands), i + 1, -1);
 if ( !(out = popen (t, "w")) ) {
      sish_err ("sish: impossibile aprire la pipe in scrittura.\n");
      free (t);
      free (data);
      return;
 }
 free (t);

 for ( i = 0, --c; i < c; i++ )
       putc ((int) data[i], out);
 pclose (out);
 free (data);
}

void process_and (char ***commands, int i)
{
 int c, status;
 pid_t pid, subp;

 for ( c = i; (*commands)[c]; c++ )
       free ((*commands)[c]);
 (*commands)[i] = NULL;

 if ( (pid = fork ()) < 0 ) {
      sish_err ("sish: impossibile eseguire il programma.\n");
      return;
 }

 if ( !pid ) {
      if ( (subp = fork ()) < 0 ) {
           sish_err ("sish: impossibile eseguire il programma.\n");
           exit (0);
      }
      if ( !subp ) {
           execv (**commands, *commands);
           exit (0);
      }
      else {
            waitpid (subp, &status, 0);
            sish_err ("\nsish: %s: esecuzione terminata.", **commands);
            exit (0);
      }
 }
}