IC221: Systems Programming (SP18)


Home Policy Calendar Units Assignments Resources

Lab 04: Strings and Command Line Arguments

Table of Contents

Preliminaries

Lab Learning Goals

  1. Write programs that manipulate C strings
  2. Perform basic pointer manipulation and arithmetic
  3. Process command line arguments
  4. Convert between strings and other data types
  5. Implement simple algorithm as described in pseudocode

Lab Setup

Run the following command

~aviv/bin/ic221-up

Change into the lab directory

cd ~/ic221/lab/04

All the material you need to complete the lab can be found in the lab directory. All material you will submit, you should place within the lab directory. Throughout this lab, we refer to the lab directory, which you should interpret as the above path.

Submission Folder

For this lab, all programs should be placed in the following folder:

~/ic221/lab/04

Only programs found in the folder will be graded.

Submission Instructions

To submit, make sure that all your programs are in the submission folder on a Computer Science department lab machine, i.e., a machine in MI301. Then issue the command:

~aviv/bin/ic221-submit

Follow the prompts and select the right submission to submit.

Common Mistakes:

  • You are not logged into a lab machine and are instead on your local machine. Solution: ssh into a lab machine and run the submit script.
  • You did not copy your code from your local machine to a lab machine. Solution: use sshfs to mount your account and submit your code, or use scp to copy your code.

If you still have trouble submitting your code, email your instructor.

Test Script

To help you complete the lab, I have provide a test script that will run basic tests against your program. The script is not designed to be comprehensive, and you will graded based on a larger array of tests. To execute the test script, run it from anywhere within the lab directory.

./test.sh

README file

You are required to submit and complet the README file in your submission folder. Please answer all questions and filel in any comments there.

Compiling your code:

To compile your code, use gcc the gnu c compiler using the following command:

gcc -Wall -g palindrome.c -o palindrome

The -Wall option will provide additional warnings. The -g option will include debugging symbols in case you want to use the debugger.


Part 1: String Manipulation (30 Points)

For this part of the lab, you will complete a small program to check if the input provided by the user is a palindrome. A palindrome is a string that is the same forward and backwards.

There are two main ways for testing if a string is a palindrome:

  • Iterate from forward to back, and from back to forward, and check that they are the same.
  • Create a copy of the string, in reverse, and check that the copy matches the original.

You will implement both; however, the challenge is working with C strings. The main portion of the program, provided to you, looks like such:

int main(int argc, char * argv[]){

  char str[1024];

  printf("Enter a string:\n");

  scanf("%1023s",str);

  if(check1(str)){
    printf("Palindrome according to check 1\n");
  }else{
    printf("NOT a palindrome according to check 1\n");
  }


  if(check2(str)){
    printf("Palindrome according to check 2\n");
  }else{
    printf("NOT a palindrome according to check 2\n");
  }

}

Note that the input is read into the str string and may be substantially shorter than the length 1024. It is also passed to the check1() and check2() functions without a length argument. You should make sure that you use strlen() somewhere.

Additionally, to complete check2() which requires making a copy of the string; don't forget that C strings must be NULL terminated. If you do not NULL terminate, strcmp() will not work.

  • You must complete the palindrome program using two checks.
  • check1() and check2() must use two distinct algorithms:
    • check1() use two iterators, one from the start and one from the end of the string, to check if the front and back are the same
    • check2() copy the string to a new string, in reverse, and compare the two strings using strcmp()
  • (Extra Credit: 5 points): Complete both check1() and check2() algorithms only using pointer types, no integers. That is, you may not use integer indexing of the string like so: str[i]. If you complete the extra credit, be sure to indicate so in your README file.

Here is some sample output:

aviv@saddleback: part1 $ ./palindrome 
Enter a string:
racecar
Palindrome according to check 1
Palindrome according to check 2
aviv@saddleback: part1 $ ./palindrome 
Enter a string:
madamimadam
Palindrome according to check 1
Palindrome according to check 2
aviv@saddleback: part1 $ ./palindrome 
Enter a string:
amanaplanacanalpanama
Palindrome according to check 1
Palindrome according to check 2
aviv@saddleback: part1 $ ./palindrome 
Enter a string:
notapalindrome
NOT a palindrome according to check 1
NOT a palindrome according to check 2
aviv@saddleback: part1 $ 

Part 2: Credit Card Checking with Luhn's Algorithm (70 points)

For this part of the lab, you will implement a checksum for credit card numbers. A checksum is a method to verify that a number meets certain properties. It is an important principle in networking and other areas, and you will implement the checksum used to check if a credit card is valid. Note that just because a number passes the checksum, it doesn't make it a truly valid credit card number, it just means that it may be used as a credit card number.

Credit card numbers are checked using Luhn's Algorithm. You can find a description on wikipedia, but it is highlighted below. It occurs in three basic steps:

  • For the 16 digit card number, double ever other digit

6  4  7  4  5  6  2  3  8  9  9  7  2  7  5  6  <- number
|     |     |     |     |     |     |     |    
v     v     v     v     v     v     v     v   
12   14    10     4    16    18     4    10    <- double every other digit

  • If the double of the digit is greater than 9, that is two digits in length, replace it with the sum of the digits of the product

6  4  7  4  5  6  2  3  8  9  9  7  2  7  5  6  <- number
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
v  |  v  |  v  |  v  |  v  |  v  |  v  |  v  |
12 | 14  | 10  |  4  | 16  | 18  |  4  | 10  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
v  v  v  v  v  v  v  v  v  v  v  v  v  v  v  v
3  4  5  4  1  6  4  3  7  9  9  7  4  7  1  6  <- sum of digits if >9

  • Take the sum of the remaining numbers
3 + 4 + 5 + 4 + 1 + 6 + 4 + 3 + 7 + 9 + 9 + 7 + 4 + 7 + 1 + 6
= 80

  • If that number is a multiple of 10, then it passes the checksum.
80%10 = 0 <- PASS

For your program to work, you must take a 16 digit credit card number on the command line and print VALID or INVALID. Like so:

aviv@saddleback: part2 $ ./creditcardcheck 6474562389972756
VALID
aviv@saddleback: part2 $ ./creditcardcheck 6474562389972757
INVALID

Additionally, your program should detect three error conditions: (1) No argument provided; (2) Invalid length of credit card number; (3) Invalid numbers in credit card. Here's an example:

aviv@saddleback: part2 $ ./creditcardcheck 
ERROR: require credit card number
aviv@saddleback: part2 $ ./creditcardcheck 12345
ERROR: Invalid credit card number: Bad Length
aviv@saddleback: part2 $ ./creditcardcheck 12345a7890123456
ERROR: Invalid credit card number: Bad number 'a'

To perform these checks, consider that the command line arguments are a string, and each number is currently a character. The numeric value of that character is not the same as its integer value, but they are still ordered. For example, this will check for a valid digit:

if ( c >= '0' && c <= '9' ) 
  printf("Valid digit!\n");
else
  printf("Invalid digit!\n");

Finally, because the checksum occurs over integers and not strings, you will need to convert each individual digit into a integer. Consider creating an array for the credit card number that can store all these digits:

int ccnum[16]

You can use atoi() to perform the conversion from string to integer, but you'll need to first isolate the character of the number as its own string.

char num[2];
num[0] = '8';
num[1] = '\0'; //null terminate
int i = atoi(num); //convert to integer
  • You must complete the creditcardcheck
  • Your program must print "VALID" for a valid credit card number that passes Luhn's checksum and "INVALID" for a credit card number that does not pass the checksum
  • You must take the credit card number as the command line argument and check for the following the errors and print the following error statements:
    • No argument provided: "ERROR: require credit card number" printed to stderr
    • Credit card is not the right length: "ERROR: Invalid credit card number: Bad Length" printed to stderr
    • A non-digit appears in the number: "ERROR: Invalid credit card number: Bad number '%c'" (the "%c" should be formatted to the invalid character) print to stderr
  • Your program should also return the following codes for exit values:
    • return 0 : valid credit card number
    • return 1 : error
    • return 2 : invalid credit card number
  • (Extra Credit: 5 points): Write a bash script rand_creditcard.sh that will find a random, valid credit card number by using the creditcardcheck program. You may write any additional programs to complete this tasks, such as one to create a random credit card, but your program must perform exactly as below:

    aviv@saddleback: part2 $ ./rand_creditcard.sh
    1002947242955100
    aviv@saddleback: part2 $ ./rand_creditcard.sh
    8113723297573564
    

Here is some sample output of creditcardcheck:

aviv@saddleback: part2 $ ./creditcardcheck 6474562389972756
VALID
aviv@saddleback: part2 $ ./creditcardcheck 6474562389972757
INVALID
aviv@saddleback: part2 $ ./creditcardcheck 
ERROR: require credit card number
aviv@saddleback: part2 $ ./creditcardcheck 12345
ERROR: Invalid credit card number: Bad Length
aviv@saddleback: part2 $ ./creditcardcheck 12345a7890123456
ERROR: Invalid credit card number: Bad number 'a'