/**
  * @file myshell.c
  * @Synopsis  
  * 简单的交互式shell
  * 用户输入一行命令 实现和shell 进程一样的效果。。
  * example:
  * cat demo1.txt
  * ps -e
  * ls -lath
  * top
  * who
  * ....
  * 输入上面这些命令和shell 实现一样的效果。
  *
  * 功能:
  * 子父进程使用管道实现命令传输,
  * 命令参数从终端输入,
  * 处理参数,由父进程处理写入管道。
  * 子进程从管道读取传输的命令,
  * 然后交给execlp 处理。
  * 这里也就是交给子进程来处理。
  * 当该子进程调用这个函数时,该进程的用户空间代码和
  * 数据完全被新程序替换(换脑),从新程序的启动例程开始执行。
  *
  * 类似于shell 层。
  * shell 层属于父级,
  * 当用户从终端输入命令时,
  * shell 会调用fork cp 出一个新的shell 进程,
  * 然后新的shell 进程调用 exec 执行新的程序。
  * @author MrClimb
  * @version 1.1.0
  * @date 2012-05-18
  */ #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h> int main(int argc, char **argv)
 {
     pid_t pid;// 进程编号
     /**
      * pipefd[0] stand for read file description
      * pipefd[1] stand for write file description
      */
     int pipefd[2];// 文件描述符
     char buffer[BUFSIZ];//1024*8 = 8192 
     char *arguments[BUFSIZ];
     size_t st;
     ssize_t sst; while(1){
     printf("input shell(exit):");
     
     memset(buffer,0,BUFSIZ);
     /**
      * create pipe
      * int pipe(int pipefd[2]);
      * 创建管道
      * 传出两个参数
      */
     if(pipe(pipefd)==-1)
     {
         perror("cannot create pipe");
         exit(1);
     }
     
     char param1[BUFSIZ];
     char inputp[BUFSIZ];
     memset(inputp,0,BUFSIZ);
     // 获得终端的命令输入
     fgets(inputp,BUFSIZ,stdin);
     // 处理 \n 字符 但这里不完全正确。。
     inputp[strlen(inputp)-1]='\0';
     
     if(strcmp(inputp,"exit")==0)
     {
         exit(1);
     }
     
     int i=0;
     char *sep = strtok(inputp," ");
         strcpy(buffer,sep);     int len1 = strlen(buffer);
     arguments[0] = (char *)malloc(len1+1);
     strcpy(arguments[0],buffer);
     i++;
     while((sep=strtok(NULL," "))!=NULL)
     {
         len1 = strlen(buffer);
         arguments[i] = (char *)malloc(len1+1);
         strcpy(arguments[i],sep);
         i++;
     }
     arguments[i]=NULL;     // 开始创建一个进程
     pid = fork();
     if(pid==0)
     {
 //      puts("child into...");
         int len = strlen(param1);
         st = read(pipefd[0],buffer,len);
         if(st<0)
         {
             printf("read failure...\n");
             continue;
         }
         // 这里关掉子进程 的写入管道
         // 只让子进程读数据
         close(pipefd[1]);
 //        usleep(100);
         // 进行exec 糸统调用
         execvp(buffer,arguments);
     }else if(pid>0)
     {
        // 关掉父进程从管道读取
        // 这样子父行成 管道实现 环形队列 这样实现了进程间通信
         close(pipefd[0]);
        // puts("parent into...");
         int len = strlen(param1);
         // 向管道端写入
         sst = write(pipefd[1],param1,len);    
         
         if(sst<0)
         {
             printf("write failure.....\n");
         }
       /**
  * 这里用wait 而不用usleep 
  * 一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,
  * 但PCB 还保留着,内核其中保存了一些信息,如果是正常终止则保存着退出状态,
  * 如果异常终止则保存着导致该进程终止的信号是哪个。这时候可以调用wait 获取
  * 这些信息,然后彻底清除掉这个进程。
  * 如果一个进程已经终止,但是它的父进程尚未调用wait or waitpid 对它进行清理,这时的进程装态称为
  * 僵尸zombie 进程。这里的wait 使父进程 阻塞 等待子进程终止,
  * ps u 查看到信息usleep 与 wait 两都的区别。。。
  * usleep 僵尸进程没有被清除。
  * wait 先让子进程 终止,也可以说去清理子进程
  *
  */
         //      usleep(100);
         wait(NULL);
 /**
  * 父进程作清理工作。。
  * 释放内存。。
  */
         int i=0;
         for(;arguments[i]!=NULL;i++)
         {
           printf("arguments[%d] memory free!\n",i);
           free(arguments[i]);   
         }
     }else{
         perror("cannot fork!");
         exit(1);
     }
 }
     return 0;
 }