一个命令解释器的源代码

将以下3个源代码拷贝做成3个文件,在Red hat linux 9.0 环境下 编译通过。

/*msh.c  */

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pwd.h>
#include <libgen.h>
#include <sys/types.h>

#include <readline/readline.h>
#include <readline/history.h>

char buf[BUFSIZ];
char* myptr;
char* mylim;

char lastdir[100];

void init_lastdir()
{
 getcwd(lastdir, 99);
}

void set_prompt(char* prompt)
{
 char host[100];
 char cwd[100];
 struct passwd* pwp;
 if(gethostname(host, 99) == -1) {
  strcpy(host, "unknown");
 } else {
  char* p = strchr(host, '.');
  if(p)
   *p = 0;
 }
 if(!getcwd(cwd, 99)) {
  strcpy(cwd, "unknown");
 } else {
  if(strcmp(cwd, "/") != 0)
   strcpy(cwd, basename(cwd));
 }
 pwp = getpwuid(getuid());
 sprintf(prompt, "[%s@%s %s]# ", (pwp ? pwp->pw_name : "unknown"), host, cwd);
}
extern void yylex();


void history_setup()
{
 using_history();
 stifle_history(50);
 read_history("/tmp/msh_history"); 
}

void history_finish()
{
 append_history(history_length, "/tmp/msh_history");
 history_truncate_file("/tmp/msh_history", history_max_entries);
}

void display_history_list()
{
 HIST_ENTRY** h = history_list();
 if(h) {
  int i = 0;
  while(h[i]) {
   printf("%d: %s/n", i, h[i]->line);
   i++;
  }
 }
}

int main(int argc, char** argv)
{
// int ch;
// int len;
 char* line;
 char prompt[200];
 
 signal(SIGINT, SIG_IGN);

 init_lastdir();
 history_setup(); 

 while(1) {
/*
  print_prompt();

  len = 0;
  ch = getchar();
  while(len < BUFSIZ && ch != '/n') {
   buf[len++] = ch;
   ch = getchar();
  }
  if(len == BUFSIZ) {
   printf("command is too long/n");
   break;
  }
  buf[len] = '/n';
  len++;
  buf[len] = 0;
*/
  set_prompt(prompt);
  if(!(line = readline(prompt)))
   break;
  if(*line)
   add_history(line);
  strcpy(buf, line);
  strcat(buf, "/n");

  myptr = buf;
  mylim = buf+strlen(buf);
  yylex();
 }
 history_finish();
 return 0;
}

 

 

 

/* Makefile */

CC=gcc
CFLAGS=-g -Wall
SRC=msh.c lex.yy.c

all:
 flex parsecmd.l
 $(CC) $(CFLAGS) -o msh $(SRC) -lfl -lreadline -ltermcap

clean:
 rm -f msh msh.o lex.yy.c

/*  parsecmd.l   */

%{
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>


extern char buf[];
extern char* myptr;
extern char* mylim;

extern void display_history_list();
extern void history_finish();

static int my_yyinput(char* buf, int max);
static int file_exist(const char* file, char* buffer);
static void free_resource();

static int do_exit(int, char**);
static int do_export(int, char**);
static int do_echo(int, char**);
static int do_cd(int, char**);
static int do_history(int, char**);

static void add_arg(const char* xarg);
static void add_simple_arg(const char* xarg);
static void reset_args();
static void do_list_cmd();
static int do_pipe_cmd(int argc, char** argv);
static int do_simple_cmd(int argc, char** argv, int prefd[], int postfd[]);

#undef  YY_INPUT
#define YY_INPUT(b, r, ms) (r = my_yyinput(b, ms))

char* argbuf[200];
int argcnt = 0;

typedef int (*buildin_cmd_handle)(int, char**);
typedef struct
{
 const char* cmd;
 buildin_cmd_handle handle;
} CMD_ENTRY;

const CMD_ENTRY buildin_cmd_table[] =
{
 {"exit",  do_exit},
 {"cd",   do_cd},
 {"echo",  do_echo},
 {"export",  do_export},
 {"history", do_history},
 {0, 0}
};

%}

%x  QUOTE
%x  SINGQUOTE
blank [ /t]

%%

"/""   {BEGIN QUOTE;}
<QUOTE>[^/n"]+ {add_arg(yytext);}
<QUOTE>"/""  {BEGIN 0;}
<QUOTE>/n  {BEGIN 0; do_list_cmd(); reset_args();}
";"    {add_simple_arg(yytext);}
">"    {add_simple_arg(yytext);}
"<"    {add_simple_arg(yytext);}
"|"    {add_simple_arg(yytext);}
[^ /t/n|<>;"]+ {add_arg(yytext);}
/n    {do_list_cmd(); reset_args();}
.    ;

%%

static buildin_cmd_handle get_cmd_handle(const char* cmd)
{
 int i = 0;
 while(buildin_cmd_table[i].cmd) {
  if(strcmp(buildin_cmd_table[i].cmd, cmd) == 0)
   return buildin_cmd_table[i].handle;
  i++;
 }
 return 0;
}
static void free_resource()
{
 reset_args();
}

static int do_exit(int argc, char** argv)
{
 int val = 0;
 if(argc > 1)
  val = atoi(argv[1]);
 free_resource();
 history_finish();
 exit(val);
 return 0;
}

static int do_cd(int argc, char** argv)
{
 char* dir;
 char cwd[100];
 extern char lastdir[];

 if(argc == 1) {
  if(!(dir = getenv("HOME"))) {
   printf("cd: %s/n", strerror(errno));
   return -1;
  }
 } else if(argc == 2) {
  if(strcmp(argv[1], "-") == 0) {
   dir = lastdir;
  } else if(strcmp(argv[1], "~") == 0) {
   if(!(dir = getenv("HOME"))) {
    printf("cd: %s/n", strerror(errno));
    return -1;
   }
  } else
   dir = argv[1];
 } else {
  printf("Usage: cd [dir]/n");
  return -1;
 }
 getcwd(cwd, 99);
 if(chdir(dir) == -1) {
  printf("cd: %s/n", strerror(errno));
  return -1;
 }
 strcpy(lastdir, cwd);
 return 0;
}

static int do_export(int argc, char** argv)
{
 int i = 1;
 char* p;
 while(argv[i]) {
  if((p = strchr(argv[i], '='))) {
   *p = 0;
   if(strpbrk(argv[i], "~`!@#$%^&*()-_+=|//{}[];:'/"<>,.?/")) {
    *p = '=';
    printf("export: %s: not a valid indentifier/n", argv[i]);
    i++;
    continue;
   }

   if(setenv(argv[i], p+1, 1) == -1)
    printf("export: %s/n", strerror(errno));
   *p = '=';
  }
  i++;  
 }
 return 0;
}

static int do_echo(int argc, char** argv)
{
 int i = 1;
 int j;
 int argn = 0;
 int arge = 0;
 if(argv[1]) {
  if(strcmp(argv[1], "-n") == 0) {
   argn = 1;
   i = 2;
  } else if(strcmp(argv[1], "-e") == 0) {
   arge = 1;
   i = 2;
  } else if((strcmp(argv[1], "-ne") == 0) || (strcmp(argv[1], "-en") == 0)) {
   argn = arge = 1;
   i = 2;
  }
 }
 j = i;
 while(argv[i]) {
  if(i > j)
   printf(" %s", argv[i]);
  else
   printf("%s", argv[i]);
  i++;
 }
 if(argn == 0)
  printf("/n");
 
 return 0;
}

int do_history(int argc, char** argv)
{
 display_history_list(); 
 return 0;
}

static void add_simple_arg(const char* arg)
{
 argbuf[argcnt] = (char*)malloc(strlen(arg)+1);
 strcpy(argbuf[argcnt], arg);
 argcnt++;
 argbuf[argcnt] = 0;
}

// $HOME
// $$
// $HOME$
// $HOME$HOME
static void add_arg(const char* xarg)
{
 char* arg;

 char buf[200];
 char xbuf[200];
 int i,j,k;
 int len = strlen(xarg);
 

 k = 0;

 for(i = 0; i < len; i++) {
  if(xarg[i] == '$') {
   if(xarg[i+1] == '$') {//$$,get pid
    int pid = getpid();
    sprintf(buf+k, "%d", pid);
    k = strlen(buf);
    i++;
   } else if(xarg[i+1] == 0){//$ and end
    buf[k] = '$';
    k++;
    break;
   } else {//$HOME or $HOME$OTHER
    for(j = i+1; j < len; j++) {
     if(xarg[j] == '$')
      break;
     xbuf[j-i-1] = xarg[j];
    }
    xbuf[j-i-1] = 0;
    i = j-1;
    if((arg = getenv(xbuf))) {
     strcpy(buf+k, arg);
     k += strlen(arg);
    }
   }
   
  } else {
   buf[k] = xarg[i];
   k++;
  }
 }
 buf[k] = 0;
 if(k > 0)
  add_simple_arg(buf);
}

static int file_exist(const char* file, char* buffer)
{
 int i = 0;
 const char* p;
 const char* path;
 path = getenv("PATH");
 p = path;
 while(*p != 0) {
  if(*p != ':')
   buffer[i++] = *p;
  else {
   buffer[i++] = '/';
   buffer[i] = 0;
   strcat(buffer, file);
   if(access(buffer, F_OK) == 0)
    return 1;
   i = 0;
  }
  p++;
 }
 return 0;
}

static void do_list_cmd()
{
 int i = 0;
 int j = 0;
 char* p;
 while(argbuf[i]) {
  if(strcmp(argbuf[i], ";") == 0) {//  ;
   p = argbuf[i];
   argbuf[i] = 0;
   do_pipe_cmd(i-j, argbuf+j);
   argbuf[i] = p;
   j = ++i;
  } else
   i++;
 }
 do_pipe_cmd(i-j, argbuf+j);
}

static int do_pipe_cmd(int argc, char** argv)
{
 int i = 0;
 int j = 0;
 int prepipe = 0;
 int prefd[2];
 int postfd[2];
 char* p;

 while(argv[i]) {
  if(strcmp(argv[i], "|") == 0) { // pipe
   p = argv[i];
   argv[i] = 0;

   pipe(postfd);   //create the post pipe
   //be sure not close pipe in, otherwise whenever father write to this pipe,
   //cause a Broken pipe.
   //close(postfd[0]); //father close pipe in
   
   if(prepipe) 
    do_simple_cmd(i-j, argv+j, prefd, postfd);
   else
    do_simple_cmd(i-j, argv+j, 0, postfd);
   argv[i] = p;
   prepipe = 1;
   prefd[0] = postfd[0];
   prefd[1] = postfd[1];
   j = ++i;
  } else
   i++;
 }
 if(prepipe)
  do_simple_cmd(i-j, argv+j, prefd, 0);
 else
  do_simple_cmd(i-j, argv+j, 0, 0);
 return 0;
}


static int predo_for_redirect(int argc, char** argv, int* re)
{
 int i;
 int redirect = 0; 
 for(i = 1; i < argc; i++) {
  if(strcmp(argv[i], "<") == 0) {
   redirect = 1;
   argv[i] = 0;
   break;
  } else if(strcmp(argv[i], ">") == 0) {
   redirect = 2;
   argv[i] = 0;
   break; 
  }
 }
 if(redirect) {// need redirect stdin or stdout
  if(argv[i+1]) {
   int fd;
   if(redirect == 2) {
    if((fd = open(argv[i+1], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR)) == -1) {
     fprintf(stderr, "Open out %s failed/n", argv[i+1]);
     return 1;
    }
    dup2(fd, STDOUT_FILENO);
   } else {//redirect == 1
    if((fd = open(argv[i+1], O_RDONLY, S_IRUSR|S_IWUSR)) == -1) {
     fprintf(stderr, "Open in %s failed/n", argv[i+1]);
     return 1;
    }
    dup2(fd, STDIN_FILENO);
   }
  } else {
   fprintf(stderr, "Bad redirect, need more arg/n");
   return 1;
  }
 }
 if(re)
  *re = redirect;
 return 0;
}

static int do_simple_cmd(int argc, char** argv, int prefd[], int postfd[])
{
 int pid;
 int status;
 buildin_cmd_handle hd;
 if(argc == 0)
  return 0;

 if(prefd == 0 && postfd == 0) {
 // a very simple buitin command, not have pre and post pipe, father should do the
 // builtin command itself.
  if((hd = get_cmd_handle(argv[0]))) {
   if(predo_for_redirect(argc, argv, 0))
    return 1;
   (*hd)(argc, argv);
   return 0;
  }
 }
 
 if((pid = fork()) == 0) {//child
  // reset the signal INT handle to default
  int redirect = 0;
  signal(SIGINT, SIG_DFL);

  if(predo_for_redirect(argc, argv, &redirect))
   exit(1);
  
  if(redirect != 1 && prefd) {//has a pre pipe, redirect stdin
   close(prefd[1]);
   if(prefd[0] != STDIN_FILENO) {
 //   fprintf(stderr, "redirect stdin/n");
    dup2(prefd[0], STDIN_FILENO);
    close(prefd[0]);
   }
  }
  if(redirect != 2 && postfd) {//has a post pipe, redirect stdout
   close(postfd[0]);
   if(postfd[1] != STDOUT_FILENO) {
 //   fprintf(stderr, "redirect stdout/n");
    dup2(postfd[1], STDOUT_FILENO);
    close(postfd[1]);
   }
  }
  if((hd = get_cmd_handle(argv[0]))) {
   (*hd)(argc, argv);
   exit(0);
  }

  char buffer[100];
  if(file_exist(argv[0], buffer)) {
 //  fprintf(stderr, "exec command %s/n", buffer);
   execv(buffer, argv);
  }
  else {
   fprintf(stderr, "-msh: %s: command not found/n", argv[0]);
   exit(0);
  }
 }
 waitpid(pid, &status, 0);
 if(postfd) { // no
  close(postfd[1]); // must close this fd here.
 }
 return 0;
}


static void reset_args()
{
 int i;
 for(i = 0; i < argcnt; i++) {
  free(argbuf[i]);
  argbuf[i] = 0;
 }
 argcnt = 0;
}

static int my_yyinput(char* buf, int max)
{
 int n;
 n = (max < (mylim-myptr)) ? max : (mylim-myptr);

 if(n > 0) {
  memcpy(buf, myptr, n);
  myptr += n;
 }
 return n;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值