461 lines
11 KiB
Plaintext
461 lines
11 KiB
Plaintext
|
|
||
|
/*
|
||
|
* CS-252
|
||
|
* shell.y: parser for shell
|
||
|
*
|
||
|
* This parser compiles the following grammar:
|
||
|
*
|
||
|
* cmd [arg]* [> filename]
|
||
|
*
|
||
|
* you must extend it to understand the complete shell grammar
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
%code requires
|
||
|
{
|
||
|
#include <string>
|
||
|
|
||
|
#if __cplusplus > 199711L
|
||
|
#define register // Deprecated in C++11 so remove the keyword
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
%union
|
||
|
{
|
||
|
char *string_val;
|
||
|
// Example of using a c++ type in yacc
|
||
|
std::string *cpp_string;
|
||
|
}
|
||
|
|
||
|
%token <string_val> WORD
|
||
|
%token <string_val> SUBCOMMAND
|
||
|
%token NOTOKEN NEWLINE PIPE GREAT LESS GREATGREAT TWOGREAT GREATAMP GREATGREATAMP AMP
|
||
|
|
||
|
%{
|
||
|
//#define yylex yylex
|
||
|
#include <cstdio>
|
||
|
#include "command.hh"
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <fnmatch.h>
|
||
|
#include <dirent.h>
|
||
|
|
||
|
//#define DEBUG_YAK
|
||
|
#ifdef DEBUG_YAK
|
||
|
# define DEBUG_PRINT(x) printf x
|
||
|
#else
|
||
|
# define DEBUG_PRINT(X) do {} while(0)
|
||
|
#endif
|
||
|
|
||
|
/*extern char* lastArg = NULL;*/
|
||
|
|
||
|
void yyerror(const char * s);
|
||
|
int yylex(void);
|
||
|
//void myunputc(int, char* );
|
||
|
|
||
|
int alphaCaseCompare(const dirent** a, const dirent** b) {
|
||
|
/*int rank = strcasecmp((*a)->d_name, (*b)->d_name);*/
|
||
|
/*if (rank == 0) {*/
|
||
|
/*return -1 * strcmp((*a)->d_name, (*b)->d_name);*/
|
||
|
/*}*/
|
||
|
|
||
|
/*return rank;*/
|
||
|
|
||
|
return strcmp((*a)->d_name, (*b)->d_name);
|
||
|
}
|
||
|
|
||
|
#define MAXFILENAME 1024
|
||
|
int expandWildcard(char* prefix, char* suffix) {
|
||
|
/*fprintf(stderr, "-> %s, %s\n", prefix, suffix);*/
|
||
|
int sum = 0;
|
||
|
|
||
|
if (suffix == NULL || suffix[0] == 0) {
|
||
|
// suffix is empty. Put prefix in argument.
|
||
|
Command::_currentSimpleCommand->insertArgument(strdup(prefix));
|
||
|
/*fprintf(stderr, "Insert argument\n");*/
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Obtain the next component in the suffix
|
||
|
// Also advance suffix.
|
||
|
char * s = strchr(suffix, '/');
|
||
|
char component[MAXFILENAME];
|
||
|
memset(component, 0, MAXFILENAME * (sizeof component[0]));
|
||
|
|
||
|
if (s != NULL) {
|
||
|
// Copy up to the first "/"
|
||
|
strncpy(component, suffix, s - suffix);
|
||
|
suffix = s + 1;
|
||
|
} else {
|
||
|
// Last part of path. Copy whole thing.
|
||
|
strcpy(component, suffix);
|
||
|
suffix = suffix + strlen(suffix);
|
||
|
}
|
||
|
|
||
|
// Now we need to expand the component
|
||
|
char newPrefix[MAXFILENAME];
|
||
|
memset(newPrefix, 0, MAXFILENAME * (sizeof newPrefix[0]));
|
||
|
|
||
|
if ( strchr(component, '*') == NULL && strchr(component, '?') == NULL) {
|
||
|
// component does not have wildcards
|
||
|
sprintf(newPrefix,"%s/%s", prefix, component);
|
||
|
return expandWildcard(newPrefix, suffix);
|
||
|
}
|
||
|
|
||
|
// Component has wildcards
|
||
|
// Convert component to regular expression
|
||
|
/*char * expbuf = compile( [> ... <] )*/
|
||
|
char* dir = (char *) malloc(sizeof(char) * 1024);
|
||
|
// If prefix is empty then list current directory
|
||
|
if ( prefix == NULL || prefix[0] == 0 ) {
|
||
|
prefix = strdup(".");
|
||
|
}
|
||
|
|
||
|
dir = prefix;
|
||
|
|
||
|
DIR * d = opendir(dir);
|
||
|
if (d == NULL) {
|
||
|
return sum;
|
||
|
}
|
||
|
|
||
|
struct dirent** namelist;
|
||
|
int nameCount = scandir(dir, &namelist, NULL, alphasort);
|
||
|
/*dirent* ent;*/
|
||
|
|
||
|
// Now we need to check what entries match
|
||
|
/*while ((ent = readdir(d)) != NULL) {*/
|
||
|
for(int i = 0; i < nameCount; i++) {
|
||
|
// Check if name matches
|
||
|
char* filename = namelist[i]->d_name;
|
||
|
/*if (advance(ent->d_name, expbuf) ) {*/
|
||
|
int result = fnmatch(component, filename, FNM_PATHNAME | FNM_PERIOD);
|
||
|
/*fprintf(stderr, "%s vs %s -> %d\n", component, ent->d_name, result);*/
|
||
|
|
||
|
if (result == 0) {
|
||
|
// Entry matches. Add name of entry
|
||
|
// that matches to the prefix and
|
||
|
// call expandWildcard(..) recursively
|
||
|
if (strcmp(prefix, ".") == 0) {
|
||
|
sprintf(newPrefix,"%s", filename);
|
||
|
} else if (strcmp(prefix, "/") == 0) {
|
||
|
sprintf(newPrefix, "%s%s", prefix, filename);
|
||
|
} else {
|
||
|
sprintf(newPrefix,"%s/%s", prefix, filename);
|
||
|
}
|
||
|
|
||
|
/*fprintf(stderr, "%s\n", newPrefix);*/
|
||
|
sum += expandWildcard(newPrefix, suffix);
|
||
|
}
|
||
|
free(namelist[i]);
|
||
|
namelist[i] = NULL;
|
||
|
}
|
||
|
|
||
|
free(namelist);
|
||
|
namelist = NULL;
|
||
|
|
||
|
/*free(newPrefix);*/
|
||
|
/*newPrefix = NULL;*/
|
||
|
/*free(component);*/
|
||
|
/*component = NULL;*/
|
||
|
/*closedir(d);*/
|
||
|
return sum;
|
||
|
}
|
||
|
|
||
|
void substringVariable(char* str) {
|
||
|
char* dollar = strchr(str, '$');
|
||
|
char* prefix = NULL;
|
||
|
char* suffix = NULL;
|
||
|
|
||
|
if (dollar != NULL && dollar[1] == '{') {
|
||
|
char* varname = dollar + 2;
|
||
|
while (dollar[0] != '}' && dollar[0] != '\0') {
|
||
|
dollar++;
|
||
|
}
|
||
|
|
||
|
if (dollar[0] == '\0') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dollar[0] = '\0';
|
||
|
varname = strdup(varname);
|
||
|
dollar[0] = '}';
|
||
|
|
||
|
suffix = strdup(dollar + 1);
|
||
|
dollar = strchr(str, '$');
|
||
|
dollar[0] = '\0';
|
||
|
prefix = strdup(str);
|
||
|
dollar[0] = '$';
|
||
|
|
||
|
free(str);
|
||
|
char* value = getenv(varname);
|
||
|
str = (char *) malloc(sizeof(char) * (strlen(prefix) + strlen(value) + strlen(suffix) + 1));
|
||
|
strcpy(str, prefix);
|
||
|
strcat(str, value);
|
||
|
strcat(str, suffix);
|
||
|
|
||
|
substringVariable(str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void expandString(char* str) {
|
||
|
// Check for variables.
|
||
|
substringVariable(str);
|
||
|
|
||
|
// Check for wildcards.
|
||
|
if (strchr(str, '*') != NULL || strchr(str, '?') != NULL) {
|
||
|
int sum = 0;
|
||
|
char* prefix = NULL;
|
||
|
char* suffix = NULL;
|
||
|
|
||
|
char* split = (char *) strchr(str, '*');
|
||
|
if (strchr(str, '/') != NULL) {
|
||
|
if (split == NULL) {
|
||
|
split = (char *) strchr(str, '?');
|
||
|
}
|
||
|
while (split[0] != '/') {
|
||
|
split--;
|
||
|
}
|
||
|
|
||
|
suffix = strdup(++split);
|
||
|
split--;
|
||
|
if (split != str) {
|
||
|
split[0] = '\0';
|
||
|
prefix = strdup(str);
|
||
|
split[0] = '/';
|
||
|
} else {
|
||
|
char temp = split[1];
|
||
|
split[1] = '\0';
|
||
|
prefix = strdup(str);
|
||
|
split[1] = temp;
|
||
|
}
|
||
|
} else {
|
||
|
suffix = strdup(str);
|
||
|
}
|
||
|
|
||
|
/*fprintf(stderr, "%s, %s (%s)\n", prefix, suffix, split);*/
|
||
|
|
||
|
sum = expandWildcard(prefix, suffix);
|
||
|
if (sum <= 0) {
|
||
|
Command::_currentSimpleCommand->insertArgument(str);
|
||
|
}
|
||
|
} else {
|
||
|
Command::_currentSimpleCommand->insertArgument(str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
%}
|
||
|
|
||
|
%%
|
||
|
|
||
|
goal:
|
||
|
commands
|
||
|
;
|
||
|
|
||
|
commands:
|
||
|
command
|
||
|
| commands command
|
||
|
;
|
||
|
|
||
|
command:
|
||
|
simple_command
|
||
|
;
|
||
|
|
||
|
simple_command:
|
||
|
command_and_args pipe_opt_list iomodifier_opt_list NEWLINE {
|
||
|
if (! Command::_currentCommand._parseFailure) {
|
||
|
DEBUG_PRINT((" Yacc: Execute command\n"));
|
||
|
/*Command::_currentCommand.print();*/
|
||
|
Command::_currentCommand.execute();
|
||
|
}
|
||
|
}
|
||
|
| command_and_args pipe_opt_list iomodifier_opt_list AMP NEWLINE {
|
||
|
if (! Command::_currentCommand._parseFailure) {
|
||
|
DEBUG_PRINT((" Yacc: Execute background command\n"));
|
||
|
Command::_currentCommand._background = 1;
|
||
|
Command::_currentCommand.execute();
|
||
|
}
|
||
|
}
|
||
|
| NEWLINE {
|
||
|
Command::_currentCommand.execute();
|
||
|
}
|
||
|
| error NEWLINE { yyerrok; }
|
||
|
;
|
||
|
|
||
|
command_and_args:
|
||
|
command_word argument_list {
|
||
|
Command::_currentCommand.insertSimpleCommand( Command::_currentSimpleCommand );
|
||
|
}
|
||
|
;
|
||
|
|
||
|
argument_list:
|
||
|
argument_list argument
|
||
|
| /* can be empty */
|
||
|
;
|
||
|
|
||
|
argument:
|
||
|
WORD {
|
||
|
char* cleaned = (char *) malloc(sizeof(char) * ( strlen($1)));
|
||
|
int orgIndex = 0;
|
||
|
int cleanIndex = 0;
|
||
|
|
||
|
for ( ; orgIndex < strlen($1) ; ) {
|
||
|
if ($1[orgIndex] == '\\') {
|
||
|
orgIndex++;
|
||
|
}
|
||
|
|
||
|
cleaned[cleanIndex] = $1[orgIndex];
|
||
|
|
||
|
if ($1[orgIndex] == '\0') {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
orgIndex++;
|
||
|
cleanIndex++;
|
||
|
}
|
||
|
cleaned[cleanIndex] = '\0';
|
||
|
|
||
|
|
||
|
if (cleaned[0] == '~') {
|
||
|
char* expanded = (char *) malloc(sizeof(char) * (strlen(cleaned) + 64));
|
||
|
if (cleaned[1] == '/') {
|
||
|
strcpy(expanded, getenv("HOME"));
|
||
|
strcat(expanded, cleaned + 1);
|
||
|
} else if (cleaned[1] == '\0') {
|
||
|
strcpy(expanded, getenv("HOME"));
|
||
|
} else {
|
||
|
char* namestr = NULL;
|
||
|
char* name = (char *) strchr(cleaned, '/');
|
||
|
if (name != NULL) {
|
||
|
while (name[0] != '~') {
|
||
|
name--;
|
||
|
}
|
||
|
name++;
|
||
|
char* slash = (char *) strchr(cleaned, '/');
|
||
|
slash[0] = '\0';
|
||
|
namestr = strdup(cleaned + 1);
|
||
|
slash[0] = '/';
|
||
|
} else {
|
||
|
namestr = strdup(cleaned + 1);
|
||
|
}
|
||
|
|
||
|
/*fprintf(stderr, "%s\n", namestr);*/
|
||
|
strcpy(expanded, "/homes/");
|
||
|
strcat(expanded, namestr);
|
||
|
if (name != NULL) {
|
||
|
strcat(expanded, "/");
|
||
|
strcat(expanded, cleaned + 1);
|
||
|
}
|
||
|
}
|
||
|
free(cleaned);
|
||
|
cleaned = expanded;
|
||
|
/*fprintf(stderr, "expanded: %s\n", cleaned);*/
|
||
|
}
|
||
|
|
||
|
DEBUG_PRINT((" Yacc: insert argument \"%s\"\n", cleaned ));
|
||
|
expandString(cleaned);
|
||
|
/*Command::_currentSimpleCommand->insertArgument( cleaned );*/
|
||
|
}
|
||
|
;
|
||
|
|
||
|
command_word:
|
||
|
WORD {
|
||
|
DEBUG_PRINT((" Yacc: insert command \"%s\"\n", $1));
|
||
|
Command::_currentSimpleCommand = new SimpleCommand();
|
||
|
/*expandString($1);*/
|
||
|
Command::_currentSimpleCommand->insertArgument( $1 );
|
||
|
}
|
||
|
;
|
||
|
|
||
|
iomodifier_opt_list:
|
||
|
iomodifier_opt_list iomodifier_opt
|
||
|
| /* can be empty */
|
||
|
;
|
||
|
|
||
|
iomodifier_opt:
|
||
|
GREAT WORD {
|
||
|
if (Command::_currentCommand._outFile && Command::_currentCommand._outFile[0]) {
|
||
|
yyerror("Ambiguous output redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: insert output \"%s\"\n", $2));
|
||
|
Command::_currentCommand._outFile = $2;
|
||
|
}
|
||
|
| LESS WORD {
|
||
|
if (Command::_currentCommand._inFile && Command::_currentCommand._inFile[0]) {
|
||
|
yyerror("Ambiguous input redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: insert input \"%s\"\n", $2));
|
||
|
Command::_currentCommand._inFile = $2;
|
||
|
}
|
||
|
| TWOGREAT WORD {
|
||
|
if (Command::_currentCommand._errFile && Command::_currentCommand._errFile[0]) {
|
||
|
yyerror("Ambiguous error redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: insert stderr \"%s\"\n", $2));
|
||
|
Command::_currentCommand._errFile = $2;
|
||
|
}
|
||
|
| GREATAMP WORD {
|
||
|
if (Command::_currentCommand._errFile && Command::_currentCommand._errFile[0]
|
||
|
&& Command::_currentCommand._outFile && Command::_currentCommand._outFile[0]) {
|
||
|
yyerror("Ambiguous output and error redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: insert output and error \"%s\"\n", $2));
|
||
|
Command::_currentCommand._outFile = $2;
|
||
|
Command::_currentCommand._errFile = $2;
|
||
|
Command::_currentCommand._mergeOutErr = 1;
|
||
|
}
|
||
|
| GREATGREAT WORD {
|
||
|
if (Command::_currentCommand._outFile && Command::_currentCommand._outFile[0]) {
|
||
|
yyerror("Ambiguous append output redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: append output \"%s\"\n", $2));
|
||
|
Command::_currentCommand._appendOut = 1;
|
||
|
Command::_currentCommand._outFile = $2;
|
||
|
}
|
||
|
| GREATGREATAMP WORD {
|
||
|
if (Command::_currentCommand._errFile && Command::_currentCommand._errFile[0]
|
||
|
&& Command::_currentCommand._outFile && Command::_currentCommand._outFile[0]) {
|
||
|
yyerror("Ambiguous append output and error redirect\n");
|
||
|
Command::_currentCommand._parseFailure = 1;
|
||
|
}
|
||
|
DEBUG_PRINT((" Yacc: append output and error \"%s\"\n", $2));
|
||
|
Command::_currentCommand._appendOut = 1;
|
||
|
Command::_currentCommand._appendErr = 1;
|
||
|
Command::_currentCommand._outFile = $2;
|
||
|
Command::_currentCommand._errFile = $2;
|
||
|
Command::_currentCommand._mergeOutErr = 1;
|
||
|
}
|
||
|
;
|
||
|
|
||
|
pipe_opt_list:
|
||
|
pipe_opt_list pipe_opt
|
||
|
| /* can be empty */
|
||
|
;
|
||
|
|
||
|
pipe_opt:
|
||
|
PIPE command_and_args {
|
||
|
DEBUG_PRINT((" Yacc: pipe command\n"));
|
||
|
/* insert magic */
|
||
|
}
|
||
|
;
|
||
|
|
||
|
%%
|
||
|
|
||
|
void yyerror(const char * s)
|
||
|
{
|
||
|
fprintf(stderr,"%s", s);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
main()
|
||
|
{
|
||
|
yyparse();
|
||
|
}
|
||
|
#endif
|