Bring in basecode from development repo.
This commit is contained in:
commit
8ca7e77e25
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
*~
|
||||||
|
#*
|
||||||
|
*.d
|
||||||
|
*.o
|
||||||
|
*.out
|
||||||
|
bin/*
|
||||||
|
build/*
|
23
.gitlab-ci.yml
Normal file
23
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
image: hwrunner:latest
|
||||||
|
variables:
|
||||||
|
GIT_SSL_NO_VERIFY: "true"
|
||||||
|
EXEC: mush
|
||||||
|
HW_DIR: hw4
|
||||||
|
before_script:
|
||||||
|
- make clean all -C ${HW_DIR}
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- run
|
||||||
|
- test
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- echo "Build done"
|
||||||
|
run:
|
||||||
|
stage: run
|
||||||
|
script:
|
||||||
|
- cd ${HW_DIR} && bin/${EXEC} < rsrc/run_test.mush
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- cd ${HW_DIR} && bin/${EXEC}_tests -S --verbose=0 --timeout 5
|
61
hw4/Makefile
Normal file
61
hw4/Makefile
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
CC := gcc
|
||||||
|
YACC := bison
|
||||||
|
LEX := flex
|
||||||
|
SRCD := src
|
||||||
|
TSTD := tests
|
||||||
|
BLDD := build
|
||||||
|
BIND := bin
|
||||||
|
INCD := include
|
||||||
|
|
||||||
|
MAIN := $(BLDD)/main.o
|
||||||
|
|
||||||
|
ALL_SRCF := $(shell find $(SRCD) -type f -name *.c)
|
||||||
|
ALL_OBJF := $(patsubst $(SRCD)/%,$(BLDD)/%,$(ALL_SRCF:.c=.o))
|
||||||
|
ALL_FUNCF := $(filter-out $(MAIN) $(AUX), $(ALL_OBJF))
|
||||||
|
|
||||||
|
TEST_SRC := $(shell find $(TSTD) -type f -name *.c)
|
||||||
|
|
||||||
|
INC := -I $(INCD)
|
||||||
|
|
||||||
|
CFLAGS := -Wall -Werror -Wno-unused-function -MMD
|
||||||
|
COLORF := -DCOLOR
|
||||||
|
DFLAGS := -g -DDEBUG -DCOLOR
|
||||||
|
PRINT_STAMENTS := -DERROR -DSUCCESS -DWARN -DINFO
|
||||||
|
|
||||||
|
TEST_LIB := -lcriterion
|
||||||
|
LIBS :=
|
||||||
|
|
||||||
|
EXEC := mush
|
||||||
|
TEST_EXEC := $(EXEC)_tests
|
||||||
|
|
||||||
|
.PHONY: clean all setup debug
|
||||||
|
|
||||||
|
all: setup $(BIND)/$(EXEC) $(BIND)/$(TEST_EXEC)
|
||||||
|
|
||||||
|
debug: CFLAGS += $(DFLAGS) $(PRINT_STAMENTS) $(COLORF)
|
||||||
|
debug: all
|
||||||
|
|
||||||
|
setup: $(BIND) $(BLDD)
|
||||||
|
$(BIND):
|
||||||
|
mkdir -p $(BIND)
|
||||||
|
$(BLDD):
|
||||||
|
mkdir -p $(BLDD)
|
||||||
|
|
||||||
|
$(BIND)/$(EXEC): $(BLDD)/mush.tab.o $(BLDD)/mush.lex.o $(ALL_OBJF)
|
||||||
|
$(CC) $^ -o $@ $(LIBS)
|
||||||
|
|
||||||
|
$(BIND)/$(TEST_EXEC): $(ALL_FUNCF) $(BLDD)/mush.tab.o $(BLDD)/mush.lex.o $(TEST_SRC)
|
||||||
|
$(CC) $(CFLAGS) $(INC) $(ALL_FUNCF) $(TEST_SRC) $(TEST_LIB) $(LIBS) -o $@
|
||||||
|
|
||||||
|
$(BLDD)/%.o: $(SRCD)/%.c $(INCD)/$(EXEC).tab.h
|
||||||
|
$(CC) $(CFLAGS) $(INC) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BLDD) $(BIND)
|
||||||
|
|
||||||
|
# Cancel the implicit rule that is doing the wrong thing.
|
||||||
|
%.c: %.y
|
||||||
|
%.c: %.l
|
||||||
|
|
||||||
|
.PRECIOUS: $(BLDD)/*.d
|
||||||
|
-include $(BLDD)/*.d
|
BIN
hw4/demo/mush
Executable file
BIN
hw4/demo/mush
Executable file
Binary file not shown.
88
hw4/include/debug.h
Normal file
88
hw4/include/debug.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#ifndef DEBUG_H
|
||||||
|
#define DEBUG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define NL "\n"
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
#define KNRM "\033[0m"
|
||||||
|
#define KRED "\033[1;31m"
|
||||||
|
#define KGRN "\033[1;32m"
|
||||||
|
#define KYEL "\033[1;33m"
|
||||||
|
#define KBLU "\033[1;34m"
|
||||||
|
#define KMAG "\033[1;35m"
|
||||||
|
#define KCYN "\033[1;36m"
|
||||||
|
#define KWHT "\033[1;37m"
|
||||||
|
#define KBWN "\033[0;33m"
|
||||||
|
#else
|
||||||
|
#define KNRM ""
|
||||||
|
#define KRED ""
|
||||||
|
#define KGRN ""
|
||||||
|
#define KYEL ""
|
||||||
|
#define KBLU ""
|
||||||
|
#define KMAG ""
|
||||||
|
#define KCYN ""
|
||||||
|
#define KWHT ""
|
||||||
|
#define KBWN ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
#define DEBUG
|
||||||
|
#define INFO
|
||||||
|
#define WARN
|
||||||
|
#define ERROR
|
||||||
|
#define SUCCESS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debug(S, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, KMAG "DEBUG: %s:%s:%d " KNRM S NL, __FILE__, \
|
||||||
|
__extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define debug(S, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef INFO
|
||||||
|
#define info(S, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, KBLU "INFO: %s:%s:%d " KNRM S NL, __FILE__, \
|
||||||
|
__extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define info(S, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WARN
|
||||||
|
#define warn(S, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, KYEL "WARN: %s:%s:%d " KNRM S NL, __FILE__, \
|
||||||
|
__extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define warn(S, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SUCCESS
|
||||||
|
#define success(S, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, KGRN "SUCCESS: %s:%s:%d " KNRM S NL, __FILE__, \
|
||||||
|
__extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define success(S, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ERROR
|
||||||
|
#define error(S, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, KRED "ERROR: %s:%s:%d " KNRM S NL, __FILE__, \
|
||||||
|
__extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define error(S, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DEBUG_H */
|
53
hw4/include/mush.h
Normal file
53
hw4/include/mush.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* DO NOT MODIFY THE CONTENTS OF THIS FILE.
|
||||||
|
* IT WILL BE REPLACED DURING GRADING
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "syntax.h"
|
||||||
|
|
||||||
|
/* Names of special store variables to hold results from job execution. */
|
||||||
|
#define JOB_VAR "JOB"
|
||||||
|
#define STATUS_VAR "STATUS"
|
||||||
|
#define OUTPUT_VAR "OUTPUT"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If you find it convenient, you may assume that the maximum number of jobs
|
||||||
|
* that can exist at one time is given by the following preprocessor symbol.
|
||||||
|
* Your code should continue to work even if the particular value of this
|
||||||
|
* symbol is changed before compilation.
|
||||||
|
*/
|
||||||
|
#define MAX_JOBS 10
|
||||||
|
|
||||||
|
/* Functions in program store module. */
|
||||||
|
int prog_list(FILE *out);
|
||||||
|
int prog_insert(STMT *stmt);
|
||||||
|
int prog_delete(int min, int max);
|
||||||
|
void prog_reset();
|
||||||
|
STMT *prog_fetch();
|
||||||
|
STMT *prog_next();
|
||||||
|
STMT *prog_goto(int lineno);
|
||||||
|
|
||||||
|
/* Functions in data store module. */
|
||||||
|
char *store_get_string(char *var);
|
||||||
|
int store_get_int(char *var, long *valp);
|
||||||
|
int store_set_string(char *var, char *val);
|
||||||
|
int store_set_int(char *var, long val);
|
||||||
|
void store_show(FILE *f);
|
||||||
|
|
||||||
|
/* Functions in execution module. */
|
||||||
|
int exec_interactive();
|
||||||
|
int exec_stmt(STMT *stmt);
|
||||||
|
char *eval_to_string(EXPR *expr);
|
||||||
|
long eval_to_numeric(EXPR *expr);
|
||||||
|
|
||||||
|
/* Functions in jobs module. */
|
||||||
|
int jobs_init(void);
|
||||||
|
int jobs_fini(void);
|
||||||
|
int jobs_run(PIPELINE *pp);
|
||||||
|
int jobs_expunge(int jobid);
|
||||||
|
int jobs_wait(int jobid);
|
||||||
|
int jobs_poll(int jobid);
|
||||||
|
int jobs_cancel(int jobid);
|
||||||
|
int jobs_pause(void);
|
||||||
|
char *jobs_get_output(int jobid);
|
||||||
|
int jobs_show(FILE *file);
|
125
hw4/include/mush.tab.h
Normal file
125
hw4/include/mush.tab.h
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/* A Bison parser, made by GNU Bison 3.5.1. */
|
||||||
|
|
||||||
|
/* Bison interface for Yacc-like parsers in C
|
||||||
|
|
||||||
|
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
|
||||||
|
Inc.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison. */
|
||||||
|
|
||||||
|
/* Undocumented macros, especially those whose name start with YY_,
|
||||||
|
are private implementation details. Do not rely on them. */
|
||||||
|
|
||||||
|
#ifndef YY_YY_INCLUDE_MUSH_TAB_H_INCLUDED
|
||||||
|
# define YY_YY_INCLUDE_MUSH_TAB_H_INCLUDED
|
||||||
|
/* Debug traces. */
|
||||||
|
#ifndef YYDEBUG
|
||||||
|
# define YYDEBUG 1
|
||||||
|
#endif
|
||||||
|
#if YYDEBUG
|
||||||
|
extern int yydebug;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Token type. */
|
||||||
|
#ifndef YYTOKENTYPE
|
||||||
|
# define YYTOKENTYPE
|
||||||
|
enum yytokentype
|
||||||
|
{
|
||||||
|
NUMBER = 258,
|
||||||
|
NAME = 259,
|
||||||
|
WORD = 260,
|
||||||
|
STRING = 261,
|
||||||
|
LIST = 262,
|
||||||
|
DELETE = 263,
|
||||||
|
RUN = 264,
|
||||||
|
CONT = 265,
|
||||||
|
STOP = 266,
|
||||||
|
BG = 267,
|
||||||
|
CAPTURE = 268,
|
||||||
|
WAIT = 269,
|
||||||
|
POLL = 270,
|
||||||
|
CANCEL = 271,
|
||||||
|
PAUSE = 272,
|
||||||
|
SET = 273,
|
||||||
|
UNSET = 274,
|
||||||
|
IF = 275,
|
||||||
|
GOTO = 276,
|
||||||
|
SOURCE = 277,
|
||||||
|
EQ = 278,
|
||||||
|
PIPE = 279,
|
||||||
|
LESS = 280,
|
||||||
|
GREATER = 281,
|
||||||
|
EQUAL = 282,
|
||||||
|
LESSEQ = 283,
|
||||||
|
GREATEQ = 284,
|
||||||
|
AND = 285,
|
||||||
|
OR = 286,
|
||||||
|
NOT = 287,
|
||||||
|
LPAREN = 288,
|
||||||
|
RPAREN = 289,
|
||||||
|
PLUS = 290,
|
||||||
|
MINUS = 291,
|
||||||
|
TIMES = 292,
|
||||||
|
DIVIDE = 293,
|
||||||
|
MOD = 294,
|
||||||
|
COMMA = 295,
|
||||||
|
SHARP = 296,
|
||||||
|
DOLLAR = 297,
|
||||||
|
EOL = 298,
|
||||||
|
EoF = 299,
|
||||||
|
UNKNOWN = 300
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Value type. */
|
||||||
|
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||||
|
union YYSTYPE
|
||||||
|
{
|
||||||
|
#line 30 "src/mush.y"
|
||||||
|
|
||||||
|
int number;
|
||||||
|
char *string;
|
||||||
|
STMT *stmt;
|
||||||
|
EXPR *expr;
|
||||||
|
ARG *args;
|
||||||
|
COMMAND *cmds;
|
||||||
|
PIPELINE *pline;
|
||||||
|
|
||||||
|
#line 113 "include/mush.tab.h"
|
||||||
|
|
||||||
|
};
|
||||||
|
typedef union YYSTYPE YYSTYPE;
|
||||||
|
# define YYSTYPE_IS_TRIVIAL 1
|
||||||
|
# define YYSTYPE_IS_DECLARED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
extern YYSTYPE yylval;
|
||||||
|
|
||||||
|
int yyparse (void);
|
||||||
|
|
||||||
|
#endif /* !YY_YY_INCLUDE_MUSH_TAB_H_INCLUDED */
|
212
hw4/include/syntax.h
Normal file
212
hw4/include/syntax.h
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* DO NOT MODIFY THE CONTENTS OF THIS FILE.
|
||||||
|
* IT WILL BE REPLACED DURING GRADING
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The elements of this enumerated type are used in the "class" field
|
||||||
|
* of the "STMT" structure to specify the particular kind of statement
|
||||||
|
* that the structure represents. The parenthesized names in the comments
|
||||||
|
* indicate which element (if any) of the "members" union in the STMT
|
||||||
|
* structure is used by each kind of statement.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NO_STMT_CLASS,
|
||||||
|
LIST_STMT_CLASS, // "list" statement
|
||||||
|
DELETE_STMT_CLASS, // "delete" statement (delete_stmt)
|
||||||
|
RUN_STMT_CLASS, // "run" statement
|
||||||
|
CONT_STMT_CLASS, // "cont" statement
|
||||||
|
STOP_STMT_CLASS, // "stop" statement
|
||||||
|
WAIT_STMT_CLASS, // "wait" statement (jobctl_stmt)
|
||||||
|
POLL_STMT_CLASS, // "poll" statement (jobctl_stmt)
|
||||||
|
CANCEL_STMT_CLASS, // "cancel" statement (jobctl_stmt)
|
||||||
|
PAUSE_STMT_CLASS, // "pause" statement
|
||||||
|
SET_STMT_CLASS, // "set" statement (set_stmt)
|
||||||
|
UNSET_STMT_CLASS, // "unset" statement (unset_stmt)
|
||||||
|
IF_STMT_CLASS, // "if" statement (if_stmt)
|
||||||
|
GOTO_STMT_CLASS, // "goto" statement (goto_stmt)
|
||||||
|
SOURCE_STMT_CLASS, // "source" statement (source_stmt)
|
||||||
|
FG_STMT_CLASS, // pipeline run in foreground (sys_stmt)
|
||||||
|
BG_STMT_CLASS // pipeline run in background (sys_stmt)
|
||||||
|
} STMT_CLASS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is used to represent a statement.
|
||||||
|
* The value in the "class" field tells what kind of statement it is.
|
||||||
|
* Depending on this value, one of the fields of the "members" union
|
||||||
|
* may be valid.
|
||||||
|
*/
|
||||||
|
typedef struct stmt {
|
||||||
|
STMT_CLASS class;
|
||||||
|
int lineno;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
int from;
|
||||||
|
int to;
|
||||||
|
} delete_stmt;
|
||||||
|
struct {
|
||||||
|
struct pipeline *pipeline;
|
||||||
|
} sys_stmt;
|
||||||
|
struct {
|
||||||
|
struct expr *expr;
|
||||||
|
} jobctl_stmt;
|
||||||
|
struct {
|
||||||
|
char *name;
|
||||||
|
struct expr *expr;
|
||||||
|
} set_stmt;
|
||||||
|
struct {
|
||||||
|
char *name;
|
||||||
|
} unset_stmt;
|
||||||
|
struct {
|
||||||
|
struct expr *expr;
|
||||||
|
int lineno;
|
||||||
|
} if_stmt;
|
||||||
|
struct {
|
||||||
|
int lineno;
|
||||||
|
} goto_stmt;
|
||||||
|
struct {
|
||||||
|
char *file;
|
||||||
|
} source_stmt;
|
||||||
|
} members;
|
||||||
|
} STMT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is used to represent a command. The "args" field points
|
||||||
|
* to the head of as a nonempty linked list of "args" that make up the command.
|
||||||
|
* The first "arg" is interpreted as the name of the command; the remainder
|
||||||
|
* are arguments to it. The "next" field is used to link commands into a
|
||||||
|
* pipeline.
|
||||||
|
*/
|
||||||
|
typedef struct command {
|
||||||
|
struct arg *args;
|
||||||
|
struct command *next;
|
||||||
|
} COMMAND;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is used to represent a "pipeline". The "commands" field
|
||||||
|
* points to the head of a nonempty list of commands. The "input_file" field,
|
||||||
|
* if non-NULL, is the name of a file from which input to the first command
|
||||||
|
* in the pipeline is to be redirected. Similarly, the "output_file" field,
|
||||||
|
* if non-NULL, is the name of a file to which output from the last command
|
||||||
|
* in the pipeline is to be redirected. If the "capture_output" field is
|
||||||
|
* nonzero, then instead of being redirected to a file, the output from the
|
||||||
|
* last command in the pipeline is to be redirected to a pipe. This pipe is
|
||||||
|
* to be read by the main process, which will "captures" the output and make
|
||||||
|
* it available as the value of a variable in the data store.
|
||||||
|
*/
|
||||||
|
typedef struct pipeline {
|
||||||
|
COMMAND *commands;
|
||||||
|
char *input_file;
|
||||||
|
char *output_file;
|
||||||
|
int capture_output;
|
||||||
|
} PIPELINE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Values of this enumerated type are used to identify the various kinds
|
||||||
|
* of expressions.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NO_EXPR_CLASS,
|
||||||
|
LIT_EXPR_CLASS, // literal string (value)
|
||||||
|
NUM_EXPR_CLASS, // numeric variable (variable)
|
||||||
|
STRING_EXPR_CLASS, // string variable (variable)
|
||||||
|
UNARY_EXPR_CLASS, // unary expression (unary_expr)
|
||||||
|
BINARY_EXPR_CLASS // binary expression (binary_expr)
|
||||||
|
} EXPR_CLASS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Values of this enumerated type are used to identify the various kinds
|
||||||
|
* of operators in expressions.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NO_OPRTR,
|
||||||
|
NOT_OPRTR, // "not" operator (unary_expr)
|
||||||
|
AND_OPRTR, // "and" operator (binary_expr)
|
||||||
|
OR_OPRTR, // "or" operator (binary_expr)
|
||||||
|
EQUAL_OPRTR, // "equal to" operator (binary_expr)
|
||||||
|
LESS_OPRTR, // "less than" operator (binary_expr)
|
||||||
|
GREATER_OPRTR, // "greater than" operator (binary_expr)
|
||||||
|
LESSEQ_OPRTR, // "less than or equal" operator (binary_expr)
|
||||||
|
GREATEQ_OPRTR, // "greater than or equal" operator (binary_expr)
|
||||||
|
PLUS_OPRTR, // "plus" (binary_expr)
|
||||||
|
MINUS_OPRTR, // "minus" (binary_expr)
|
||||||
|
TIMES_OPRTR, // "times" (binary_expr)
|
||||||
|
DIVIDE_OPRTR, // "divide" (binary_expr)
|
||||||
|
MOD_OPRTR // "mod" (binary_expr)
|
||||||
|
} OPRTR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Values of this enumerated type are used to identify the various kinds
|
||||||
|
* values that an expression can have.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NO_VALUE_TYPE,
|
||||||
|
NUM_VALUE_TYPE, // numeric value
|
||||||
|
STRING_VALUE_TYPE // string value
|
||||||
|
} VALUE_TYPE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is used to represent an "expression".
|
||||||
|
*/
|
||||||
|
typedef struct expr {
|
||||||
|
EXPR_CLASS class;
|
||||||
|
VALUE_TYPE type;
|
||||||
|
union {
|
||||||
|
char *variable;
|
||||||
|
char *value;
|
||||||
|
struct {
|
||||||
|
OPRTR oprtr;
|
||||||
|
struct expr *arg;
|
||||||
|
} unary_expr;
|
||||||
|
struct {
|
||||||
|
OPRTR oprtr;
|
||||||
|
struct expr *arg1;
|
||||||
|
struct expr *arg2;
|
||||||
|
} binary_expr;
|
||||||
|
} members;
|
||||||
|
} EXPR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is used to represent an "argument", which is
|
||||||
|
* a single element of a command. Arguments contain arbitrary expressions,
|
||||||
|
* which allows commands to be constructed that depend on the values
|
||||||
|
* of variables.
|
||||||
|
*/
|
||||||
|
typedef struct arg {
|
||||||
|
EXPR *expr;
|
||||||
|
struct arg *next;
|
||||||
|
} ARG;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following functions are used to print representations
|
||||||
|
* of the various syntactic objects to a specified output stream.
|
||||||
|
* A nonzero value for the "parens" argument of show_expr() causes
|
||||||
|
* a compound expression to be printed within enclosing parentheses.
|
||||||
|
*/
|
||||||
|
void show_stmt(FILE *file, STMT *stmt);
|
||||||
|
void show_pipeline(FILE *file, PIPELINE *pline);
|
||||||
|
void show_command(FILE *file, COMMAND *cmd);
|
||||||
|
void show_expr(FILE *file, EXPR *expr, int parens);
|
||||||
|
void show_oprtr(FILE *file, OPRTR oprtr);
|
||||||
|
void show_lineno(FILE *file, int lineno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following functions are use to free the various syntactic
|
||||||
|
* objects. Freeing an object with one of these functions implies
|
||||||
|
* freeing all the objects that it references.
|
||||||
|
*/
|
||||||
|
void free_stmt(STMT *stmt);
|
||||||
|
void free_pipeline(PIPELINE *pline);
|
||||||
|
void free_commands(COMMAND *cmd);
|
||||||
|
void free_args(ARG *args);
|
||||||
|
void free_expr(EXPR *expr);
|
||||||
|
void free_oprtr(OPRTR oprtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The following functions are use to make deep copies of the
|
||||||
|
* various syntactic objects.
|
||||||
|
*/
|
||||||
|
PIPELINE *copy_pipeline(PIPELINE *pline);
|
||||||
|
COMMAND *copy_commands(COMMAND *cmd);
|
||||||
|
ARG *copy_args(ARG *args);
|
||||||
|
EXPR *copy_expr(EXPR *expr);
|
7
hw4/rsrc/bg_test.mush
Normal file
7
hw4/rsrc/bg_test.mush
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
10 sleep 10 &
|
||||||
|
15 echo line 15
|
||||||
|
20 sleep 5 &
|
||||||
|
25 echo line 25
|
||||||
|
30 sleep 3 &
|
||||||
|
35 echo line 35
|
||||||
|
run
|
6
hw4/rsrc/cancel_test.mush
Normal file
6
hw4/rsrc/cancel_test.mush
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
10 sleep 10 &
|
||||||
|
15 set j10 = #JOB
|
||||||
|
20 sleep 2
|
||||||
|
30 cancel #j10
|
||||||
|
40 wait #j10
|
||||||
|
run
|
7
hw4/rsrc/delete_test.mush
Normal file
7
hw4/rsrc/delete_test.mush
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
10 echo line 10
|
||||||
|
20 echo line 20
|
||||||
|
30 echo line 30
|
||||||
|
40 echo line 40
|
||||||
|
50 echo line 50
|
||||||
|
delete 10,25
|
||||||
|
list
|
7
hw4/rsrc/fg_test.mush
Normal file
7
hw4/rsrc/fg_test.mush
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
10 sleep 10
|
||||||
|
15 echo line 15
|
||||||
|
20 sleep 5
|
||||||
|
25 echo line 25
|
||||||
|
30 sleep 3
|
||||||
|
35 echo line 35
|
||||||
|
run
|
4
hw4/rsrc/goto_test.mush
Normal file
4
hw4/rsrc/goto_test.mush
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
10 goto 30
|
||||||
|
20 stop
|
||||||
|
30 echo yes
|
||||||
|
run
|
3
hw4/rsrc/list_test.mush
Normal file
3
hw4/rsrc/list_test.mush
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
10 echo line 10
|
||||||
|
20 echo line 20
|
||||||
|
list
|
4
hw4/rsrc/loop1.mush
Normal file
4
hw4/rsrc/loop1.mush
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
10 echo hello
|
||||||
|
20 sleep 1
|
||||||
|
30 goto 10
|
||||||
|
run
|
8
hw4/rsrc/loop2.mush
Normal file
8
hw4/rsrc/loop2.mush
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
10 set x = 1
|
||||||
|
20 echo #x
|
||||||
|
25 sleep 1
|
||||||
|
30 set x = !#x
|
||||||
|
35 echo #x
|
||||||
|
40 sleep 1
|
||||||
|
50 goto 10
|
||||||
|
run
|
3
hw4/rsrc/pause_test.mush
Normal file
3
hw4/rsrc/pause_test.mush
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
10 sleep 10 &
|
||||||
|
20 pause
|
||||||
|
run
|
3
hw4/rsrc/pipeline_test.mush
Normal file
3
hw4/rsrc/pipeline_test.mush
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
10 cat "/usr/share/dict/words" | grep program | grep sub > "pipeline_test.out"
|
||||||
|
20 cat "pipeline_test.out"
|
||||||
|
run
|
4
hw4/rsrc/run_test.mush
Normal file
4
hw4/rsrc/run_test.mush
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
10 echo line 10
|
||||||
|
20 echo line 20
|
||||||
|
30 echo line 30
|
||||||
|
run
|
4
hw4/rsrc/stop_test.mush
Normal file
4
hw4/rsrc/stop_test.mush
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
10 echo line 10
|
||||||
|
20 stop
|
||||||
|
30 echo line 30
|
||||||
|
run
|
19
hw4/rsrc/wait_test.mush
Normal file
19
hw4/rsrc/wait_test.mush
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
10 sleep 10 &
|
||||||
|
11 set j10 = #JOB
|
||||||
|
12 echo j10 #j10
|
||||||
|
15 echo line 15
|
||||||
|
20 sleep 5 &
|
||||||
|
21 set j20 = #JOB
|
||||||
|
22 echo j20 #j20
|
||||||
|
25 echo line 25
|
||||||
|
30 sleep 3 &
|
||||||
|
31 set j30 = #JOB
|
||||||
|
32 echo j30 #j30
|
||||||
|
35 echo line 35
|
||||||
|
40 wait #j30
|
||||||
|
45 echo j30 #STATUS
|
||||||
|
50 wait #j20
|
||||||
|
55 echo j20 #STATUS
|
||||||
|
60 wait #j10
|
||||||
|
65 echo j10 #STATUS
|
||||||
|
run
|
392
hw4/src/execution.c
Normal file
392
hw4/src/execution.c
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
/*
|
||||||
|
* DO NOT MODIFY THE CONTENTS OF THIS FILE.
|
||||||
|
* IT WILL BE REPLACED DURING GRADING
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "mush.h"
|
||||||
|
#include "mush.tab.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mush: Execution engine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int yylex_destroy();
|
||||||
|
extern void push_input(FILE *in);
|
||||||
|
extern int pop_input(void);
|
||||||
|
extern int input_depth(void);
|
||||||
|
extern STMT *mush_parsed_stmt;
|
||||||
|
|
||||||
|
static int exec_run();
|
||||||
|
static int exec_cont();
|
||||||
|
|
||||||
|
#define PROMPT "mush: "
|
||||||
|
|
||||||
|
/* Uncomment this to enable tracing of the parser. */
|
||||||
|
//int yydebug = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A quit handler is installed to allow user-initiated escape
|
||||||
|
* from constructs that wait for signals.
|
||||||
|
*/
|
||||||
|
static volatile sig_atomic_t got_quit = 0;
|
||||||
|
|
||||||
|
static void handler(int sig) {
|
||||||
|
got_quit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Target for longjmp() to jump to after a low-level error has
|
||||||
|
* occurred, e.g. in expression evaluation.
|
||||||
|
*/
|
||||||
|
static jmp_buf onerror;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Top-level interpreter loop.
|
||||||
|
* Reads single statements from stdin and either inserts them into the program,
|
||||||
|
* if they have a line number, otherwise executes them immediately.
|
||||||
|
*/
|
||||||
|
int exec_interactive() {
|
||||||
|
signal(SIGQUIT, SIG_IGN);
|
||||||
|
while(1) {
|
||||||
|
if(!input_depth() && isatty(fileno(stdin)))
|
||||||
|
fprintf(stdout, "%s", PROMPT);
|
||||||
|
fflush(stdout);
|
||||||
|
if(!yyparse()) {
|
||||||
|
STMT *stmt = mush_parsed_stmt;
|
||||||
|
if(stmt != NULL) {
|
||||||
|
if(stmt->lineno) {
|
||||||
|
prog_insert(stmt);
|
||||||
|
} else {
|
||||||
|
if(stmt->class == RUN_STMT_CLASS) {
|
||||||
|
free_stmt(stmt);
|
||||||
|
stmt = NULL;
|
||||||
|
exec_run();
|
||||||
|
} else if(stmt->class == CONT_STMT_CLASS) {
|
||||||
|
free_stmt(stmt);
|
||||||
|
stmt = NULL;
|
||||||
|
exec_cont();
|
||||||
|
} else {
|
||||||
|
exec_stmt(stmt);
|
||||||
|
free_stmt(stmt);
|
||||||
|
stmt = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(pop_input())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!input_depth() && isatty(fileno(stdin))) {
|
||||||
|
store_show(stderr);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
jobs_show(stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yylex_destroy();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enter an execution loop starting at the beginning of the program.
|
||||||
|
*/
|
||||||
|
static int exec_run() {
|
||||||
|
prog_reset();
|
||||||
|
return exec_cont();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enter an execution loop starting at the current line number.
|
||||||
|
*/
|
||||||
|
static int exec_cont() {
|
||||||
|
int err = 0;
|
||||||
|
STMT *stmt;
|
||||||
|
stmt = prog_fetch();
|
||||||
|
if(stmt == NULL) {
|
||||||
|
fprintf(stderr, "No statement to execute\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(setjmp(onerror))
|
||||||
|
return -1;
|
||||||
|
signal(SIGQUIT, handler);
|
||||||
|
while(!got_quit) {
|
||||||
|
stmt = prog_fetch();
|
||||||
|
if(!stmt) {
|
||||||
|
fprintf(stderr, "STOP (end of program)\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prog_next();
|
||||||
|
err = exec_stmt(stmt);
|
||||||
|
if(err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
signal(SIGQUIT, SIG_IGN);
|
||||||
|
if(got_quit)
|
||||||
|
fprintf(stderr, "Quit!\n");
|
||||||
|
got_quit = 0;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute a statement.
|
||||||
|
* This function is called from exec_run().
|
||||||
|
* It can also be called separately to execute an individual statement
|
||||||
|
* read interactively.
|
||||||
|
*
|
||||||
|
* Successful execution (except for STOP) returns 0.
|
||||||
|
* Successful execution of STOP returns 1.
|
||||||
|
* Unsuccessful execution returns -1.
|
||||||
|
*/
|
||||||
|
int exec_stmt(STMT *stmt) {
|
||||||
|
int val; char *str;
|
||||||
|
FILE *in;
|
||||||
|
if(setjmp(onerror))
|
||||||
|
return -1;
|
||||||
|
if(stmt->lineno)
|
||||||
|
debug("execute statement %d", stmt->lineno);
|
||||||
|
switch(stmt->class) {
|
||||||
|
case LIST_STMT_CLASS:
|
||||||
|
prog_list(stdout);
|
||||||
|
break;
|
||||||
|
case DELETE_STMT_CLASS:
|
||||||
|
prog_delete(stmt->members.delete_stmt.from, stmt->members.delete_stmt.to);
|
||||||
|
break;
|
||||||
|
case STOP_STMT_CLASS:
|
||||||
|
if(stmt->lineno)
|
||||||
|
fprintf(stderr, "STOP at line %d\n", stmt->lineno);
|
||||||
|
return 1;
|
||||||
|
case GOTO_STMT_CLASS:
|
||||||
|
if(!prog_goto(stmt->members.goto_stmt.lineno))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
case SET_STMT_CLASS:
|
||||||
|
switch(stmt->members.set_stmt.expr->type) {
|
||||||
|
case NUM_VALUE_TYPE:
|
||||||
|
val = eval_to_numeric(stmt->members.set_stmt.expr);
|
||||||
|
store_set_int(stmt->members.set_stmt.name, val);
|
||||||
|
break;
|
||||||
|
case STRING_VALUE_TYPE:
|
||||||
|
str = eval_to_string(stmt->members.set_stmt.expr);
|
||||||
|
store_set_string(stmt->members.set_stmt.name, str);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UNSET_STMT_CLASS:
|
||||||
|
store_set_string(stmt->members.unset_stmt.name, NULL);
|
||||||
|
break;
|
||||||
|
case IF_STMT_CLASS:
|
||||||
|
val = eval_to_numeric(stmt->members.if_stmt.expr);
|
||||||
|
if(val) {
|
||||||
|
if(!prog_goto(stmt->members.if_stmt.lineno))
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SOURCE_STMT_CLASS:
|
||||||
|
in = fopen(stmt->members.source_stmt.file, "r");
|
||||||
|
if(!in) {
|
||||||
|
fprintf(stderr, "Couldn't open source file: '%s'\n",
|
||||||
|
stmt->members.source_stmt.file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
push_input(in);
|
||||||
|
break;
|
||||||
|
case FG_STMT_CLASS:
|
||||||
|
{
|
||||||
|
PIPELINE *pp = stmt->members.sys_stmt.pipeline;
|
||||||
|
int job = jobs_run(pp);
|
||||||
|
store_set_int(JOB_VAR, job);
|
||||||
|
int status = jobs_wait(job);
|
||||||
|
store_set_int(STATUS_VAR, status);
|
||||||
|
if(pp->capture_output) {
|
||||||
|
char *output = jobs_get_output(job);
|
||||||
|
debug("Captured output: '%s'", output);
|
||||||
|
store_set_string(OUTPUT_VAR, output);
|
||||||
|
}
|
||||||
|
jobs_expunge(job);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BG_STMT_CLASS:
|
||||||
|
{
|
||||||
|
int job = jobs_run(stmt->members.sys_stmt.pipeline);
|
||||||
|
store_set_int(JOB_VAR, job);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WAIT_STMT_CLASS:
|
||||||
|
{
|
||||||
|
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
|
||||||
|
int status = jobs_wait(job);
|
||||||
|
store_set_int(STATUS_VAR, status);
|
||||||
|
jobs_expunge(job);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case POLL_STMT_CLASS:
|
||||||
|
{
|
||||||
|
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
|
||||||
|
int status = jobs_poll(job);
|
||||||
|
store_set_int(STATUS_VAR, status);
|
||||||
|
if(status >= 0)
|
||||||
|
jobs_expunge(job);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CANCEL_STMT_CLASS:
|
||||||
|
{
|
||||||
|
int job = eval_to_numeric(stmt->members.jobctl_stmt.expr);
|
||||||
|
jobs_cancel(job);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PAUSE_STMT_CLASS:
|
||||||
|
{
|
||||||
|
jobs_pause();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown statement class: %d\n", stmt->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression, returning an integer result.
|
||||||
|
* It is assumed that the jmp_buf onerror has been initialized by the caller
|
||||||
|
* with a control point to escape to in case there is an error during evaluation.
|
||||||
|
*/
|
||||||
|
long eval_to_numeric(EXPR *expr) {
|
||||||
|
char *endp, *str1, *str2;
|
||||||
|
long opr1, opr2;
|
||||||
|
int err;
|
||||||
|
switch(expr->class) {
|
||||||
|
case LIT_EXPR_CLASS:
|
||||||
|
opr1 = strtol(expr->members.value, &endp, 0);
|
||||||
|
if(*endp == '\0') {
|
||||||
|
return opr1;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Literal '%s' is not an integer\n", expr->members.value);
|
||||||
|
longjmp(onerror, 0);
|
||||||
|
}
|
||||||
|
case STRING_EXPR_CLASS:
|
||||||
|
case NUM_EXPR_CLASS:
|
||||||
|
err = store_get_int(expr->members.variable, &opr1);
|
||||||
|
if(err) {
|
||||||
|
fprintf(stderr, "Variable %s does not have an integer value\n",
|
||||||
|
expr->members.variable);
|
||||||
|
longjmp(onerror, 0);
|
||||||
|
}
|
||||||
|
return opr1;
|
||||||
|
fprintf(stderr, "String variable %s in expression not implemented\n",
|
||||||
|
expr->members.variable);
|
||||||
|
abort();
|
||||||
|
case UNARY_EXPR_CLASS:
|
||||||
|
opr1 = eval_to_numeric(expr->members.unary_expr.arg);
|
||||||
|
switch(expr->members.unary_expr.oprtr) {
|
||||||
|
case NOT_OPRTR:
|
||||||
|
return opr1 ? 0 : 1;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown unary numeric operator: %d\n",
|
||||||
|
expr->members.unary_expr.oprtr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
case BINARY_EXPR_CLASS:
|
||||||
|
if(expr->members.binary_expr.oprtr == EQUAL_OPRTR) {
|
||||||
|
if(expr->members.binary_expr.arg1->type == NUM_VALUE_TYPE &&
|
||||||
|
expr->members.binary_expr.arg2->type == NUM_VALUE_TYPE) {
|
||||||
|
opr1 = eval_to_numeric(expr->members.binary_expr.arg1);
|
||||||
|
opr2 = eval_to_numeric(expr->members.binary_expr.arg2);
|
||||||
|
return opr1 == opr2;
|
||||||
|
} else {
|
||||||
|
str1 = eval_to_string(expr->members.binary_expr.arg1);
|
||||||
|
str2 = eval_to_string(expr->members.binary_expr.arg2);
|
||||||
|
return !strcmp(str1, str2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opr1 = eval_to_numeric(expr->members.binary_expr.arg1);
|
||||||
|
opr2 = eval_to_numeric(expr->members.binary_expr.arg2);
|
||||||
|
switch(expr->members.binary_expr.oprtr) {
|
||||||
|
case AND_OPRTR:
|
||||||
|
return opr1 && opr2;
|
||||||
|
case OR_OPRTR:
|
||||||
|
return opr1 || opr2;
|
||||||
|
case PLUS_OPRTR:
|
||||||
|
return opr1 + opr2;
|
||||||
|
case MINUS_OPRTR:
|
||||||
|
return opr1 - opr2;
|
||||||
|
case TIMES_OPRTR:
|
||||||
|
return opr1 * opr2;
|
||||||
|
case DIVIDE_OPRTR:
|
||||||
|
return opr1 / opr2;
|
||||||
|
case MOD_OPRTR:
|
||||||
|
return opr1 % opr2;
|
||||||
|
case LESS_OPRTR:
|
||||||
|
return opr1 < opr2;
|
||||||
|
case GREATER_OPRTR:
|
||||||
|
return opr1 > opr2;
|
||||||
|
case LESSEQ_OPRTR:
|
||||||
|
return opr1 <= opr2;
|
||||||
|
case GREATEQ_OPRTR:
|
||||||
|
return opr1 >= opr2;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown binary numeric operator: %d\n",
|
||||||
|
expr->members.binary_expr.oprtr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate an expression, returning a string result.
|
||||||
|
* It is assumed that the jmp_buf onerror has been initialized by the caller
|
||||||
|
* with a control point to escape to in case there is an error during evaluation.
|
||||||
|
*/
|
||||||
|
char *eval_to_string(EXPR *expr) {
|
||||||
|
char *str1, *str2;
|
||||||
|
switch(expr->class) {
|
||||||
|
case LIT_EXPR_CLASS:
|
||||||
|
return expr->members.value;
|
||||||
|
case NUM_EXPR_CLASS:
|
||||||
|
case STRING_EXPR_CLASS:
|
||||||
|
str1 = store_get_string(expr->members.variable);
|
||||||
|
if(!str1) {
|
||||||
|
fprintf(stderr, "Variable %s does not have a value\n",
|
||||||
|
expr->members.variable);
|
||||||
|
longjmp(onerror, 0);
|
||||||
|
}
|
||||||
|
return str1;
|
||||||
|
case UNARY_EXPR_CLASS:
|
||||||
|
str1 = eval_to_string(expr->members.unary_expr.arg);
|
||||||
|
switch(expr->members.unary_expr.oprtr) {
|
||||||
|
default:
|
||||||
|
(void)str1;
|
||||||
|
fprintf(stderr, "Unknown unary string operator: %d\n",
|
||||||
|
expr->members.unary_expr.oprtr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
case BINARY_EXPR_CLASS:
|
||||||
|
str1 = eval_to_string(expr->members.binary_expr.arg1);
|
||||||
|
str2 = eval_to_string(expr->members.binary_expr.arg2);
|
||||||
|
switch(expr->members.binary_expr.oprtr) {
|
||||||
|
default:
|
||||||
|
(void)str1; (void)str2;
|
||||||
|
fprintf(stderr, "Unknown binary string operator: %d\n",
|
||||||
|
expr->members.binary_expr.oprtr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
223
hw4/src/jobs.c
Normal file
223
hw4/src/jobs.c
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "mush.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the "jobs" module for Mush.
|
||||||
|
* It maintains a table of jobs in various stages of execution, and it
|
||||||
|
* provides functions for manipulating jobs.
|
||||||
|
* Each job contains a pipeline, which is used to initialize the processes,
|
||||||
|
* pipelines, and redirections that make up the job.
|
||||||
|
* Each job has a job ID, which is an integer value that is used to identify
|
||||||
|
* that job when calling the various job manipulation functions.
|
||||||
|
*
|
||||||
|
* At any given time, a job will have one of the following status values:
|
||||||
|
* "new", "running", "completed", "aborted", "canceled".
|
||||||
|
* A newly created job starts out in with status "new".
|
||||||
|
* It changes to status "running" when the processes that make up the pipeline
|
||||||
|
* for that job have been created.
|
||||||
|
* A running job becomes "completed" at such time as all the processes in its
|
||||||
|
* pipeline have terminated successfully.
|
||||||
|
* A running job becomes "aborted" if the last process in its pipeline terminates
|
||||||
|
* with a signal that is not the result of the pipeline having been canceled.
|
||||||
|
* A running job becomes "canceled" if the jobs_cancel() function was called
|
||||||
|
* to cancel it and in addition the last process in the pipeline subsequently
|
||||||
|
* terminated with signal SIGKILL.
|
||||||
|
*
|
||||||
|
* In general, there will be other state information stored for each job,
|
||||||
|
* as required by the implementation of the various functions in this module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the jobs module.
|
||||||
|
* @details This function is used to initialize the jobs module.
|
||||||
|
* It must be called exactly once, before any other functions of this
|
||||||
|
* module are called.
|
||||||
|
*
|
||||||
|
* @return 0 if initialization is successful, otherwise -1.
|
||||||
|
*/
|
||||||
|
int jobs_init(void) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finalize the jobs module.
|
||||||
|
* @details This function is used to finalize the jobs module.
|
||||||
|
* It must be called exactly once when job processing is to be terminated,
|
||||||
|
* before the program exits. It should cancel all jobs that have not
|
||||||
|
* yet terminated, wait for jobs that have been cancelled to terminate,
|
||||||
|
* and then expunge all jobs before returning.
|
||||||
|
*
|
||||||
|
* @return 0 if finalization is completely successful, otherwise -1.
|
||||||
|
*/
|
||||||
|
int jobs_fini(void) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print the current jobs table.
|
||||||
|
* @details This function is used to print the current contents of the jobs
|
||||||
|
* table to a specified output stream. The output should consist of one line
|
||||||
|
* per existing job. Each line should have the following format:
|
||||||
|
*
|
||||||
|
* <jobid>\t<pgid>\t<status>\t<pipeline>
|
||||||
|
*
|
||||||
|
* where <jobid> is the numeric job ID of the job, <status> is one of the
|
||||||
|
* following strings: "new", "running", "completed", "aborted", or "canceled",
|
||||||
|
* and <pipeline> is the job's pipeline, as printed by function show_pipeline()
|
||||||
|
* in the syntax module. The \t stand for TAB characters.
|
||||||
|
*
|
||||||
|
* @param file The output stream to which the job table is to be printed.
|
||||||
|
* @return 0 If the jobs table was successfully printed, -1 otherwise.
|
||||||
|
*/
|
||||||
|
int jobs_show(FILE *file) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a new job to run a pipeline.
|
||||||
|
* @details This function creates a new job and starts it running a specified
|
||||||
|
* pipeline. The pipeline will consist of a "leader" process, which is the direct
|
||||||
|
* child of the process that calls this function, plus one child of the leader
|
||||||
|
* process to run each command in the pipeline. All processes in the pipeline
|
||||||
|
* should have a process group ID that is equal to the process ID of the leader.
|
||||||
|
* The leader process should wait for all of its children to terminate before
|
||||||
|
* terminating itself. The leader should return the exit status of the process
|
||||||
|
* running the last command in the pipeline as its own exit status, if that
|
||||||
|
* process terminated normally. If the last process terminated with a signal,
|
||||||
|
* then the leader should terminate via SIGABRT.
|
||||||
|
*
|
||||||
|
* If the "capture_output" flag is set for the pipeline, then the standard output
|
||||||
|
* of the last process in the pipeline should be redirected to be the same as
|
||||||
|
* the standard output of the pipeline leader, and this output should go via a
|
||||||
|
* pipe to the main Mush process, where it should be read and saved in the data
|
||||||
|
* store as the value of a variable, as described in the assignment handout.
|
||||||
|
* If "capture_output" is not set for the pipeline, but "output_file" is non-NULL,
|
||||||
|
* then the standard output of the last process in the pipeline should be redirected
|
||||||
|
* to the specified output file. If "input_file" is set for the pipeline, then
|
||||||
|
* the standard input of the process running the first command in the pipeline should
|
||||||
|
* be redirected from the specified input file.
|
||||||
|
*
|
||||||
|
* @param pline The pipeline to be run. The jobs module expects this object
|
||||||
|
* to be valid for as long as it requires, and it expects to be able to free this
|
||||||
|
* object when it is finished with it. This means that the caller should not pass
|
||||||
|
* a pipeline object that is shared with any other data structure, but rather should
|
||||||
|
* make a copy to be passed to this function.
|
||||||
|
*
|
||||||
|
* @return -1 if the pipeline could not be initialized properly, otherwise the
|
||||||
|
* value returned is the job ID assigned to the pipeline.
|
||||||
|
*/
|
||||||
|
int jobs_run(PIPELINE *pline) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for a job to terminate.
|
||||||
|
* @details This function is used to wait for the job with a specified job ID
|
||||||
|
* to terminate. A job has terminated when it has entered the COMPLETED, ABORTED,
|
||||||
|
* or CANCELED state.
|
||||||
|
*
|
||||||
|
* @param jobid The job ID of the job to wait for.
|
||||||
|
* @return the exit status of the job leader, as returned by waitpid(),
|
||||||
|
* or -1 if any error occurs that makes it impossible to wait for the specified job.
|
||||||
|
*/
|
||||||
|
int jobs_wait(int jobid) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Poll to find out if a job has terminated.
|
||||||
|
* @details This function is used to poll whether the job with the specified ID
|
||||||
|
* has terminated. This is similar to jobs_wait(), except that this function returns
|
||||||
|
* immediately without waiting if the job has not yet terminated.
|
||||||
|
*
|
||||||
|
* @param jobid The job ID of the job to wait for.
|
||||||
|
* @return the exit status of the job leader, as returned by waitpid(), if the job
|
||||||
|
* has terminated, or -1 if the job has not yet terminated or if any other error occurs.
|
||||||
|
*/
|
||||||
|
int jobs_poll(int jobid) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Expunge a terminated job from the jobs table.
|
||||||
|
* @details This function is used to expunge (remove) a job that has terminated from
|
||||||
|
* the jobs table, so that space in the table can be used to start some new job.
|
||||||
|
* In order to be expunged, a job must have terminated; if an attempt is made to expunge
|
||||||
|
* a job that has not yet terminated, it is an error. Any resources (exit status,
|
||||||
|
* open pipes, captured output, etc.) that were being used by the job are finalized
|
||||||
|
* and/or freed and will no longer be available.
|
||||||
|
*
|
||||||
|
* @param jobid The job ID of the job to expunge.
|
||||||
|
* @return 0 if the job was successfully expunged, -1 if the job could not be expunged.
|
||||||
|
*/
|
||||||
|
int jobs_expunge(int jobid) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Attempt to cancel a job.
|
||||||
|
* @details This function is used to attempt to cancel a running job.
|
||||||
|
* In order to be canceled, the job must not yet have terminated and there
|
||||||
|
* must not have been any previous attempt to cancel the job.
|
||||||
|
* Cancellation is attempted by sending SIGKILL to the process group associated
|
||||||
|
* with the job. Cancellation becomes successful, and the job actually enters the canceled
|
||||||
|
* state, at such subsequent time as the job leader terminates as a result of SIGKILL.
|
||||||
|
* If after attempting cancellation, the job leader terminates other than as a result
|
||||||
|
* of SIGKILL, then cancellation is not successful and the state of the job is either
|
||||||
|
* COMPLETED or ABORTED, depending on how the job leader terminated.
|
||||||
|
*
|
||||||
|
* @param jobid The job ID of the job to cancel.
|
||||||
|
* @return 0 if cancellation was successfully initiated, -1 if the job was already
|
||||||
|
* terminated, a previous attempt had been made to cancel the job, or any other
|
||||||
|
* error occurred.
|
||||||
|
*/
|
||||||
|
int jobs_cancel(int jobid) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the captured output of a job.
|
||||||
|
* @details This function is used to retrieve output that was captured from a job
|
||||||
|
* that has terminated, but that has not yet been expunged. Output is captured for a job
|
||||||
|
* when the "capture_output" flag is set for its pipeline.
|
||||||
|
*
|
||||||
|
* @param jobid The job ID of the job for which captured output is to be retrieved.
|
||||||
|
* @return The captured output, if the job has terminated and there is captured
|
||||||
|
* output available, otherwise NULL.
|
||||||
|
*/
|
||||||
|
char *jobs_get_output(int jobid) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pause waiting for a signal indicating a potential job status change.
|
||||||
|
* @details When this function is called it blocks until some signal has been
|
||||||
|
* received, at which point the function returns. It is used to wait for a
|
||||||
|
* potential job status change without consuming excessive amounts of CPU time.
|
||||||
|
*
|
||||||
|
* @return -1 if any error occurred, 0 otherwise.
|
||||||
|
*/
|
||||||
|
int jobs_pause(void) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
12
hw4/src/main.c
Normal file
12
hw4/src/main.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "mush.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
jobs_init();
|
||||||
|
exec_interactive();
|
||||||
|
jobs_fini();
|
||||||
|
}
|
2219
hw4/src/mush.lex.c
Normal file
2219
hw4/src/mush.lex.c
Normal file
File diff suppressed because it is too large
Load Diff
2418
hw4/src/mush.tab.c
Normal file
2418
hw4/src/mush.tab.c
Normal file
File diff suppressed because it is too large
Load Diff
136
hw4/src/program.c
Normal file
136
hw4/src/program.c
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "mush.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the "program store" module for Mush.
|
||||||
|
* It maintains a set of numbered statements, along with a "program counter"
|
||||||
|
* that indicates the current point of execution, which is either before all
|
||||||
|
* statements, after all statements, or in between two statements.
|
||||||
|
* There should be no fixed limit on the number of statements that the program
|
||||||
|
* store can hold.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output a listing of the current contents of the program store.
|
||||||
|
* @details This function outputs a listing of the current contents of the
|
||||||
|
* program store. Statements are listed in increasing order of their line
|
||||||
|
* number. The current position of the program counter is indicated by
|
||||||
|
* a line containing only the string "-->" at the current program counter
|
||||||
|
* position.
|
||||||
|
*
|
||||||
|
* @param out The stream to which to output the listing.
|
||||||
|
* @return 0 if successful, -1 if any error occurred.
|
||||||
|
*/
|
||||||
|
int prog_list(FILE *out) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Insert a new statement into the program store.
|
||||||
|
* @details This function inserts a new statement into the program store.
|
||||||
|
* The statement must have a line number. If the line number is the same as
|
||||||
|
* that of an existing statement, that statement is replaced.
|
||||||
|
* The program store assumes the responsibility for ultimately freeing any
|
||||||
|
* statement that is inserted using this function.
|
||||||
|
* Insertion of new statements preserves the value of the program counter:
|
||||||
|
* if the position of the program counter was just before a particular statement
|
||||||
|
* before insertion of a new statement, it will still be before that statement
|
||||||
|
* after insertion, and if the position of the program counter was after all
|
||||||
|
* statements before insertion of a new statement, then it will still be after
|
||||||
|
* all statements after insertion.
|
||||||
|
*
|
||||||
|
* @param stmt The statement to be inserted.
|
||||||
|
* @return 0 if successful, -1 if any error occurred.
|
||||||
|
*/
|
||||||
|
int prog_insert(STMT *stmt) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Delete statements from the program store.
|
||||||
|
* @details This function deletes from the program store statements whose
|
||||||
|
* line numbers fall in a specified range. Any deleted statements are freed.
|
||||||
|
* Deletion of statements preserves the value of the program counter:
|
||||||
|
* if before deletion the program counter pointed to a position just before
|
||||||
|
* a statement that was not among those to be deleted, then after deletion the
|
||||||
|
* program counter will still point the position just before that same statement.
|
||||||
|
* If before deletion the program counter pointed to a position just before
|
||||||
|
* a statement that was among those to be deleted, then after deletion the
|
||||||
|
* program counter will point to the first statement beyond those deleted,
|
||||||
|
* if such a statement exists, otherwise the program counter will point to
|
||||||
|
* the end of the program.
|
||||||
|
*
|
||||||
|
* @param min Lower end of the range of line numbers to be deleted.
|
||||||
|
* @param max Upper end of the range of line numbers to be deleted.
|
||||||
|
*/
|
||||||
|
int prog_delete(int min, int max) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the program counter to the beginning of the program.
|
||||||
|
* @details This function resets the program counter to point just
|
||||||
|
* before the first statement in the program.
|
||||||
|
*/
|
||||||
|
void prog_reset(void) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch the next program statement.
|
||||||
|
* @details This function fetches and returns the first program
|
||||||
|
* statement after the current program counter position. The program
|
||||||
|
* counter position is not modified. The returned pointer should not
|
||||||
|
* be used after any subsequent call to prog_delete that deletes the
|
||||||
|
* statement from the program store.
|
||||||
|
*
|
||||||
|
* @return The first program statement after the current program
|
||||||
|
* counter position, if any, otherwise NULL.
|
||||||
|
*/
|
||||||
|
STMT *prog_fetch(void) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Advance the program counter to the next existing statement.
|
||||||
|
* @details This function advances the program counter by one statement
|
||||||
|
* from its original position and returns the statement just after the
|
||||||
|
* new position. The returned pointer should not be used after any
|
||||||
|
* subsequent call to prog_delete that deletes the statement from the
|
||||||
|
* program store.
|
||||||
|
*
|
||||||
|
* @return The first program statement after the new program counter
|
||||||
|
* position, if any, otherwise NULL.
|
||||||
|
*/
|
||||||
|
STMT *prog_next() {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform a "go to" operation on the program store.
|
||||||
|
* @details This function performs a "go to" operation on the program
|
||||||
|
* store, by resetting the program counter to point to the position just
|
||||||
|
* before the statement with the specified line number.
|
||||||
|
* The statement pointed at by the new program counter is returned.
|
||||||
|
* If there is no statement with the specified line number, then no
|
||||||
|
* change is made to the program counter and NULL is returned.
|
||||||
|
* Any returned statement should only be regarded as valid as long
|
||||||
|
* as no calls to prog_delete are made that delete that statement from
|
||||||
|
* the program store.
|
||||||
|
*
|
||||||
|
* @return The statement having the specified line number, if such a
|
||||||
|
* statement exists, otherwise NULL.
|
||||||
|
*/
|
||||||
|
STMT *prog_goto(int lineno) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
102
hw4/src/store.c
Normal file
102
hw4/src/store.c
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the "data store" module for Mush.
|
||||||
|
* It maintains a mapping from variable names to values.
|
||||||
|
* The values of variables are stored as strings.
|
||||||
|
* However, the module provides functions for setting and retrieving
|
||||||
|
* the value of a variable as an integer. Setting a variable to
|
||||||
|
* an integer value causes the value of the variable to be set to
|
||||||
|
* a string representation of that integer. Retrieving the value of
|
||||||
|
* a variable as an integer is possible if the current value of the
|
||||||
|
* variable is the string representation of an integer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current value of a variable as a string.
|
||||||
|
* @details This function retrieves the current value of a variable
|
||||||
|
* as a string. If the variable has no value, then NULL is returned.
|
||||||
|
* Any string returned remains "owned" by the data store module;
|
||||||
|
* the caller should not attempt to free the string or to use it
|
||||||
|
* after any subsequent call that would modify the value of the variable
|
||||||
|
* whose value was retrieved. If the caller needs to use the string for
|
||||||
|
* an indefinite period, a copy should be made immediately.
|
||||||
|
*
|
||||||
|
* @param var The variable whose value is to be retrieved.
|
||||||
|
* @return A string that is the current value of the variable, if any,
|
||||||
|
* otherwise NULL.
|
||||||
|
*/
|
||||||
|
char *store_get_string(char *var) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current value of a variable as an integer.
|
||||||
|
* @details This retrieves the current value of a variable and
|
||||||
|
* attempts to interpret it as an integer. If this is possible,
|
||||||
|
* then the integer value is stored at the pointer provided by
|
||||||
|
* the caller.
|
||||||
|
*
|
||||||
|
* @param var The variable whose value is to be retrieved.
|
||||||
|
* @param valp Pointer at which the returned value is to be stored.
|
||||||
|
* @return If the specified variable has no value or the value
|
||||||
|
* cannot be interpreted as an integer, then -1 is returned,
|
||||||
|
* otherwise 0 is returned.
|
||||||
|
*/
|
||||||
|
int store_get_int(char *var, long *valp) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the value of a variable as a string.
|
||||||
|
* @details This function sets the current value of a specified
|
||||||
|
* variable to be a specified string. If the variable already
|
||||||
|
* has a value, then that value is replaced. If the specified
|
||||||
|
* value is NULL, then any existing value of the variable is removed
|
||||||
|
* and the variable becomes un-set. Ownership of the variable and
|
||||||
|
* the value strings is not transferred to the data store module as
|
||||||
|
* a result of this call; the data store module makes such copies of
|
||||||
|
* these strings as it may require.
|
||||||
|
*
|
||||||
|
* @param var The variable whose value is to be set.
|
||||||
|
* @param val The value to set, or NULL if the variable is to become
|
||||||
|
* un-set.
|
||||||
|
*/
|
||||||
|
int store_set_string(char *var, char *val) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the value of a variable as an integer.
|
||||||
|
* @details This function sets the current value of a specified
|
||||||
|
* variable to be a specified integer. If the variable already
|
||||||
|
* has a value, then that value is replaced. Ownership of the variable
|
||||||
|
* string is not transferred to the data store module as a result of
|
||||||
|
* this call; the data store module makes such copies of this string
|
||||||
|
* as it may require.
|
||||||
|
*
|
||||||
|
* @param var The variable whose value is to be set.
|
||||||
|
* @param val The value to set.
|
||||||
|
*/
|
||||||
|
int store_set_int(char *var, long val) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print the current contents of the data store.
|
||||||
|
* @details This function prints the current contents of the data store
|
||||||
|
* to the specified output stream. The format is not specified; this
|
||||||
|
* function is intended to be used for debugging purposes.
|
||||||
|
*
|
||||||
|
* @param f The stream to which the store contents are to be printed.
|
||||||
|
*/
|
||||||
|
void store_show(FILE *f) {
|
||||||
|
// TO BE IMPLEMENTED
|
||||||
|
abort();
|
||||||
|
}
|
357
hw4/src/syntax.c
Normal file
357
hw4/src/syntax.c
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
/*
|
||||||
|
* DO NOT MODIFY THE CONTENTS OF THIS FILE.
|
||||||
|
* IT WILL BE REPLACED DURING GRADING
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "syntax.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mush: Functions for manipulating syntax trees.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void show_stmt(FILE *file, STMT *stmt) {
|
||||||
|
show_lineno(file, stmt->lineno);
|
||||||
|
switch(stmt->class) {
|
||||||
|
case LIST_STMT_CLASS:
|
||||||
|
fprintf(file, "list");
|
||||||
|
break;
|
||||||
|
case DELETE_STMT_CLASS:
|
||||||
|
fprintf(file, "delete %d, %d",
|
||||||
|
stmt->members.delete_stmt.from,
|
||||||
|
stmt->members.delete_stmt.to);
|
||||||
|
break;
|
||||||
|
case RUN_STMT_CLASS:
|
||||||
|
fprintf(file, "run");
|
||||||
|
break;
|
||||||
|
case CONT_STMT_CLASS:
|
||||||
|
fprintf(file, "cont");
|
||||||
|
break;
|
||||||
|
case STOP_STMT_CLASS:
|
||||||
|
fprintf(file, "stop");
|
||||||
|
break;
|
||||||
|
case FG_STMT_CLASS:
|
||||||
|
show_pipeline(file, stmt->members.sys_stmt.pipeline);
|
||||||
|
break;
|
||||||
|
case BG_STMT_CLASS:
|
||||||
|
show_pipeline(file, stmt->members.sys_stmt.pipeline);
|
||||||
|
fprintf(file, "& ");
|
||||||
|
break;
|
||||||
|
case WAIT_STMT_CLASS:
|
||||||
|
fprintf(file, "wait ");
|
||||||
|
show_expr(file, stmt->members.jobctl_stmt.expr, 0);
|
||||||
|
break;
|
||||||
|
case POLL_STMT_CLASS:
|
||||||
|
fprintf(file, "poll ");
|
||||||
|
show_expr(file, stmt->members.jobctl_stmt.expr, 0);
|
||||||
|
break;
|
||||||
|
case CANCEL_STMT_CLASS:
|
||||||
|
fprintf(file, "cancel ");
|
||||||
|
show_expr(file, stmt->members.jobctl_stmt.expr, 0);
|
||||||
|
break;
|
||||||
|
case PAUSE_STMT_CLASS:
|
||||||
|
fprintf(file, "pause");
|
||||||
|
break;
|
||||||
|
case SET_STMT_CLASS:
|
||||||
|
fprintf(file, "set ");
|
||||||
|
fprintf(file, "%s = ", stmt->members.set_stmt.name);
|
||||||
|
show_expr(file, stmt->members.set_stmt.expr, 0);
|
||||||
|
break;
|
||||||
|
case UNSET_STMT_CLASS:
|
||||||
|
fprintf(file, "unset ");
|
||||||
|
fprintf(file, "%s", stmt->members.unset_stmt.name);
|
||||||
|
break;
|
||||||
|
case IF_STMT_CLASS:
|
||||||
|
fprintf(file, "if ");
|
||||||
|
show_expr(file, stmt->members.if_stmt.expr, 0);
|
||||||
|
fprintf(file, " goto %d", stmt->members.if_stmt.lineno);
|
||||||
|
break;
|
||||||
|
case GOTO_STMT_CLASS:
|
||||||
|
fprintf(file, "goto %d", stmt->members.goto_stmt.lineno);
|
||||||
|
break;
|
||||||
|
case SOURCE_STMT_CLASS:
|
||||||
|
fprintf(file, "source %s", stmt->members.source_stmt.file);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown statement class: %d\n", stmt->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
fprintf(file, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_pipeline(FILE *file, PIPELINE *pline) {
|
||||||
|
COMMAND *cmds = pline->commands;
|
||||||
|
while(cmds) {
|
||||||
|
show_command(file, cmds);
|
||||||
|
if(cmds->next)
|
||||||
|
fprintf(file, " | ");
|
||||||
|
cmds = cmds->next;
|
||||||
|
}
|
||||||
|
if(pline->input_file)
|
||||||
|
fprintf(file, " < %s", pline->input_file);
|
||||||
|
if(pline->capture_output)
|
||||||
|
fprintf(file, " >@");
|
||||||
|
if(pline->output_file)
|
||||||
|
fprintf(file, " > %s", pline->output_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_command(FILE *file, COMMAND *cmd) {
|
||||||
|
ARG *arg = cmd->args;
|
||||||
|
while(arg) {
|
||||||
|
show_expr(file, arg->expr, 0);
|
||||||
|
if(arg->next)
|
||||||
|
fprintf(file, " ");
|
||||||
|
arg = arg->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_expr(FILE *file, EXPR *expr, int parens) {
|
||||||
|
switch(expr->class) {
|
||||||
|
case LIT_EXPR_CLASS:
|
||||||
|
fprintf(file, "%s", expr->members.value);
|
||||||
|
break;
|
||||||
|
case NUM_EXPR_CLASS:
|
||||||
|
fprintf(file, "#%s", expr->members.variable);
|
||||||
|
break;
|
||||||
|
case STRING_EXPR_CLASS:
|
||||||
|
fprintf(file, "$%s", expr->members.variable);
|
||||||
|
break;
|
||||||
|
case UNARY_EXPR_CLASS:
|
||||||
|
if(parens)
|
||||||
|
fprintf(file, "(");
|
||||||
|
show_oprtr(file, expr->members.unary_expr.oprtr);
|
||||||
|
fprintf(file, " ");
|
||||||
|
show_expr(file, expr->members.unary_expr.arg, 1);
|
||||||
|
if(parens)
|
||||||
|
fprintf(file, ")");
|
||||||
|
break;
|
||||||
|
case BINARY_EXPR_CLASS:
|
||||||
|
if(parens)
|
||||||
|
fprintf(file, "(");
|
||||||
|
show_expr(file, expr->members.binary_expr.arg1, 1);
|
||||||
|
fprintf(file, " ");
|
||||||
|
show_oprtr(file, expr->members.binary_expr.oprtr);
|
||||||
|
fprintf(file, " ");
|
||||||
|
show_expr(file, expr->members.binary_expr.arg2, 1);
|
||||||
|
if(parens)
|
||||||
|
fprintf(file, ")");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_oprtr(FILE *file, OPRTR oprtr) {
|
||||||
|
switch(oprtr) {
|
||||||
|
case PLUS_OPRTR:
|
||||||
|
fprintf(file, "+");
|
||||||
|
break;
|
||||||
|
case MINUS_OPRTR:
|
||||||
|
fprintf(file, "-");
|
||||||
|
break;
|
||||||
|
case TIMES_OPRTR:
|
||||||
|
fprintf(file, "*");
|
||||||
|
break;
|
||||||
|
case DIVIDE_OPRTR:
|
||||||
|
fprintf(file, "/");
|
||||||
|
break;
|
||||||
|
case MOD_OPRTR:
|
||||||
|
fprintf(file, "%s", "%");
|
||||||
|
break;
|
||||||
|
case AND_OPRTR:
|
||||||
|
fprintf(file, "&&");
|
||||||
|
break;
|
||||||
|
case OR_OPRTR:
|
||||||
|
fprintf(file, "||");
|
||||||
|
break;
|
||||||
|
case NOT_OPRTR:
|
||||||
|
fprintf(file, "!");
|
||||||
|
break;
|
||||||
|
case EQUAL_OPRTR:
|
||||||
|
fprintf(file, "==");
|
||||||
|
break;
|
||||||
|
case LESS_OPRTR:
|
||||||
|
fprintf(file, "<");
|
||||||
|
break;
|
||||||
|
case GREATER_OPRTR:
|
||||||
|
fprintf(file, ">");
|
||||||
|
break;
|
||||||
|
case LESSEQ_OPRTR:
|
||||||
|
fprintf(file, "<=");
|
||||||
|
break;
|
||||||
|
case GREATEQ_OPRTR:
|
||||||
|
fprintf(file, ">=");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown operator: %d\n", oprtr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_lineno(FILE *file, int lineno) {
|
||||||
|
if(lineno)
|
||||||
|
fprintf(file, "%7d\t", lineno);
|
||||||
|
else
|
||||||
|
fprintf(file, "\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_stmt(STMT *stmt) {
|
||||||
|
if(stmt == NULL) // Produced by error recovery production
|
||||||
|
return;
|
||||||
|
switch(stmt->class) {
|
||||||
|
case LIST_STMT_CLASS:
|
||||||
|
case DELETE_STMT_CLASS:
|
||||||
|
case RUN_STMT_CLASS:
|
||||||
|
case CONT_STMT_CLASS:
|
||||||
|
case STOP_STMT_CLASS:
|
||||||
|
break;
|
||||||
|
case FG_STMT_CLASS:
|
||||||
|
free_pipeline(stmt->members.sys_stmt.pipeline);
|
||||||
|
break;
|
||||||
|
case BG_STMT_CLASS:
|
||||||
|
free_pipeline(stmt->members.sys_stmt.pipeline);
|
||||||
|
break;
|
||||||
|
case GOTO_STMT_CLASS:
|
||||||
|
break;
|
||||||
|
case WAIT_STMT_CLASS:
|
||||||
|
case POLL_STMT_CLASS:
|
||||||
|
case CANCEL_STMT_CLASS:
|
||||||
|
free_expr(stmt->members.jobctl_stmt.expr);
|
||||||
|
break;
|
||||||
|
case SET_STMT_CLASS:
|
||||||
|
free(stmt->members.set_stmt.name);
|
||||||
|
free_expr(stmt->members.set_stmt.expr);
|
||||||
|
break;
|
||||||
|
case UNSET_STMT_CLASS:
|
||||||
|
free(stmt->members.unset_stmt.name);
|
||||||
|
break;
|
||||||
|
case IF_STMT_CLASS:
|
||||||
|
free_expr(stmt->members.if_stmt.expr);
|
||||||
|
break;
|
||||||
|
case SOURCE_STMT_CLASS:
|
||||||
|
free(stmt->members.source_stmt.file);
|
||||||
|
break;
|
||||||
|
case PAUSE_STMT_CLASS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown statement class: %d\n", stmt->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
free(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_pipeline(PIPELINE *pline) {
|
||||||
|
free_commands(pline->commands);
|
||||||
|
if(pline->input_file)
|
||||||
|
free(pline->input_file);
|
||||||
|
if(pline->output_file)
|
||||||
|
free(pline->output_file);
|
||||||
|
free(pline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_commands(COMMAND *cmd) {
|
||||||
|
if(cmd->next)
|
||||||
|
free_commands(cmd->next);
|
||||||
|
if(cmd->args)
|
||||||
|
free_args(cmd->args);
|
||||||
|
free(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_args(ARG *args) {
|
||||||
|
if(args->next)
|
||||||
|
free_args(args->next);
|
||||||
|
free_expr(args->expr);
|
||||||
|
free(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_expr(EXPR *expr) {
|
||||||
|
switch(expr->class) {
|
||||||
|
case LIT_EXPR_CLASS:
|
||||||
|
free(expr->members.value);
|
||||||
|
break;
|
||||||
|
case NUM_EXPR_CLASS:
|
||||||
|
free(expr->members.variable);
|
||||||
|
break;
|
||||||
|
case STRING_EXPR_CLASS:
|
||||||
|
free(expr->members.variable);
|
||||||
|
break;
|
||||||
|
case UNARY_EXPR_CLASS:
|
||||||
|
free_expr(expr->members.unary_expr.arg);
|
||||||
|
break;
|
||||||
|
case BINARY_EXPR_CLASS:
|
||||||
|
free_expr(expr->members.binary_expr.arg1);
|
||||||
|
free_expr(expr->members.binary_expr.arg2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
free(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
PIPELINE *copy_pipeline(PIPELINE *pline) {
|
||||||
|
if(!pline)
|
||||||
|
return NULL;
|
||||||
|
PIPELINE *copy = calloc(sizeof(PIPELINE), 1);
|
||||||
|
copy->commands = copy_commands(pline->commands);
|
||||||
|
copy->capture_output = pline->capture_output;
|
||||||
|
if(pline->input_file)
|
||||||
|
copy->input_file = strdup(pline->input_file);
|
||||||
|
if(pline->output_file)
|
||||||
|
copy->output_file = strdup(pline->output_file);
|
||||||
|
return(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND *copy_commands(COMMAND *cmd) {
|
||||||
|
if(!cmd)
|
||||||
|
return NULL;
|
||||||
|
COMMAND *copy = calloc(sizeof(COMMAND), 1);
|
||||||
|
copy->next = copy_commands(cmd->next);
|
||||||
|
copy->args = copy_args(cmd->args);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARG *copy_args(ARG *args) {
|
||||||
|
if(!args)
|
||||||
|
return NULL;
|
||||||
|
ARG *copy = calloc(sizeof(ARG), 1);
|
||||||
|
copy->next = copy_args(args->next);
|
||||||
|
copy->expr = copy_expr(args->expr);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPR *copy_expr(EXPR *expr) {
|
||||||
|
if(!expr)
|
||||||
|
return NULL;
|
||||||
|
EXPR *copy = calloc(sizeof(EXPR), 1);
|
||||||
|
copy->class = expr->class;
|
||||||
|
copy->type = expr->type;
|
||||||
|
switch(expr->class) {
|
||||||
|
case LIT_EXPR_CLASS:
|
||||||
|
copy->members.value = strdup(expr->members.value);
|
||||||
|
break;
|
||||||
|
case NUM_EXPR_CLASS:
|
||||||
|
copy->members.variable = strdup(expr->members.variable);
|
||||||
|
break;
|
||||||
|
case STRING_EXPR_CLASS:
|
||||||
|
copy->members.variable = strdup(expr->members.variable);
|
||||||
|
break;
|
||||||
|
case UNARY_EXPR_CLASS:
|
||||||
|
copy->members.unary_expr.oprtr = expr->members.unary_expr.oprtr;
|
||||||
|
copy->members.unary_expr.arg = copy_expr(expr->members.unary_expr.arg);
|
||||||
|
break;
|
||||||
|
case BINARY_EXPR_CLASS:
|
||||||
|
copy->members.binary_expr.oprtr = expr->members.binary_expr.oprtr;
|
||||||
|
copy->members.binary_expr.arg1 = copy_expr(expr->members.binary_expr.arg1);
|
||||||
|
copy->members.binary_expr.arg2 = copy_expr(expr->members.binary_expr.arg2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown expression class: %d\n", expr->class);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
19
hw4/tests/base_tests.c
Normal file
19
hw4/tests/base_tests.c
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This just checks if mush exits normally on an empty input.
|
||||||
|
* It is not very interesting, unfortunately.
|
||||||
|
* It is just a place holder where something more interesting might go.
|
||||||
|
*/
|
||||||
|
Test(basecode_suite, mush_eof_test, .timeout=20)
|
||||||
|
{
|
||||||
|
char *cmd = "ulimit -t 10; bin/mush < /dev/null";
|
||||||
|
|
||||||
|
int code = WEXITSTATUS(system(cmd));
|
||||||
|
cr_assert_eq(code, EXIT_SUCCESS,
|
||||||
|
"Program exited with %d instead of EXIT_SUCCESS",
|
||||||
|
code);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user