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