CSE 2431 LAB 3 SU 21
Due: Sunday, June 27th at 11:30 p.m.
1. Goal: Enhance the shell written for Lab 1. 2. Introduction
This lab assignment is an extension to the Linux/UNIX Shell interface which was built in Lab 1. You will add a history feature into your Linux/UNIX Shell which will allow users to view and possibly rerun any one of up to the 5 most recently entered commands. When your program outputs these commands (when the user utilizes the h or history command feature – see below) these commands will be numbered starting at 1 (the least recent of the last 5 commands) and will be numbered up to 5 (the most recent of the last 5 commands). If the user executes the same command twice consecutively, it will appear twice in the history. A working version of the shell for Lab 1, shellA.c, has been placed on Carmen in the Labs folder which you can download into a lab3 directory which you create.
We will use a historyBuff array to store the user’s command history (up to the last 5 commands). This historyBuff array should be declared in main, and has a size equal to 5 times the maximum length of a command, which for this lab we will set to be 50. Therefore, the historyBuff array will be an array of char of size 250.
Notice that the setup function in the shellA.c source code uses a file system call to read the command which the user has entered on the command line into the inputBuff array which is passed to the setup function. You should notice, however, that after setup reads the command into inputBuff, the command in inputBuff is modified by the setup function, so we cannot use the inputBuff array to get a copy of the command; we can pass another array to setup, though, which we will call commandCopy (also declared in main), to store a copy of the command before it is modified (you have to figure out how to store the copy of the command before inputBuff gets modified). After the commandCopy array has a copy of the command, and after the setup function terminates and returns to main, we can use the commandCopy array to store a copy of the command which the user entered in the historyBuff array (Be sure to pay close attention to what is said below about the required null byte terminator for strings in C, which will need to be placed at the end of the command in commandCopy).
One modification to Lab 1: In lab 1, we assumed that the maximum line length for a command entered by the user would be 40 characters. Here, we increase that to 50 characters. Therefore, since you need to store the last 5 commands in the history, and each line can be up to 50 characters (including the null byte termination), you need a historyBuff of 250 characters. You can just declare (in main) an array of char of length 250 called historyBuff, so the name of this array will be a constant pointer which points to the array. This will allow you
1
1
to access the various sequences of up to 50 characters which correspond to each command in the history.
IMPORTANT REMINDER: When storing commands, be very sure that there is a null byte at the end of the command (recall that the null byte is the char ‘\0’)! Recall that C string operations cannot be depended on to work correctly if a string is not terminated by a null byte string terminator. You should put this null byte at the end of each command after you copy it into the commandCopy array in setup; BE SURE TO DO THIS IN THE CODE IN THE setup FUNCTION; do not try to do it after setup terminates and returns to main! A C string, by definition, is null byte terminated; C library functions that work on strings ASSUME that any string has a null byte terminator. Overlooking this important fact will cause your code to exhibit bugs which are very difficult to find and correct, so make sure you spend the time necessary to get this right from the beginning, and the lab will be much less stressful!
3. Assignment
A user will be able to list the 5 most recently entered commands when the user types the command history or its alias/shortcut h. When your shell program outputs the commands in the list, it should number them from 1) (the least recently executed or oldest command in the list) to 5) (the most recently executed command in the history list; and the numbers in the list of commands should be printed as stated above, that is, the number followed by ‘)’ ). Each command in the list should be printed on a separate line. The h or history command should not be placed in the history list. With the list of previous commands output with h or history, the user can run any of those previous 5 commands by entering rnum where ‘num’ is the number of that command in the history list (and num will always be from 1 to 5, inclusive, not 0 to 4). So, for example, after entering the command history or h, if the user then, after the history list is printed, runs the command r3, the 3rd command in the list should be printed out and run again. Also, the user should be able to run the most recent command again by entering rr for ‘run most recent,’ which should print out and execute the last command (most recent command) in the history list. For rnum, you can assume that no spaces will separate the r and the number and that the number will be followed by ‘\n’ when entered by the user on the command line. Also, when rr is used to execute the most recent command you may assume that it is immediately followed by ‘\n’ on the command line. To simplify the code, we will assume that the user can only enter either rnum or rr immediately after entering history or h; after entering h/history, the user can execute any number of rr/rnum commands. You can assume that a user of your shell will comply with these restrictions.
As stated above, any command that is executed in this fashion (that is, using rr or rnum) should be echoed on the user’s screen and the command should not be placed in the history buffer again (it is already there). (rnum and rr do not go into the history list; the actual command that they are used to execute does not go into the history list again either; in other words, executing a previous command again using rr or rnum does not modify the history list at all).
2
2
For a better idea of how a similar (BUT NOTE CAREFULLY, NOT IDENTICAL!!!!!!) history feature actually works in the Linux CSE Environment, execute a few commands in the terminal window then type history. To execute a command from the history list type !num (for example, !3, with no space between the ! and the num). To execute the most recent command type !!. Depending on whether it is set up in your aliases for commands, h may work as well. Also note that when you type !num or !!, the full command from the history list is output then executed. If you type history again you can see the commands that were repeated are now in the updated history list. Our implementation of history does not use !, but rather r. Also, our implementation of the history feature is not identical to the Linux feature in all ways, so please do not assume that it is. Trying this in Linux just gives you a sense of how such a feature works, but in your shell, it should be implemented as described earlier, and not based on the way it is implemented in Linux.
How to implement the rr/rnum feature: After the setup function is called, and you store a copy of the user’s command in commandCopy, you can check to see if the command is rr or rnum (r1, for example). If you determine that it is rr or rnum, you have a copy of the command that the user wants to execute in the historyBuff array (and you can use this copy to echo the command, which is supposed to be done for rr/rnum; you can write a function to echo the command); however, in order to have the forked child execute the command, you need the command to be parsed into substrings, and to have an args array of char pointers pointing to the substrings that you can use to invoke execvp. How can the commandCopy be used to get an args array? Well, the setup function parses commands and produces an args array, but it does this after reading the command the user entered using a read file system call. We can use a boolean variable (int in C), which we call rerun, which we will pass to setup. If rerun is 0, then setup will read the command the user entered on the command line using a read call, but if rerun is 1, then instead of doing a read system call, setup will use the command we pass it with commandCopy (which can be copied into inputBuff before setup parses the command, if rerun is 1). You will need to put code in the setup function to modify it so that it works this way.
history/h implementation: I recommend that you write a function to store each command that the user has entered on the command line, up to the last 5, in the historyBuff array. The function can be called something such as updateHistoryBuff. You can also write a separate function to print the commands in the historyBuff, something such as printHistoryBuff. You will need to decide when to call these functions, but having separate functions to do these things will make your code more modular, and much easier to debug, and you should put this code in separate functions (that is, do NOT put this code in main or in a function that does other work).
As stated before, your source code for Part A should be in a source file named shellA.c, which should have the code you add to the version of shellA.c which you download from the Lab 3 folder on stdlinux. Also, you are permitted to use string operations in the C standard library, and indeed, you are strongly encouraged to do so (It is always better not to reinvent the wheel!). You will not be able to do everything with library string operations, but they will help
3
3
with a lot of what you need to do; you’ll need to write some functions to do string work a well, but it will be easier to use a library function where possible. Always keep in mind, as mentioned earlier, that library functions for strings in C ASSUME that any string passed as a parameter is null byte (‘\0’) terminated; be very sure that any string you pass to a library function has a null byte termination (Actually, if it does not, it is NOT a “string” in C!).
4. Submission
Please put all code for Part A in “shellA.c” (modify the code which you copy from the project directory); at the beginning of your source file you need to tell the grader your full name (It should be in a comment on the very first line of the source file).
Submit this file on Carmen by starting a firefox web browser in the stdlinux environment: $ firefox https://carmen.osu.edu/#
Submit only the source file specified above, and not an executable, or you will not get credit. Please DO NOT ZIP your source code files before submitting to Carmen.
All necessary code, and #include directives for necessary .h files from the C standard library, should be in a single C source code file for the lab. To make things easier for the grader(s), you should compile your code as follows (your Linux prompt may be different from $, but that does not matter) for each part:
$ gcc shellA.c -o shellA
To run the code, you should use: $ shellA
Make very sure that your code compiles with no errors or warnings when using these compilation commands. If your code does not compile when the grader grades your lab, you will get no credit for the lab. Also make sure that your executable for each part runs without errors, and that it performs as described above.
To test your code, you can use the following Linux commands (remember that the shell from lab 1 could run commands in the background or in the foreground):
ls
pwd date
ls & pwd &
4
4
history rr
h
r2
[history list with above commands, numbered, should be printed] [Command 5, that is, pwd &, should be echoed and executed again) [history list with above commands, numbered, should be printed]
[Command 2, that is, pwd, should be echoed to the screen and executed; try other values besides 2 to make sure they work too]
The code and documentation you write and submit must be your own independent, individual work. If the files do not compile or have runtime errors (such as segfault), no credit will be given for this lab.
You should test your lab (of course!), so that you know when the grader runs it, it will perform as described. Test cases which you use should show the last 5 commands that have been entered – seen by the command numbers displayed when the user types history. The test cases should also demonstrate the rr and rnum commands.
A late penalty of 25% will be assessed for any lab not submitted by the due date/time shown at the top of this document, but submitted within 24 hours of the due date and time.
You are welcome to use Piazza to discuss questions/problems you are having with the lab with the instructor or with other students. Please be sure, however, that you do not post code, unless you leave your post private (as it will be by default).
5
5