|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
/* longest possible command */
|
|
#define BUFFERSIZE 4096
|
|
|
|
typedef struct _parser {
|
|
char data[BUFFERSIZE];
|
|
int num_data;
|
|
|
|
char *args[32];
|
|
int num_args;
|
|
|
|
int argc;
|
|
const char const **argv;
|
|
|
|
void (*state)(struct _parser *);
|
|
|
|
} Parser;
|
|
|
|
void parser_double_quotes(Parser *);
|
|
void parser_single_quotes(Parser *);
|
|
void parser_normal_state(Parser *);
|
|
void parser_seen_asteriks(Parser *);
|
|
void parser_end_of_token(Parser *);
|
|
|
|
void parser_variable_state(Parser *p)
|
|
{
|
|
int position = 0;
|
|
int c;
|
|
|
|
for(c = getc(stdin); c >= '0' && c <='9'; c=getc(stdin))
|
|
{
|
|
position = 10*position + (c - '0');
|
|
}
|
|
|
|
ungetc(c, stdin);
|
|
|
|
p->args[p->num_args] = p->args[p->num_args-1];
|
|
p->args[p->num_args-1] = p->argv[position];
|
|
p->num_args++;
|
|
|
|
p->state = parser_normal_state;
|
|
}
|
|
|
|
void parser_finalize(Parser *p)
|
|
{
|
|
|
|
if(p->data[p->num_data] != '\0')
|
|
{
|
|
p->data[p->num_data] = '\0';
|
|
} else {
|
|
p->num_args--;
|
|
}
|
|
|
|
p->args[p->num_args] = NULL;
|
|
p->state = 0;
|
|
|
|
}
|
|
|
|
void parser_seen_asteriks(Parser *p)
|
|
{
|
|
int c = getc(stdin);
|
|
switch(c)
|
|
{
|
|
case '}':
|
|
parser_finalize(p);
|
|
return;
|
|
default:
|
|
p->data[p->num_data++] = '*';
|
|
ungetc(c, stdin);
|
|
p->state = parser_normal_state;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void parser_single_quotes(Parser *p)
|
|
{
|
|
int c = getc(stdin);
|
|
switch(c)
|
|
{
|
|
case EOF:
|
|
fprintf(stderr, "Invalid Input");
|
|
exit(-1);
|
|
case '\'':
|
|
p->state = parser_end_of_token;
|
|
return;
|
|
default:
|
|
p->data[p->num_data++] = c;
|
|
}
|
|
}
|
|
|
|
void parser_double_quotes(Parser *p)
|
|
{
|
|
int c = getc(stdin);
|
|
switch(c)
|
|
{
|
|
case EOF:
|
|
fprintf(stderr, "Invalid Input");
|
|
exit(-1);
|
|
case '"':
|
|
p->state = parser_end_of_token;
|
|
return;
|
|
default:
|
|
p->data[p->num_data++] = c;
|
|
}
|
|
}
|
|
|
|
void parser_end_of_token(Parser *p)
|
|
{
|
|
|
|
if(p->num_data == 0 || p->data[p->num_data-1] == '\0')
|
|
{
|
|
// empty token, return to normal state;
|
|
p->state = parser_normal_state;
|
|
return;
|
|
}
|
|
|
|
p->data[p->num_data++] = '\0';
|
|
|
|
p->args[p->num_args++] = &(p->data[p->num_data]);
|
|
}
|
|
|
|
void parser_normal_state(Parser *p)
|
|
{
|
|
int c = getc(stdin);
|
|
switch(c)
|
|
{
|
|
case '\\':
|
|
p->data[p->num_data++] = getc(stdin);
|
|
return;
|
|
case ' ':
|
|
p->state = parser_end_of_token;
|
|
return;
|
|
case '*':
|
|
p->state = parser_seen_asteriks;
|
|
return;
|
|
case '"':
|
|
p->state = parser_double_quotes;
|
|
return;
|
|
case '\'':
|
|
p->state = parser_single_quotes;
|
|
return;
|
|
case '$':
|
|
p->state = parser_variable_state;
|
|
return;
|
|
case EOF:
|
|
fprintf(stderr, "Invalid Input");
|
|
exit(-1);
|
|
default:
|
|
p->data[p->num_data++] = c;
|
|
}
|
|
}
|
|
|
|
void expand_command(Parser *p)
|
|
{
|
|
pid_t processId;
|
|
int status;
|
|
|
|
// initialize parser state
|
|
p->num_data = 0;
|
|
p->num_args = 0;
|
|
|
|
p->args[p->num_args++] = &(p->data[0]);
|
|
p->state = parser_normal_state;
|
|
|
|
while(p->state)
|
|
{
|
|
p->state(p);
|
|
}
|
|
|
|
processId = fork();
|
|
|
|
if(processId < 0)
|
|
{
|
|
exit(-1);
|
|
} else if(processId == 0)
|
|
{
|
|
execvp(p->args[0], p->args);
|
|
} else {
|
|
wait(&status);
|
|
if(status != 0)
|
|
{
|
|
fprintf(stderr, "Subprocess failed.");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char const *argv[])
|
|
{
|
|
int c;
|
|
Parser p;
|
|
p.argc = argc;
|
|
p.argv = argv;
|
|
|
|
while((c = getc(stdin)) != EOF)
|
|
{
|
|
if(c == '{')
|
|
{
|
|
int n = getc(stdin);
|
|
if(n == '*')
|
|
{
|
|
fflush(stdout);
|
|
expand_command(&p);
|
|
} else if(n == EOF){
|
|
putchar(c);
|
|
break;
|
|
} else {
|
|
putchar(c);
|
|
putchar(n);
|
|
}
|
|
} else {
|
|
putchar(c);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|