Merging HW4_CODE
This commit is contained in:
commit
9c7b99693b
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,4 +1,7 @@
|
|||
bin/
|
||||
build/
|
||||
*~
|
||||
#*
|
||||
*.d
|
||||
*.o
|
||||
*.out
|
||||
bin/*
|
||||
build/*
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
image: hwrunner:latest
|
||||
variables:
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
EXEC: sfmm
|
||||
HW_DIR: hw3
|
||||
CPU_LIMIT: 60
|
||||
FILE_LIMIT: 1000000
|
||||
EXEC: mush
|
||||
HW_DIR: hw4
|
||||
before_script:
|
||||
- make clean all -C ${HW_DIR}
|
||||
stages:
|
||||
|
@ -18,12 +16,8 @@ build:
|
|||
run:
|
||||
stage: run
|
||||
script:
|
||||
- ulimit -t ${CPU_LIMIT}
|
||||
- ulimit -f ${FILE_LIMIT}
|
||||
- cd ${HW_DIR} && bin/${EXEC}
|
||||
- cd ${HW_DIR} && bin/${EXEC} < rsrc/run_test.mush
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- ulimit -t ${CPU_LIMIT}
|
||||
- ulimit -f ${FILE_LIMIT}
|
||||
- cd ${HW_DIR} && bin/${EXEC}_tests -S --verbose=0 --timeout 30
|
||||
- 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
|
400
hw4/src/execution.c
Normal file
400
hw4/src/execution.c
Normal file
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* 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);
|
||||
char *output = jobs_get_output(job);
|
||||
if(output)
|
||||
store_set_string(OUTPUT_VAR, output);
|
||||
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) {
|
||||
char *output = jobs_get_output(job);
|
||||
if(output)
|
||||
store_set_string(OUTPUT_VAR, output);
|
||||
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();
|
||||
}
|
||||
break;
|
||||
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