/*{{{}}}*/
/*{{{  #includes*/
#ifdef CONFIG_H
#   include "config.h"
#endif

#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define SHELL_C
#define I_GETMSG_C
#define I_PROMPT_C
#define I_GETTK_C
#define I_INIT_C

#include "origami.h"
#include <h/envvar_str.h>
#include <lib/ori_add_lib.h>
/*}}}  */

/*{{{  variables*/
public boolean force_sh=False;
public boolean secure=
#ifdef SECURE
                   True;
#else
                   False;
#endif
public char const *shell_name;

#define MAX_PIPES 4
private int p_used=0;
private struct ph { FILE *f;pid_t id; } pipe_handle[MAX_PIPES];
/*}}}  */

/*{{{  get_shell*/
private const char *get_shell(void)
 { const char *active_shell;
   static const char bourne_shell[]=BOURNE_SHELL;

   if (secure)
      active_shell=shell_name;
   else if (force_sh)
      active_shell=bourne_shell;
   else
    { active_shell=getenv(SHELL);
      if (!active_shell || !active_shell[0])
         active_shell=bourne_shell;
    }
   if (active_shell && !active_shell[0])
      active_shell=0;
   return(active_shell);
 }
/*}}}  */
/*{{{  close_pipe_f*/
private void close_pipe_f(void)
 {
   while (p_used)
      fclose(pipe_handle[--p_used].f);
   if (rcfile)
      fclose(rcfile);
   if (msg_file>=0)
      close(msg_file);
 }
/*}}}  */
/*{{{  flush_all*/
private void flush_all(void) { fflush(stdout);fflush(stderr); }
/*}}}  */
/*{{{  waitfor*/
private int waitfor(pid_t pid)
{ int i;
  pid_t j;

  while (pid!=(j=waitpid(pid,&i,0)) || WIFSTOPPED(i))
     if (-1==j)
        return(-1);
  return(WIFEXITED(i) ? WEXITSTATUS(i) : -1);
}
/*}}}  */

/*{{{  subshell*/
int subshell(const char * const cmd)
{
  pid_t child;
  const char *active_shell;

  if (!(active_shell=get_shell()))
     return(EX_NOPERM);
  flush_all();
  signal(SIGINT,SIG_IGN);
  switch ((child=fork()))
   { case 0:
      /*{{{  child*/
        signal(SIGINT,SIG_DFL);
        close_pipe_f();
        execl(active_shell,active_shell,cmd?"-c":(char*)0,cmd,(char*)0);
        exit(127);
      /*}}}  */
     default:
      /*{{{  wait for child*/
      { int status;

        status=waitfor(child);
        signal(SIGINT,ori_abort);
        return status;
      }
      /*}}}  */
     case -1:
      /*{{{  fork failed*/
        msg_message(M_FORKFAIL);
        signal(SIGINT,ori_abort);
        return(-1);
      /*}}}  */
   }
}
/*}}}  */
/*{{{  open_shell*/
public FILE *open_shell(const char * const prog,const char * const type)
 {
   /*{{{  variables*/
   FILE *fout;
   const char *active_shell;
   int d[2];
   boolean reading;
   /*}}}  */

   fout=0;
   if
    (    p_used<MAX_PIPES
      && (type && ((reading=(type[0]=='r')) || type[0]=='w') && type[1]=='\0')
      && (active_shell=get_shell())
      && !pipe(d)
    )
    { flush_all();
      signal(SIGINT,SIG_IGN);
      switch ((pipe_handle[p_used].id=fork()))
       { case 0:
          /*{{{  child*/
            signal(SIGINT,SIG_DFL);
            close_pipe_f();
            if (reading)
             /*{{{  stdout*/
             { int to;

               to=fileno(stdout);
               fclose(stdout);
               dup2(d[1],to);
               close(d[0]);
             }
             /*}}}  */
            else
             /*{{{  stdin*/
             { int to;

               to=fileno(stdin);
               fclose(stdin);
               dup2(d[0],to);
               close(d[1]);
             }
             /*}}}  */
            execl(active_shell,active_shell,"-c",prog,(char*)0);
            exit(127);
          /*}}}  */
         default:
          /*{{{  setup filehandle*/
            fout=pipe_handle[p_used].f=fdopen(d[reading?0:1],type);
            if (fout)
             { close(d[reading?1:0]);
               p_used++;
               break;
             }
            else
               goto fork_fail_handling;
          /*}}}  */
         case -1:
         fork_fail_handling:
          /*{{{  error, fork failed*/
            close(d[0]);
            close(d[1]);
            if (pipe_handle[p_used].id>=0)
             { kill(pipe_handle[p_used].id,SIGTERM);
               waitfor(pipe_handle[p_used].id);
             }
            msg_message(M_FORKFAIL);
            signal(SIGINT,ori_abort);
            break;
          /*}}}  */
       }
    }
   return(fout);
 }
/*}}}  */
/*{{{  close_shell*/
public int close_shell(FILE *f)
 { struct ph *p;

   p=pipe_handle+MAX_PIPES;
   do
      if (f==(--p)->f)
       { int r;

         fclose(p->f);
         r=waitfor(p->id);
         *p=pipe_handle[--p_used];
         pipe_handle[p_used].f=0;
         return(r);
       }
   while (p!=pipe_handle);

   return(-1);
 }
/*}}}  */
