-------------------------------------- # Specifications: Extract and compile a C++ program contained in a tar archive -------------------------------------- -Ian! D. Allen - idallen@idallen.ca This is an example of a script that uses many of the scripting features learned this term. The solution is posted under the "Shell Scripts" section of the Notes area. References and Readings: Running Linux: Chapters 4, 7, 13 Learning Unix: Chapters 1, 3, 4, 5 Lecture Notes Online Notes The script described below must be written to conform to the script writing checklist: script_checklist.txt and to the script style given in: script_style.txt. All user input (command line arguments or input via "read") must be fully validated before being used in expressions. Do not process null, empty, missing, unreadable, or otherwise bad input! Scripts without *useful* block comments will be severely penalized. See the script_style.txt file for the expected commenting style. Failure to follow the script_checklist.txt will lose you many marks. This scripting is straightforward; build this script one piece at a time and test each piece before adding a new piece. Each of the required pieces is available in existing files posted in the Notes area. ------------------ Syntax and Purpose ------------------ Syntax: $0 [ tarfile [ progname ] ] Example usage: ./tarcompile.sh mytar.tar.gz argv The user will be prompted to enter any missing command line arguments. This script will expand a compressed (gzip) tar archive containing C++ source code and compile the C++ source code for the given program name. It will compare the size of the compiled program against a system program. Note: If the program name given by the user is "argv", the source code name that we will be looking for inside the compressed tar archive is expected to be "argv.cpp". The argument you are expected to give to the script is just the "argv" part of the name. The script will construct and use the "argv.cpp" source code name internally. ------------------ Optional Arguments ------------------ This script has optional command line arguments. Accept two or less arguments on the command line. (If there are more than two arguments, print an appropriate error and exit the script with return code 2.) If there are less than two arguments, prompt for and read the missing arguments (up to two). The first optional argument is presumed to be a compressed (gzip) tar file. If the argument is missing, prompt for it and read it. The second optional argument is presumed to be the compiled name of a program whose source is contained in the tar archive. If the second argument is missing, prompt for it and read it. In other words: If the user supplies two command line arguments, don't prompt for any input; transfer the two arguments to shell variables and use them as the tar file and program names. If the user supplies only one command line argument (the tar file name), transfer it to a variable and prompt for and read the missing program name argument. If the user supplies no command line arguments, prompt for and read both the tar file name and the program name. See the optional_args_*.sh.txt scripts in the Notes for examples of how to do optional arguments. Make sure your prompts for input appear on standard error, not standard output. A good user interface will echo user input (including any command line arguments given) back to the user. This is a good idea both for debugging your script and for giving the user feedback on what data the script is actually processing: Echo the user's two input names back to the user surrounded by single quotes; also construct the name of the source code file from the program name, put it in a variable, and echo that variable as well: $ ./tarcompile.sh mytar.tar.gz argv Input tarfile: 'mytar.tar.gz' Program name: 'argv' Source name: 'argv.cpp' ---------------- Input Validation ---------------- If either of the two input strings is empty (null string), print an appropriate error message and exit the script with return code 2. (An empty string can be specified on a Unix shell command line by using two quote characters with nothing between them: "" or '' ) Note: An empty *string* is not the same as an empty *file*. Make sure the compressed tar file argument is a non-empty, plain file. (That is two things you need to test.) If the file is not readable, print a warning on stderr and then add read permissions to the file. If the program source name argument supplied by the user (e.g. argv.cpp) is not found in the table of contents of this compressed tar archive, print an appropriate error message and exit with status 3. (We can't compile a program that doesn't exist!) (Hint: Generate a table of contents of the tar archive and pipe that output into a comand that can search for the program name. Use the search program return status to know if the name was found in the table of contents. You can redirect the output of the search program to /dev/null so that it does not appear on the screen.) ---------- Processing ---------- In the specifications below, the example name "argv" is just an example - the actual program name you use is the name given by the user as input. Do not hard-code the name "argv" in the script! 1. We don't want our script to overwrite any existing program file or program source file, so we rename any existing files to have ".bak" on the end: If there is an existing pathname matching the given program source name (e.g. argv.cpp), print a warning message and rename that pathname by adding ".bak" to the name. In other words, rename an existing argv.cpp to be argv.cpp.bak; but, only if the source pathname argv.cpp currently exists. 2. Extract the contents of the compressed tar archive. If this fails, print an appropriate error message and exit the script with status 4. 3. Make sure the program source file (e.g. argv.cpp that was just extracted from the tar archive) is a non-empty, plain file. (That is two things you need to test.) 4. If the program source file is not readable, print a warning on stderr and then add read permissions to the file. 5. If there is an existing pathname matching the given program name file (i.e. an already existing argv file), print a warning message and rename that pathname by adding ".bak" to the name. In other words, rename an existing argv to be argv.bak; but, only if the program file pathname argv currently exists. 6. Compile the given program source using the Unix C++ compiler and name the executable output file with the given program name, e.g. compile source "argv.cpp" into an executable file named "argv". If this compile fails (bad exit status), print an appropriate error message and exit the script with status 5. 7. Count the number of bytes (characters) in the resulting executable output file (e.g. in the output file argv). Compare that number against the number of bytes in the system file "/bin/echo" and print one of three messages on standard output in this format: Program NNNN ( XXX) is smaller than /bin/echo ( YYY) Program NNNN ( XXX) is the same size as /bin/echo Program NNNN ( XXX) is larger than /bin/echo ( YYY) - NNNN will be the name of the compiled program given by the user - the number XXX is the size (in bytes) of the compiled program - the number YYY is the size (in bytes) of /bin/echo Replace NNNN, XXX, and YYY with the correct variable contents in your script code. Do not output NNNN or XXX or YYY. The actual pathname /bin/echo should appear in only *one* place in your script - do not repeat this string constant all through your code. Define it in one place. Reduce maintenance costs! ------------- Documentation ------------- Follow the comment and header conventions given in file script_style.txt. Follow the script writing checklist given in file script_checklist.txt. Scripts without comments are heavily penalized. ---------- Test Suite ---------- Test your script. The sample inputs and output shown below are not a complete test suite. I will try to find test cases using glob patterns and blanks that will make your scripts abort or misbehave. Sample test output follows. Do not use the prompt examples "ENTER INPUT" given in the test suite - use a better prompt (not UPPER CASE) that tells the user exactly how many things to input and what to input. The test cases below are not exhaustive - make sure your program works without error for *any* inputs of any type containing any kind of special characters or blanks. Also try adding/removing permissions on various files to check that all the error messages are working. Test Preparation: $ tar czf mytar.tar.gz argv.cpp # create a test tar archive $ rm argv* # remove existing files Test1: $ ./tarcompile.sh mytar.tar.gz argv Input tarfile: 'mytar.tar.gz' Program name: 'argv' Source name: 'argv.cpp' Program argv ( 12849) is larger than /bin/echo ( 12472) Test2: $ ./tarcompile.sh ENTERINPUT1: # write a better prompt message mytar.tar.gz # user enters tar file name ENTERINPUT2: # write a better prompt message argv # user enters compiled program name Input tarfile: 'mytar.tar.gz' Program name: 'argv' Source name: 'argv.cpp' ... warning message about argv.cpp existing and renaming it to argv.cpp.bak ... warning message about argv existing and renaming it to argv.bak Program argv ( 12849) is larger than /bin/echo ( 12472) Write your own good prompts and error messages; do not copy the bad examples above. Try testing your script with blanks and GLOB characters in all your file names (including the program name). (I will do this.)