feat: added files
This commit is contained in:
parent
11f28060e7
commit
a39e2d1aaf
20
course_tools/README.md
Normal file
20
course_tools/README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# CSE 320 Fall 2020 Course Tools
|
||||||
|
|
||||||
|
|
||||||
|
Run the script to install the 320 course tools and packages.
|
||||||
|
```
|
||||||
|
$ bash vm-setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script will ask for sudo privileges to install packages.
|
||||||
|
|
||||||
|
**NOTE THIS TOOL RUNS APT UPGRADE**
|
||||||
|
|
||||||
|
Once the script has been run, read the comments at the end of the script
|
||||||
|
in order to install packages into the Sublime Text editor. The commented
|
||||||
|
commands in the script are an old version of an automatic installation
|
||||||
|
procedure, which doesn't work any more. What you should do instead is to
|
||||||
|
first manually install "Package Control", then "SublimeLinter", "SublimeLinter-gcc",
|
||||||
|
and "TrailingSpaces". Then, copy file `SublimeLinter.sublime-settings` to
|
||||||
|
directory `~/.config/sublime-text-3/Packages/User` as described in the
|
||||||
|
comments.
|
15
course_tools/SublimeLinter.sublime-settings
Normal file
15
course_tools/SublimeLinter.sublime-settings
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// SublimeLinter Settings - User
|
||||||
|
{
|
||||||
|
"delay":0.25,
|
||||||
|
"linters":{
|
||||||
|
"gcc":{
|
||||||
|
"disable":false,
|
||||||
|
"c_executable":"gcc",
|
||||||
|
"args":["-Wall"],
|
||||||
|
"I":[
|
||||||
|
"${file_path}/../include",
|
||||||
|
"${project_path}/include"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
132
course_tools/boxfort-commit-ac0507b
Normal file
132
course_tools/boxfort-commit-ac0507b
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
From ac0507b3f45fe58100b528baeb8ca04270b4a8ff Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Franklin \"Snaipe\" Mathieu" <me@snai.pe>
|
||||||
|
Date: Mon, 23 Mar 2020 05:52:23 +0000
|
||||||
|
Subject: timeout-posix: fix race condition
|
||||||
|
|
||||||
|
The posix timeout code was racy -- if a timeout was created, and
|
||||||
|
cancelled before the watchdog had any chance to run (because the worker
|
||||||
|
would exit too quickly, or because the thread would not be scheduled
|
||||||
|
quickly enough). This, in turn, made the watchdog wait forever for the
|
||||||
|
timeout queue to be nonempty.
|
||||||
|
|
||||||
|
This fixes the race by preventing the watchdog from ever waiting for the
|
||||||
|
queue to fill up -- it's actually not possible for the queue to be
|
||||||
|
empty during initialization, because the watchdog thread will be made to
|
||||||
|
wait for the initialization lock to be released. This means that the
|
||||||
|
only time where the queue is empty is when the watchdog has been
|
||||||
|
started, but the worker already exited/the timeout was cancelled.
|
||||||
|
|
||||||
|
In addition, this fix simplifies slightly the way that the watchdog is
|
||||||
|
collected -- we no longer try to join the thread, but we make it
|
||||||
|
detached from the get go.
|
||||||
|
|
||||||
|
This addresses Snaipe/Criterion#345.
|
||||||
|
|
||||||
|
diff --git a/src/timeout-posix.c b/src/timeout-posix.c
|
||||||
|
index 53bd181..2e9a210 100644
|
||||||
|
--- a/src/timeout-posix.c
|
||||||
|
+++ b/src/timeout-posix.c
|
||||||
|
@@ -22,13 +22,13 @@
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <assert.h>
|
||||||
|
+#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
-#include <time.h>
|
||||||
|
+#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
-#include <errno.h>
|
||||||
|
-#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
+#include <time.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "sandbox.h"
|
||||||
|
@@ -48,11 +48,9 @@ static struct {
|
||||||
|
int thread_active;
|
||||||
|
pthread_mutex_t sync;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
- pthread_cond_t termcond;
|
||||||
|
} self = {
|
||||||
|
.sync = PTHREAD_MUTEX_INITIALIZER,
|
||||||
|
.cond = PTHREAD_COND_INITIALIZER,
|
||||||
|
- .termcond = PTHREAD_COND_INITIALIZER,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int timespec_cmp(struct timespec *a, struct timespec *b)
|
||||||
|
@@ -96,8 +94,6 @@ static void to_timespec(double timeout, struct timespec *timeo)
|
||||||
|
static void *timeout_killer_fn(void *nil)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&self.sync);
|
||||||
|
- while (!self.requests)
|
||||||
|
- pthread_cond_wait(&self.cond, &self.sync);
|
||||||
|
|
||||||
|
struct bxfi_timeout_request *req;
|
||||||
|
for (;;) {
|
||||||
|
@@ -125,7 +121,7 @@ static void *timeout_killer_fn(void *nil)
|
||||||
|
free(req);
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
- pthread_cond_broadcast(&self.termcond);
|
||||||
|
+ self.thread_active = 0;
|
||||||
|
pthread_mutex_unlock(&self.sync);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
@@ -137,10 +133,6 @@ void bxfi_reset_timeout_killer(void)
|
||||||
|
|
||||||
|
memcpy(&self.sync, &mutex, sizeof (mutex));
|
||||||
|
memcpy(&self.cond, &cond, sizeof (cond));
|
||||||
|
- memcpy(&self.termcond, &cond, sizeof (cond));
|
||||||
|
-
|
||||||
|
- if (self.requests)
|
||||||
|
- pthread_join(self.thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||||||
|
@@ -159,10 +151,16 @@ int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||||||
|
|
||||||
|
pthread_mutex_lock(&self.sync);
|
||||||
|
if (!self.requests) {
|
||||||
|
- if (self.thread_active)
|
||||||
|
- pthread_join(self.thread, NULL);
|
||||||
|
+ pthread_attr_t attrs;
|
||||||
|
+ if ((rc = pthread_attr_init(&attrs)) == -1) {
|
||||||
|
+ rc = -errno;
|
||||||
|
+ goto error;
|
||||||
|
+ }
|
||||||
|
+ pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
||||||
|
+
|
||||||
|
self.thread_active = 1;
|
||||||
|
- rc = -pthread_create(&self.thread, NULL, timeout_killer_fn, NULL);
|
||||||
|
+ rc = -pthread_create(&self.thread, &attrs, timeout_killer_fn, NULL);
|
||||||
|
+ pthread_attr_destroy(&attrs);
|
||||||
|
if (rc)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
@@ -177,7 +175,6 @@ int bxfi_push_timeout(struct bxfi_sandbox *instance, double timeout)
|
||||||
|
*nptr = req;
|
||||||
|
|
||||||
|
pthread_cond_broadcast(&self.cond);
|
||||||
|
- pthread_cond_broadcast(&self.termcond);
|
||||||
|
pthread_mutex_unlock(&self.sync);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
@@ -204,17 +201,6 @@ void bxfi_cancel_timeout(struct bxfi_sandbox *instance)
|
||||||
|
}
|
||||||
|
if (cancelled) {
|
||||||
|
pthread_cond_broadcast(&self.cond);
|
||||||
|
- if (!self.requests) {
|
||||||
|
- while (self.cancelled && !self.requests)
|
||||||
|
- pthread_cond_wait(&self.termcond, &self.sync);
|
||||||
|
- if (self.requests)
|
||||||
|
- goto end;
|
||||||
|
- if (self.thread_active) {
|
||||||
|
- pthread_join(self.thread, NULL);
|
||||||
|
- self.thread_active = 0;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
}
|
||||||
|
-end:
|
||||||
|
pthread_mutex_unlock(&self.sync);
|
||||||
|
}
|
BIN
course_tools/criterion.zip
Normal file
BIN
course_tools/criterion.zip
Normal file
Binary file not shown.
231
course_tools/git-submit
Executable file
231
course_tools/git-submit
Executable file
|
@ -0,0 +1,231 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import requests
|
||||||
|
from requests import get
|
||||||
|
from requests.exceptions import ConnectTimeout, ConnectionError, HTTPError
|
||||||
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||||
|
from datetime import datetime
|
||||||
|
from signal import signal, SIGINT, SIGTSTP, SIG_IGN
|
||||||
|
from subprocess import run, PIPE
|
||||||
|
from shlex import split
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from sys import exit
|
||||||
|
|
||||||
|
RED = '\033[1;31m'
|
||||||
|
GRE = '\033[1;32m'
|
||||||
|
YEL = '\033[0;33m'
|
||||||
|
BLU = '\033[1;34m'
|
||||||
|
PUR = '\033[1;35m'
|
||||||
|
CYA = '\033[1;36m'
|
||||||
|
BOLD = '\033[1m'
|
||||||
|
DEF = '\033[0m'
|
||||||
|
|
||||||
|
BASE_URL = 'https://cse320.starkeffect.com:3000/submit'
|
||||||
|
server_timeout = None
|
||||||
|
|
||||||
|
|
||||||
|
def yn_prompt():
|
||||||
|
prompt = input('{}Continue? Please enter Y/n{}: '.format(PUR, DEF))
|
||||||
|
while prompt != 'Y' and prompt.lower() != 'n':
|
||||||
|
prompt = input('{}Continue? Please enter Y/n{}: '.format(PUR, DEF))
|
||||||
|
if prompt != 'Y':
|
||||||
|
print('{}Aborting...{}'.format(YEL, DEF))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def first_api_call():
|
||||||
|
global server_timeout
|
||||||
|
try:
|
||||||
|
res = get(BASE_URL + '/timeout', params=None, timeout=15, verify=False)
|
||||||
|
res_json = res.json()
|
||||||
|
server_timeout = res_json['timeout']
|
||||||
|
except (ConnectionError, ConnectTimeout, HTTPError):
|
||||||
|
print('Error connecting to the server. Try again. (FIRST_SERVER_CALL)')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def initial_message():
|
||||||
|
print('Only commits made from master or merged into master will be ',
|
||||||
|
'considered. If you would like to submit a commit from a different ',
|
||||||
|
'branch, please merge the branch into master first.')
|
||||||
|
yn_prompt()
|
||||||
|
|
||||||
|
|
||||||
|
def api_call(endpoint, query):
|
||||||
|
global server_timeout
|
||||||
|
try:
|
||||||
|
res = get(BASE_URL + endpoint, params=query, timeout=server_timeout,
|
||||||
|
verify=False)
|
||||||
|
except (ConnectionError, ConnectTimeout, HTTPError):
|
||||||
|
print('Error connecting to the server. Try again. ',
|
||||||
|
'(SERVER_CONNECTION_ERROR)')
|
||||||
|
exit(1)
|
||||||
|
res_json = res.json()
|
||||||
|
if 'errmsg' in res_json:
|
||||||
|
print(res_json['errmsg'])
|
||||||
|
exit(1)
|
||||||
|
return res_json
|
||||||
|
|
||||||
|
|
||||||
|
def validate_git_directory():
|
||||||
|
cmd = split('git remote show origin')
|
||||||
|
proc = run(cmd, stdout=PIPE)
|
||||||
|
if proc.returncode:
|
||||||
|
exit(proc.returncode)
|
||||||
|
# You are not expected to understand this
|
||||||
|
return [line for line in proc.stdout.decode().split('\n') if
|
||||||
|
line.strip().startswith('Push')][0].split('/')[-1].split('.')[0]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_commit(commit):
|
||||||
|
# check current user branch
|
||||||
|
branch_cmd = split('git rev-parse --abbrev-ref HEAD')
|
||||||
|
branch_proc = run(branch_cmd, stdout=PIPE, stderr=PIPE)
|
||||||
|
if branch_proc.returncode:
|
||||||
|
print('{}Error{}: Uninitialized repository. Please initialize your repo.'.format(RED, DEF))
|
||||||
|
exit(branch_proc.returncode)
|
||||||
|
current_branch = [line for line in branch_proc.stdout.decode().split('\n')][0]
|
||||||
|
if not current_branch == 'master':
|
||||||
|
print('{}Error{}: Run git submit from the master branch'.format(RED, DEF))
|
||||||
|
exit(1)
|
||||||
|
# check branch commit was made from
|
||||||
|
if not commit == 'master':
|
||||||
|
if len(commit) < 8:
|
||||||
|
print('{}Error{}: Use at least the first 8 characters of the commit hash.'.format(RED, DEF))
|
||||||
|
exit(1)
|
||||||
|
commit_branch_cmd = split('git branch --contains {}'.format(commit))
|
||||||
|
commit_branch_proc = run(commit_branch_cmd, stdout=PIPE, stderr=PIPE)
|
||||||
|
if commit_branch_proc.returncode:
|
||||||
|
print('{}Error{}: The specified commit hash is invalid.'.format(RED, DEF))
|
||||||
|
exit(commit_branch_proc.returncode)
|
||||||
|
branches = [branch for branch in commit_branch_proc.stdout.decode().split('\n')]
|
||||||
|
on_master = False
|
||||||
|
for branch in branches:
|
||||||
|
branch_name = [word.strip() for word in branch.split('*')][-1]
|
||||||
|
if branch_name == 'master':
|
||||||
|
on_master = True
|
||||||
|
if not on_master:
|
||||||
|
print('{}Error{}: The submitted commit should be from master. Merge the branch with the commit into master.'.format(RED, DEF))
|
||||||
|
exit(1)
|
||||||
|
cmd = "bash -c 'git log --branches=master* --not --remotes --pretty=oneline'"
|
||||||
|
proc = run(cmd, stdout=PIPE, shell=True)
|
||||||
|
if proc.returncode:
|
||||||
|
print(proc.stdout)
|
||||||
|
exit(proc.returncode)
|
||||||
|
elif commit == 'master' and proc.stdout:
|
||||||
|
print('{}Error{}: Push your commits to the remote using: git push'.format(RED, DEF))
|
||||||
|
exit(1)
|
||||||
|
elif commit in proc.stdout.decode():
|
||||||
|
print('{}Error{}: Push your commits to the remote using: git push'.format(RED, DEF))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_tag(tag):
|
||||||
|
response = api_call('/verify/{}'.format(tag), None)
|
||||||
|
if 'err' in response:
|
||||||
|
print('{}Error{}: {}'.format(RED, DEF, response['err']))
|
||||||
|
exit(1)
|
||||||
|
if not response['valid']:
|
||||||
|
print('{}Error{}: {}'.format(RED, DEF, response['msg']))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def check_for_resubmission(args):
|
||||||
|
status = api_call('/status', args)
|
||||||
|
print('{}Submission Time{}: {}'.format(BLU, DEF, status['time']))
|
||||||
|
if status['submitted']:
|
||||||
|
print('{}Resubmitting Homework{}: {} -- Are you {}sure{} you wish to resubmit '
|
||||||
|
'(this may result in lateday penalties)?'.format(YEL, DEF, CYA + args['tag'] + DEF, BOLD, DEF))
|
||||||
|
else:
|
||||||
|
print('{}Submitting Homework{}: {} -- Are you {}sure{}? '
|
||||||
|
.format(YEL, DEF, CYA + args['tag'] + DEF, BOLD, DEF))
|
||||||
|
yn_prompt()
|
||||||
|
return status['submitted'], status['time']
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_repo_state():
|
||||||
|
run(split('git -c color.status=always status'))
|
||||||
|
print('{}Current Repo State{}: Are you {}sure{} you have committed and pushed all the files needed '
|
||||||
|
'(such as .h files)?'.format(YEL, DEF, BOLD, DEF))
|
||||||
|
yn_prompt()
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_commit(commit):
|
||||||
|
cmd = split('git --no-pager -c color.ui=always show {} --pretty=fuller --quiet'.format(commit))
|
||||||
|
proc = run(cmd)
|
||||||
|
if proc.returncode:
|
||||||
|
exit(proc.returncode)
|
||||||
|
print('{}Submission Commit{}: Are you {}sure{} this is the commit you wish to submit?'.format(YEL, DEF, BOLD, DEF))
|
||||||
|
yn_prompt()
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_submission(tag):
|
||||||
|
print('{}Confirm Submission{}: Are you {}sure{} you want to submit {}? Your previous submission (if any) will be '
|
||||||
|
'{}overwritten{}!'.format(YEL, DEF, BOLD, DEF, CYA + tag + DEF, BOLD, DEF))
|
||||||
|
yn_prompt()
|
||||||
|
|
||||||
|
|
||||||
|
def trigger_submission(args):
|
||||||
|
info = api_call('', args)
|
||||||
|
return info['late'], info['lateDays']
|
||||||
|
|
||||||
|
|
||||||
|
def submission_info(**kwargs):
|
||||||
|
run(split('git pull --quiet'))
|
||||||
|
late = kwargs['late']
|
||||||
|
tag = kwargs['tag']
|
||||||
|
resubmit = kwargs['submit']
|
||||||
|
late_days = kwargs['days']
|
||||||
|
attempt_time = kwargs['time']
|
||||||
|
if late:
|
||||||
|
print('{}Urgent{}: {} was overdue. You did not have enough late days remaining.'.format(RED, DEF, tag))
|
||||||
|
print('{}Info{}: You have {} lateday(s) remaining.'.format(BLU, DEF, late_days))
|
||||||
|
if resubmit:
|
||||||
|
print('{}Alert{}: Your last submission will be taken into consideration.'.format(YEL, DEF))
|
||||||
|
else:
|
||||||
|
print('{}Urgent{}: You will be given an automatic zero for this assignment. Please meet your Professor\n'
|
||||||
|
'after lecture or during office hours or please email us at cse320@cs.stonybrook.edu with\n'
|
||||||
|
'{}[CSE320] - {} Overdue{} as the subject.'.format(RED, DEF, BLU, tag, DEF))
|
||||||
|
else:
|
||||||
|
print('{}Success{}: {} submission successful. Your assignment was submitted on {}.'
|
||||||
|
.format(GRE, DEF, tag, attempt_time))
|
||||||
|
print('{}Info{}: You have {} lateday(s) remaining.'.format(BLU, DEF, late_days))
|
||||||
|
print('Thank you for submitting your homework! We are working hard to get your grades out as soon as possible.')
|
||||||
|
print('If you have any concerns please email us at cse320@cs.stonybrook.edu with {}[{}]{} in the subject.'
|
||||||
|
.format(BLU, tag, DEF))
|
||||||
|
print('{}The CSE 320 Team{}'.format(PUR, DEF))
|
||||||
|
|
||||||
|
|
||||||
|
def main(arg_parser):
|
||||||
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
|
first_api_call()
|
||||||
|
initial_message()
|
||||||
|
net_id = validate_git_directory()
|
||||||
|
args = arg_parser.parse_args()
|
||||||
|
attempt_time = datetime.now()
|
||||||
|
validate_tag(args.TAG)
|
||||||
|
validate_commit(args.commit)
|
||||||
|
resubmit, time = check_for_resubmission({'tag': args.TAG, 'repo': net_id, 'attemptTime': attempt_time.isoformat()})
|
||||||
|
confirm_repo_state()
|
||||||
|
confirm_commit(args.commit)
|
||||||
|
confirm_submission(args.TAG)
|
||||||
|
signal(SIGINT, SIG_IGN)
|
||||||
|
signal(SIGTSTP, SIG_IGN)
|
||||||
|
late, late_days = trigger_submission({'tag': args.TAG, 'repo': net_id, 'commit': args.commit})
|
||||||
|
submission_info(tag=args.TAG, time=time, submit=resubmit, late=late, days=late_days)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class GitArgParser(ArgumentParser):
|
||||||
|
def error(self, message):
|
||||||
|
print('{}Error{}: {}'.format(RED, DEF, message))
|
||||||
|
self.print_help()
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = GitArgParser(prog='git submit', description='Submit your homework assignment using git.')
|
||||||
|
parser.add_argument('TAG', type=str, help='The homework you wish to submit. Can have one of the following values: '
|
||||||
|
'hw0, hw1, hw2, hw3, hw4, hw5')
|
||||||
|
parser.add_argument('-c', dest='commit', type=str, required=False, default='master',
|
||||||
|
help='Used if you wish to submit a commit that is not the latest. COMMIT is the SHA value of '
|
||||||
|
'your commit.')
|
||||||
|
main(parser)
|
124
course_tools/vm-setup.sh
Executable file
124
course_tools/vm-setup.sh
Executable file
|
@ -0,0 +1,124 @@
|
||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
echo
|
||||||
|
if hash figlet 2> /dev/null; then
|
||||||
|
echo "320 Setup" | figlet -f banner
|
||||||
|
else
|
||||||
|
echo "320 Setup"
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Updating..."
|
||||||
|
# General updates
|
||||||
|
sudo apt-get update -y
|
||||||
|
echo "Graphics issues from 8/2021 now fixed with VirtualBox 6.1.27 or later -- doing apt-get upgrade"
|
||||||
|
sudo apt-get upgrade -y
|
||||||
|
sudo apt-get autoremove -y 2>&1 > /dev/null
|
||||||
|
|
||||||
|
# Extras
|
||||||
|
sudo apt-get install -y figlet terminator htop dialog wget tree 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "vm-tools" | figlet -f mini
|
||||||
|
sudo apt-get install -y open-vm-tools-desktop 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Git" | figlet -f mini
|
||||||
|
sudo apt-get install -y git 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Gitk" | figlet -f mini
|
||||||
|
sudo apt-get install -y gitk 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo Git Submit | figlet -f mini
|
||||||
|
mkdir -p $HOME/.local
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
cp -p git-submit $HOME/.local/bin
|
||||||
|
chmod +x $HOME/.local/bin/git-submit
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Readline" | figlet -f mini
|
||||||
|
sudo apt-get install -y libreadline-dev readline-doc 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Clang" | figlet -f mini
|
||||||
|
sudo apt-get install -y clang 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "GDB" | figlet -f mini
|
||||||
|
sudo apt-get install -y gdb cgdb 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Valgrind" | figlet -f mini
|
||||||
|
sudo apt-get install -y valgrind 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "GCC and tools" | figlet -f mini
|
||||||
|
sudo apt-get install -y gcc make binutils 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "POSIX man pages" | figlet -f mini
|
||||||
|
sudo apt-get install -y manpages-posix-dev 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Ncurses" | figlet -f mini
|
||||||
|
sudo apt-get install -y libncurses-dev 2>&1 > /dev/null
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Criterion" | figlet -f mini
|
||||||
|
#sudo add-apt-repository -y ppa:snaipewastaken/ppa
|
||||||
|
#sudo apt-get update
|
||||||
|
#sudo apt-get install -y criterion-dev
|
||||||
|
sudo unzip -d / criterion.zip
|
||||||
|
|
||||||
|
dialog --keep-tite --title "Sublime Text" --yesno "Do you want to install Sublime with plugins?" 5 50
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
|
||||||
|
# Add Sublime key
|
||||||
|
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add - 2>&1 > /dev/null
|
||||||
|
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list 2>&1 > /dev/null
|
||||||
|
|
||||||
|
sudo apt-get update -y
|
||||||
|
|
||||||
|
echo "Installing..."
|
||||||
|
echo "Sublime Editor" | figlet -f mini
|
||||||
|
sudo apt-get install sublime-text 2>&1 > /dev/null
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.config"
|
||||||
|
mkdir -p "$HOME/.config/sublime-text-3"
|
||||||
|
mkdir -p "$HOME/.config/sublime-text-3/Installed Packages"
|
||||||
|
mkdir -p "$HOME/.config/sublime-text-3/Packages"
|
||||||
|
mkdir -p "$HOME/.config/sublime-text-3/Packages/User"
|
||||||
|
|
||||||
|
# EWS (8/8/2020)
|
||||||
|
# The following configuration is somewhat obsolete, and in any case is too sensitive to
|
||||||
|
# changes to Sublime and its plugins. When I tried to run it just now, it left things
|
||||||
|
# broken. Instead I have manually installed the following for use in CSE 320:
|
||||||
|
#
|
||||||
|
# Package Control
|
||||||
|
# SublimeLinter
|
||||||
|
# SublimeLinter-gcc
|
||||||
|
# TrailingSpaces
|
||||||
|
#
|
||||||
|
# The only really important thing is to copy SublimeLinter.sublime-settings to
|
||||||
|
# ~/.config/sublime-text-3/Packages/User as in the last commented line below, so that
|
||||||
|
# the linter works correctly with our project setup.
|
||||||
|
#
|
||||||
|
# The rest of this stuff I have commented out for now.
|
||||||
|
#
|
||||||
|
#touch "$HOME/.config/sublime-text-3/Packages/User/Package Control.sublime-settings"
|
||||||
|
#touch "$HOME/.config/sublime-text-3/Installed Packages/Package Control.sublime-package"
|
||||||
|
#wget -qO - https://packagecontrol.io/Package%20Control.sublime-package > "$HOME/.config/sublime-text-3/Installed Packages/Package Control.sublime-package"
|
||||||
|
#
|
||||||
|
#echo "{\"bootstrapped\":true,\"installed_packages\":[\"Package Control\",\"TrailingSpaces\",\"SublimeLinter\",\"SublimeLinter-contrib-gcc\"]}" > "$HOME/.config/sublime-text-3/Packages/User/Package Control.sublime-settings"
|
||||||
|
#echo "{\"trailing_spaces_trim_on_save\": true}" > "$HOME/.config/sublime-text-3/Packages/User/trailing_spaces.sublime-settings"
|
||||||
|
#echo "{\"ignored_packages\":[\"Vintage\"],\"hot_exit\":false,\"save_on_focus_lost\":true,\"translate_tabs_to_spaces\":true}" > "$HOME/.config/sublime-text-3/Packages/User/Preferences.sublime-settings"
|
||||||
|
cp -p SublimeLinter.sublime-settings ~/.config/sublime-text-3/Packages/User
|
||||||
|
fi
|
||||||
|
echo "-----------------------------"
|
||||||
|
echo "!ATTN!" | figlet
|
||||||
|
echo "-----------------------------"
|
||||||
|
echo -e "If you \e[31;1mcannot\e[0m execute git submit add the following to your ~/.bashrc or other relevant terminal config"
|
||||||
|
echo "export PATH=\$PATH:$HOME/.local/bin"
|
754
hw0-doc/README.md
Normal file
754
hw0-doc/README.md
Normal file
|
@ -0,0 +1,754 @@
|
||||||
|
# CSE320 Spring 2022
|
||||||
|
|
||||||
|
In this course you will be using Linux as your primary development
|
||||||
|
environment. In addition, we will be providing you with a git
|
||||||
|
repository hosted on a department GitLab server. This document will
|
||||||
|
briefly explain the course tools and outline the required setup for
|
||||||
|
this course.
|
||||||
|
|
||||||
|
## Setting up your CSE320 Git repository
|
||||||
|
|
||||||
|
Git is an open-source distributed version control system. We will use
|
||||||
|
git repositories to manage your homework submissions. In addition, the
|
||||||
|
use of git allows the Professor and TAs to access and view your your
|
||||||
|
code remotely in order to assist you. While some students may be
|
||||||
|
familiar with version control and git, we ask that everyone complete
|
||||||
|
the following tutorial and instructions. This will ensure that
|
||||||
|
everyone in the course has the same background knowledge and can
|
||||||
|
submit their homeworks.
|
||||||
|
|
||||||
|
We are using a CSE department supported git web interface, called
|
||||||
|
gitlab. This is similar to github, bitbucket, etc. It is an interface
|
||||||
|
to help manage git repositories. These services are INTERFACES to git,
|
||||||
|
not git itself. You *may not* use external repositories as we will
|
||||||
|
use the repo provided to you to grade your submitted work and share
|
||||||
|
gradesheets with you.
|
||||||
|
|
||||||
|
To setup your repository:
|
||||||
|
|
||||||
|
1. Navigate to
|
||||||
|
[https://gitlab02.cs.stonybrook.edu](https://gitlab02.cs.stonybrook.edu/)
|
||||||
|
and log into it with your CS email account (user name only, do not
|
||||||
|
include the `@cs.stonybrook.edu`). If you forgot your CS email
|
||||||
|
password you can reset it by following the instructions
|
||||||
|
[here](https://auth01.cs.stonybrook.edu:10443/). If those
|
||||||
|
instructions fail, please email `rt@cs.stonybrook.edu` requesting a
|
||||||
|
password reset. A response may take up to 24-48 hours.
|
||||||
|
2. Once you have logged in the creation of your repo will be triggered.
|
||||||
|
Normally this will occur within a few minutes. If not, then send an
|
||||||
|
email to `cse320@cs.stonybrook.edu` and we will look into it.
|
||||||
|
Sometimes the 'bot responsible for creating the repos has to be reset.
|
||||||
|
|
||||||
|
## Setting up Linux Environment
|
||||||
|
|
||||||
|
Since C is a systems level language, frequently the behavior from one
|
||||||
|
person’s computer to another can vary. In the past, we have provided a
|
||||||
|
common server for students to use, but this presented a few
|
||||||
|
problems. When you wanted to compile your assignment, you would have
|
||||||
|
to continuously transfer the file to the server and then compile
|
||||||
|
it. If you had any mistakes, you would have to either edit it on the
|
||||||
|
server or make the change locally and upload it again. This became
|
||||||
|
very tedious which often led to students compiling and testing
|
||||||
|
locally on their own machines. This was not always a good idea as
|
||||||
|
something that seemed to work for you didn’t always work for the
|
||||||
|
grader which caused many issues. Also, many tools, which assist in
|
||||||
|
locating and fixing errors in C code, do not exist in Windows and OSX
|
||||||
|
environments. So students who installed operating systems such as
|
||||||
|
[Linux](https://en.wikipedia.org/wiki/Linux) were at an advantage over
|
||||||
|
the students who did not.
|
||||||
|
|
||||||
|
> :nerd: This document will also outline the homework management and
|
||||||
|
submission process. In this class, you will be creating increasingly
|
||||||
|
complex C projects which may involve many files. To satisfy these
|
||||||
|
requirements, we will be using git to manage & submit your homework
|
||||||
|
assignments.
|
||||||
|
|
||||||
|
> :nerd: While we will try to provide the basics for what needs to be
|
||||||
|
done, it will ultimately be up to you to learn how to use these
|
||||||
|
tools.
|
||||||
|
|
||||||
|
To help alleviate the above issues and to setup a local environment
|
||||||
|
with the necessary course tools, you must install your working
|
||||||
|
environment using one of these two options:
|
||||||
|
|
||||||
|
- Option 1: A Virtual Machine running Linux (Encouraged Option)
|
||||||
|
- Option 2: Multi-Boot/Install Linux on your machine
|
||||||
|
|
||||||
|
Option 1 is encouraged for the following reasons:
|
||||||
|
- Quick setup
|
||||||
|
- Ease of use in your native OS
|
||||||
|
- Easy to reset if errors in VM environment
|
||||||
|
- All course tools are pre-installed
|
||||||
|
- Simulate multiple cores on a single core system
|
||||||
|
|
||||||
|
We have put a lot of effort into setting up a pre-configured VM. If
|
||||||
|
for some reason you are unable or unwilling to use this, we have
|
||||||
|
provided basic instructions for Option 2 with a script to install all
|
||||||
|
the course tools.
|
||||||
|
|
||||||
|
If you choose option 2, you should have some idea what you are doing,
|
||||||
|
already be comfortable with Linux, and be aware that we probably won't
|
||||||
|
have the resources to debug any issues you might encounter. If you
|
||||||
|
deviate in any other way from these procedures, it is completely at
|
||||||
|
your peril.
|
||||||
|
|
||||||
|
### Option 1: A Virtual Machine running Linux
|
||||||
|
|
||||||
|
Students often use either [VMware](https://www.vmware.com) or
|
||||||
|
[VirtualBox](https://www.virtualbox.org/) to run virtual machines.
|
||||||
|
We recommend that you use VirtualBox. It is free, and it runs
|
||||||
|
on all of the most popular platforms.
|
||||||
|
|
||||||
|
In order to run a virtual machine, your machine must support 64-bit
|
||||||
|
hardware virtualization. Most machines built after 2006 should support
|
||||||
|
this. However, not all machines have the option enabled. You may need
|
||||||
|
to modify your BIOS settings to enable this feature. As each machine
|
||||||
|
has a different BIOS, it is up to you to find and enable this feature
|
||||||
|
on your own machine.
|
||||||
|
|
||||||
|
Download and install the VirtualBox platform package appropriate
|
||||||
|
for your computer from [this site](https://www.virtualbox.org/wiki/Downloads).
|
||||||
|
|
||||||
|
> :exclamation: Because of recent changes made to the way VirtualBox interfaces
|
||||||
|
> with the graphics drivers on various platforms, it is important that you make
|
||||||
|
> sure to install VirtualBox version 6.1.27 or greater. With older versions,
|
||||||
|
> the course VM image will probably not be able to access the display properly.
|
||||||
|
|
||||||
|
#### Running the Linux VM
|
||||||
|
|
||||||
|
We will be using Linux Mint 20 "Ulyana" -- Cinnamon as this semester's OS. We
|
||||||
|
have taken the time to set up the VM so it simply needs to be opened
|
||||||
|
in your virtualization program. The provided Linux virtual machine
|
||||||
|
has all the tools required for various aspects of this course; for
|
||||||
|
example, homework submission is pre-installed.
|
||||||
|
|
||||||
|
To get started, download the VM from here:
|
||||||
|
[Google Drive]
|
||||||
|
(https://drive.google.com/file/d/1rwUM_rm4sEC-we-i-siOPWDnQEtH6mdC/view?usp=sharing)
|
||||||
|
(it's nearly 5 gb so give it some time).
|
||||||
|
This should result in your having a file called **CSE320_Spring22.ova**.
|
||||||
|
This can be imported directly into VirtualBox by choosing
|
||||||
|
"Import Appliance" from the "File" menu and then browsing to select
|
||||||
|
the file you downloaded. Click "Next", review the VM settings,
|
||||||
|
and then click on "Import". Once the import has completed, you should
|
||||||
|
have a VM called "CSE 320". Select this and click on
|
||||||
|
"Start" to boot the VM.
|
||||||
|
|
||||||
|
#### Login Info
|
||||||
|
|
||||||
|
Upon booting, you will be automatically logged in as user `student`.
|
||||||
|
The login info for your reference is:
|
||||||
|
|
||||||
|
|
||||||
|
| Username | Password |
|
||||||
|
|:----------------|:-------------|
|
||||||
|
| `student` | `cse320` |
|
||||||
|
|
||||||
|
You will need the password in order to obtain superuser access via `sudo`
|
||||||
|
to install software, and you might need to enter both the user name and
|
||||||
|
the password if the screen lock should kick in after you have left the VM
|
||||||
|
idle for some time.
|
||||||
|
|
||||||
|
#### VirtualBox Guest Additions
|
||||||
|
|
||||||
|
The VirtualBox Guest Additions are software components that are added
|
||||||
|
to the guest operating system that runs in your VM, to make the VM more
|
||||||
|
convenient to use. Examples of things in the Guest Additions are accelerated
|
||||||
|
video drivers, support for clipboard and drag-and-drop between the VM
|
||||||
|
and the host system, ability to resize the VM window, and so on.
|
||||||
|
There is a version of the Guest Additions installed in the VM,
|
||||||
|
but since the Guest Additions need to match the version of VirtualBox
|
||||||
|
that you are using, you should reinstall them. To do this, you should
|
||||||
|
start the VM, then from the "Devices" menu (probably in the titlebar of
|
||||||
|
the VM window, or wherever top-level application menus appear on your
|
||||||
|
system) select "Insert Guest Additions CD Image". This might cause
|
||||||
|
a CD image to be downloaded over the network. If the system offers to
|
||||||
|
auto-run the CD, allow it to do so. Otherwise you might have to use
|
||||||
|
file manager (under Linux Mint) to open the CD manually. Once started,
|
||||||
|
it can take several minutes for the installation to complete.
|
||||||
|
|
||||||
|
#### VM Snapshots
|
||||||
|
|
||||||
|
If you choose to install additional tools or other programs to your
|
||||||
|
environment, you may want to take a snapshot of your VM. This may save
|
||||||
|
you the time of installing your additional software again, in the
|
||||||
|
unfortunate event of an unusable VM. Refer to the appropriate VirtualBox
|
||||||
|
documentation to learn how to take a snapshot of your VM.
|
||||||
|
|
||||||
|
### Option 2: Multi-Boot/Install Linux on your machine
|
||||||
|
|
||||||
|
> Remember, if you choose this option, you should have some idea what
|
||||||
|
you are doing, already be comfortable with Linux, and be aware that
|
||||||
|
we probably won't have the resources to debug any issues you might
|
||||||
|
encounter. If you deviate in any other way from these procedures,
|
||||||
|
it is completely at your peril.
|
||||||
|
|
||||||
|
Install [Linux Mint 20 "Ulyana" - Cinnamon 64-bit](https://linuxmint.com/edition.php?id=281)
|
||||||
|
or 20.04 Ubuntu variant (as long as you are using gcc 9.3.0) as a dual-boot or fresh
|
||||||
|
install.
|
||||||
|
|
||||||
|
Clone the [CSE320 course tools](https://gitlab02.cs.stonybrook.edu/cse320/course_tools)
|
||||||
|
(https://gitlab02.cs.stonybrook.edu/cse320/course_tools) repository
|
||||||
|
into your Linux environment. You may need to install git first.
|
||||||
|
|
||||||
|
Follow the README in the `course_tools` repo.
|
||||||
|
|
||||||
|
#### Note about MacOS with Apple M1 Processor
|
||||||
|
|
||||||
|
We are aware that a number of students are now using Macs with an M1 processor.
|
||||||
|
The M1 hardware uses the ARM instruction set, which is different than the
|
||||||
|
x86-64 instruction set which the course Linux Mint VM uses. At the time of this
|
||||||
|
writing, we do not have any reliable information that would indicate that it would
|
||||||
|
be possible to run the Linux Mint VM on an M1. It might be possible to run
|
||||||
|
it using QEMU, which is a full x86-64 emulator that is independent of the
|
||||||
|
underlying host system hardware and for which versions exist for Macs running
|
||||||
|
on the M1, though to date we do not have any information from anyone who has
|
||||||
|
succeeded in running the VM this way (please tell us if you have managed to do it).
|
||||||
|
However, even if in fact the VM can be run this way it is likely to be very slow.
|
||||||
|
So, our best advice at this time would be to try to identify some x86-64-based
|
||||||
|
computer that you can use for the course, rather than supposing that you will
|
||||||
|
be able to use an M1-based computer.
|
||||||
|
|
||||||
|
### Working in Unix
|
||||||
|
|
||||||
|
We understand that many of the students taking this class are new to
|
||||||
|
CLI (Command-line interface). You can find a quick crash course in
|
||||||
|
[Appendix A of Learn Python the Hard Way](https://learnpythonthehardway.org/book/appendixa.html).
|
||||||
|
|
||||||
|
> :nerd: For more advanced usage refer
|
||||||
|
[here](http://www.ibm.com/developerworks/library/l-lpic1-103-1/). This
|
||||||
|
is a REALLY good resource so we recommend bookmarking it for later
|
||||||
|
reference.
|
||||||
|
|
||||||
|
> :nerd: It is **very** important that you properly shut down the Linux Mint
|
||||||
|
operating system when you are finished using it, rather than just
|
||||||
|
"X-ing out" the VirtualBox VM window. The latter is equivalent to
|
||||||
|
going and yanking your desktop PC's power plug out of the wall without
|
||||||
|
shutting down Windows, and it can cause data loss and even corruption.
|
||||||
|
Use the shutdown icon from the "Mint" menu in the lower left corner
|
||||||
|
of the desktop to shutdown Linux Mint. At that point, it will be safe
|
||||||
|
to power off the VM.
|
||||||
|
|
||||||
|
> :nerd: Depending on the host system on which you installed VirtualBox,
|
||||||
|
"keyboard integration" and "mouse integration" might or might not be
|
||||||
|
supported. If they are supported, then you will be able to fairly
|
||||||
|
seamlessly move your mouse in and out of the VM window and what you
|
||||||
|
type on the keyboard will go to the proper place. If these features
|
||||||
|
are not supported, then you will need to click on the VM window in
|
||||||
|
order to use it, at which point the mouse and keyboard will be "captured"
|
||||||
|
by the VM. In order to regain control of the mouse and cursor, you
|
||||||
|
will need to press the "host key", which is identified at the right-hand
|
||||||
|
side of the bottom icon tray of the VirtualBox window. On some systems,
|
||||||
|
the default host key is "Right Ctrl".
|
||||||
|
|
||||||
|
> :nerd: To open a terminal window, you can click on the terminal
|
||||||
|
icon (which should be fairly evident), or you can press CTRL + ALT + T.
|
||||||
|
|
||||||
|
#### Text Editor
|
||||||
|
|
||||||
|
A _good_ basic text editor is the key for C development.
|
||||||
|
|
||||||
|
We have pre-installed Sublime Text with plugins such as a C linter to
|
||||||
|
assist with C development on the given VM. A linter displays compiler
|
||||||
|
errors on top of your code much like an IDE. If you do install another
|
||||||
|
editor we recommend looking into a similar feature described as it
|
||||||
|
will aid development.
|
||||||
|
|
||||||
|
You may use another text editor if you so desire. Some popular ones
|
||||||
|
are Atom, Vim, Emacs and VSCode. Each have their own linters that you
|
||||||
|
can look into installing.
|
||||||
|
|
||||||
|
**DO NOT** install and use a full IDE (Clion, Netbeans, or Eclipse);
|
||||||
|
there are many parts of the compilation process that are hidden from
|
||||||
|
you. Not only would you miss out on valuable information pertinent
|
||||||
|
to the course but your project is not guaranteed to build in an
|
||||||
|
environment separate from the IDE.
|
||||||
|
|
||||||
|
## Homework Management & Submission
|
||||||
|
|
||||||
|
#### Setting up your CSE320 repository
|
||||||
|
|
||||||
|
Once your repository has been created on gitlab, you must clone it in
|
||||||
|
your Linux environment. Open a new terminal window
|
||||||
|
and type `git clone GIT_URL`. You should replace `GIT_URL` with the
|
||||||
|
URL to your repository. You can find it by navigating to your projects
|
||||||
|
page on GitLab and selecting the https option.
|
||||||
|
|
||||||
|
> Your repo should be cloned into your home directory (`/home/student/` or AKA `~/`)
|
||||||
|
|
||||||
|
Alternatively if you add an ssh-key to your gitlab account you can
|
||||||
|
clone, pull, push, etc. using the URL under the SSH option (**highly
|
||||||
|
recommended** An SSH key can be done at any time).
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
- [Generating SSH key](http://docs.gitlab.com/ce/ssh/README.html)
|
||||||
|
|
||||||
|
#### First Commit to your Repo
|
||||||
|
|
||||||
|
Open a terminal and from the home directory enter the following command:
|
||||||
|
(replacing REPO_NAME with your repo's name)
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ subl REPO_NAME
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The text editor, Sublime, will open and your repo's contents will be
|
||||||
|
shown on the sidebar. Open the `README.md` file and add the text with
|
||||||
|
the following information relevant to you.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# FIRST_NAME LAST_NAME
|
||||||
|
## ID_NUMBER
|
||||||
|
:FAVORITE_EMOJI:
|
||||||
|
PROFESSOR_NAME - SECTION_NUMBER
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find your favorite emoji code among these
|
||||||
|
[https://gist.github.com/rxaviers/7360908](https://gist.github.com/rxaviers/7360908).
|
||||||
|
After that you can save and close the file and return to your terminal.
|
||||||
|
|
||||||
|
In your terminal, type the following commands, replacing `EMAIL` with
|
||||||
|
your CS email address and `NAME` with your name:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git config --global user.email "EMAIL"
|
||||||
|
$ git config --global user.name "FIRST_NAME LAST_NAME"
|
||||||
|
$ git config --global push.default simple
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
**NOTE:** This will change your settings for all repos. If you want to
|
||||||
|
have different settings for other repos on your machine then omit
|
||||||
|
`--global`
|
||||||
|
|
||||||
|
Change directories into your repo `cd REPO_NAME`
|
||||||
|
|
||||||
|
Then run the following commands:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git status
|
||||||
|
$ git add README.md
|
||||||
|
$ git commit -m "My First Commit"
|
||||||
|
$ git push
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
> The `git push` command will prompt for username and password if you used HTTPS.
|
||||||
|
|
||||||
|
The output will look **similar** to:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git status
|
||||||
|
On branch master
|
||||||
|
Your branch is up-to-date with 'origin/master'.
|
||||||
|
Changes not staged for commit:
|
||||||
|
(use "git add file..." to update what will be committed)
|
||||||
|
(use "git checkout -- file..." to discard changes in working directory)
|
||||||
|
|
||||||
|
modified: README.md
|
||||||
|
|
||||||
|
no changes added to commit (use "git add" and/or "git commit -a")
|
||||||
|
$ git add README.md
|
||||||
|
$ git commit -m "My First Commit"
|
||||||
|
[master XXXXXXX] My First Commit
|
||||||
|
1 files changed, X insertions(+), X deletions(-)
|
||||||
|
$ git push
|
||||||
|
Counting objects: 4, done.
|
||||||
|
Delta compression using up to 4 threads.
|
||||||
|
Compressing objects: 100% (4/4), done.
|
||||||
|
Writing objects: 100% (4/4), 980 bytes | 0 bytes/s, done.
|
||||||
|
Total 4 (delta 2), reused 0 (delta 0)
|
||||||
|
To ssh://git@gitlab02.cs.stonybrook.edu:130/CSE320_Fall20/REPONAME.git
|
||||||
|
XXXXXXX..XXXXXXX master -> master
|
||||||
|
Branch master set up to track remote branch master from origin.
|
||||||
|
$
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This is the basic usage of git. We check the `status` of which files
|
||||||
|
are tracked/untracked. Then we `add` them and we `commit` them along
|
||||||
|
with a message. Lastly and most importantly we `push` them to the
|
||||||
|
remote repository on the gitlab server. If the push was successful,
|
||||||
|
you can navigate back to the page `https://gitlab02.cs.stonybrook.edu`
|
||||||
|
and select your repository. Inside your repository, select the files
|
||||||
|
option on the left menu. You should now see the file `README.md` with
|
||||||
|
the contents you added to it.
|
||||||
|
|
||||||
|
> :scream: Once a commit has been made, its contents cannot be changed.
|
||||||
|
> In addition, the GitLab server has been configured so that it is not
|
||||||
|
> possible to delete any commits that have been pushed to the "master"
|
||||||
|
> branch. This means that any junk you commit to the master branch and
|
||||||
|
> push to the server will persist there forever in your repo, as well as
|
||||||
|
> in copies that we have to store. In view of this, it is is important that
|
||||||
|
> you take great care not to commit junk files, especially files that are
|
||||||
|
> very large or binary files that are generated by the compiler.
|
||||||
|
> Each time you commit, you should first use `git status` to carefully review
|
||||||
|
> the set of files to be committed. Use `git reset` to remove any files that
|
||||||
|
> are staged for commit but should not be. We strongly recommend that you
|
||||||
|
> *never* use commands such as `git add .` or `git add --all`, as these have
|
||||||
|
> the potential to add a lot of junk to your commit. Instead, `git add` each
|
||||||
|
> file individually, after perhaps using `git diff` to remind yourself of the
|
||||||
|
> reason for the commit and to see if the changes are as they should be.
|
||||||
|
|
||||||
|
#### Git Tutorial
|
||||||
|
|
||||||
|
We recommend you complete Codecademy’s git tutorial found
|
||||||
|
[here](https://www.codecademy.com/learn/learn-git) if you are
|
||||||
|
unfamilar with git.
|
||||||
|
|
||||||
|
If you’re interested in learning more information about git or
|
||||||
|
expanding your knowledge, refer to these references:
|
||||||
|
- [git-book](https://git-scm.com/book/en/v2) - Chapter 2 is a MUST
|
||||||
|
read chapter, checkout git aliases!
|
||||||
|
- [Learn Git Branching](http://learngitbranching.js.org/) - An
|
||||||
|
interactive tutorial on git branching
|
||||||
|
- [git cheat sheet](https://scotch.io/bar-talk/git-cheat-sheet)
|
||||||
|
|
||||||
|
# Homework 0
|
||||||
|
|
||||||
|
#### Obtaining Assignment Code
|
||||||
|
|
||||||
|
1. Navigate to your repository directory (`cd ~/REPO_NAME`) in your VM
|
||||||
|
(using the terminal).
|
||||||
|
|
||||||
|
2. An assignment, such as this one, will tell you the code is located
|
||||||
|
at a particular address. For `hw0` it is:
|
||||||
|
`https://gitlab02.cs.stonybrook.edu/cse320/hw0.git`
|
||||||
|
|
||||||
|
3. Add this remote repository as an additional remote into your
|
||||||
|
existing repository. We will name the new remote HW0_CODE.
|
||||||
|
|
||||||
|
If you use HTTPS:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git remote add HW0_CODE https://gitlab02.cs.stonybrook.edu/cse320/hw0.git
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use SSH:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git remote add HW0_CODE ssh://git@gitlab02.cs.stonybrook.edu:130/cse320/hw0.git
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Fetch all the refs in this new repository. This command will prompt
|
||||||
|
for username and password if you used HTTPS.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git fetch HW0_CODE
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Finally, merge and commit the files from the `HW0_CODE` remote's
|
||||||
|
`master` branch into your existing repository’s `master` branch.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git merge -m "Merging HW0_CODE" HW0_CODE/master
|
||||||
|
```
|
||||||
|
|
||||||
|
> :nerd: If you get an error mentioning 'unrelated histories' try
|
||||||
|
again adding this flag: `--allow-unrelated-histories`
|
||||||
|
|
||||||
|
6. If you type the command `ls` you should now see a directory called `hw0`.
|
||||||
|
|
||||||
|
7. Push these base files to your remote repository (gitlab). This
|
||||||
|
command will prompt for username and password if you used HTTPS.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git push
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Your Homework 0 Working Directory
|
||||||
|
|
||||||
|
The directory structure of your repo will now look **similar** to
|
||||||
|
this. Use `ls -a` or `tree -a` to see the hidden files that begin with
|
||||||
|
`.`
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
YOUR_REPO
|
||||||
|
├── .git
|
||||||
|
│ ├── ...
|
||||||
|
├── .gitignore
|
||||||
|
├── .gitlab-ci.yml
|
||||||
|
├── hw0
|
||||||
|
│ ├── academic_honesty.txt
|
||||||
|
│ ├── include
|
||||||
|
│ │ └── hi.h
|
||||||
|
│ ├── Makefile
|
||||||
|
│ ├── README.md
|
||||||
|
│ ├── src
|
||||||
|
│ │ ├── hi.c
|
||||||
|
│ │ └── main.c
|
||||||
|
│ └── tests
|
||||||
|
│ └── test.c
|
||||||
|
└── README.md
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Information about each file is explained below.
|
||||||
|
|
||||||
|
> :nerd: Enter `subl REPO_NAME` (or `subl .` if you are in your repo
|
||||||
|
already) as you did before to easily follow along and look inside
|
||||||
|
each file
|
||||||
|
|
||||||
|
- `.gitignore` - This is a file that tells git to ignore certain
|
||||||
|
directories or files that you don't want committed. For example, the
|
||||||
|
`bin` and `build` directories are ignored.
|
||||||
|
This is because we don't want executables and other generated binary files
|
||||||
|
pushed to your remote repository, only source code.
|
||||||
|
- `.gitlab-ci.yml` This is gitlab's own continuous integration
|
||||||
|
configuration file, explained in a later section.
|
||||||
|
- `hw0/` - This is your first homework directory, throughout the
|
||||||
|
semester we'll be adding each homework directory in this fashion
|
||||||
|
'hw#' where # is the homework number. Inside the `hw0/` directory,
|
||||||
|
you will find:
|
||||||
|
- `README.md` - This is a file where you can detail notes about the project.
|
||||||
|
- `Makefile` - This is your ultimate compilation automation
|
||||||
|
tool. The program `make` will use this file to properly compile
|
||||||
|
your assignment.
|
||||||
|
- `include/` - This is where we keep our `.h` headers. Unlike
|
||||||
|
Java, C is a one pass compilation language, which keeps the
|
||||||
|
overhead of creating symbol tables and structures low. Since all
|
||||||
|
functions must be defined before use, we utilize a header file
|
||||||
|
to make the symbols available across multiple files.
|
||||||
|
- `hi.h` - This is our header file for `hw0`. **Examine the
|
||||||
|
contents of this file.**
|
||||||
|
- `src/` - This is where we keep our `.c` source files. These
|
||||||
|
files contain the actual definitions of the functions declared
|
||||||
|
in our headers.
|
||||||
|
- `main.c` - This file contains the C main function, in this
|
||||||
|
course you will **ALWAYS** need to keep your main function
|
||||||
|
in its own C file isolated from the rest of your functions
|
||||||
|
that you implement.
|
||||||
|
- `hi.c` - The helper function, `hi`, is defined (implemented)
|
||||||
|
here. Each function **does not** need its own file, only
|
||||||
|
`main()` needs to be in its own file.
|
||||||
|
- `tests/` - This is where we keep our unit tests. There is an
|
||||||
|
EXCELLENT unit testing framework called
|
||||||
|
[criterion](http://criterion.readthedocs.io/en/master/intro.html)
|
||||||
|
that we will be using in the course.
|
||||||
|
- `test.c` - This file contains the implementation of our
|
||||||
|
testing framework. The Makefile will compile these files
|
||||||
|
with all of the non-`main.c` files. This gives the testing
|
||||||
|
framework access to your helper functions.
|
||||||
|
|
||||||
|
> **Do not** modify or alter `.gitlab-ci.yml`, the `Makefile` or the
|
||||||
|
`README.md` for any assignment unless otherwise instructed.
|
||||||
|
|
||||||
|
### Academic Honesty Statement
|
||||||
|
|
||||||
|
In this course we take Academic Honesty EXTREMELY seriously. Read the
|
||||||
|
statement in `academic_honesty.txt` using your favorite text editor or
|
||||||
|
using the following command (type `man cat` for more information on
|
||||||
|
this tool).
|
||||||
|
|
||||||
|
From your repository's root directory type:
|
||||||
|
<pre>
|
||||||
|
$ cat hw0/academic_honesty.txt
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Next, we will append the Academic Honesty Statement into your
|
||||||
|
repository's README along with the date and your "signature"
|
||||||
|
confirming that you have read the statement and agree with the policy
|
||||||
|
and commit it to your repo.
|
||||||
|
|
||||||
|
From your repository's root directory type the following commands into
|
||||||
|
your terminal, filling in `YOUR_NAME` with the appropriate information
|
||||||
|
in the second command.
|
||||||
|
|
||||||
|
> :nerd: The second "crazy" command is an example of redirection which
|
||||||
|
can be done between programs on the command-line. We will learn more
|
||||||
|
about redirection and how it works later in the semester.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ cd hw0
|
||||||
|
$ cat academic_honesty.txt <(echo "$(date -u) - YOUR_NAME") >> ../README.md
|
||||||
|
$ git add --all
|
||||||
|
$ git commit -m "Academic Honesty statement"
|
||||||
|
$ git push
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
#### CI File
|
||||||
|
|
||||||
|
This semester we want to ensure that students don't get caught by
|
||||||
|
silly mistakes or overlook anything while working on their
|
||||||
|
assignments. We will use GitLab's [_Continuous
|
||||||
|
Integration_](https://en.wikipedia.org/wiki/Continuous_integration)
|
||||||
|
feature to minimize such incidents. It is an automated tool that will
|
||||||
|
make sure your work compiles and passes basic tests.
|
||||||
|
|
||||||
|
There is a `.gitlab-ci.yml` file in the base code. This file is used
|
||||||
|
to set up a clean vm, compile your code, run your unit tests, and
|
||||||
|
lastly run your program on the gitlab server. When looking on gitlab
|
||||||
|
you will notice a red :x: or green :heavy_check_mark:. Each represents
|
||||||
|
the result of a 'CI pipeline'. Go to the Pipelines tab to view the
|
||||||
|
results of your run. We will provide this file with each homework. The
|
||||||
|
CI runs when gitlab "gets around" to doing it so don't be alarmed if
|
||||||
|
you don't see your result right away. Also, note that sometimes
|
||||||
|
a "Runner System Failure" might occur due to problems on the server.
|
||||||
|
A failure reported by CI system does *not* mean that "your commit failed"
|
||||||
|
in the sense that what you committed and pushed failed to make it to the
|
||||||
|
gitlab server; it means that an error occurred while the system was trying
|
||||||
|
to compile and run what you committed. You can always use the `Repository`
|
||||||
|
tab of the gitlab web interface to see what commits you have pushed to the
|
||||||
|
server. If a commit is shown there, then it is safely on the server.
|
||||||
|
|
||||||
|
#### Hello, World!
|
||||||
|
|
||||||
|
In the terminal, navigate into the `hw0` folder of your repository
|
||||||
|
(ie. `~/REPO_NAME/hw0/`). Open the file `include/hi.h` and examine its
|
||||||
|
contents, read the comments, and follow the directions in the
|
||||||
|
files. These directions step you through the base files in the HW to
|
||||||
|
familiarize you with basic files structure and the included code
|
||||||
|
complements.
|
||||||
|
|
||||||
|
In the terminal, type `make` to build the `hw0` base code. This will
|
||||||
|
compile your c code into executables.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ make
|
||||||
|
mkdir -p bin build
|
||||||
|
gcc build/hi.o build/main.o -o bin/hi
|
||||||
|
gcc -Wall -Werror -std=gnu11 -g -DDEBUG -I include build/hi.o tests/test.c -lcriterion -o bin/hi_tests
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
An executable named `hi` and `hi_tests` will be created in the `bin`
|
||||||
|
folder of the `hw0` directory. You can execute either program by
|
||||||
|
typing `bin/hi` or `bin/hi_tests` from the `hw0` directory into the
|
||||||
|
terminal.
|
||||||
|
|
||||||
|
Running `bin/hi` will print "Hello, World!" before exiting. Running
|
||||||
|
`bin/hi_tests` will fail a unit test and print the warning `"Assertion
|
||||||
|
failed: say_hi() function did not say 'Hi'"`.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ bin/hi
|
||||||
|
Hello, World!
|
||||||
|
$ bin/hi_tests
|
||||||
|
[----] tests/test.c:15: Assertion failed: say_hi() function did not say 'Hi'
|
||||||
|
[FAIL] CSE320_Suite::test_it_really_does_say_hi: (0.00s)
|
||||||
|
[====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
To do this assignment, modify the `say_hi()` function in `src/hi.c` to
|
||||||
|
satisfy the unit test (i.e. `return "Hi"`). This is will now make the
|
||||||
|
program do what the unit test expects and as a result pass the unit
|
||||||
|
test.
|
||||||
|
|
||||||
|
Rebuild the hw0 executables by typing in ‘make’ to your terminal. Run
|
||||||
|
the program again to make sure it satisfies requirements
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ make
|
||||||
|
mkdir -p bin build
|
||||||
|
gcc -Wall -Werror -std=gnu11 -g -DDEBUG -I include -c -o build/hi.o src/hi.c
|
||||||
|
gcc build/hi.o build/main.o -o bin/hi
|
||||||
|
gcc -Wall -Werror -std=gnu11 -g -DDEBUG -I include build/hi.o tests/test.c -lcriterion -o bin/hi_tests
|
||||||
|
$ bin/hi
|
||||||
|
Hi, World!
|
||||||
|
$ bin/hi_tests
|
||||||
|
[====] Synthesis: Tested: 1 | Passing: 1 | Failing: 0 | Crashing: 0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
To save your code changes, `add` them to the staging area of
|
||||||
|
git. `Commit` the changes to a local commit on your VM. Then `push`
|
||||||
|
the commits to the remote server (gitlab).
|
||||||
|
|
||||||
|
You can be sure that everything compiles correctly if at some point a
|
||||||
|
check appears next to your repo on gitlab.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git status
|
||||||
|
On branch master
|
||||||
|
Your branch is up-to-date with 'origin/master'.
|
||||||
|
Changes not staged for commit:
|
||||||
|
(use "git add file..." to update what will be committed)
|
||||||
|
(use "git checkout -- file..." to discard changes in working directory)
|
||||||
|
|
||||||
|
modified: src/hi.c
|
||||||
|
|
||||||
|
no changes added to commit (use "git add" and/or "git commit -a")
|
||||||
|
$ git add src/hi.c
|
||||||
|
$ git commit -m "Hi Fix"
|
||||||
|
[master XXXXXXX] updated hi.c
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
$ git push
|
||||||
|
Username for 'https://gitlab02.cs.stonybrook.edu': REPO_NAME
|
||||||
|
Password for 'https://REPO_NAME@gitlab02.cs.stonybrook.edu':
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## How to submit with `git submit`
|
||||||
|
|
||||||
|
This semester you will be submitting all assignments using our custom
|
||||||
|
git command: `git submit`
|
||||||
|
|
||||||
|
The usage for `git submit` is:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git submit [-c COMMIT_HASH] TAG
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The **TAG** is which assignment you are tagging, for example: `hw0` or
|
||||||
|
`hw1` or `hw2`. This is the format for all tags (with a few
|
||||||
|
exceptions) that we will use this semester `hw#` where `#` is the
|
||||||
|
assignment number.
|
||||||
|
|
||||||
|
The `-c` flag is optional. **COMMIT** is the SHA of the commit you
|
||||||
|
wish to submit. In case you wanted to submit a different commit than
|
||||||
|
your most current one you would just provide the SHA for the commit to
|
||||||
|
be submitted. You can view your commit SHAs using the following
|
||||||
|
command:
|
||||||
|
|
||||||
|
> The SHA is the alphanumeric string before the first hyphen.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git log --pretty=format:"%h - %s - %ar"
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
With `git submit`, you may:
|
||||||
|
|
||||||
|
* Submit your assignment **only** from the master branch.
|
||||||
|
|
||||||
|
* The commits that you are submitting must be on the master branch or
|
||||||
|
merged into master (commit hash is on master).
|
||||||
|
|
||||||
|
* You may use other branches if you wish, but you cannot use
|
||||||
|
git-submit from these branches.
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git submit hw0
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This will submit your latest pushed commit for `hw0`. You can submit
|
||||||
|
as many times as you wish prior to any deadline.
|
||||||
|
|
||||||
|
#### How to check your submission
|
||||||
|
|
||||||
|
It creates a special branch in your repo called 'submit'. The only
|
||||||
|
reason it is special is that only instructors have permission to push
|
||||||
|
to it, hence why you need a special tool to submit your assignment. So
|
||||||
|
the submit tool tags the commit that you want to submit and merges
|
||||||
|
that commit into the submit branch under an authorized user.
|
||||||
|
|
||||||
|
Also, you should see a submission commit on the 'submit' branch. The
|
||||||
|
submission commit is a commit with a commit message formatted as
|
||||||
|
"<tag-name> submission commit". This you can see by navigating to
|
||||||
|
Repository > Commits and selecting the 'submit' branch from the
|
||||||
|
dropdown.
|
||||||
|
|
||||||
|
If you see both of these things, you have successfully submitted the
|
||||||
|
assignment. A successfully submitted homework assignment will have a
|
||||||
|
tag for that particular homework which you can see if you go to your
|
||||||
|
project in Gitlab and navigate to Repository > Tags.
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
These are the tools and the dev environment you will be working with
|
||||||
|
for the rest of the semester. We have provided you with these tools to
|
||||||
|
ease the protocols of this course and prevent mishaps from
|
||||||
|
occurring.
|
||||||
|
|
||||||
|
So that you do not delay your ability to start on `hw1`, you should make
|
||||||
|
every effort to complete the steps above by **February 4, 2022**, 11:59PM.
|
||||||
|
After that date it will no longer be possible to `git submit hw0`,
|
||||||
|
however you should still make the commits to your repository described
|
||||||
|
above prior to submitting `hw1`.
|
||||||
|
Although `hw0` will not be formally scored and weighted into the final grade
|
||||||
|
calculations, you will not receive credit for any other assignments
|
||||||
|
in this course unless the signed Academic Honesty statement has previously
|
||||||
|
been committed to your repository and thereby incorporated into the
|
||||||
|
submissions for those assignments.
|
BIN
hw1-doc/ECMA-404_2nd_edition_december_2017.pdf
Normal file
BIN
hw1-doc/ECMA-404_2nd_edition_december_2017.pdf
Normal file
Binary file not shown.
1196
hw1-doc/README.md
Normal file
1196
hw1-doc/README.md
Normal file
File diff suppressed because it is too large
Load Diff
1168
hw2-doc/DebuggingRef.md
Normal file
1168
hw2-doc/DebuggingRef.md
Normal file
File diff suppressed because it is too large
Load Diff
420
hw2-doc/README.md
Normal file
420
hw2-doc/README.md
Normal file
|
@ -0,0 +1,420 @@
|
||||||
|
# Homework 2 Debugging and Fixing - CSE 320 - Spring 2022
|
||||||
|
#### Professor Eugene Stark
|
||||||
|
|
||||||
|
### **Due Date: Friday 3/4/2022 @ 11:59pm**
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
In this assignment you are tasked with updating an old piece of
|
||||||
|
software, making sure it compiles, and that it works properly
|
||||||
|
in your VM environment.
|
||||||
|
|
||||||
|
Maintaining old code is a chore and an often hated part of software
|
||||||
|
engineering. It is definitely one of the aspects which are seldom
|
||||||
|
discussed or thought about by aspiring computer science students.
|
||||||
|
However, it is prevalent throughout industry and a worthwhile skill to
|
||||||
|
learn. Of course, this homework will not give you a remotely
|
||||||
|
realistic experience in maintaining legacy code or code left behind by
|
||||||
|
previous engineers but it still provides a small taste of what the
|
||||||
|
experience may be like. You are to take on the role of an engineer
|
||||||
|
whose supervisor has asked you to correct all the errors in the
|
||||||
|
program, plus add additional functionality.
|
||||||
|
|
||||||
|
By completing this homework you should become more familiar
|
||||||
|
with the C programming language and develop an understanding of:
|
||||||
|
|
||||||
|
- How to use tools such as `gdb` and `valgrind` for debugging C code.
|
||||||
|
- Modifying existing C code.
|
||||||
|
- C memory management and pointers.
|
||||||
|
- Working with files and the C standard I/O library.
|
||||||
|
|
||||||
|
## The Existing Program
|
||||||
|
|
||||||
|
Your goal will be to debug and extend an old program called `par`,
|
||||||
|
which was written by Adam M. Costello and posted to Usenet in 1993.
|
||||||
|
I have rearranged the original source code and re-written the `Makefile`
|
||||||
|
to conform to the format we are using for the assignments in this course.
|
||||||
|
Besides a bug that was present in the original version, I have introduced
|
||||||
|
a few additional bugs here and there to make things more interesting
|
||||||
|
and educational for you :wink:.
|
||||||
|
Although you will need to correct these bugs in order to make the program
|
||||||
|
function, they do not otherwise change the program behavior from what
|
||||||
|
the author intended.
|
||||||
|
|
||||||
|
The `par` program is a simple paragraph reformatter. It is basically
|
||||||
|
designed to read text from the standard input, parse the text into
|
||||||
|
paragraphs, which are delimited by empty lines, chop each paragraph up
|
||||||
|
into a sequence of words (forgetting about the original line breaks),
|
||||||
|
choose new line breaks to optimize some criteria that are designed to
|
||||||
|
produce a pleasing result, and the finally output the paragraph with
|
||||||
|
the new line breaks. There are several parameters that can be set
|
||||||
|
which affect the result: the width of the output text, the length of
|
||||||
|
a "prefix" and a "suffix" to be prepended and appended to each output line,
|
||||||
|
a parameter "hang", which affects the default value of "prefix", and
|
||||||
|
a boolean parameter "last", which affects the way the last line of a
|
||||||
|
paragraph is treated.
|
||||||
|
|
||||||
|
What you have to do is to first get the program to compile (for the most part,
|
||||||
|
I did not modify the original code, which requires some changes for it
|
||||||
|
to compile cleanly with the compiler and settings we are using).
|
||||||
|
Then, you need to test the program and find and fix the bugs that prevent it
|
||||||
|
from functioning properly. Some of the bugs existed in the original version and
|
||||||
|
some I introduced for the purposes of this assignment.
|
||||||
|
Finally, you will make some modifications to the program.
|
||||||
|
|
||||||
|
As you work on the program, limit the changes you make to the minimum necessary
|
||||||
|
to achieve the specified objectives. Don't rewrite the program;
|
||||||
|
assume that it is essentially correct and just fix a few compilation errors and
|
||||||
|
bugs as described below. You will likely find it helpful to use `git` for this (I did).
|
||||||
|
Make exploratory changes first on a side branch (*i.e.* not the master branch),
|
||||||
|
then when you think you have understood the proper changes that need to be made,
|
||||||
|
go back and apply those changes to the master branch. Using `git` will help you
|
||||||
|
to back up if you make changes that mess something up.
|
||||||
|
|
||||||
|
### Getting Started - Obtain the Base Code
|
||||||
|
|
||||||
|
Fetch base code for `hw2` as you did for the previous assignments.
|
||||||
|
You can find it at this link:
|
||||||
|
[https://gitlab02.cs.stonybrook.edu/cse320/hw2](https://gitlab02.cs.stonybrook.edu/cse320/hw2).
|
||||||
|
|
||||||
|
Once again, to avoid a merge conflict with respect to the file `.gitlab-ci.yml`,
|
||||||
|
use the following command to merge the commits:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
git merge -m "Merging HW2_CODE" HW2_CODE/master --strategy-option=theirs
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
> :nerd: I hope that by now you would have read some `git` documentation to find
|
||||||
|
> out what the `--strategy-option=theirs` does, but in case you didn't :angry:
|
||||||
|
> I will say that merging in `git` applies a "strategy" (the default strategy
|
||||||
|
> is called "recursive", I believe) and `--strategy-option` allows an option
|
||||||
|
> to be passed to the strategy to modify its behavior. In this case, `theirs`
|
||||||
|
> means that whenever a conflict is found, the version of the file from
|
||||||
|
> the branch being merged (in this case `HW2_CODE/master`) is to be used in place
|
||||||
|
> of the version from the currently checked-out branch. An alternative to
|
||||||
|
> `theirs` is `ours`, which makes the opposite choice. If you don't specify
|
||||||
|
> one of these options, `git` will leave conflict indications in the file itself
|
||||||
|
> and it will be necessary for you to edit the file and choose the code you want
|
||||||
|
> to use for each of the indicated conflicts.
|
||||||
|
|
||||||
|
Here is the structure of the base code:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
.
|
||||||
|
├── .gitlab-ci.yml
|
||||||
|
└── hw2
|
||||||
|
├── doc
|
||||||
|
│ ├── par.1
|
||||||
|
│ ├── par.doc
|
||||||
|
│ └── protoMakefile
|
||||||
|
├── hw2.sublime-project
|
||||||
|
├── include
|
||||||
|
│ ├── buffer.h
|
||||||
|
│ ├── debug.h
|
||||||
|
│ ├── errmsg.h
|
||||||
|
│ └── reformat.h
|
||||||
|
├── Makefile
|
||||||
|
├── rsrc
|
||||||
|
│ ├── banner.txt
|
||||||
|
│ ├── gettysburg.txt
|
||||||
|
│ └── loremipsum.txt
|
||||||
|
├── src
|
||||||
|
│ ├── buffer.c
|
||||||
|
│ ├── errmsg.c
|
||||||
|
│ ├── main.c
|
||||||
|
│ ├── par.c
|
||||||
|
│ └── reformat.c
|
||||||
|
├── test_output
|
||||||
|
│ └── .git-keep
|
||||||
|
└── tests
|
||||||
|
├── basecode_tests.c
|
||||||
|
├── rsrc
|
||||||
|
│ ├── banner.txt
|
||||||
|
│ ├── basic.in -> gettysburg.txt
|
||||||
|
│ ├── basic.out
|
||||||
|
│ ├── blank_lines.txt
|
||||||
|
│ ├── EOF.in
|
||||||
|
│ ├── EOF.out
|
||||||
|
│ ├── gettysburg.txt
|
||||||
|
│ ├── loremipsum.txt
|
||||||
|
│ ├── prefix_suffix.in -> banner.txt
|
||||||
|
│ ├── prefix_suffix.out
|
||||||
|
│ ├── valgrind_leak.in -> gettysburg.txt
|
||||||
|
│ ├── valgrind_leak.out
|
||||||
|
│ ├── valgrind_uninitialized.err
|
||||||
|
│ ├── valgrind_uninitialized.in -> loremipsum.txt
|
||||||
|
│ └── valgrind_uninitialized.out
|
||||||
|
├── test_common.c
|
||||||
|
└── test_common.h
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The `src` directory contains C source code files `buffer.c`. `par.c`, `reformat.c`,
|
||||||
|
and `errmsg.c`, which were part of the original code. In addition, I have added
|
||||||
|
a new file `main.c`, with a single `main()` function that simply calls
|
||||||
|
`original_main()` in `par.c`. This is to satisfy our requirement (for Criterion)
|
||||||
|
that `main()` is the only function in `main.c`.
|
||||||
|
|
||||||
|
The `include` directory contains C header files `buffer.h`, `reformat.h`, and
|
||||||
|
`errmsg.h`, which were part of the original source code. I have also added our
|
||||||
|
`debug.h` header file which may be of use to you.
|
||||||
|
|
||||||
|
The `doc` directory contains documentation files that were part of the original
|
||||||
|
distribution of `par`. The file `par.1` is in the format traditionally used
|
||||||
|
for Unix manual pages. This file `par.` is intended to be processed with the
|
||||||
|
the formatting program `nroff` with argument `-man`; for example:
|
||||||
|
`nroff -man doc/par.1 | less` could be used to format and view its contents.
|
||||||
|
|
||||||
|
The `tests` directory contains C source code (in file `basecode_tests.c`) for some Criterion
|
||||||
|
tests that can help guide you toward bugs in the program. These are not guaranteed
|
||||||
|
to be complete or exhaustive. The `test_common.c` and `test_common.h` contain auxiliary code
|
||||||
|
used by the tests. The subdirectory `tests/rsrc` contains input files and reference output files
|
||||||
|
that are used by the tests.
|
||||||
|
The `par` program was not designed to be particularly conducive to unit testing,
|
||||||
|
so all the tests we will make (including the tests used in grading) will be so-called
|
||||||
|
"black box" tests, which test the input-output behavior of the program running as a
|
||||||
|
separate process from the test driver.
|
||||||
|
The `test_common.c` file contains helper functions for launching an instance of `par`
|
||||||
|
as a separate process, redirecting `stdin` from an input file, collecting the
|
||||||
|
output produced on `stdout` and `stderr`, checking the exit status of the program,
|
||||||
|
and comparing the output against reference output.
|
||||||
|
|
||||||
|
The `test_output` directory is a "dummy" directory which is used to hold the output
|
||||||
|
produced when you run the Criterion tests. Look there if you want to understand,
|
||||||
|
for example, why the tests reported that the output produced by your program was
|
||||||
|
not as expected.
|
||||||
|
|
||||||
|
Before you begin work on this assignment, you should read the rest of this
|
||||||
|
document. In addition, we additionally advise you to read the
|
||||||
|
[Debugging Document](DebuggingRef.md). One of the main goals of this assignment
|
||||||
|
is to get you to learn how to use the `gdb` debugger, so you should right away
|
||||||
|
be looking into how to use this while working on the tasks in the following sections.
|
||||||
|
|
||||||
|
# Part 1: Debugging and Fixing
|
||||||
|
|
||||||
|
You are to complete the following steps:
|
||||||
|
|
||||||
|
1. Clean up the code; fixing any compilation issues, so that it compiles
|
||||||
|
without error using the compiler options that have been set for you in
|
||||||
|
the `Makefile`.
|
||||||
|
Use `git` to keep track of the changes you make and the reasons for them, so that you can
|
||||||
|
later review what you have done and also so that you can revert any changes you made that
|
||||||
|
don't turn out to be a good idea in the end.
|
||||||
|
|
||||||
|
2. Fix bugs.
|
||||||
|
|
||||||
|
Run the program, exercising the various options, and look for cases in which the program
|
||||||
|
crashes or otherwise misbehaves in an obvious way. We are only interested in obvious
|
||||||
|
misbehavior here; don't agonize over program behavior that might just have been the choice
|
||||||
|
of the original author. You should use the provided Criterion tests to help point the way,
|
||||||
|
though they are not guaranteed to be exhaustive.
|
||||||
|
|
||||||
|
3. Use `valgrind` to identify any memory leaks or other memory access errors.
|
||||||
|
Fix any errors you find.
|
||||||
|
|
||||||
|
Run `valgrind` using a command of the following form:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ valgrind --leak-check=full --show-leak-kinds=all --undef-value-errors=yes [PAR PROGRAM AND ARGS]
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Note that the bugs that are present will all manifest themselves in some way
|
||||||
|
either as incorrect output, program crashes or as memory errors that can be
|
||||||
|
detected by `valgrind`. It is not necessary to go hunting for obscure issues
|
||||||
|
with the program output.
|
||||||
|
Also, do not make gratuitous changes to the program output, as this will
|
||||||
|
interfere with our ability to test your code.
|
||||||
|
|
||||||
|
> :scream: The author of this program was pretty fastidious about freeing memory before
|
||||||
|
> exiting the program. Once you have fixed the bugs, the program should exit without
|
||||||
|
> any type of memory leak reported by `valgrind`, including memory that is "still reachable"
|
||||||
|
> at the time of exit. "Still reachable" memory corresponds to memory that is in use
|
||||||
|
> when the program exits and can still be reached by following pointers from variables
|
||||||
|
> in the program. Although some people consider it to be untidy for a program
|
||||||
|
> to exit with "still reachable" memory, it doesn't cause any particular problem.
|
||||||
|
> For the present program, however, there should not be any "still reachable" memory.
|
||||||
|
|
||||||
|
> :scream: You are **NOT** allowed to share or post on PIAZZA
|
||||||
|
> solutions to the bugs in this program, as this defeats the point of
|
||||||
|
> the assignment. You may provide small hints in the right direction,
|
||||||
|
> but nothing more.
|
||||||
|
|
||||||
|
# Part 2: Changes to the Program
|
||||||
|
|
||||||
|
## Rewrite/Extend Options Processing
|
||||||
|
|
||||||
|
The basecode version of `par` performs its own *ad hoc* processing of command-line options.
|
||||||
|
This is likely due to the fact that there did not exist a commonly accepted library
|
||||||
|
package for performing this function at the time the program was written.
|
||||||
|
However, as options processing is a common function that is performed by most programs,
|
||||||
|
and it is desirable for programs on the same system to be consistent in how they interpret
|
||||||
|
their arguments, there have been more elaborate standardized libraries that have been written
|
||||||
|
for this purpose. In particular, the POSIX standard specifies a `getopt()` function,
|
||||||
|
which you can read about by typing `man 3 getopt`. A significant advantage to using a
|
||||||
|
standard library function like `getopt()` for processing command-line arguments,
|
||||||
|
rather than implementing *ad hoc* code to do it, is that all programs that use
|
||||||
|
the standard function will perform argument processing in the same way
|
||||||
|
rather than having each program implement its own quirks that the user has to remember.
|
||||||
|
|
||||||
|
For this part of the assignment, you are to replace the original argument-processing
|
||||||
|
code in `main()` by code that uses the GNU `getopt` library package.
|
||||||
|
In addition to the POSIX standard `getopt()` function, the GNU `getopt` package
|
||||||
|
provides a function `getopt_long()` that understands "long forms" of option
|
||||||
|
arguments in addition to the traditional single-letter options.
|
||||||
|
In your revised program, `main()` should use `getopt_long()` to traverse the
|
||||||
|
command-line arguments, and it should support the following option syntax
|
||||||
|
(in place of what was originally used by the program):
|
||||||
|
|
||||||
|
- `--version` (long form only):
|
||||||
|
Print the version number of the program.
|
||||||
|
|
||||||
|
- `-w WIDTH` (short form) or `--width WIDTH` (long form):
|
||||||
|
Set the output paragraph width to `WIDTH`.
|
||||||
|
|
||||||
|
- `-p PREFIX` (short form) or `--prefix PREFIX` (long form):
|
||||||
|
Set the value of the "prefix" parameter to `PREFIX`.
|
||||||
|
|
||||||
|
- `-s SUFFIX` (short form) or `--suffix SUFFIX` (long form):
|
||||||
|
Set the value of the "suffix" parameter to `SUFFIX`.
|
||||||
|
|
||||||
|
- `-h HANG` (short form) or `--hang HANG` (long form):
|
||||||
|
Set the value of the "hang" parameter to `HANG`.
|
||||||
|
|
||||||
|
- `-l LAST` (short form) or either `--last` or
|
||||||
|
`--no-last` (long form):
|
||||||
|
Set the value of the boolean "last" parameter.
|
||||||
|
For the short form, the values allowed for `LAST` should be either
|
||||||
|
`0` or `1`.
|
||||||
|
|
||||||
|
- `-m MIN` (short form) or either `--min` or `--no-min` (long form).
|
||||||
|
Set the value of the boolean "min" parameter.
|
||||||
|
For the short form, the values allowed for `MIN` should be either
|
||||||
|
`0` or `1`.
|
||||||
|
|
||||||
|
You will probably need to read the Linux "man page" on the `getopt` package.
|
||||||
|
This can be accessed via the command `man 3 getopt`. If you need further information,
|
||||||
|
search for "GNU getopt documentation" on the Web.
|
||||||
|
|
||||||
|
> :scream: You MUST use the `getopt_long()` function to process the command line
|
||||||
|
> arguments passed to the program. Your program should be able to handle cases where
|
||||||
|
> the (non-positional) flags are passed IN ANY order. Make sure that you test the
|
||||||
|
> program with prefixes of the long option names, as well as the full names.
|
||||||
|
|
||||||
|
## Revise the Error Message Scheme
|
||||||
|
|
||||||
|
The original program uses a very *ad hoc* scheme for error-message reporting:
|
||||||
|
if an error occurs, a string describing the error is stored into a global
|
||||||
|
character array `errmsg` with a hard-coded maximum size. (This hard-coded
|
||||||
|
size has an occurrence in the `fprintf()` format string in `par.c`,
|
||||||
|
which creates undesirable implicit coupling between `par.c` and `errmsg.c`.)
|
||||||
|
At various points in the program, the existence of an error condition is checked
|
||||||
|
by looking to see if the first character of the error message string is a null
|
||||||
|
character `'\0'`. Before the program terminates, if an error message exists,
|
||||||
|
then it is printed and the program exits with an error status, otherwise it exits
|
||||||
|
with a success indication.
|
||||||
|
|
||||||
|
Your job is to revise the error message scheme to make it somewhat more general
|
||||||
|
and to eliminate the hard-coded limitation on the length of an error message.
|
||||||
|
In particular, you should replace the interface defined in `errmsg.h` by the
|
||||||
|
following function prototypes (exactly as shown):
|
||||||
|
|
||||||
|
```c
|
||||||
|
/**
|
||||||
|
* @brief Set an error indication, with a specified error message.
|
||||||
|
* @param msg Pointer to the error message. The string passed by the caller
|
||||||
|
* will be copied.
|
||||||
|
*/
|
||||||
|
void set_error(char *msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test whether there is currently an error indication.
|
||||||
|
* @return 1 if an error indication currently exists, 0 otherwise.
|
||||||
|
*/
|
||||||
|
int is_error();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Issue any existing error message to the specified output stream.
|
||||||
|
* @param file Stream to which the error message is to be issued.
|
||||||
|
* @return 0 if either there was no existing error message, or else there
|
||||||
|
* was an existing error message and it was successfully output.
|
||||||
|
* Return non-zero if the attempt to output an existing error message
|
||||||
|
* failed.
|
||||||
|
*/
|
||||||
|
int report_error(FILE *file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear any existing error indication and free storage occupied by
|
||||||
|
* any existing error message.
|
||||||
|
*/
|
||||||
|
void clear_error();
|
||||||
|
```
|
||||||
|
|
||||||
|
The global array `errmsg` should be removed from `errmsg.h` and replaced
|
||||||
|
by a pointer variable declared as `static char *` in `errmsg.c`.
|
||||||
|
The functions whose prototypes are given above should be implemented so
|
||||||
|
that there is no fixed maximum imposed on the length of an error message.
|
||||||
|
This means that error messages should be dynamically allocated on the
|
||||||
|
heap (for example, using `strdup()`). The implementation should take care
|
||||||
|
not to leak any memory used for error messages; for example if a new error
|
||||||
|
message is set when one already exists. Before exiting, the program should
|
||||||
|
call `clear_error()` to cause any existing error message to be freed.
|
||||||
|
|
||||||
|
# Part 3: Testing the Program
|
||||||
|
|
||||||
|
For this assignment, you have been provided with a basic set of
|
||||||
|
Criterion tests to help you debug the program.
|
||||||
|
|
||||||
|
In the `tests/basecode_tests.c` file, there are five test examples.
|
||||||
|
You can run these with the following command:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ bin/par_tests
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
To obtain more information about each test run, you can supply the
|
||||||
|
additional option `--verbose=1`.
|
||||||
|
You can also specify the option `-j1` to cause the tests to be run sequentially,
|
||||||
|
rather than in parallel using multiple processes, as is the default.
|
||||||
|
The `-j1` flag is necessary if the tests could interfere with each other in
|
||||||
|
some way if they are run in parallel (such as writing the same output file).
|
||||||
|
You will probably find it useful to know this; however the basecode tests have
|
||||||
|
been written so that they each use output files named after the test and
|
||||||
|
(hopefully) will not interfere with each other.
|
||||||
|
|
||||||
|
The tests have been constructed so that they will point you at most of the
|
||||||
|
problems with the program.
|
||||||
|
Each test has one or more assertions to make sure that the code functions
|
||||||
|
properly. If there was a problem before an assertion, such as a "segfault",
|
||||||
|
the test will print the error to the screen and continue to run the
|
||||||
|
rest of the tests.
|
||||||
|
The basecode test cases check the program operation by reading input from
|
||||||
|
a pre-defined input file, redirecting `stdout` and `stderr` to output files,
|
||||||
|
and comparing the output produced against pre-defined reference files.
|
||||||
|
Some of the tests use `valgrind` to verify that no memory errors are found.
|
||||||
|
If errors are found, then you can look at the log file that is left behind
|
||||||
|
(in the `test_output` directory) by the test code.
|
||||||
|
Alternatively, you can better control the information that `valgrind` provides
|
||||||
|
if you run it manually.
|
||||||
|
|
||||||
|
The tests included in the base code are not true "unit tests", because they all
|
||||||
|
run the program as a black box using `system()`.
|
||||||
|
You should be able to follow the pattern to construct some additional tests of
|
||||||
|
your own, and you might find this helpful while working on the program.
|
||||||
|
You are encouraged to try to write some of these tests so that you learn how
|
||||||
|
to do it. Note that in the next homework assignment unit tests will likely
|
||||||
|
be very helpful to you and you will be required to write some of your own.
|
||||||
|
Criterion documentation for writing your own tests can be found
|
||||||
|
[here](http://criterion.readthedocs.io/en/master/).
|
||||||
|
|
||||||
|
> :scream: Be sure that you test non-default program options to make sure that
|
||||||
|
> the program does not crash or otherwise misbehave when they are used.
|
||||||
|
|
||||||
|
# Hand-in Instructions
|
||||||
|
|
||||||
|
Ensure that all files you expect to be on your remote repository are committed
|
||||||
|
and pushed prior to submission.
|
||||||
|
|
||||||
|
This homework's tag is: `hw2`
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ git submit hw2
|
||||||
|
</pre>
|
1079
hw3-doc/README.md
Normal file
1079
hw3-doc/README.md
Normal file
File diff suppressed because it is too large
Load Diff
621
hw4-doc/README.md
Normal file
621
hw4-doc/README.md
Normal file
|
@ -0,0 +1,621 @@
|
||||||
|
# Homework 4 Scripting Language - CSE 320 - Spring 2022
|
||||||
|
#### Professor Eugene Stark
|
||||||
|
|
||||||
|
### **Due Date: Friday 4/15/2022 @ 11:59pm**
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The goal of this assignment is to become familiar with low-level Unix/POSIX system
|
||||||
|
calls related to processes, signal handling, files, and I/O redirection.
|
||||||
|
You will implement an interpreter, called `mush`, for a simple scripting language
|
||||||
|
that is capable of managing multiple concurrently executing "jobs".
|
||||||
|
|
||||||
|
### Takeaways
|
||||||
|
|
||||||
|
After completing this assignment, you should:
|
||||||
|
|
||||||
|
* Understand process execution: forking, executing, and reaping.
|
||||||
|
* Understand signal handling.
|
||||||
|
* Understand the use of "dup" to perform I/O redirection.
|
||||||
|
* Have a more advanced understanding of Unix commands and the command line.
|
||||||
|
* Have gained experience with C libraries and system calls.
|
||||||
|
* Have enhanced your C programming abilities.
|
||||||
|
|
||||||
|
## Hints and Tips
|
||||||
|
|
||||||
|
* We **strongly recommend** that you check the return codes of **all** system calls
|
||||||
|
and library functions. This will help you catch errors.
|
||||||
|
* **BEAT UP YOUR OWN CODE!** Use a "monkey at a typewriter" approach to testing it
|
||||||
|
and make sure that no sequence of operations, no matter how ridiculous it may
|
||||||
|
seem, can crash the program.
|
||||||
|
* Your code should **NEVER** crash, and we will deduct points every time your
|
||||||
|
program crashes during grading. Especially make sure that you have avoided
|
||||||
|
race conditions involving process termination and reaping that might result
|
||||||
|
in "flaky" behavior. If you notice odd behavior you don't understand:
|
||||||
|
**INVESTIGATE**.
|
||||||
|
* You should use the `debug` macro provided to you in the base code.
|
||||||
|
That way, when your program is compiled without `-DDEBUG`, all of your debugging
|
||||||
|
output will vanish, preventing you from losing points due to superfluous output.
|
||||||
|
|
||||||
|
> :nerd: When writing your program, try to comment as much as possible and stay
|
||||||
|
> consistent with code formatting. Keep your code organized, and don't be afraid
|
||||||
|
> to introduce new source files if/when appropriate.
|
||||||
|
|
||||||
|
### Reading Man Pages
|
||||||
|
|
||||||
|
This assignment will involve the use of many system calls and library functions
|
||||||
|
that you probably haven't used before.
|
||||||
|
As such, it is imperative that you become comfortable looking up function
|
||||||
|
specifications using the `man` command.
|
||||||
|
|
||||||
|
The `man` command stands for "manual" and takes the name of a function or command
|
||||||
|
(programs) as an argument.
|
||||||
|
For example, if I didn't know how the `fork(2)` system call worked, I would type
|
||||||
|
`man fork` into my terminal.
|
||||||
|
This would bring up the manual for the `fork(2)` system call.
|
||||||
|
|
||||||
|
> :nerd: Navigating through a man page once it is open can be weird if you're not
|
||||||
|
> familiar with these types of applications.
|
||||||
|
> To scroll up and down, you simply use the **up arrow key** and **down arrow key**
|
||||||
|
> or **j** and **k**, respectively.
|
||||||
|
> To exit the page, simply type **q**.
|
||||||
|
> That having been said, long `man` pages may look like a wall of text.
|
||||||
|
> So it's useful to be able to search through a page.
|
||||||
|
> This can be done by typing the **/** key, followed by your search phrase,
|
||||||
|
> and then hitting **enter**.
|
||||||
|
> Note that man pages are displayed with a program known as `less`.
|
||||||
|
> For more information about navigating the `man` pages with `less`,
|
||||||
|
> run `man less` in your terminal.
|
||||||
|
|
||||||
|
Now, you may have noticed the `2` in `fork(2)`.
|
||||||
|
This indicates the section in which the `man` page for `fork(2)` resides.
|
||||||
|
Here is a list of the `man` page sections and what they are for.
|
||||||
|
|
||||||
|
| Section | Contents |
|
||||||
|
| ----------------:|:--------------------------------------- |
|
||||||
|
| 1 | User Commands (Programs) |
|
||||||
|
| 2 | System Calls |
|
||||||
|
| 3 | C Library Functions |
|
||||||
|
| 4 | Devices and Special Files |
|
||||||
|
| 5 | File Formats and Conventions |
|
||||||
|
| 6 | Games, et al |
|
||||||
|
| 7 | Miscellanea |
|
||||||
|
| 8 | System Administration Tools and Daemons |
|
||||||
|
|
||||||
|
From the table above, we can see that `fork(2)` belongs to the system call section
|
||||||
|
of the `man` pages.
|
||||||
|
This is important because there are functions like `printf` which have multiple
|
||||||
|
entries in different sections of the `man` pages.
|
||||||
|
If you type `man printf` into your terminal, the `man` program will start looking
|
||||||
|
for that name starting from section 1.
|
||||||
|
If it can't find it, it'll go to section 2, then section 3 and so on.
|
||||||
|
However, there is actually a Bash user command called `printf`, so instead of getting
|
||||||
|
the `man` page for the `printf(3)` function which is located in `stdio.h`,
|
||||||
|
we get the `man` page for the Bash user command `printf(1)`.
|
||||||
|
If you specifically wanted the function from section 3 of the `man` pages,
|
||||||
|
you would enter `man 3 printf` into your terminal.
|
||||||
|
|
||||||
|
> :scream: Remember this: **`man` pages are your bread and butter**.
|
||||||
|
> Without them, you will have a very difficult time with this assignment.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Fetch and merge the base code for `hw4` as described in `hw1`.
|
||||||
|
You can find it at this link: https://gitlab02.cs.stonybrook.edu/cse320/hw4
|
||||||
|
|
||||||
|
Here is the structure of the base code:
|
||||||
|
<pre>
|
||||||
|
.
|
||||||
|
├── .gitlab-ci.yml
|
||||||
|
└── hw4
|
||||||
|
├── demo
|
||||||
|
│ └── mush
|
||||||
|
├── include
|
||||||
|
│ ├── debug.h
|
||||||
|
│ ├── mush.h
|
||||||
|
│ ├── mush.tab.h
|
||||||
|
│ └── syntax.h
|
||||||
|
├── Makefile
|
||||||
|
├── rsrc
|
||||||
|
│ ├── bg_test.mush
|
||||||
|
│ ├── cancel_test.mush
|
||||||
|
│ ├── delete_test.mush
|
||||||
|
│ ├── fg_test.mush
|
||||||
|
│ ├── goto_test.mush
|
||||||
|
│ ├── list_test.mush
|
||||||
|
│ ├── loop1.mush
|
||||||
|
│ ├── loop2.mush
|
||||||
|
│ ├── pause_test.mush
|
||||||
|
│ ├── pipeline_test.mush
|
||||||
|
│ ├── run_test.mush
|
||||||
|
│ ├── stop_test.mush
|
||||||
|
│ └── wait_test.mush
|
||||||
|
├── src
|
||||||
|
│ ├── execution.c
|
||||||
|
│ ├── jobs.c
|
||||||
|
│ ├── main.c
|
||||||
|
│ ├── mush.lex.c
|
||||||
|
│ ├── mush.tab.c
|
||||||
|
│ ├── program.c
|
||||||
|
│ ├── store.c
|
||||||
|
│ └── syntax.c
|
||||||
|
└── tests
|
||||||
|
└── base_tests.c
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
If you run `make`, the code should compile correctly, resulting in an
|
||||||
|
executable `bin/mush`. If you run this program, it doesn't do very
|
||||||
|
much, because there are a number of pieces that you have to fill in.
|
||||||
|
|
||||||
|
## `Mush`: Overview
|
||||||
|
|
||||||
|
The `mush` language is a simple programming language which was roughly
|
||||||
|
inspired by the classic programming language BASIC.
|
||||||
|
A `mush` program consists of a set of *statements*, with one statement
|
||||||
|
per line of program text.
|
||||||
|
The syntax of statements is given by the following context-free grammar:
|
||||||
|
|
||||||
|
```
|
||||||
|
<statement> ::= list
|
||||||
|
| delete <lineno> , <lineno>
|
||||||
|
| run
|
||||||
|
| cont
|
||||||
|
| <lineno> stop
|
||||||
|
| <optional_lineno> set <name> = <expr>
|
||||||
|
| <optional_lineno> unset <name>
|
||||||
|
| <optional_lineno> goto <lineno>
|
||||||
|
| <optional_lineno> if <expr> goto <lineno>
|
||||||
|
| <optional_lineno> source <file>
|
||||||
|
| <optional_lineno> <pipeline>
|
||||||
|
| <optional_lineno> <pipeline> &
|
||||||
|
| <optional_lineno> wait <expr>
|
||||||
|
| <optional_lineno> poll <expr>
|
||||||
|
| <optional_lineno> cancel <expr>
|
||||||
|
| <optional_lineno> pause
|
||||||
|
```
|
||||||
|
|
||||||
|
Some kinds of statements have required *line numbers*, other kinds of
|
||||||
|
statements have no line numbers, and for some statements the line numbers
|
||||||
|
are optional. In general, when the `mush` interpreter reads a statement
|
||||||
|
without a line number, it is executed immediately, whereas when it reads
|
||||||
|
a statement with a line number it is not immediately executed, but instead
|
||||||
|
is saved in the *program store*.
|
||||||
|
The program store maintains a set of statements, each of which has a
|
||||||
|
line number. In addition, the program store maintains a *program counter*,
|
||||||
|
which keeps track of the next statement to be executed when `mush` is
|
||||||
|
in "run mode".
|
||||||
|
|
||||||
|
The `list`, `delete`, `run`, and `cont` statements have no line numbers,
|
||||||
|
and so can only be executed immediately. The `list` statement causes
|
||||||
|
`mush` to list the contents of the program store. The `delete` statement
|
||||||
|
deletes statements from the program store whose line numbers lie within
|
||||||
|
a specified range. The `run` statement causes `mush` to reset the program
|
||||||
|
counter to the lowest-numbered statement in the program store and to begin
|
||||||
|
running automatically. The `cont` statement causes `mush` to continue
|
||||||
|
automatic execution that has been stopped by the execution of a `stop` statement.
|
||||||
|
Since a `stop` statement has a required line number, such a statement
|
||||||
|
can never be executed immediately, but rather only from the program store
|
||||||
|
during automatic execution.
|
||||||
|
|
||||||
|
The remaining statements have optional line numbers, and so can be executed
|
||||||
|
either immediately or from the program store.
|
||||||
|
The `set` statement is used to set the value of a variable to be the result
|
||||||
|
of evaluating an expression.
|
||||||
|
The `unset` statement is used to un-set the value of a variable, leaving it
|
||||||
|
with no value.
|
||||||
|
The `goto` statement resets the program counter so that the next statement to
|
||||||
|
be executed is the one with the specified line number.
|
||||||
|
The `if` statement causes control to be transferred conditionally to the
|
||||||
|
statement with the specified line number, if the specified expression evaluates
|
||||||
|
to a non-zero number.
|
||||||
|
The `source` statement causes `mush` to interpret the statements in the specified
|
||||||
|
file before continuing on with the current program.
|
||||||
|
|
||||||
|
A statement can also consist of a *pipeline*, to be executed either in the
|
||||||
|
"foreground" or in the "background". A pipeline consists of a sequence of
|
||||||
|
*commands*, separated by vertical bars (`|`), with possible
|
||||||
|
*input redirection*, specified using `<` followed by a filename,
|
||||||
|
*output redirection*, specified using `>` followed by a filename,
|
||||||
|
or *output capturing*, specified using `>@`.
|
||||||
|
A pipeline is executed by `mush` in much the same fashion as it would be
|
||||||
|
executed by a shell such as `bash`: a group of processes is created to run
|
||||||
|
the commands concurrently, with the output of each command in the pipeline
|
||||||
|
redirected to become the input of the next command in the pipeline.
|
||||||
|
If input redirection is specified, then the first command in the pipeline
|
||||||
|
has its input redirected from the specified file.
|
||||||
|
If output redirection is specified, then the last command in the pipeline
|
||||||
|
has its output redirected to the specified file.
|
||||||
|
If output capturing is specified, then the output of the last command in the
|
||||||
|
pipeline is read by the `mush` interpreter itself, which makes it available
|
||||||
|
as the value of a variable that can be referenced by the execution of
|
||||||
|
subsequent statements in the program.
|
||||||
|
|
||||||
|
Each command in a pipeline consists of a nonempty sequence of *args*,
|
||||||
|
where the first arg in the command specifies the name of a program to be run
|
||||||
|
and the remaining args are supplied to the program as part of its argument
|
||||||
|
vector. In `mush`, an arg takes the form of an *atomic expression*,
|
||||||
|
which can be either a *string variable*, a *numeric variable*,
|
||||||
|
a *string literal*, or an arbitrary expression enclosed in parentheses.
|
||||||
|
|
||||||
|
The syntax of pipelines, commands, and args is given by the following grammar:
|
||||||
|
|
||||||
|
```
|
||||||
|
<pipeline> ::= <command_list>
|
||||||
|
| <pipeline> < <file>
|
||||||
|
| <pipeline> > <file>
|
||||||
|
| <pipeline> >@
|
||||||
|
|
||||||
|
<command_list> ::= <command>
|
||||||
|
| <command> | <command_list>
|
||||||
|
|
||||||
|
<command> ::= <arg_list>
|
||||||
|
|
||||||
|
<arg_list> ::= <arg>
|
||||||
|
| <arg> <arg_list>
|
||||||
|
|
||||||
|
<arg> ::= <atomic_expr>
|
||||||
|
|
||||||
|
<atomic_expr> ::= <literal_string>
|
||||||
|
| <numeric_var>
|
||||||
|
| <string_var>
|
||||||
|
| ( <expr> )
|
||||||
|
```
|
||||||
|
|
||||||
|
`Mush` supports *expressions* built up from *string variables*,
|
||||||
|
*numeric variables*, and *literal strings*, using various unary
|
||||||
|
and binary operators, as given by the following grammar:
|
||||||
|
|
||||||
|
```
|
||||||
|
<expr> ::= <atomic_expr>
|
||||||
|
| <expr> == <expr>
|
||||||
|
| <expr> < <expr>
|
||||||
|
| <expr> > <expr>
|
||||||
|
| <expr> <= <expr>
|
||||||
|
| <expr> >= <expr>
|
||||||
|
| <expr> && <expr>
|
||||||
|
| <expr> || <expr>
|
||||||
|
| ! <expr>
|
||||||
|
| <expr> + <expr>
|
||||||
|
| <expr> - <expr>
|
||||||
|
| <expr> * <expr>
|
||||||
|
| <expr> / <expr>
|
||||||
|
| <expr> % <expr>
|
||||||
|
```
|
||||||
|
|
||||||
|
A *string variable* consists of a `$` symbol followed by a *name*,
|
||||||
|
which is a sequence of alphanumeric characters and underscores,
|
||||||
|
beginning with an alphabetic character or an underscore.
|
||||||
|
A *numeric variable* is similar, except it uses a `#` symbol in place
|
||||||
|
of the `$`.
|
||||||
|
A *literal string* is either a *number*, which consists of digits,
|
||||||
|
a *word*, which consists of non-whitespace characters which do not otherwise
|
||||||
|
have some special meaning to `mush`, or a *quoted string*, which is enclosed
|
||||||
|
in double quotes and which may contain special characters.
|
||||||
|
A *filename* that appears in the input or output redirection part of a
|
||||||
|
pipeline is permitted to be either a word or a quoted string.
|
||||||
|
This allows simple filenames without special characters to be specified
|
||||||
|
without quotes. Filenames that contain special characters (including `/`)
|
||||||
|
must be specified as quoted strings.
|
||||||
|
|
||||||
|
Here is a simple example of a `mush` program:
|
||||||
|
|
||||||
|
```
|
||||||
|
10 echo "Let's start!"
|
||||||
|
20 set x = 0
|
||||||
|
30 date >@
|
||||||
|
40 set d = $OUTPUT
|
||||||
|
50 echo The date and time is: $d
|
||||||
|
60 sleep 1
|
||||||
|
70 set x = #x + 1
|
||||||
|
80 if #x <= 10 goto 30
|
||||||
|
90 stop
|
||||||
|
```
|
||||||
|
|
||||||
|
The remaining types of statements that `mush` understands have to do with
|
||||||
|
the manipulation of concurrently executing *jobs*.
|
||||||
|
Each time `mush` executes a pipeline statement, a new job is created.
|
||||||
|
`Mush` keeps track of the existing jobs in a *jobs table*.
|
||||||
|
Each job in the jobs table has an associated *job ID*, which is a nonnegative
|
||||||
|
integer that uniquely identifies the job.
|
||||||
|
After starting a job, `mush` sets the value of the `JOB` variable to be
|
||||||
|
the job ID of the job that was started.
|
||||||
|
For a foreground job, `mush` waits for the job to complete and then sets the
|
||||||
|
value of the `STATUS` variable to be the exit status of the job.
|
||||||
|
`Mush` then *expunges* the job from the jobs table.
|
||||||
|
For a background job, `mush` does not wait for the job to complete, but instead
|
||||||
|
continues execution. At a later time, a `wait` statement can be executed
|
||||||
|
in order to wait for the background job to complete, to collect its
|
||||||
|
exit status, and to expunge the job. Alternatively, a `poll` statement can
|
||||||
|
be executed to check whether the job has terminated without waiting if it
|
||||||
|
has not. If the job has terminated, then the exit status is collected and
|
||||||
|
the job is expunged with a `poll` statement, similarly to a `wait` statement.
|
||||||
|
Execution of a `cancel` statement makes an attempt to cancel a specified
|
||||||
|
background job. A `SIGKILL` signal is sent to the process group to which the
|
||||||
|
processes in the jobs belong. If the processes have not already terminated,
|
||||||
|
then they will terminate upon receiving the `SIGKILL` signal.
|
||||||
|
A `wait` statement may be used to wait for this termination to occur and
|
||||||
|
to expunge the canceled job from the jobs table.
|
||||||
|
Note that the `wait`, `poll`, and `cancel` statements all permit the use of an
|
||||||
|
arbitrary expression to specify the job ID.
|
||||||
|
|
||||||
|
The final kind of statement that `mush` supports is the `pause` statement.
|
||||||
|
This statement causes execution to be suspended pending the receipt of a signal
|
||||||
|
that might indicate a change in the status of jobs in the jobs table.
|
||||||
|
When such a signal is received, execution continues.
|
||||||
|
This way, `mush` can wait for a change in job status without consuming an
|
||||||
|
excessive amount of CPU time.
|
||||||
|
|
||||||
|
### Demonstration version
|
||||||
|
|
||||||
|
To help you understand how `mush` is intended to behave, I have provided a
|
||||||
|
demonstration version as a binary with the assignment basecode.
|
||||||
|
This can be found as the executable `demo/mush`.
|
||||||
|
This demonstration version is intended as an aid to understanding only;
|
||||||
|
it should not be regarded as a specification of what you are to do.
|
||||||
|
It is likely that the demonstration version has some bugs or that its
|
||||||
|
behavior does not conform in some respects to what is stated here and in
|
||||||
|
the specifications in the basecode.
|
||||||
|
|
||||||
|
## Tasks to be Completed
|
||||||
|
|
||||||
|
Included in the basecode for this assignment is an implementation of a
|
||||||
|
parser for `mush` statements and the basic control structure of the
|
||||||
|
`mush` interpreter. A number of modules have been left for you to
|
||||||
|
implement. These are:
|
||||||
|
|
||||||
|
* A *program store* module, which is used to hold a `mush` program
|
||||||
|
and manage the program counter.
|
||||||
|
|
||||||
|
* A *data store* module, which is used to keep track of the current values
|
||||||
|
of the variables used in a `mush` program.
|
||||||
|
|
||||||
|
* A *jobs* module, which keeps track of the currently executing jobs using
|
||||||
|
a jobs table, and implements job manipulation functions used to execute
|
||||||
|
and wait for pipelines, collect exit status, perform input and output
|
||||||
|
redirection, and implement the output capture feature of `mush`.
|
||||||
|
|
||||||
|
### The Program Store Module
|
||||||
|
|
||||||
|
Specifications and stubs for the functions that make up the program store module
|
||||||
|
of `mush` are given in the source file `src/program.c`.
|
||||||
|
Implementation of these functions from the specifications should be relatively
|
||||||
|
straightforward, so I will not spend additional space on them here.
|
||||||
|
The choice of data structure used to represent the program store has been left
|
||||||
|
to you.
|
||||||
|
Pay close attention to what the specifications say about who has the responsibility
|
||||||
|
for freeing the memory associated with statements in the store.
|
||||||
|
A correct implementation should not leak memory associated with program statements,
|
||||||
|
and of course it should not suffer from double free bugs and the like.
|
||||||
|
|
||||||
|
### The Data Store Module
|
||||||
|
|
||||||
|
Specifications and stubs for the functions that make up the data store module
|
||||||
|
of `mush` are given in the source file `src/store.c`.
|
||||||
|
Once again, I expect that implementation of these functions should be relatively
|
||||||
|
straightforward. As for the program store, the choice of data structure used
|
||||||
|
to implement the data store is for you to make and you should pay attention to
|
||||||
|
what the specifications say about who is responsible for freeing memory.
|
||||||
|
|
||||||
|
### The Jobs Module
|
||||||
|
|
||||||
|
Specifications and stubs for the functions that make up the jobs module
|
||||||
|
of `mush` are given in the source file `src/jobs.c`.
|
||||||
|
It is this module that is likely to be unfamiliar and to present some challenges
|
||||||
|
to you, so I am providing some additional guidance here.
|
||||||
|
|
||||||
|
* You will need to implement some form of "jobs table" in this module,
|
||||||
|
to keep track of the jobs that have been created but not yet expunged.
|
||||||
|
The data structure you use is up to you. If you find it convenient,
|
||||||
|
you may assume that at most `JOBS_MAX` jobs can exist at one time,
|
||||||
|
where `JOBS_MAX` is a C preprocessor symbol defined in `mush.h`.
|
||||||
|
Write your code so that it does not depend on a particular value for
|
||||||
|
`JOBS_MAX`; do not hard-code the value into your implementation.
|
||||||
|
|
||||||
|
* Your jobs module will need to make use of handlers for two types of signals.
|
||||||
|
The first is the `SIGCHLD` signal used to obtain notifications when a child
|
||||||
|
process terminates. This has been discussed in class and can also be found
|
||||||
|
in the textbook.
|
||||||
|
The second type of signal you will need to handle is the `SIGIO` signal used
|
||||||
|
to obtain notifications when a file descriptor is ready for reading.
|
||||||
|
This will be important to enable your program to capture output from
|
||||||
|
concurrently executing background jobs without the need to commit to waiting
|
||||||
|
for data from any one of them at any particular time. This is discussed
|
||||||
|
further below.
|
||||||
|
|
||||||
|
* For correct operation, your implementation will likely have to make use of
|
||||||
|
the `sigprocmask()` function to mask signals during times when a signal handler
|
||||||
|
should be prevented from running. You will likely also need to use the
|
||||||
|
`sigsuspend()` function under certain circumstances to await the arrival of a
|
||||||
|
signal.
|
||||||
|
|
||||||
|
* When executing a pipeline consisting of N commands, a total of N+1 processes
|
||||||
|
should be used. One of these processes, which we will call the pipeline
|
||||||
|
*leader*, should be the direct child of the main `mush` process.
|
||||||
|
The remaining `N` processes will be children of the leader process, and will
|
||||||
|
each execute one of the commands in the pipeline.
|
||||||
|
The leader process should set itself into a new process group using its own
|
||||||
|
process ID as the process group ID, and its `N` child processes should belong
|
||||||
|
to this process group. This is so that job cancellation can be performed by
|
||||||
|
sending just one `SIGKILL`, directed at the process group for the job.
|
||||||
|
The leader process should wait for and reap its `N` children before terminating.
|
||||||
|
The main `mush` process should use its `SIGCHLD` handler to receive notifications
|
||||||
|
about the termination of pipeline leader processes and to collect their
|
||||||
|
exit status.
|
||||||
|
|
||||||
|
* Besides the `fork()` system call used to create the processes, the creation of the pipeline
|
||||||
|
will involve the use of the `open()`, `pipe()`, and `dup2()` system calls to set up the pipes
|
||||||
|
and redirections, and the `execvp()` system call must be used to execute the individual
|
||||||
|
commands.
|
||||||
|
|
||||||
|
> **Important:** You **must** create the processes in a pipeline using calls to
|
||||||
|
> `fork()` and `execvp()`. You **must not** use the `system()` function, nor use any
|
||||||
|
> form of shell in order to create the pipeline, as the purpose of the assignment is
|
||||||
|
> to give you experience with using the system calls involved in doing this.
|
||||||
|
|
||||||
|
* Once having set up the pipeline, the pipeline leader will use `wait()` or `waitpid()`
|
||||||
|
to await the completion of the processes in the pipeline.
|
||||||
|
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.
|
||||||
|
|
||||||
|
* The `pipe()` and `dup2()` system calls should be used to perform the input
|
||||||
|
and output redirection associated with a pipeline, as discussed in class and
|
||||||
|
in the textbook. Files used for input and output redirection should be opened
|
||||||
|
using the `open()` system call. For correct operation of a pipeline, care
|
||||||
|
should be taken while setting up the pipeline that each process makes sure to
|
||||||
|
`close()` pipe file descriptors that it does not use.
|
||||||
|
|
||||||
|
* The capturing of output from a pipeline by the main `mush` process is to be
|
||||||
|
accomplished as follows. Before forking the pipeline leader, a pipe should
|
||||||
|
be created to provide a way to redirect output from the last process in the
|
||||||
|
pipeline back to the main `mush` process. The redirection will be accomplished
|
||||||
|
using `dup2()` as usual. The main `mush` process will need to save the file
|
||||||
|
descriptor for the read side of the pipe in the jobs table along with other
|
||||||
|
state information from that job. Output from the pipeline will be collected
|
||||||
|
by the main `mush` process by reading from the read side of the pipe and
|
||||||
|
saving what is read in memory. Automatic dynamic allocation of however much
|
||||||
|
memory is required to hold the output can be accomplished by using the
|
||||||
|
`open_memstream()` function to obtain a `FILE` object to which the data can
|
||||||
|
be written.
|
||||||
|
|
||||||
|
The main technical issue involved in output capturing is how to arrange for
|
||||||
|
the main `mush` process to collect the output produced from multiple
|
||||||
|
concurrently executing pipelines, without having to block waiting for any one
|
||||||
|
of them to produce output at any given time. This can be done using so-called
|
||||||
|
*asynchronous I/O*. When the main `mush` process creates the pipe from which
|
||||||
|
it will read the captured data, it should perform the following system calls
|
||||||
|
(`readfd` is the file descriptor for the read side of the pipe):
|
||||||
|
|
||||||
|
```
|
||||||
|
fcntl(readfd, F_SETFL, O_NONBLOCK);
|
||||||
|
fcntl(readfd, F_SETFL, O_ASYNC);
|
||||||
|
fcntl(readfd, F_SETOWN, getpid());
|
||||||
|
```
|
||||||
|
|
||||||
|
The first of these calls enables *non-blocking I/O* on the file descriptor.
|
||||||
|
This means that an attempt to `read()` the file descriptor when no data is
|
||||||
|
available will not cause the main `mush` process to block (*i.e.* wait for
|
||||||
|
data to arrive); rather the `read()` will return immediately with an error
|
||||||
|
and `errno` set to `EWOULDBLK`.
|
||||||
|
The second call sets *asynchronous mode* on the file descriptor.
|
||||||
|
When this is set, the operating system kernel will send a `SIGIO` signal
|
||||||
|
whenever there has been a change in status of the file descriptor; for example,
|
||||||
|
whenever data becomes available for reading.
|
||||||
|
The third call is necessary to set the "ownership" of the file descriptor
|
||||||
|
to the main `mush` process, so that the kernel knows to which process
|
||||||
|
the `SIGIO` signals should be directed.
|
||||||
|
|
||||||
|
Once you have done this, then the main `mush` process can use a handler for
|
||||||
|
`SIGIO` signals to become notified when there is output that needs to be
|
||||||
|
captured. It can then poll each of the file descriptors from which output
|
||||||
|
is supposed to be captured, using `read()` to read input from each of them
|
||||||
|
and save it in memory, until `EWOULDBLK` indicates that there is no more data
|
||||||
|
currently available. This way, it can collect the captured output in a timely
|
||||||
|
fashion without getting "stuck" waiting for output that might take an
|
||||||
|
indefinite amount of time to arrive.
|
||||||
|
|
||||||
|
For more information, you will have to look at the man pages for the various
|
||||||
|
system calls involved, including `pipe()`, `dup2()`, `fcntl()`, `open()`, `read()`,
|
||||||
|
`signal()` (or `sigaction()`), `sigprocmask()`, and `sigsuspend()`.
|
||||||
|
|
||||||
|
## Using `gdb` to Debug Multi-process Programs
|
||||||
|
|
||||||
|
Although it gets harder to debug using `gdb` once multiple processes are involved,
|
||||||
|
there is some support for it. The `gdb` command `set follow-fork-mode parent`
|
||||||
|
causes `gdb` to follow the parent process after a `fork()` (this is the default).
|
||||||
|
Similarly, the command `set follow-fork-mode child` causes `gdb` to follow the child
|
||||||
|
process instead.
|
||||||
|
|
||||||
|
## Provided Components
|
||||||
|
|
||||||
|
### The `mush.h` Header File
|
||||||
|
|
||||||
|
The `mush.h` header file that we have provided gives function prototypes for
|
||||||
|
the functions that you are to implement, and contains a few other related
|
||||||
|
definitions. The actual specifications for the functions will be found
|
||||||
|
as comments attached to stubs for these functions in the various C source files.
|
||||||
|
|
||||||
|
> :scream: **Do not make any changes to `mush.h`. It will be replaced
|
||||||
|
> during grading, and if you change it, you will get a zero!**
|
||||||
|
|
||||||
|
### The `syntax.h` Header File
|
||||||
|
|
||||||
|
The `syntax.h` header file that we have provided defines the data structures
|
||||||
|
used to represent parsed `mush` statements. Mostly, you don't have to know
|
||||||
|
much about the details of these data structures, except, for example,
|
||||||
|
that you will need to be able to extract some information from them,
|
||||||
|
such as the pipeline from a foreground or background pipeline statement.
|
||||||
|
To avoid memory leaks, you will need to use the various `free_xxx()`
|
||||||
|
functions provided to free syntactic objects when they are no longer being used.
|
||||||
|
You will also need to use the function provided to make a copy of a pipeline
|
||||||
|
object in a certain situation -- see the specification for `jobs_run()` for
|
||||||
|
more information.
|
||||||
|
|
||||||
|
> :scream: **Do not make any changes to `syntax.h`. It will be replaced
|
||||||
|
> during grading, and if you change it, you will get a zero!**
|
||||||
|
|
||||||
|
### The `syntax.c` Source File
|
||||||
|
|
||||||
|
The `syntax.c` source file that we have provided contains the implementations
|
||||||
|
of the various functions for which prototypes are given in `syntax.h`.
|
||||||
|
|
||||||
|
> :scream: **Do not make any changes to `syntax.c`. It will be replaced
|
||||||
|
> during grading, and if you change it, you will get a zero!**
|
||||||
|
|
||||||
|
### The `mush.lex.c`, `mush.tab.c`, and `mush.tab.h` Files
|
||||||
|
|
||||||
|
The basecode provides a parser for the `mush` language. This parser is
|
||||||
|
implemented using the GNU `bison` parser generator. and the GNU `flex`
|
||||||
|
lexical analyzer generator. The `mush.lex.c`, `mush.tab.c`, and `mush.tab.h`
|
||||||
|
files are auto-generated files produced by the `bison` and `flex` programs.
|
||||||
|
|
||||||
|
> :scream: **None of these files should be changed or edited.
|
||||||
|
> Do *not* do the sloppy things that lots of people seem to do,
|
||||||
|
> namely, editing these files, reformatting them or otherwise mutating them,
|
||||||
|
> and then committing the changed results to `git`. You will regret it
|
||||||
|
> if you do this, and you have been duly warned!**
|
||||||
|
|
||||||
|
### The `demo/mush` Executable
|
||||||
|
|
||||||
|
The file `demo/mush` is an executable program that behaves more or less like
|
||||||
|
how your program should behave when it is finished.
|
||||||
|
|
||||||
|
> :scream: The behavior of the demo program should be regarded as an example
|
||||||
|
> implementation only, not a specification. If there should be any discrepancy
|
||||||
|
> between the behavior of the demo program and what it says either in this document
|
||||||
|
> or in the specifications in the header files, the latter should be regarded
|
||||||
|
> as authoritative.
|
||||||
|
|
||||||
|
### The `rsrc` Directory
|
||||||
|
|
||||||
|
The `rsrc` directory contains some sample `mush` scripts which I used while
|
||||||
|
writing the demo version. They were mostly designed very quickly to exercise
|
||||||
|
the basic features of `mush`, to verify that they worked to a first cut.
|
||||||
|
One way to run them is to type *e.g.* `source rsrc/xxx_test.mush` to the
|
||||||
|
`mush` prompt, to get it to read and execute the test.
|
||||||
|
If you have run one test and you want to run another, you should use the
|
||||||
|
`delete` command to clear any statements from the program store that might
|
||||||
|
have been left by the first test, otherwise they might interfere with the
|
||||||
|
new test.
|
||||||
|
|
||||||
|
### The `tests` Directory
|
||||||
|
|
||||||
|
The `tests` directory contains just one file, `base_tests.c`, which contains one
|
||||||
|
Criterion test that isn't very interesting. This file is basically just a
|
||||||
|
placeholder where you can put tests you might think of yourself.
|
||||||
|
|
||||||
|
## Hand-in instructions
|
||||||
|
As usual, make sure your homework compiles before submitting.
|
||||||
|
Test it carefully to be sure that doesn't crash or exhibit "flaky" behavior
|
||||||
|
due to race conditions.
|
||||||
|
Use `valgrind` to check for memory errors and leaks.
|
||||||
|
Besides `--leak-check=full`, also use the option `--track-fds=yes`
|
||||||
|
to check whether your program is leaking file descriptors because
|
||||||
|
they haven't been properly closed.
|
||||||
|
You might also want to look into the `valgrind` `--trace-children` and related
|
||||||
|
options.
|
||||||
|
|
||||||
|
Submit your work using `git submit` as usual.
|
||||||
|
This homework's tag is: `hw4`.
|
1280
reference_doc/CSE320_ReferenceDoc.md
Normal file
1280
reference_doc/CSE320_ReferenceDoc.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user