将以下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;
}