Parsing Command-Line Shells for UNIX
#include "parse.h"
/*
Removes the initial and trailing spaces in the given string
*/
char *trim(char *string)
{
char *p=string;
while(*p && isspace(*p))
p++;
if(!*p)
return p;
while(isspace(p[strlen(p)-1]))
p[strlen(p)-1]=0;
return p;
}
/*
Splits a string using the given delimiter, returns a list of string pointers
to the split parts, the last element in the list is NULL, n is updated to
the number of elements in the list
*/
char **split(char *string,char *delim,int *n)
{
char **parts=NULL;
char *token;
(*n)=0;
token = strtok(string,delim);
while(token!=NULL)
{
if(parts==NULL)
parts = (char **)malloc(2*sizeof(char*));
else
parts=(char **)realloc(parts,((*n)+2)*sizeof(char*));
parts[(*n)++] = token;
token = strtok(NULL,delim);
}
if((*n)>0)
parts[(*n)] = NULL;
return parts;
}
/*
Splits a command string using spaces, returns a list of string pointers
to the split parts, the last element in the list is NULL, n is updated to
the number of elements in the list leaves strings ' ' unchanged
*/
char **split_args(char *string,int *n)
{
char **parts=NULL;
char *p,*token;
(*n)=0;
p = string;
while(*p && isspace(*p))
p++;
if(!*p)
return NULL;
token = p;
while(token!=NULL)
{
if(parts==NULL)
parts = (char **)malloc(2*sizeof(char*));
else
parts=(char **)realloc(parts,((*n)+2)*sizeof(char*));
parts[(*n)++] = token;
if(*p=='\'')
{
p++;
parts[(*n)-1] = p;
while(*p && *p!='\'')
p++;
if(!*p) /* unclosed quote */
{
free(parts);
return NULL;
}
}
else
while(*p && !isspace(*p))
p++;
if(*p)
*(p++)=0;
while(*p && isspace(*p))
p++;
if(!*p)
token=NULL;
else
token=p;
}
parts[(*n)] = NULL;
return parts;
}
/*
Retrieves the redirection files for the given command and the clean
command string without redirections, the results are saved in a dynamically
allocated command_t structure
*/
command_t *split_redirections(char *command)
{
char *p=command;
char redir[3];
char *file = NULL;
command_t *cmd;
int i;
cmd = (command_t*) malloc(sizeof(command_t));
cmd->in_file = NULL;
cmd->out_file = NULL;
cmd->cat_file = NULL;
while(*p && *p!='<' && *p!='>')
p++;
cmd->command = (char *)malloc(p-command+1);
strncpy(cmd->command,command,p-command);
cmd->command[p-command]=0;
if(!*p)
return cmd;
p=command;
while(1) {
while(*p && *p!='<' && *p!='>')
p++;
if(!*p)
return cmd;
redir[0] = *p;
redir[1] = 0;
if(*(p+1)=='>')
{
p++;
redir[1]=*p;
redir[2] = 0;
}
p++;
while(*p && isspace(*p))
p++;
if(!*p)
return cmd;
file = p;
while(*p && !isspace(*p))
p++;
if(*p)
*(p++) =0;
if(redir[0]=='<')
cmd->in_file=strdup(file);
else if(redir[0]=='>' && redir[1]==0)
cmd->out_file=strdup(file);
else
cmd->cat_file=strdup(file);
}
return cmd;
}
/*
Frees all the allocated memory used by a command_t structure
*/
void free_command(command_t *cmd)
{
if(cmd->args)
free(cmd->args);
free(cmd->command);
if(cmd->in_file!=NULL)
free(cmd->in_file);
if(cmd->out_file!=NULL)
free(cmd->out_file);
if(cmd->cat_file!=NULL)
free(cmd->cat_file);
free(cmd);
}
/*
Frees the allocated memory for the command list given in cmd of size n
*/
void free_commands(command_t **cmd,int n)
{
int i;
for(i=0; iargs = split_args(cmd[i]->command,&m); // split(cmd[i]->command," ",&m);
if(cmd[i]->args==NULL)
{
fprintf(stderr,"Error: invalid command: '%s'\n",commands[i]);
for(;i>=0; i--)
free_command(cmd[i]);
free(cmd);
cmd=NULL;
break;
}
}
free(commands);
return cmd;
}
#ifndef PARSE_H
#define PARSE_H
#include
#include
#include
#include
typedef struct
{
char *in_file;
char *out_file;
char *cat_file;
char *command;
char **args;
}command_t;
command_t **parse(char *line,int *n);
char *trim(char *string);
char **split(char *string,char *delim,int *n);
command_t *split_redirections(char *command);
void free_command(command_t *cmd);
void free_commands(command_t **cmd,int n);
#endif /* PARSE_H*/
#include
#include
#include
#include
#include
#include "parse.h"
int last_status = 0;
/* Executes a single command using the given pipes for redirection
*/
int execute_command(command_t *cmd,int pipe_in,int pipe_out,int *status)
{
pid_t pid;
int in_fd,out_fd;
*status = 0;
if(!strcmp(cmd->args[0],"exit"))
{
if(cmd->args[1]!=NULL)
last_status = atoi(cmd->args[1]);
return 1;
}
pid = fork();
if(pid<0)
{
fprintf(stderr,"Error: unable to fork(). Exiting...\n");
exit(1);
}
else if(pid ==0)
{
if(cmd->in_file!=NULL) /* if the command has an input redirection*/
{
if((in_fd=open(cmd->in_file,O_RDONLY))==-1)
{
fprintf(stderr,"Error: Unable to open file: '%s'\n",cmd->in_file);
exit(1);
}
dup2(in_fd,STDIN_FILENO); /* use file as stdin */
close(in_fd); /* close duplicate */
if(pipe_in!=-1) /* if there was an input pipe, close it*/
close(pipe_in);
}
else if(pipe_in!=-1) /* else, if connected to an input pipe */
dup2(pipe_in,STDIN_FILENO);
if(cmd->out_file!=NULL || cmd->cat_file!=NULL) /* if the command has an output redirection*/
{
if(cmd->out_file!=NULL) /* create file */
{
if((out_fd=open(cmd->out_file,O_WRONLY|O_CREAT|O_TRUNC,0666))==-1) /* create new file */
{
fprintf(stderr,"Error: Unable to open file: '%s'\n",cmd->out_file);
exit(1);
}
}
else
{
if((out_fd=open(cmd->cat_file,O_WRONLY|O_CREAT|O_APPEND,0666))==-1) /* append to file */
{
fprintf(stderr,"Error: Unable to open file: '%s'\n",cmd->cat_file);
exit(1);
}
}
dup2(out_fd,STDOUT_FILENO); /* use file as stdout */
close(out_fd); /* close duplicate */
if(pipe_out!=-1) /* if there was an output pipe, close it*/
close(pipe_out);
}
else if(pipe_out!=-1) /* else, if connected to an output pipe */
dup2(pipe_out,STDOUT_FILENO);
execvp(cmd->args[0],cmd->args);
fprintf(stderr,"Error: Invalid command: '%s'\n",cmd->args[0]);
exit(1);
}
wait(status);
*status = WEXITSTATUS(*status);
return 0;
}
/* Executes all the commands in the command list */
int execute_all_commands(command_t **commands,int n)
{
int i;
int **pipe_fd;
int pipe_in,pipe_out;
int exit_shell = 0;
int status;
if(i==1) /* if only 1 command, execute it directly */
{
exit_shell=execute_command(commands[0],-1,-1,&status);
if(!exit_shell)
last_status = status;
}
else
{
pipe_fd = (int**) malloc((n-1)*sizeof(int*));
for(i=0; i1)
{
input = fopen(argv[1],"rt");
if(input==NULL)
{
fprintf(stderr,"Unable to open file: '%s'\n",argv[1]);
exit(1);
}
interactive = 0;
}
else {
input = stdin;
interactive = 1;
}
while(!quit)
{
if(interactive)
{
printf("? ");
fflush(stdout);
}
if(getline(&line,&linesize,input)!=-1)
{
commands = parse(line,&ncommands);
if(commands!=NULL)
{
quit = execute_all_commands(commands,ncommands);
free_commands(commands,ncommands);
}
}
else
quit = 1;
}
free(line);
exit(last_status);
}