首页 > > 详细

CSCI 3120作业代做、C++程序语言作业调试、代做c/c++课程作业、program作业代写 代做Python程序|代写Python程序

CSCI 3120 Assignment 2
Due May 30 by 9:00 AM ADT
NOTES
You may wish to create your program using multiple files. And you may wish
to use a Makefile. If you have more than one file for this assignment, please
create a tar file containing all files needed to compile and execute your program
(including your Makefile, if you use one), and upload that tar file to
Brightspace. If you don’t use a Makefile and your program requires more than
a simple “gcc a2.c -o a2” to compile, you must include that information in
the comment at the top of your main program file. BUT in such a case you really
should use a Makefile!
As always for this course, this assignment must compile and run on the FCS
Unix server csci3120.cs.dal.ca. Do not use any of the other FCS servers
for this course.
As in A1, this program should follow the C-coding-style-notes (found in
the “Assignments and related material” folder on Brightspace.
[1] As you may know, there are lots of different command shells available for Unix-type systems;
ash, bash, csh, dash, fish, ksh, sh, tcsh and zsh immediately come to mind, but I’m sure
you can find more. For this question, you will create your own (rudimentary) shell using C. You
can create your program using any system you like, but your program must compile and run on
csci3120.cs.dal.ca; if it doesn’t, the marker is authorized to give you 0, regardless of how
much work you have put into your program.
All of the above shells are complex programs which provide many, many features. This assignment
question requires you to only implement a tiny subset of the functionality of a normal shell. Below
I list each type of command-line you are to implement, and how many points each of those pieces
is worth. Your shell should prompt the user for input with the prompt “$ ”, as seen below in the
sample commands.
In the examples below, the input typed by a user is in red, and output by the shell is in black.
Your shell should implement the following capabilities. These capabilities are explained further
below, along with simplifying assumptions that your program may make. The first few show sample
output from the executed commands, but for brevity some of the rest do not show the output. Try
the commands yourself in your shell to see the sort of output to expect.
(i) (1 point) If the command is exit, your shell terminates. This is what a terminal session which
starts your program and then immediately runs the exit command should look like (where
” is whatever prompt you have at your regular shell):
./a2
$ exit

1
(ii) (5 points) Run a program which takes no arguments and waitpid() for the executed program
to finish, then print another prompt and wait for another command line; examples:
$ who

$ date

(iii) (5 points) Run a program with arguments; examples:
$ who am i

$ ls -l /etc/passwd /etc/hosts
-rw-r--r--. 1 root root 158 Sep 10 2018 /etc/hosts
-rw-r--r--. 1 root root 1279 May 6 09:58 /etc/passwd
(iv) (5 points) Run a sequence of commands on one line; examples
$ date ; sleep 10 ; date
Sat May 16 09:05:36 ADT 2020
Sat May 16 09:05:46 ADT 2020
$ echo The users on the system right now are: ; who
The users on the system right now are:

(v) (9 points) Run two programs (each with or without arguments) where the output of the first
program is piped into the input of the second program; examples
$ date | sed s/.*://
$ ls -l | grep rwx
(vi) (3 points) Implement “background jobs”; that is, if a command ends with “&”, rather than
your shell waiting for it to complete, the shell continues on with other commands on the line
(if any), or outputs a prompt and waits for another command.
$ xz some-big-file-to-be-compressed &
$ compute-1000000-digits-of-pi & sleep 3 ; ps uaxw
(vii) (2 points) (If you don’t implement this part, you will create Zombies!) Just before printing the
prompt, use the waitpid() system call (with the WNOHANG option!) to “reap” any background
jobs which have terminated. (Use -1 as the first argument to waitpid().) If a background
job has terminated, output its process id (pid). In the following example, imagine
means the user has gone away for a few minutes and then returned.
$ sleep 10 & date

$ who

Process 4321 terminated
$
(viii) (3 bonus points) If there are two commands on a line separated by “&&”, execute the second
command if and only if it the first command exits with a success indication. (The waitpid()
system call returns this information.) Your program only needs to implement this for lines
with exactly two commands; example
$ grep root /etc/passwd && echo There is root!
2
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
There is root!
$ grep tree /etc/passwd && echo There is tree!
$
Hints, simplifications, and more details:
(i) Normal shells don’t require space around command separators such as “;”, “&” and “&&”. To
make it easier for you to parse the command lines, you may assume that these separators are
always surrounded by white space (the first two may legally be at the very end of a line as
well).
(ii) Speaking of parsing the input line, you may use the readline library if you want (and if you
know what that is). But if you don’t mind having the minimal line-editing capability provided
by the Linux terminal driver (which is considerably less than what shells provide for you),
don’t worry about it. I’m not worrying, and the markers will not expect you to do that.
(iii) Normal shells implement “wildcard” characters, such as “*”. You definitely do not have
to do those! Or variables. Or I/O redirection. Or programming constructs. Or commandline
editing features (e.g., going back and forth with arrow keys). Or any other feature not
mentioned above! Not being able to go back and forth (with arrow keys or control-this or
sec-that) will mean you will have to type carefully when testing your shell, or you can copy
and paste entire command lines from (say) an editor window into the terminal window running
your shell.
(iv) Although “real” shells allow arbitrarily complex pipelines (as they are called) using the “|”,
“;”, “&” and “&&” features (and others), (e.g., “p1 | p2 | p3 && p4 ; p5 & p6”), for
this assignment you don’t have to implement the ability to handle any combination of “&”
with either “&&” or “|”. Nor do you have to allow more than one “|” per line. You do have
to implement the ability to parse and execute lines with more than one “&” and/or “;”, such
as “p1 ; p2 args ; p3”, “p1 arg1 arg2 & p2 args ; p3 arg1 arg2” and even
“p1 arg & p2 args ; p3 & p4” where, as you might guess, the “ps” are arbitrary
programs.
(v) Ordinary shells do some trickery to avoid having background jobs output text to your screen.
You really don’t want to try to implement that! So if you use a put a program into the
background and it outputs text, that output will get mixed in with whatever other output is
coming out (including your $ prompt. Don’t let this issue worry you. If you want to see
what messes can occur, save the following three lines of code into a file called (for example)
delay5, execute the command chmod 755 delay5 , and then inside your shell try a
command line like $ ./delay5 & and wait 5 seconds to see what happens.
#! /bin/sh
sleep 5
echo $0 is now outputting stuff!
(vi) Your program should not crash and burn if the user types in an invalid command line. However,
as soon as your program detects some syntax it don’t handle, it can output a short message,
terminate processing of the command line, and go back to the waitpid()/prompt section.
(vii) Normal shells allow long command lines. You may make the restriction that the command
line will never be longer than 80 chars. Which, if you think about it, also limits the number
3
of tokens on a command line. Feel free to use these limitations to simplify the design of your
program. (But please try not to do something horrible when a much more elegant solution is
more or less obvious.)
(viii) String parsing in C is a bit of a nuisance, unless you decide to get out heavy-duty tools. For
this assignment, you might find the following code sketch (which is not industrial-strength,
and needs some modification for this assignment, but should give you some ideas) useful:
char * tok;
char cmd_name[MAX_CHARS + 1];
...
// Read a new line into "str", making sure it is < MAX_CHARS chars long.
...
// Now parse out the tokens ("words") on that line.
tok = strtok(str, " \t");
if (tok == NULL)
{
// Empty line, nothing to process here, go read another line
}
strcpy(cmd_name, tok);
while ((tok = strtok(NULL, " \t")) != NULL)
{
// look at the token pointed to by tok and
// see if it is a ’;’, a ’&’, or an argument
// if a ’;’ or ’&’ fork()/execvp() the command
// and then keep processing the line.
// And if you do the bonus part also check for ’&&’
// Keep in mind strcmp() returns 0 if two strings match!
// Also note that you have to strcpy() (or equivalent)
// the string tok points to into your own character array
// before the next call to strtok(), otherwise that token will
// be gone forever!
}
(ix) The shell looks through the directories in your PATH variable to find the first one containing
an executable file by the same name as the program name on the command line. Don’t
bother dealing with that yourself. Rather, instead of plain exec() use execvp(), which will
perform the hunt itself. The first argument is a pointer to the command name (“cmd_name”
above). The second argument to execvp() is an array of pointers; each pointer of that array
points to one of the arguments, and the next element of that array must be set to NULL.
(x) You must use execvp() (or one of the other exec() system calls); you are absolutely not
allowed to use the system() function.
This is a fairly significant piece of work. Don’t worry about catching every possible input error a
user might make. If your program correctly processes correct input (as above), and it doesn’t crash
on invalid input, you can be happy.
4
This is not a programming course, and therefore I am not your programming instructor. But, having
said that, here are some suggestions as to how to proceed which I think will help you successfully
complete this assignment. You don’t have to follow them, but I would suggest you consider doing
so, unless you are a whiz-bang programmer who doesn’t need any help at all.
I would strongly suggest you start by writing a program that can output a prompt, read a line of
text, and parse the line of text into individual tokens (using strtok(), or, if you prefer, some
other technique). When you can do that and output the tokens to your screen, give yourself a pat
on the back, copy that code to another file in case of accidents, and carry on.
Now might be a good time to think about how you will structure your program so that it can handle
more than one command on a line.
After that, extend the program so that it prompts and reads in a loop, until the input line is “exit”,
at which point your program exits. When you can do that, give yourself a pat on the back, copy
that code to another file in case of accidents, and carry on.
After that, figure out how to correctly call execvp() for a command with no arguments, and to
wait() or waitpid() for it to finish. Try that out with commands like who or date and see if
you get sensible output on your screen. When you can do that, give yourself a pat on the back,
copy that code to another file in case of accidents, and carry on.
After that, add the ability for the command to have arguments. This is (essentially) just additional
work with strtok(), strcpy() and copying pointers into an array.
After that, add the ability to have multiple commands on one line, separated by “;”.
The next one (piping from one program to another) is tricky; you might consider whether you
want to do it now, or after everything else is done. You know how to parse a line into commands
with arguments, but now you will have to fork() and execvp() two commands, but only after
you use pipe() to create an unnamed pipe. In the child processes (after fork() but before
execvp()) you must “detach” standard output (of the first command) from the terminal and send
it to the pipe. Similarly you must detach the standard input of the second command from the
keyboard and instead read from the pipe. You will want to read the man page for dup2(). Keep
in mind that standard input (“stdin”) is file descriptor 0 and standard output (“stdout”) is file
descriptor 1. Also don’t forget to close() unused ends of pipes in all the right places, so that
your program does not end up with more and more pipes. (But I suggest that you first get piping
working, then add code to close() unused pipe ends and check you haven’t broken anything.)
Next up: allow so-called “background jobs”, where a command is followed by “&”. This should
be relatively easy, because all you have to do is not waitpid() for the child to finish. Having
done that, you should next implement the waitpid() functionality for these “background jobs”.
After all the above, the only thing to do is to implement the bonus “&&” feature. In this case
waitpid() is your friend, because it returns information you can use to determine whether the
first command succeeded.
EVALUATION:
If your program does not compile on csci3120.cs.dal.ca it is worth 0 points, no matter
5
how good it is otherwise. Similarly, if your program compiles but crashes (when running on
csci3120.cs.dal.ca) on most or all input, it is still worth 0.
If your program compiles and runs without crashing on correct input, you will receive points for
each feature you correctly implement, as per the point values above.
While there are no points specifically allocated to code style, quality and readability, the markers
have the prerogative to deduct marks if they feel your code fails to meet reasonable standards in
any of these three areas.
6

联系我们 - QQ: 99515681 微信:codinghelp2
© 2014 www.7daixie.com
程序代写网!