C Homework Help l Assignment l Project in C

Congratulations on passing with flying colors, if that’s what you want then we can make sure achieve it.

This is an example on C programming assignment help. We can custom design the solutions to match your skill level (either advanced, intermediate or basic), just let us know if you need us for C programming project help.

Anyone for Tennis?

For this project, you will be writing a program to compute the score for a tennis game between two teams A and B.

Your program should read, from standard input, a sequence of characters indicating the winner of each individual point, and print to standard output the current score at various points during the game.

Note: although it has been released in Week 2, we recommended that you begin this assignment in Week 3, because some of the material covered in Week 3 (functions and arrays) will be very useful in helping you to structure your program appropriately.

Tennis Scoring

A tennis match is divided into “sets”, “games” and “points”. Full details of the tennis scoring system can be found at http://en.wikipedia.org/wiki/Tennis_score.

To win a game, one team must score at least four points and at least two points more than the other team has scored during that game.

For historical reasons, scores within a game are reported according to the following convention, where the number of points won by the serving team is shown on the left hand side, those of the receiving team along the top row:

0 1 2 3
0 Love-All Love-15 Love-30 Love-40
1 15-Love 15-All 15-30 15-40
2 30-Love 30-15 30-All 30-40
3 40-Love 40-15 40-30 Deuce

Once both teams have three or more points, the score is reported as:

  • “Deuce” if the two teams have won the same number of points
  • “Advantage Server” if the serving team is ahead by one point
  • “Advantage Receiver” if the receiving team is ahead by one point

To win a set, one team must win at least 6 games, and at least 2 games more that the other team has won during that game.

To win the match, one team must win at least 3 sets.

Note: We will follow the rules used at Wimbledon, where a tie-break is never played in the 5th set. If the 5th set goes to a score of 6-6, normal games continue to be played until one team has a 2-game lead. Unless attempting the “Bonus” section, you can assume that the first four sets in the match will never go to a score of 6-6, so you do not have to worry about tie-breaks.

If Team A serves for the first game, then Team B will serve for the second game, and the serve will continue to alternate between the two teams for the rest of the match, without regard to sets; in other words, if Team A serves for the last game of one set, then Team B will serve for the first game of the following set.

Input and Output

The input to your program will be a series of characters A, B and S, with line breaks at appropriate intervals. The character A indicates a point won by Team A; B indicates a point won by Team B.

The character S indicates that your program should print the score at that point in the match, in the following format:

Team ? to serve:

?-? ?-? ?-?

???-???

The first line indicates which team is about to serve (A or B); the second line indicates the score for all completed sets as well as the current set, and the third line indicates the score within the current game (from the perspective of the serving team). For each set, the number of games won by the serving team should be printed first, followed by a dash, followed by the number of games won by the receiving team.

When your program detects that the match is finished, it should print the final score in this format:

Team ? wins: ?-? ?-? ?-? ?-?

The final score is a list of the scores for each set, with the score of the match-winning team printed first. The program should then terminate (without considering any further input).

For example, if the input to your program is BSAAAABBBS, the output should look like this:

Team A to serve: 
0-0
Love-15

Team B to serve:
0-1
40-Love

This indicates that, when the first S instruction is processed, no sets or games have been won by either team, and Team B (who are receiving) have scored one point in the first game. When the second S is processed, Team A has won one game, and Team B (who are now serving) have scored three points in the second game. Note that a blank line should be printed under each score.

tenisgame.c

#include <stdio.h>

int main()
{
    /* this means that the first team to serve is team A */
    int serving = -1;

    // on start of the match, no points, no games, no sets are won by either players
    int pointsWonA = 0;
    int pointsWonB = 0;
    int gamesWonA = 0;
    int gamesWonB = 0;
    int setsWonA = 0;
    int setsWonB = 0;

    /*  we need a place for us to save the set scores
        so we will make 2 arrays one for player A set scores
        and the other for player B set scores, and in that array
        in position 0 we will save the scores of the first set,
        on position 1 score of the second set and so on*/
    /* the result can be at most 3:2, so the max number of sets is 5 */
    int setCounter = 0;
    int setScorePlayerA[5];
    int setScorePlayerB[5];

    int ch;

    while ( (ch = getchar()) != EOF)
    {
        if (ch == 'S')
        {
            /* first we are printing what team is serving */
            printf("Team %c to serve:\n", ((serving==-1)?'A':'B'));

            /* now we print all of the completed sets scores */
            int i;
            for (i=0; i<setCounter; i++)
            {
                if ( serving == -1 )
                    printf("%d-%d ", setScorePlayerA[i], setScorePlayerB[i]);
                else
                    printf("%d-%d ", setScorePlayerB[i], setScorePlayerA[i]);
            }
            /* now we can print the current set score */
            if ( serving == -1 )
                printf("%d-%d\n", gamesWonA, gamesWonB);
            else
                printf("%d-%d\n", gamesWonB, gamesWonA);
            /* and now print the current game score */
            if (pointsWonA < 3 || pointsWonB <3)
            {
                if ( serving == -1 )
                {
                    if (pointsWonA == 0)
                    {
                        printf("Love");
                    }
                    else
                    {
                        printf("%d", calculatePointsToPrint(pointsWonA));
                    }
                    printf("-");
                    if (pointsWonB == 0)
                    {
                        printf("Love");
                    }
                    else
                    {
                        printf("%d", calculatePointsToPrint(pointsWonB));
                    }
                }
                else
                {
                    if (pointsWonB == 0)
                    {
                        printf("Love");
                    }
                    else
                    {
                        printf("%d", calculatePointsToPrint(pointsWonB));
                    }
                    printf("-");
                    if (pointsWonA == 0)
                    {
                        printf("Love");
                    }
                    else
                    {
                        printf("%d", calculatePointsToPrint(pointsWonA));
                    }
                }
                printf("\n");
            }
            else if (pointsWonA == pointsWonB)
            {
                printf("Deuce");
            }
            else
            {
                if (serving == -1)
                {
                    if (pointsWonA > pointsWonB)
                    {
                        printf("Advantage Server\n");
                    }
                    else
                        printf("Advantage Receiver\n");
                }
                else
                {
                    if (pointsWonB > pointsWonA)
                    {
                        printf("Advantage Server\n");
                    }
                    else
                        printf("Advantage Receiver\n");
                }
            }
            /* after we print all we need to print new line */
            printf("\n");
        }
        else if (ch == 'A')
        {
            /* add points to the player A */
            pointsWonA++;

            /* check to see if he won the game
            that means that player A won at least 4 points,
            and that the diffrence between the points is at least 2 */
            if (pointsWonA >= 4 && (pointsWonA - pointsWonB) >= 2  )
            {
                /* we are updating the games won */
                gamesWonA++;
                /* now we need to check did player A won the set
                    that means that he won at least 6 games, and the
                    diffrence between the games won is >=2 */
                if (gamesWonA >= 6 && (gamesWonA - gamesWonB) >= 2)
                {

                    /* player A has won the set, so we save the set score and update the games won */
                    setScorePlayerA[setCounter] = gamesWonA;
                    setScorePlayerB[setCounter] = gamesWonB;
                    setCounter ++ ;
                    gamesWonA = 0;
                    gamesWonB = 0;

                    /* we are updating the sets won */
                    setsWonA ++;
                    /* now we check to see did player A won the game,
                        that means that he won at least 3 sets */
                    if (setsWonA >= 3)
                    {
                        printf("Player A wins: ");
                        int i;
                        for (i=0; i<setCounter; i++)
                        {
                                printf("%d-%d ", setScorePlayerA[i], setScorePlayerB[i]);
                        }
                        printf("\n");
                    }

                }
                /* player A has won the game so we can reset the points for both player */
                pointsWonA = 0;
                pointsWonB = 0;
                /* and after every game we need to change the player serving */
                serving = serving * -1;
            }

        }
        else if (ch == 'B')
        {
            /* add points to the player B */
            pointsWonB++;
            /* check to see if he won the game
            that means that player B won at least 4 points,
            and that the diffrence between the points is at least 2 */
            if (pointsWonB >= 4 && (pointsWonB - pointsWonA) >= 2  )
            {
                /* we are updating the games won */
                gamesWonB++;
                /* now we need to check did player B won the set
                    that means that he won at least 6 games, and the
                    diffrence between the games won is >=2 */
                if (gamesWonB >= 6 && (gamesWonB - gamesWonA) >= 2)
                {
                    /* player A has won the set, so we save the set score and update the games won */
                    setScorePlayerA[setCounter] = gamesWonA;
                    setScorePlayerB[setCounter] = gamesWonB;
                    setCounter ++ ;
                    gamesWonA = 0;
                    gamesWonB = 0;

                    /* we are updating the sets won */
                    setsWonB ++;
                    /* now we check to see did player A won the game,
                        that means that he won at least 3 sets */
                    if (setsWonB >= 3)
                    {
                        printf("Player B wins: ");
                        int i;
                        for (i=0; i<setCounter; i++)
                        {
                                printf("%d-%d ", setScorePlayerB[i], setScorePlayerA[i]);
                        }
                        printf("\n");
                    }
                }
                /* player A has won the game so we can reset the points for both player */
                pointsWonA = 0;
                pointsWonB = 0;
                /* and after every game we need to change the player serving */
                serving = serving * -1;
            }
        }
    }
}

int calculatePointsToPrint(int pointsWon)
{
    if (pointsWon == 1)
    {
        return 15;
    }
    if (pointsWon == 2)
    {
        return 30;
    }
    if (pointsWon == 3)
    {
        return 40;
    }
}

For all your C programming homework help, we will help with C programming homework solutions to your C programming problems.

C Programming Homework Help

Banker’s Algorithm

Problem 1

You are given a system with 4 types of resources: A, B, C, and D. Some of these resources are allocated to processes and you only have no A, 1 B, 1 C, and no D left to allocate. You have 5 processes in the system that hold the following resources:

A B C D
P0 0 1 1 1
P1 2 4 0 1
P2 1 2 1 3
P3 1 0 0 0
P4 0 0 2 1

The operating system has been collecting resource requests from the processes. The current set of requests are:

A B C D
P0 1 0 1 0
P1 1 1 0 3
P2 0 3 4 1
P3 0 1 0 0
P4 0 0 2 1

Based on the results of the banker’s algorithm, should the operating system grant all of the requests whether all at once or one at a time?

Justify your answer.

Answer

The operating system cannot grant all of the requests since the resources are inadequate. However, assuming a process will release all granted resources once it has finished utilizing the resources granted according to its request (the assumption of banker’s algorithm), the operating system cannot grant resources for all processes. Following banker’s algorithm, P3, P0 and P4 can be granted resources but P1 and P2 cannot.

Here is the explanation. We use tuples (A, B, C, D) to represent the numbers of resources of each type. Currently, it is (0, 1, 1, 0).

(1) P3 requires (0, 1, 0, 0), which can be satisfied and results in (0, 1, 1, 0) – (0, 1, 0, 0) = (0, 0, 1, 0) resource left. Now P3 has (1, 0, 0, 0) + (0, 1, 0, 0) = (1, 1, 0, 0) resources; After P3 release all, the operating system has (0, 0, 1, 0) + (1, 1, 0, 0) = (1, 1, 1, 0) resource.

(2) P0 requires (1, 0, 1, 0), which can be satisfied and results in (1, 1, 1, 0) – (1, 0, 1, 0) = (0, 1, 0, 0) resource left. Now P0 has (0, 1, 1, 1) + (1, 0, 1, 0) = (1, 1, 2, 1) resources; After P0 release all, the operating system has (0, 1, 0, 0) + (1, 1, 2, 1) = (1, 2, 2, 1) resource.

(3) P4 requires (0, 0, 2, 1), which can be satisfied and results in (1, 2, 2, 1) – (0, 0, 2, 1) = (1, 2, 0, 0) resource left. Now P4 has (0, 0, 2, 1) + (0, 0, 2, 1) = (0, 0, 4, 2) resources; After P4 release all, the operating system has (1, 2, 0, 0) + (0, 0, 4, 2) = (1, 2, 4, 2) resource.

(4) Now P1 requires 3 Ds and P2 required 3 Bs. Neither of them can be satisfied by the current resource set (1, 2, 4, 2).

Problem 2:

Getting any single student from home amid tra_c to his or her classes is solvable.

However, getting every student from home to classes amid tra_c is a bigger problem, especially to students. For Dal, this involves over 15,000 students.

Suppose that Dal was willing to provide money to ease the problem of getting students from

home to class on time though the city tra_c. For each of prevention, avoidance, and detection and recovery, outline and explain a solution to this problem or indicate why that solution approach is not applicable to the problem.

Answer:

In the event that a framework does not utilize either a deadlock-prevention or a deadlock-avoidance algorithm, then a deadlock situation may happen. In nature’s domain, the framework must give:

An algorithm that inspects the state of the framework to figure out if a deadlock has happened

An algorithm to recuperate from the deadlock

Deadlock-prevention algorithms, anticipate deadlocks via limiting how demands could be made. The restrictions guarantee that no less than one of the fundamental conditions for deadlock can’t happen and, subsequently, that deadlocks can’t hold. Conceivable reactions of counteracting deadlocks by this strategy, on the other hand, are low gadget usage and decreased framework throughput.

An option technique for keeping away from deadlocks is to require extra data about how assets are to be asked. For instance, in a framework with one tape drive and one printer, we may be told that process P will ask for first the tape drive, and later the printer, before discharging both assets. Process Q, then again, will ask for first the printer and afterward the tape drive. With this learning of the complete grouping of appeals and discharges for each one methodology, we can choose for each one appeal whether the procedure ought to hold up so as to dodge a conceivable future deadlock. Each one solicitation obliges that in settling on this choice the framework consider the assets at present accessible, the assets as of now designated to each one methodology, and the future demands and arrivals of each one procedure. The different algorithms contrast in the sum and kind of data needed. The least complex and most helpful model obliges that each one procedure announce the greatest number of assets of each one sort that it may require. Given this from the earlier data, it is conceivable to build an algorithm that guarantees that the framework will never enter a deadlocked state. Such an algorithm characterizes the deadlock-avoidance approach. A deadlock-avoidance algorithm dynamically looks at the asset portion state to guarantee that a round hold up condition can never exist

code.c

#include <stdio.h>
#include <conio.h>
int main()
{
    int MaxVal[20][20], need[20][20], alloc[20][20], avail[20], completed[20], safeSequence[20];
    int p, r, i, j, process, count;
    count = 0;
    printf("Enter the Number  of processes in current Program : ");
    scanf("%d", &p);
    for(i = 0; i< p; i++)
        completed[i] = 0;
    printf("\n\nEnter the Number  of resources current Program  : ");
    scanf("%d", &r);
    printf("\n\nEnter the Maximum Number of  context for each process current Program: ");
    for(i = 0; i < p; i++)
    {
        printf("\nFor Number of process %d : ", i + 1);
        for(j = 0; j < r; j++)
            scanf("%d", &MaxVal[i][j]);
    }
    printf("\n\nEnter the allocation for each process current Program: ");
    for(i = 0; i < p; i++)
    {
        printf("\nFor Number of  process current Program %d : ",i + 1);
        for(j = 0; j < r; j++)
            scanf("%d", &alloc[i][j]);  
    }
    printf("\n\nEnter the Available Resources current Program: ");
    for(i = 0; i < r; i++)
        scanf("%d", &avail[i]); 
    for(i = 0; i < p; i++)
        for(j = 0; j < r; j++)
            need[i][j] = MaxVal[i][j] - alloc[i][j];
    do
    {
        printf("\n Max context:\tAllocation context in current Program:\n");
        for(i = 0; i < p; i++)
        {
            for( j = 0; j < r; j++)
                printf("%d  ", MaxVal[i][j]);
            printf("\t\t");
            for( j = 0; j < r; j++)
                printf("%d  ", alloc[i][j]);
            printf("\n");
        }
        process = -1;
        for(i = 0; i < p; i++)
        {
            if(completed[i] == 0)
            {
                process = i ;
                for(j = 0; j < r; j++)
                {
                    if(avail[j] < need[i][j])
                    {
                        process = -1;
                        break;
                    }
                }
            }
            if(process != -1)
                break;
        }
        if(process != -1)
        {
            printf("\nProcess %d runs to completion!", process + 1);
            safeSequence[count] = process + 1;
            count++;
            for(j = 0; j < r; j++)
            {
                avail[j] += alloc[process][j];
                alloc[process][j] = 0;
                MaxVal[process][j] = 0;
                completed[process] = 1;
            }
        }
    } while(count != p && process != -1);
    if(count == p)
    {
        printf("\nThe system is in a dispalying with out warning message !!\n");
        printf("Safe Sequence : < ");
        for( i = 0; i < p; i++)
            printf("%d  ", safeSequence[i]);
        printf(">\n");
    }
    else
        printf("\nThe system is in an Exception ");
    getch();
}

Problem 3:

Design and implement a multi-threaded program with a critical section.

critical.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <make_puzzle.h>

#define NUM_THREADS 5

typedef struct _thread_data_t {
    int tid;
    double stuff;
} thread_data_t;
double shared_x;
pthread_mutex_t lock_x;

void *thr_func(void *arg) {
    thread_data_t *data = (thread_data_t *)arg;
    printf("Main Thread from thr_func, thread id: %d\n", data->tid);
    pthread_mutex_lock(&lock_x);
    shared_x += data->stuff;
    printf("x = %f\n", shared_x);
    pthread_mutex_unlock(&lock_x);
    pthread_exit(NULL);
}

int main(int argc, char **argv) {
    pthread_t thr[NUM_THREADS];
    void place_words( wordlist *the_words, grid *the_grid, unsigned int seed ) ;
    int i, rc;
    thread_data_t thr_data[NUM_THREADS];
    shared_x = 0;
    pthread_mutex_init(&lock_x, NULL);
    for (i = 0; i < NUM_THREADS; ++i) {
        thr_data[i].tid = i;
        thr_data[i].stuff = (i + 1) * NUM_THREADS;
        if ((rc = pthread_create(&thr[i], NULL, thr_func, &thr_data[i]))) {
            fprintf(stderr, "Error: pthread_create, rc: %d\n", rc);
            return EXIT_FAILURE;
        }
    }
    for (i = 0; i < NUM_THREADS; ++i) {
        pthread_join(thr[i], NULL);
    }
    return EXIT_SUCCESS;
}

The following is a sample C programming homework, so if you need C programming homework help check out this porgram.

Shell & C programming

  1. Write a script that monitors the creation of .pdf or .PDF files in the current directory. Every minute it should display a list of those filenames created after the previous display. Note: the script should run forever.
  2. A telephone directory, teledir.txt, maintains records in the form name:number where number is of the form nnn-nnn-nnnn. Devise a shell script that accepts one or two arguments that could be – the name or number. If it exists in the directory, then the list should be displayed. – both. The entry is displayed if it exists and is added if it doesn’t exist in he file.You can assume that the user will enter the number as nnn-nnn-nnnn. No need to do any error checking. Also, let us keep the name simple too – assume it is just one word like “Veerasamy”
  3. Write a perl script that accepts a filename and the delimiter string as arguments, and then displays each line with its fields reversed.
  4. Write a C program to split the contents of a file specified as argument into multiple files so that each file contains at most 10000 bytes. Name the files foo.1, foo.2 and so forth if foo is the argument.
  5. Write a C program to create a family tree of tasks from the main task (total 8 tasks, A is parent of B and C, B is parent of D and C is the parent of E, F, G and H) using fork() as many times as needed. Achieve just this configuration – no need to use exec() etc.

ABCDEFGH

q1.sh

#!/bin/sh

files=''
while true
do
  newfiles=`ls *.pdf *.PDF 2> /dev/null`
  for file in $newfiles
  do
    if ! [[ $files =~ $file ]]; then
      echo $file
    fi
  done
  files=$newfiles
  sleep 60
done

q2.sh

#!/bin/sh

if [ $# -eq 1 ]; then
  grep "^$1:" teledir.txt
  grep ":$1$" teledir.txt
elif [ $# -eq 2 ]; then
  exist=`grep "^$1:$2$" teledir.txt`
  if [ "$exist" == "" ]; then
    echo "$1:$2" >> teledir.txt
  else
    echo $exist
  fi
fi

q3.pl

#!/opt/local/bin/perl
# the above is the default position for perl
# please modify it, if it is not the one on your machine

my $file = $ARGV[0];
my $delimiter = $ARGV[1];
open (FIN, "< $file");
while (<FIN>) {
  my @array = split ($delimiter, $_.chomp);
  print join ($delimiter, reverse (@array)), "\n";
}
close (FIN)

q4.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[]) {
  int fin;
  int fout;
  char bytes[10000];
  char filename[256];
  int i;

  if (argc != 2) {
    fprintf (stderr, "Usage: %s <file>\n", argv[0]);
    return -1;
  }

  /* open the file to read */
  fin = open(argv[1], O_RDONLY);

  for (i = 1; ; i++) {
    ssize_t size;

    snprintf (filename, sizeof(filename), "%s.%d", argv[1], i);
    size = read(fin, bytes, sizeof(bytes));
    if (size <= 0) { /* end of file or error break */
      break;
    }

    /* otherwise write into the new file */
    fout = open (filename, O_WRONLY | O_CREAT, 0777);
    if (fout == -1) {
      fprintf (stderr, "Error: could not create new file.\n");
      break;
    }
    write (fout, bytes, size);
    close (fout);
  }

  close (fin);
}

q5.c

#include <stdio.h>
#include <unistd.h>

int main() {
  int child;
  int i;

  printf ("A\n");
  child = fork();

  if (child == -1) {
    fprintf (stderr, "Error: could not create child process.\n");
    return -1;
  }

  if (child == 0) {
    /* B */
    printf ("B\n");
    child = fork();
    if (child == -1) {
      fprintf (stderr, "Error: could not create child process.\n");
      return -1;
    }
    if (child > 0) {
      /* D */
      printf ("D\n");
    }
  } else {
    /* A */
    child = fork();
    if (child == -1) {
      fprintf (stderr, "Error: could not create child process.\n");
      return -1;
    }
    if (child == 0) {
      /* C */
      printf ("C\n");
      for (i = 0; i < 4; i++) {
        child = fork();
        if (child == -1) {
          fprintf (stderr, "Error: could not create child process.\n");
          return -1;
        }
        if (child == 0) {
          printf ("%c\n", i + 'E');
          return 0;
        }
      }
    }
  }

}

This is one of the more complex assignments we have to help with, since it involves multiple processes and communications with pipes and only runs under Linux (Windows does not support fork, which is used in this).

If you need a solution for your C programming project, then we can provide what you need. See the following example C programming assignment help.

Simple Shell

Your task is to implement a basic shell that allows the execution of command line tools and programs. Since you have learnt about shell programming, and how to work with processes and pipes, it is time to put all of this together and write your own mini‐shell that can “almost” give bash a run for its money.

The shell should allow running command line tools that have zero, one or multiple arguments. As we’ve seen in the lectures, commands can be non‐builtin (a process is spawned to execute them), and built‐in (no process is spawned). Your shell should allow for both types of commands.

To simplify your task, from the built‐in commands, you only have to support “cd” and “exit”.

The exit command will exit the current shell session, and the cd command will be used to change the current working directory. The cd command takes both relative and absolute (relative to root) paths. For example:

$ cd csc209/assignments $ cd /home/bogdan/csc209/assignments/
$ cd ../john/csc209/assignments/
$ cd ../../home/jane/csc209/assignments/

You do NOT have to support anything involving expanding environment variables, or special symbols, e.g.,

$ cd \$HOME/csc209/assignments/
$ cd \~/csc209/assignments/
$ cd \~bogdan/csc209/assignments/

A shell generally supports a set of special operators, for various purposes like background execution or chaining commands:

  • The ‘&’ operator sends a process to the background, as we’ve seen in lectures. However, it can be used to run 2 commands in parallel as well. For example, the following commands will be run in parallel:
$ ls & whoami
  • The ‘;’ operator allows running multiple commands in one run, sequentially, e.g.,
$ ls ; whoami ; pwd 

+ The ‘&&’ operator is a logical AND operator that lets you execute a second command, if the first command runs successfully (the exit status of the first command is zero), e.g., \$ mkdir newfolder && cp file.txt newfolder/

(If the mkdir is successful, copy a file into it)

  • The ‘||’ operator is a logical OR operator that lets you execute a second command, if the first command fails (the exit status of the first command is greater than zero), e.g.,
$ [ ‐d newfolder ] || mkdir newfolder

(If the folder “newfolder” does not exist, create it)

  • The ‘|’ (pipe) operator allows chaining of commands, by sending the output of the first command as an input to a second command. You’ve already seen a few examples of this one in the lectures and labs.

Out of these special operators, for the scope of this assignment, you will only have to implement the pipe ‘|’ operator.

Your shell should allow both simple commands, which do not contain such operators, AND complex commands that can contain any number of simple commands, chained using pipe operators.

Optionally, you are welcome to try implementing more operators, although there are certain tricky aspects you will have to be careful about, in order to correctly implement their semantics, as well as their priority order (from most to least priority: ‘|’, ‘&&’ and ‘||’, ‘&’, ‘;’ ).

Your shell should also support the following redirection operators:

  • Redirection of standard input from a file: ‘<‘
  • Redirection of standard output to a file: ‘>’
  • Redirection of standard error to a file: ‘2>’
  • Redirection of both output and error: ‘&>’ You do NOT have to support redirection in append‐mode (‘>>’ and ‘2>>’), but you are welcome to extend your shell with these options as well if you wish.

Your shell does NOT have to support the following things (although you are welcome to extend your shell to make it more awesome, if you wish):

  • Expanding environment variables (preceded by $), such as in paths to commands or files.
  • Defining new environment variables for the current shell session.
  • Wildcard substitution, e.g., ls *.txt
  • Double quotes, single quotes, and back quotes Starter code

The starter code provided will guide you step by step in solving this assignment. The parser.c and parser.h files deal with parsing commands and storing them in data structures (see below) that allow you to handle them easier. The shell.c and shell.h files contain functions that process commands and execute them accordingly. You should read the code and understand it, then proceed to fill in all the “TODO” spots left for you to implement.

Basic Structures

Simple commands are stored in the following structure:

typedef struct simple_command_t {
    char *in, *out, *err; /* Files for redirection, optional */
    char **tokens; /* Program and its parameters */
    int builtin; /* Builtin commands, e.g., cd */
} simple_command;

This is used for simple commands, involving a single program and no pipe chaining (or other special operators). A simple command can use redirection – in, out and err are strings containing the names of the files where the corresponding redirection should occur (stdin should be redirected to the file whose name is stored in “in”, stdout redirected to the file whose name is stored in “out”, etc.).

The builtin flag indicates whether the command is builtin or non‐builtin. The values for the “builtin” flag are: 0 (non‐builtin), 1 for “cd”, and 2 for “exit”. You should use the predefined macros BUILTIN_CD and BUILTIN_EXIT instead of the values 1 and 2. This way your code can benefit from more clarity.

The “tokens” are basically the words in the command, followed by a NULL token. For example, “ls ‐l” will be split into 2 tokens “ls” and “‐l”, followed by a NULL pointer. The command “ls –l | wc ‐l” will contain 5 tokens (parsing out all whitespaces, like blankspace and tab), followed by a NULL token.

Hint: the NULL token simplifies your task when using execlp and execvp calls).

Complex commands are defined in the following structure “command”:

typedef struct command_t {
    struct command_t *cmd1, *cmd2; /* Two commands chained using a
    special operator;
    In turn, each one can contain multiple commands itself; */
    simple_command* scmd; /* Simple command, no pipe */

    char oper[2]; /* In this assignment, consider only "|". Optionally,
    implement others: ";", "&&", etc. */ } command;

This is a general form of a complex command. A complex command can be simple (no special operators), in which case scmd is non‐NULL, or it can be complex in which case scmd will be NULL.

If it is a complex command, cmd1 and cmd2 are the two chained commands. In turn, cmd1 and cmd2 can be complex commands themselves. In the starter code, you are given a hint on how to deal with them by using recursion. The “oper” string is dedicated for storing the special operator chaining cmd1 and cmd2.

Try to first go through the code, and to understand the provided starter code. Next, try to follow the detailed instructions in the code, and fill in the code in the spots we left for you to complete, indicated by “TODO” comments.

In this assignment, you are NOT allowed to use the “popen” or “system” functions.

For file I/O, redirection and processes, you are working at a low level using file descriptors.

Consequently, you should only use POSIX functions, like open and close. Functions such as fopen or fclose are not to be used in this assignment.

Appendix. Sample shell commands you should try (just a few examples)

$ pwd
$ ls ‐l
$ ls ‐l > file.out
$ cat file.out
$ ls ‐l | wc –l
$ ls ‐l | grep file1
$ ls ‐l > filelist.out | wc ‐l
$ cat filelist.out
$ ls ‐l | wc ‐l > wc.out
$ cat wc.out
$ ls –l > ls.out | wc –l > wc.out
$ cat ls.out
$ cat wc.out
$ rm –f ls.out wc.out
$ ls ‐l | grep bogdan | wc –l
$ ps aux | grep bogdan | grep bash | grep –v grep | wc –l
$ ls –l | grepp bogdan
(should say something like: "grepp: No such file or directory")
$ lsa –l | grep bogdan
(similarly, regarding mistyped "lsa" command)

Also, make sure to run several commands involving ‘cd’, as shown in the handout (only the ones you should implement).

Once you are done testing these and more, one cool thing you could try is to run your shell program within your own shell. This way you can create a new shell session as a child process of your own shell.

$ ./shell
/home/bogdan> ./shell
/home/bogdan> echo Woo‐hoo, new shell within the shell
Woo‐hoo, new shell within the shell
/home/bogdan> exit
/home/bogdan> echo I am back to the parent shell
I am back to the parent shell
/home/bogdan> exit
$

After the second “exit”, you exit the parent shell as well, so you are back to your regular terminal prompt.

makefile

build:
    gcc shell.c parser.c -o shell -g -Wall

clean:
    rm -f shell

parser.h

#ifndef __PARSER_H__
#define __PARSER_H__

#include "shell.h"

/* Determine if a token is a special operator (like '|') */
int is_operator(char *token);

/* Determine if a command is builtin */
int is_builtin(char *token);

/* Determine if a path is relative or absolute (relative to root) */
int is_relative(char* path);

/* Determine if a command is complex (has an operator like pipe '|') */
int is_complex_command(char **tokens);

/* Parse a line into its tokens */
void parse_line(char *line, char **tokens);

/* Extract redirections of stdin, stdout, or stderr */
int extract_redirections(char** tokens, simple_command* cmd);

/* Construct command */
command* construct_command(char** tokens);

/* Release resources */
void release_command(command *cmd);

/* Print command */
void print_command(command *cmd, int level);

#endif

parser.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "parser.h"
#include "shell.h"

/* Determine if a token is a special operator (like '|') */
int is_operator(char *token) {
    /** 
     * Optional: edit this if you wish to parse other operators
     * like ";", "&&", etc.
     */
    return (strcmp(token, "|") == 0);
}

/* Determine if a command is builtin */
int is_builtin(char *token) {
    /** 
     * Optional: Edit this if you wish to handle more builtin commands 
     */
    return (strcmp(token, "cd") == 0 || strcmp(token, "exit") == 0);
}

/* Determine if a path is relative or absolute (relative to root) */
int is_relative(char* path) {
    return (path[0] != '/'); 
}

/* Determine if a command is complex (has an operator like pipe '|') */
int is_complex_command(char **tokens) {

    int i = 0;
    while(tokens[i]) {      
        if (is_operator(tokens[i]))
            return 1;
        i++;
    }   
    return 0;
}

/* Parse a line into its tokens/words */
void parse_line(char *line, char **tokens) {

    while (*line != '\0') {
        /* Replace all whitespaces with \0 */
        while (*line == ' ' || *line == '\t' || *line == '\n')
            *line++ = '\0';

        /* Store the position of the token in the line */
        *tokens++ = line;
        //printf("token: %s\n", *(tokens-1));

        /* Ignore non-whitespaces, until next whitespace delimiter */
        while (*line != '\0' && *line != ' ' && 
               *line != '\t' && *line != '\n') 
            line++;                 
    }
    *tokens = '\0';
}

int extract_redirections(char** tokens, simple_command* cmd) {

    int i = 0;
    int skipcnt = 0;

    while(tokens[i]) {

        int skip = 0;       
        if (!strcmp(tokens[i], ">")) {
            if(!tokens[i+1])
                return -1;
            cmd->out = tokens[i+1];
            skipcnt += 2;
            skip = 1;
        }           
        if (!strcmp(tokens[i], "<")) {
            if(!tokens[i+1])
                return -1;
            cmd->in = tokens[i+1];
            skipcnt += 2;
            skip = 1;
        }           
        if (!strcmp(tokens[i], "2>")) {
            if(!tokens[i+1])
                return -1;
            cmd->err = tokens[i+1];
            skipcnt += 2;
            skip = 1;
        }
        if (!strcmp(tokens[i], "&>")) {
            if(!tokens[i+1])
                return -1;
            cmd->out = tokens[i+1];
            cmd->err = tokens[i+1];
            skipcnt += 2;
            skip = 1;
        }

        if(skip)   i++;

        i++;
    }

    if(skipcnt == 0) {
        cmd->tokens = tokens;
        return 0;
    }

    cmd->tokens = malloc((i-skipcnt+1) * sizeof(char*));

    int j = 0;
    i = 0;
    while(tokens[i]) {
        if (!strcmp(tokens[i], "<") ||
            !strcmp(tokens[i], ">") ||
            !strcmp(tokens[i], "2>") ||
            !strcmp(tokens[i], "&>"))
            i += 2;
        else
            cmd->tokens[j++] = tokens[i++];
    }
    cmd->tokens[j] = 0;

    return 0;
}

/* Construct command */
command* construct_command(char** tokens) {

    /* Initialize a new command */  
    command *cmd = malloc(sizeof(command));
    cmd->cmd1 = cmd->cmd2 = NULL;
    cmd->scmd = NULL;

    if (!is_complex_command(tokens)) {

        /* Simple command */
        cmd->scmd = malloc(sizeof(simple_command));

        if (is_builtin(tokens[0])) {
            cmd->scmd->builtin = 1;
            cmd->scmd->tokens = tokens;
        }
        else {
            cmd->scmd->builtin = 0;
            int err = extract_redirections(tokens, cmd->scmd);
            if (err == -1) {
                printf("Error extracting redirections!\n"); 
                return NULL;
            }
        }
    }
    else {
        /* Complex command */

        char **t1 = tokens, **t2;
        int i = 0;
        while(tokens[i]) {
            if(is_operator(tokens[i])) {
                strncpy(cmd->oper, tokens[i], 2);
                tokens[i] = 0;
                t2 = &(tokens[i+1]);
                break;
            }
            i++;
        }

        /* Recursively construct the rest of the commands */
        cmd->cmd1 = construct_command(t1);
        cmd->cmd2 = construct_command(t2);
    }

    return cmd;
}

/* Release resources */
void release_command(command *cmd) {

    if(cmd->scmd)
        if(cmd->scmd->in || cmd->scmd->out || cmd->scmd->err) 
            free(cmd->scmd->tokens);

    if(cmd->cmd1)
        release_command(cmd->cmd1);

    if(cmd->cmd2)
        release_command(cmd->cmd2);     
}

/* Print command */
void print_command(command *cmd, int level) {

    int i;
    for(i = 0; i < level; i++)
        printf("  ");

    if(cmd->scmd) {

        i = 0;
        while(cmd->scmd->tokens[i]) { 
            printf("%s ", cmd->scmd->tokens[i]);
            i++;
        }

        if(cmd->scmd->in) 
            printf("< %s ", cmd->scmd->in);

        if(cmd->scmd->out) 
            printf("> %s ", cmd->scmd->out);

        if(cmd->scmd->err) 
            printf("2> %s ", cmd->scmd->err);

        printf("\n");
        return;      
    }

    printf("Pipeline:\n");

    if(cmd->cmd1)
        print_command(cmd->cmd1, level+1);

    if(cmd->cmd2)
        print_command(cmd->cmd2, level+1);

}

shell.h

#ifndef _SHELL_H
#define _SHELL_H

/* built-in commands */
#define BUILTIN_CD   1
#define BUILTIN_EXIT 2

typedef struct simple_command_t {
    char *in, *out, *err;    /* Files for redirection, optional */
    char **tokens;           /* Program and its parameters */
    int builtin;             /* Builtin commands, e.g., cd */
} simple_command;

typedef struct command_t {
    struct command_t *cmd1, *cmd2;  /* Two commands piped between each other; 
                     In turn, each one can contain multiple commands itself; */
    simple_command* scmd; /* Simple command, no pipe */
    char oper[2];   /* In this assignment, consider only "|".
                    Optional: implement other operators: ";", "&&", etc. */
} command;

#endif

shell.c

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <mcheck.h>

#include "parser.h"
#include "shell.h"

/**
 * Program that simulates a simple shell.
 * The shell covers basic commands, including builtin commands (cd and exit only),
 * standard I/O redirection and piping (|).
 *
 * You should not have to worry about more complex operators such as "&&", ";", etc.
 * Your program does not have to address environment variable substitution (e.g., $HOME),
 * double quotes, single quotes, or back quotes.
 */

#define MAX_DIRNAME 100
#define MAX_COMMAND 1024
#define MAX_TOKEN 128

/* Functions to implement, see below after main */
int execute_cd(char** words);
int execute_nonbuiltin(simple_command *s);
int execute_simple_command(simple_command *cmd);
int execute_complex_command(command *cmd);

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

    char cwd[MAX_DIRNAME];           /* Current working directory */
    char command_line[MAX_COMMAND];  /* The command */
    char *tokens[MAX_TOKEN];         /* Command tokens (program name, parameters, pipe, etc.) */

    while (1) {

        /* Display prompt */
        getcwd(cwd, MAX_DIRNAME-1);
        printf("%s> ", cwd);

        /* Read the command line */
        gets(command_line);

        /* Parse the command into tokens */
        parse_line(command_line, tokens);

        /* Empty command */
        if (!(*tokens))
            continue;

        /* Exit */
        if (strcmp(tokens[0], "exit") == 0)
            exit(0);

        /* Construct chain of commands, if multiple commands */
        command *cmd = construct_command(tokens);
        //print_command(cmd, 0);

        int exitcode = 0;
        if (cmd->scmd) {
            exitcode = execute_simple_command(cmd->scmd);
            if (exitcode == -1)
                break;
        }
        else {
            exitcode = execute_complex_command(cmd);
            if (exitcode == -1)
                break;
        }
        release_command(cmd);
    }

    return 0;
}

/**
 * Changes directory to a path specified in the words argument;
 * For example: words[0] = "cd"
 *              words[1] = "csc209/assignment2/"
 * Your command should handle both relative paths to the current
 * working directory, and absolute paths relative to root,
 * e.g., relative path:  cd csc209/assignment2/
 *       absolute path:  cd /u/bogdan/csc209/assignment2/
 * You don't have to handle paths containing "~" or environment
 * variables like $HOME, etc..
 */
int execute_cd(char** words) {

    /**
     * TODO:
     * The first word contains the "cd" string, the second one contains the path.
     * Check possible errors:
     * - The words pointer could be NULL, the first string or the second string
     *   could be NULL, or the first string is not a cd command
     * - If so, return an EXIT_FAILURE status to indicate something is wrong.
     */

    if (words == NULL)
        return EXIT_FAILURE;

    if (*words == NULL || (*words) + 1 == NULL)
        return EXIT_FAILURE;

    if (*words != NULL && strcmp(*words, "cd") != 0)
        return EXIT_FAILURE;

    /**
     * TODO:
     * The safest way would be to first determine if the path is relative
     * or absolute (see is_relative function provided).
     * - If it's not relative, then simply change the directory to the path
     * specified in the second word in the array.
     * - If it's relative, then make sure to get the current working directory,
     * append the path in the second word to the current working directory and
     * change the directory to this path.
     * Hints: see chdir and getcwd man pages.
     * Return the success/error code obtained when changing the directory.
     */

    char **path = words + 1;
    char pwd[1024];

    // get process working directory
    if (getcwd(pwd, 1024) == NULL)
        return EXIT_FAILURE;

    // if path is relative append it to a current working directory path
    if (is_relative(path)) {
        strcat(pwd, "/");
        strcat(pwd, *path);
    }
    // else copy path to a pwd
    else
        strcpy(pwd, *path);

    // execute chdir command and change process's working directory
    if (chdir(pwd) == -1) {
        perror(*path);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

/**
 * Executes a regular command, based on the tokens provided as
 * an argument.
 * For example, "ls -l" is represented in the tokens array by
 * 2 strings "ls" and "-l", followed by a NULL token.
 * The command "ls -l | wc -l" will contain 5 tokens,
 * followed by a NULL token.
 */
int execute_command(char **tokens)
{
    if (tokens == NULL || *tokens == NULL)
        return -1;

    // execute command
    if (execvp(*tokens, tokens) == -1) {

        // if there was and error send it to stderr
        char error_message[128];
        strcpy(error_message, *tokens);
        strcat(error_message, ": command failed to execute");
        perror(error_message);

        return -1;
    }

    return 0;
}

/**
 * Executes a non-builtin command.
 */
int execute_nonbuiltin(simple_command *s)
{
    if (s == NULL)
        return EXIT_FAILURE;

    // initialize file descriptors for in out and err files
    int inFileDescriptor = -1;
    int outFileDescriptor = -1;
    int errFileDescriptor = -1;

    if (s->in != NULL) {
        char processWorkDir[1024];

        // get process working directory
        if (getcwd(processWorkDir, 1024) != NULL) {
            // check if we have proper access permissions for file creation
            if (access(processWorkDir, R_OK | X_OK) != -1) {
                inFileDescriptor = open(s->in, O_RDONLY);

                if (inFileDescriptor != -1) {
                    // redirect stdin to an opened file
                    fcntl(inFileDescriptor, F_SETFD, 1);
                    dup2(inFileDescriptor, STDIN_FILENO);
                }
            }
        }
    }

    if (s->out != NULL) {
        char processWorkDir[1024];

        // get process working directory
        if (getcwd(processWorkDir, 1024) == NULL) {
            // check if we have proper access permissions for file creation
            if (access(processWorkDir, W_OK | X_OK) == -1) {
                outFileDescriptor = open(s->out, O_WRONLY);

                if (outFileDescriptor == -1) {
                    // redirect stdout to an opened file
                    fcntl(outFileDescriptor, F_SETFD, 1);
                    dup2(outFileDescriptor, STDOUT_FILENO);
                }
            }
        }
    }

    if (s->err != NULL) {
        char processWorkDir[1024];

        // get process working directory
        if (getcwd(processWorkDir, 1024) == NULL) {
            // check if we have proper access permissions for file creation
            if (access(processWorkDir, W_OK | X_OK) == -1) {
                errFileDescriptor = open(s->err, O_WRONLY);

                if (errFileDescriptor == -1) {
                    // redirect stderr to an opened file
                    fcntl(errFileDescriptor, F_SETFD, 1);
                    dup2(errFileDescriptor, STDERR_FILENO);
                }
            }
        }
    }

    /**
     * Finally, execute the command using the tokens
     * (see execute_command function provided)
     */

    execute_command(s->tokens);

    /**
     * Close all filedescriptors needed and return status generated
     * by the command execution.
     */

    // if any of the descriptos were used close it
    if (inFileDescriptor != -1)
        close(inFileDescriptor);
    if (outFileDescriptor != -1)
        close(outFileDescriptor);
    if (errFileDescriptor != -1)
        close(errFileDescriptor);
}

/**
 * Executes a simple command (no pipes).
 */
int execute_simple_command(simple_command *cmd) {
    pid_t pid;
    int status;
    int result = 0;

    /**
     *
     * Check if the command is builtin.
     * 1. If it is, then handle BUILTIN_CD (see execute_cd function provided)
     *    and BUILTIN_EXIT (simply return an appropriate exit status).
     */

    // check if it is a "cd" command
    if (cmd->builtin == BUILTIN_CD)
        status = execute_cd(cmd->tokens);

    else if (cmd->builtin == BUILTIN_EXIT)
        status = EXIT_SUCCESS;

    /**
     * 2. If it isn't, then you must execute the non-builtin command.
     * - Fork a process to execute the nonbuiltin command
     *   (see execute_nonbuiltin function provided).
     * - The parent should wait for the child.
     *   (see wait man pages).
     */
    else {
        switch (pid = fork())
        {
        case -1:
            status = -1;
            break;
        case 0:
            status = execute_nonbuiltin(cmd);
            break;
        default:
            waitpid(pid, &status, 0);
            break;
        }
    }

    return status;
}

/**
 * Executes a complex command - it can have pipes (and optionally, other operators).
 */
int execute_complex_command(command *c) {

    /**
     * Check if this is a simple command, using the scmd field.
     */

    // if simple command
    if (c->scmd != NULL)
        return execute_simple_command(c->scmd);

    /**
     * Optional: if you wish to handle more than just the
     * pipe operator '|' (the '&&', ';' etc. operators), then
     * you can add more options here.
     */

    if (!strcmp(c->oper, "|")) {
        int pfd[2];
        int pid1;
        int pid2;
        int READ_END = 0;
        int WRITE_END = 1;
        /**
         * Create a pipe "pfd" that generates a pair of file descriptors,
         * to be used for communication between the parent and the child.
         */
        if (pipe(pfd) == -1)
            exit(EXIT_FAILURE);

        switch (pid1 = fork()) {
        case -1:
            break;
        case 0:
        {
            /**
             * In the child:
             *  - close one end of the pipe pfd and close the stdout file descriptor.
             **/
            close(pfd[READ_END]);
            close(STDOUT_FILENO);

            /**  connect the stdout to the other end of the pipe (the one you didn't close). **/
            dup2(pfd[WRITE_END], STDOUT_FILENO); //  connect stdout to the other end of a pipe
            close(pfd[WRITE_END]);

            /**  execute complex command cmd1 recursively.**/
            execute_complex_command(c->cmd1);
            exit(EXIT_SUCCESS);
        }
            break;
        default:
        {
            /** In the parent:
              *fork a new process to execute cmd2 recursively.
              **/
            switch(pid2 = fork()) {
            case -1:
                break;
            case 0:
            {
                /** In child 2:
                *     close one end of the pipe pfd (the other one than the first child),
                *     and close the standard input file descriptor.
                **/
                close(pfd[WRITE_END]);
                close(STDIN_FILENO);

                /** connect the stdin to the other end of the pipe (the one you didn't close). **/
                dup2(pfd[READ_END], STDIN_FILENO);
                close(pfd[READ_END]);

                /** execute complex command cmd2 recursively. **/
                execute_complex_command(c->cmd2);
                exit(EXIT_SUCCESS);
            }
                break;
            default:
            {
                /**  - In the parent:
                *     - close both ends of the pipe.
                **/
                close(pfd[READ_END]);
                close(pfd[WRITE_END]);

                /** wait for both children to finish. **/
                waitpid(pid1, NULL, 0);
                waitpid(pid2, NULL, 0);
            }
                break;
            }
        }
            break;
        }
    }
    return 0;
}

 

We are taking care of those aspects and are constantly providing assistance for C programming assignment help. You can also seek our Expert Programming Tutorshelp in case of complex problems. Our experts also provide help with C programming assignment even after the programming project is delivered. All the work done by us and we provide help with C programming assignment and the content is guaranteed to be plagiarism free, don’t copy your friend’s solution, use our C programming project help.