DUE 14th NOV 8 AM
1.
Aim
The aim of this assignment is for you to gain some practice in C programming, to use C libraries and system calls, and to demonstrate your understanding of C and how to program to a defined interface.
2. Learning/skills outcomes
· C programming skills
· Programming to defined interfaces
· Formulate problems and identify suitable approaches to solving them
· Cognitive/intellectual skills and self management (see graduate skills framework)
3. Specification
This assignment has three parts:
1.
Write a library of simple arithmetic functions
(4 marks)
2.
Write a library of utilities to read and process user input
(8 marks)
3.
Write a simple command line shell
(8 marks)
Note we will test your solution programmatically on MINIX. It is very important that you follow the rules below for the form of your solutions and their submission. For example, do not deviate from the file names specified. If you do not adhere to the rules, you will loose marks even if your solution is logically correct. An important purpose of this assignment is for you to demonstrate that you can understand an interface definition and program to that definition. It is also important that your code executes correctly on MINIX.
You will also have to test your solutions yourself. We will not mark these tests and they should not be submitted. The files that you submit are:
· intmath.h – unmodified,
provided for you
· intmath.c – your implementation of the functions defined in intmath.h
· utils.h – unmodified,
provided for you
· utils.c – your implementation of the functions defined in utils.h
· myshell.c – your implemenation of a simple shel that uses the functions defined in utils.h
In intmath.c, utils.c and myshell.c you may find it useful to define and implement additional “helper” functions additional that are not defined in the header files. That is OK. It is up to you how you organise your solution provided you implement the functions defined in the header files and the simple shell program that uses the functions defined in utils.h.
Part 1 – Library of simple arithmetic functions
In this part you will implement the following four simple arithmetic functions:
int add(int x, int y)
adding x to y and returning the result
int subtract(int x, int y)
subtracting y from x and returning the result
int divide(int n, int d)
integer division of the numerator (dividend) n by the denominator (divisor) d and returning the result
int modulo(int n, int d)
integer division of the numerator (dividend) n by the denominator (divisor) d and returning the remainder
int division(int n, int d, int &r)
integer division of the numerator (dividend) n by the denominator (divisor) d, returning the result and recording the remainder on r
To get full marks for this part you must follow the following rules. Any deviation from the rules could result in a mark of 0 for this part.
Rules for part 1
1. The functions are defined in intmath.h. The comments to each function define the contract between yourself and the user of these functions. Your implementations must satisfy the contract. For example, you must detect and signal integer overflow using the ERANGE value for errno. Division by zero is a domain error, which you must signal with the EDOM errno.
2. You must implement the functions in a separate intmath.c file that includes intmath.h.
3. intmath.c must not have a main function.
4. You can only use the ++ and — unary increment and decrement operators in your calculations. That is, you must not use +, -, / or % in any of your calculations. You can use – as a unary operator to negate a number.
To emphasise, the following are allowed in your implementation of the arithmetic functions:
x++ x– –x ++x -x
No other built-in arithmetic operators are allowed. That is, the following are forbidden:
x – y x + y x / y x % y x * y
You may use the full range of built-in arithmetic operators to test your solution. In fact, you will almost certainly have to use all the operators to test your solution (with the possible exception of multiplication).
Hints
· You can avoid code duplication in this task. E.g. think about how to use subtract for certain inputs to add and how to write some functions in terms of others.
· Be particularly careful when INT_MIN is the numerator or denominator in division.
Part 1 deliverables
The deliverables for part 1 are the files intmath.h and intmath.c
Plan your time and do not spend too long on this part. Parts 2 and 3 do not depend on it. However, you should find it easier to approach the other parts if you complete this part.
Part 2 – Library of user input utils
In this part you will implement the following three utility functions:
int getaline(char* linebuf, int bufsize)
to read a line of user input from stdin into the character buffer linebuf. After execution, the function should return the count of characters read into the buffer and linebuf should represent a string, terminated with the null character (‘\0’). The returned count does not include the null character. bufsize is the size of linebuf. Therefore, the returned count will be at most bufsize – 1
int split(char *s, int start, char sep, char **substrings, int max)
to split the null-terminated input string s into at most max substrings. The string should be split at each occurrence of the sep character until at most max substrings are found. The first substring should start at position start (which may be 0). The use of a start position allows for “pre-processing” of s (e.g. to ignore leading characters – see trim). After execution, the function should return the number of substrings identified. The input string s will now be a buffer of null-terminated substrings. Each element of the substrings array will be a pointer to the start of a substring in s up to max substrings. If there are less than max substrings, the unused elements at the end of substrings should be set to NULL. When splitting the input string, multiple consecutive occurrences of the sep character within the string (as opposed to at the beginning or end of the string) should be ignored. It is possible for there to be more thanmax substrings in s, in which case the final string in the substrings array will contain separator characters.
int trim(char *s, char c)
to trim the null-terminated input string s (removing leading and trailing runs of the specified character, c). This function returns the position of the first character that is not the specified character in string s and terminates the string s after the last character that is not the specified character. In effect, trim allows the user of the function to skip a sequence of leading characters at the start of s and truncates s at the first occurrence of a trailing sequence of 1 or more of the specified character(s). s is modified as a result. The string from the returned start value may contain the specified character but there will leading or trailing sequences of the character. If the return value is -1, then s is a string that only contains the specified character and no other characters (apart from the string terminator character). A return value of 0 means that there is no leading sequence of separator characters.
Rules for part 2
1. The functions are defined in utils.h. The comments to each function define the contract between yourself and the user of these functions. Your implementations must satisfy the contract and the descriptions above.
2. You must implement the functions in a separate utils.c file that includes utils.h
3. utils.c must not contain a main function.
4. The only C library function you can use in your implementation is getchar in stdio to read characters one at a time from stdin. You must not use any other library functions. That is, use of the C library getline function or any string processing library functions is forbidden.
Example usage
The following program shows example usage of the three functions: getaline, trim and split.
#include
Hints
1. You can use the above program as the basis for tests of your functions. But don’t try to do all three functions at once. You may find it easier to do split and trim first and then dogetaline. You can test split and trim on any array of null character terminated characters (i.e. a statically declared string as opposed to one obtained from user input).
2. The input strings (character buffers) to getaline, split and trim must be modifiable.
3. Replacing a character in a string with the null terminator ‘\0’ in effect creates two strings.
4. You should test for end of line (‘\n’) and the end of file (EOF) when reading user input.
5. When splitting a string that contains 2 or more consecutive separator characters, consider the following example. A string has two consecutive separator characters followed by a non-separator character. In this case, one substring will end at the position of the first separator character. The next substring will start at the position after the second separator character (i.e. the first character of the next substring will be the non-separator character).
6. Do not try to be too clever in your implementation or use of these functions. Their main purpose is to read MINIX commands typed at your simple shell prompt. You need trim andsplit to treat a single line of user input as sequence of strings (i.e. the command and any parameters).
Part 2 deliverables
The deliverables for Part 3 are the files utils.h and utils.c
Note: if you get stuck on this part, you may use standard C library functions for user input to your shell in part 3 (with a penalty in lost marks).
Part 3 – simple shell
In this part you will implement the simple shell (command line processor) described in the lectures for Part 1 of the module (see slide 47 of the slides for
Part 1 lectures
).
The outline of the basic shell is as follows:
Repeat forever display a user prompt (for the user to enter a MINIX command) read and process the user input (using getaline, trim and split) fork a child process in the parent: wait for the child to finish in the child: execve the user command with any parameters
Rules for part 3
1. You must implement the shell in a separate myshell.c file.
2. For the user prompt, you should use the C library putchar function in stdio (i.e. do not use printf).
3. You must document any additional functionality of your shell in comments in the myshell.c file (see breakdown of marks below).
Part 3 breakdown of marks
· 4 marks for the basic shell functionality
· Additional marks are available for extensions to the basic functionality, such as:
· using the functions you implemented in part 2 of the assignment
· detecting and reporting errors in user commands entered (e.g. outputting an error message if the user enters an incorrect command – see errno and perror)
· detecting and continuing if no command is entered (e.g. the user just enters return at the prompt)
· providing a command for the user to exit the shell (e.g. exiting if the user enters q at the prompt)
· other enhancements of your own that make the shell more usable
Additional functionality in your shell can compensate for lost marks in other parts of the assignment. You must document additional functionality of the shell in comments in your myshell.cfile.
Part 3 deliverables
The deliverable for Part 3 is the myshell.c file.
To obtain full marks for this part you should use utils.h and utils.c as a library for your simple shell in part 3. That is, use your utility functions to process user input at your shell’s command line. However, if you get stuck on this part, you may use standard C library functions to obtain user input to your shell.
4. Compiling programs with multiple files
To compile a single C program, e.g. myprogram.c, in the current directory, type:
# cc myprogram.c
If successful, this will compile the program and output an executable to a.out. You can specify a different executable as follows:
# cc myprogram.c -o myprogram
In this assignment you will be working with multiple files. To keep things simple, all files should be in the same directory.
For example, for part 1 you will have the following files: intmath.h, intmath.c and a test program with a main function, e.g. intmath_test.c.
This means that you will have to compile and link multiple files to produce an executable. This is a two stage process with the cc compiler. First produce individual object files with the -coption to the compiler, as follows:
# cc -c intmath.c intmath_test.c
If successful, you will now have two object files: intmath.o and intmath.o.
Now use cc to link the object files and produce an executable named intmath_test, as follows:
# cc intmath.o intmath_test.o -o intmath_test
The process is the same for parts 2 and 3, with different file names.
When you want to recompile, first remove all object files (with extension “.o”) and existing executables. Then repeat the two step compilation above. A Makefile and make can be used to script the whole process of compilation, linking and cleaning (removing) old files.
5. Mark scheme
Marks will be awarded as follows:
· 4 marks for the implementation of arithmetic functions
· 8 marks for the implementation of user input utilities
· 8 marks for the simple shell
This assignment is worth 20% of the coursework component of the module and 4% of the module mark.
1. Aim
The aim of this assignment is for you to gain some practice in C
programming, to use C libraries and system calls, and to demonstrate your
understanding of C and how to program to a defined interface.
2. Learning/skills outcomes
·
C programming
skills
·
Programming to defined interfaces
·
Formulate problems and identify suitable approaches to solving them
·
Cognitive/intellectual skills and self management (see graduate skills
framework)
3. Specification
This assignment has three parts:
1.
Write a library of simple arithmetic functions
(4 marks)
2.
Write a library of utilities to read and process user input
(8 marks)
3.
Write a simple command line shell
(8 marks)
Note
we will test your solution programmatically on MINIX. It is very
important that
you follow the rules below for the form of your solutions and
their submission. For example, do not deviate from the file names specified.
If you do not adhere to the rules, you will loose marks even if your solution
is logically correct. An important purp
ose of this assignment is for you to
demonstrate that you can understand an interface definition and program to
that definition. It is also important that your code executes correctly on
MINIX.
You will also have to test your solutions yourself. We will no
t mark these
tests and they should not be submitted. The files that you submit are:
·
intmath.h
–
unmodified,
provided for you
·
intmath.c
–
your implementation of the functions
defined
in
intmath.h
·
utils.h
–
unmodified,
provided for you
·
utils.c
–
your implementation of the functions defined in
utils.h
·
myshell.c
–
your implemenation of a simple shel
that uses the
functions defined in
utils.h
intmath.h
/*
* The “intmath.h” header defines the following five integer arithmetic
* functions:
* int add(int x, int y) – returns the sum of x and y
* int subtract(int x, int y) – returns the subtraction of y from x
* int divide(int n, int d) – returns the integer division of n by d
* int modulo(int n, int d) – returns the remainder when n is divided by d
* int division(int n, int d, int *r) – returns the integer division of n by d
* and uses r to record the remainder
*
* Implementations of these functions check for overflow of the range of
* integers as described below.
*
* Implementors of these functions can only use the built-in arithmetic
* operators ++, — and unary – negation.
*/
#ifndef _INTMATH_H /* check if “intmath.h” is already included */
#define _INTMATH_H /* it is not included; define it */
/*
* int add(x, y)
*
* This function returns the sum of two integers (x and y) – adding y to x.
*
* The function checks for integer overflow. If the result of the addition of
* the two integers would exceed the defined range for integers then the value
* of errno is set to ERANGE and the function returns -1.
*
* It is the responsibility of users of this function to test for errno
* correctly and distinguish between the two cases: (1) where -1 is the valid
* result of the sum of two integers, and (2) when integer overflow would have
* occurred.
* See: http://man7.org/linux/man-pages/man3/errno.3.html for information on
* errno
*
* Parameters:
* x – the integer to add y to
* y – the integer to add to x
* Return:
* the result of adding y to x
*/
int add(int x, int y);
/*
* int subtract(int x, int y)
*
* This function returns the difference between two integers (x and y) – the
* result of subtracting y from x.
*
* The function checks for integer overflow. If the result of the subtraction of
* y from x would exceed the defined range for integers then the value
* of errno is set to ERANGE and the function returns -1.
*
* It is the responsibility of users of this function to test for errno
* correctly and distinguish between the two cases: (1) where -1 is the valid
* result of the subtraction of y from x, and (2) when integer overflow would
* have occurred.
* See: http://man7.org/linux/man-pages/man3/errno.3.html for information on
*
* Parameters:
* x – the integer subtract y from
* y – the integer to subtract from x
* Return:
* the result of subtracting y from x
*/
int subtract(int x, int y);
/*
* int divide(int n, int d)
*
* This function returns the result of integer division of the numerator (n) by
* the denominator (d).
*
* The function checks for division by 0. If d is 0 then the function
* returns -1 and errno is set to EDOM.
* There is also one case when the division can cause integer
* overflow. In which case, the function returns -1 and errno is set to ERANGE.
*
* It is the responsibility of users of this function to test for errno
* correctly and distinguish between the two cases: (1) where -1 is the valid
* result of the division of n by d, and (2) when division by 0 or
* integer overflow would have occurred.
* See: http://man7.org/linux/man-pages/man3/errno.3.html for information on
*
* Parameters:
* n – the numerator of integer division
* d – the denominator of integer division
* Return:
* the result of integer division of n by d
*/
int divide(int n, int d);
/*
* int modulo(int n, int d)
*
* This function returns the remainder of integer after division of the
* numerator (n) by the denominator (d).
*
* The function checks for division by 0. If d is 0 then the function
* returns 0 and errno is set to EDOM.
* There is also one case when the division can cause integer
* overflow. In which case, the function returns 0 and errno is set to ERANGE.
*
* It is the responsibility of users of this function to test for errno
* correctly and distinguish between the two cases: (1) where 0 is the valid
* result of n modulo d, and (2) when division by 0 or integer overflow would
* have occurred.
* See: http://man7.org/linux/man-pages/man3/errno.3.html for information on
*
* Parameters:
* n – the numerator of integer division
* d – the denominator of integer division
* Return:
* the remainder after integer division of n by d
*/
int modulo(int n, int d);
/*
* int division(int n, int d, int *r)
*
* This function returns the result of integer division of the numerator (n) by
* the denominator (d). In addition, it uses r to record the remainder of the
* division.
*
* The function checks for division by 0. If d is 0 then the function
* returns -1, errno is set to EDOM and *r will be 0.
* There is also one case when the division can cause integer
* overflow. In which case the function returns -1, errno is set to ERANGE and
* *r will be 0.
*
* It is the responsibility of users of this function to test for errno
* correctly and distinguish between the two cases: (1) where -1 is the valid
* result of the division of n by d, and (2) when division by 0 or
* integer overflow would have occurred.
* See: http://man7.org/linux/man-pages/man3/errno.3.html for information on
*
* Parameters:
* n – the numerator of integer division
* d – the denominator of integer division
* r – the address of integer to record the remainder after integer division
* of n by d
* Return:
* the result ofinteger division of n by d
*/
int division(int n, int d, int *r);
#endif /* _INTMATH_H */
utils.h
/*
* The “utils.h” header defines the following three utility functions:
* int getaline(char *linebuf, int bufsize) – read a line of user input into
* the buffer linebuf
* int split(char *s, int start, char sep, char **substrings, int max) – split
* a string at the separator character sep into at most max substrings,
* starting at position start
* int trim(char *s, char c) – trim a string of leading and trailing sequences
* of the character c, returning the position of the first non-c character
*
* Implementors of these functions should only use the C library function
* getchar.
*/
#ifndef _UTILS_H /* check if “utils.h” is already included */
#define _UTILS_H /* it is not included; define it */
/*
* int getaline(char* linebuf, int bufsize)
*
* Read a line of user input from stdin into the buffer linebuf a character at
* a time.
*
* After successful execution of the function, linebuf will contain up to
* bufsize – 1 characters from user input and will be terminated by the ‘\0’
* character. That is, linebuf can be treated as a string of up to busize -1
* characters not including the terminator character. The buffer must NOT
* contain the end of line character (‘\n’).
*
* The library function getchar can be used to read a character at a time from
* stdin.
*
* Parameters:
* linebuf – the buffer to fill with characters from the stdin stream. linebuf
* is modified by this function.
* bufsize – the size of the linebuf buffer
* Return:
* The number of characters read from user input, -1 if there is an error. If
* linebuf is filled, the number returned will be bufsize – 1 and there may be
* more user input. If the number returned is 0, there was in effect no
* user input (e.g. the only character entered was the new line character)
*/
int getaline(char* linebuf, int bufsize);
/*
* int split(char *s, int start, char sep, char **substrings, int max)
*
* Split a string, s, into at most max substrings at the given separator
* character. The substrings array is used to hold pointers to the
* resulting substrings of s. If s does not contain the separator character then
* a pointer to the single string s will be in the substrings array. Unused
* elements of the substrings buffer will be set to NULL.
*
* If s contains sequences of more than one separator between sequences of
* non-separator characters, this function will terminate a substring at the
* first separator and skip to the next non-separator character to start the
* next substring. This function does *not* treat sequences of leading or
* trailing separator characters as special cases. This means that, after
* calling split, the first and/or last elements of the substrings array may
* point to the empty string. For example, for the following inputs:
* char s[]: “–ab–c–”
* int start: 0
* char sep: ‘-‘
* int max: 4
* The substrings buffer will have points to the following strings:
* { “”, “ab”, “c”, “” }
*
* Other variations of the input string can result in empty strings at the
* beginning and/or end of the substrings buffer.
*
* To skip leading and trailing separator characters, use trim before calling
* split. If trim is used before split, and the return value of trim is used for
* the start value of split, this function will correctly return 0 for a string
* s that only contains separator characters.
*
* Parameters:
* s – the input string. This function may modify s. There is no copying of
* characters.
* start – the position in s to start the search for separator characters. The
* first substring will start from this position in s. If start is less
* than 0, the number of substrings will be 0.
* sep – the character at which substrings should be created
* substrings – the array to populate with pointers to substrings of s.
* substrings is modified by this function. Neither characters nor strings
* are copied to substrings. The elements of substrings are simply pointers
* to substrings of s. If there are less than max substrings, then the
* remaining elements of the substrings buffer will be set to NULL.
* max – the maximum number of substrings required. This should be less than
* or equal to the size of the substrings buffer.
* It is possible for s to contain more than max substrings, in
* which case the final pointer in the substrings buffer will point to a
* substring that contains one or more separator characters.
* Return:
* the number of pointers to strings in substrings. If s contains at
* least one non-separator character and max is at least 1, the number
* of substrings will be at least 1. If the number of
* substrings is less than max, then the elements of substrings from
* number to max – 1 will be NULL.
*/
int split(char *s, int start, char sep, char **substrings, int max);
/*
* int trim(char *s, char c)
*
* Trim the input string of leading and trailing sequences of the specified
* character c. The return value is the position of the first character in s
* that is not c.
*
* Parameters:
* s – the input string. s may be modified by this function, there is no
* copying of characters.
* c – the character to trim from the beginning and end of s
* Return:
* the position of the first character in s that is not c. -1 if s
* contains no other character than c (not including the terminator
* character).
*/
int trim(char *s, char c);
#endif /* _UTILS_H */
A simple shell (command interpreter)
#define TRUE 1
!
/* declare cmd, params, envp, stat, prompt, readcmd */!
!
while (TRUE) { ! !!!/* repeat forever */!
!prompt(); ! !!!!/* display prompt */
!readcmd(cmd, params); /* read input from terminal*/!
!if (fork() != 0) {! !/* fork child process */!
! !/* parent code */!
! !waitpid(-1, &stat, 0); ! !/* wait for child */!
!} else {!
! !/* child code */!
! !execve(cmd, params, envp);!/* execute command */!
!
}!
}!
A simple shell (command interpreter)
#define TRUE 1!
/* declare cmd, params, envp, stat, prompt, readcmd */!
!
while (TRUE) { ! !!!/* repeat forever */!
!prompt(); ! !!!!/* display prompt */
!readcmd(cmd, params); /* read input from terminal*/!
!if (fork() != 0) {! !/* fork child process */!
! !/* parent code */!
! !waitpid(
–
1, &stat, 0); ! !/* wait for child */!
!} else {!
! !/* child code */!
! !execve(cmd, params, envp);!/* execute command */!
!}!
}!