ZCode
ZCode

Reputation: 11

Exec given string variable of args with inputted args in c

So for this program I am asked to input the names of several programs that will be used in each child processes that will link up to pipes

Create pipes, create childs, parent closes pipe ends and wait for childs to finish

I am sure the fork,child, and pipe are fun but if you want to critique go ahead but the focus is mainly on the exec.

However this is just a small part of my code that I have issue with before creating several more childs and modifying the pipes. I came across an error in my exec that I wanted to resolve before continuing

myParent

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

    int i = 0;
    int max_line_length = 101;
    char * catprog = (char*) malloc(sizeof(char*));;
    char * datafile = (char*) malloc(sizeof(char*));
    char * numstrings = (char*) malloc(sizeof(char*));
    int nstrings;


    if(argc != 1){
      printf("%s \n", "This takes no command line arguments");
      exit(EXIT_FAILURE);
    }
    printf("%s ","Filename of spliter program:");
    fgets(catprog, max_line_length, stdin);
    printf("%s ","File name of data file:");
    fgets(datafile, max_line_length, stdin);
    printf("%s ","Number of strings to sort:");
    fgets(numstrings, max_line_length, stdin);

    if(!(nstrings = atoi(numstrings))){
      printf("%s \n","This is an invalid number.");
      exit(EXIT_FAILURE);}
    int halfnstrings = nstrings/2;

pid_t pid1 = fork();
    if(pid1 < 0){ //failed fork1
      fprintf(stderr, "Fork 1 failed\n");
      exit(EXIT_FAILURE);
    }
    else if(pid1 == 0){//child 1
      char start[max_line_length];
      char end[max_line_length];
      sprintf(start, "%d", 1);
      sprintf(end, "%d", halfnstrings);
      printf("%s",catprog); //checking if catprog is correct
      printf("%s",datafile); //checlomg of datafile
    if((execlp(catprog, catprog, datafile, start, end, NULL)) == -1){
      printf("%s \n","Cannot exec");
      exit(EXIT_FAILURE);
    }
    _exit(EXIT_SUCCESS);
    }
    else{
      wait(NULL);
      exit(EXIT_SUCCESS);
}
return 0;
}

myCat

int main(int argc, char * argv[]){
    int startline;
    int endline;
    int max_line_length = 101;

    if(argc != 4){
     printf("%s \n", "Format is: myCat <data_file> <start_line> <end_line>");
     exit(EXIT_FAILURE);
    }

    if(!(startline = atoi(argv[2]))){
       printf("%s \n","Invalid Number");
       exit(EXIT_FAILURE);
    }
    endline=atoi(argv[3]);
    if(!(endline) || endline<startline){
       printf("%s \n","Invalid Number");
       exit(EXIT_FAILURE);
    }

    path = argv[1];
     if(NULL == (pdata=fopen(path,"r"))){
      printf("%s \n", "Cannot open file.");
      exit(EXIT_FAILURE);
     }

So when I compile this and run it prints out cannot exec hence I believe that i did not input exec properly.

The inputs are:

split program is: myCat

merge program is: myMerge

data file: data.txt

num of strings: whatever number of strings to sort

myCat works by taking the data file, start line, and end line: myCat data.txt 2 7

myCat takes data file and from the start line reads it until it reaches the end line and outputs the string lines read

So my question is does white space affect execl?

Upvotes: 0

Views: 49

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 754570

There are numerous problems, most of them outlined in my comments. Here's workable code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int max_line_length = 101;
    char *catprog = (char *)malloc(max_line_length);
    char *datafile = (char *)malloc(max_line_length);
    char *numstrings = (char *)malloc(max_line_length);
    int nstrings;

    if (argc != 1)
    {
        fprintf(stderr, "%s: %s\n", argv[0], "this command takes no command line arguments");
        exit(EXIT_FAILURE);
    }

    printf("%s ", "Filename of splitter program:");
    fgets(catprog, max_line_length, stdin);
    printf("%s ", "File name of data file:");
    fgets(datafile, max_line_length, stdin);
    printf("%s ", "Number of strings to sort:");
    fgets(numstrings, max_line_length, stdin);

    catprog[strcspn(catprog, "\n")] = '\0';
    datafile[strcspn(datafile, "\n")] = '\0';

    if (!(nstrings = atoi(numstrings)))
    {
        fprintf(stderr, "%s: %s %s\n", argv[0], numstrings, "is an invalid number.");
        exit(EXIT_FAILURE);
    }
    int halfnstrings = nstrings / 2;

    pid_t pid1 = fork();
    if (pid1 < 0)
    {
        fprintf(stderr, "%s: fork failed\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    else if (pid1 == 0)
    {
        char start[max_line_length];
        char end[max_line_length];
        sprintf(start, "%d", 1);
        sprintf(end, "%d", halfnstrings);
        printf("catprog: [%s]\n", catprog);
        printf("datafile: [%s]\n", datafile);
        printf("start: [%s]\n", start);
        printf("end: [%s]\n", end);
        execlp(catprog, catprog, datafile, start, end, NULL);
        fprintf(stderr, "%s: %s %s\n", argv[0], "Cannot exec", catprog);
        exit(EXIT_FAILURE);
    }
    else
    {
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("PID %d exited 0x%.4X\n", corpse, status);
        exit(EXIT_SUCCESS);
    }
    return 0;
}

Note that error messages should be written to standard error and not standard output. They're errors; that's what stderr is for! Also, messages should identify the name of the program that created them; that's why they all contain argv[0] as the first part of the message. Spaces before newlines are sloppy coding.

Note that it is crucial to remove the newlines that fgets() carefully retains. Also note that you should error check the calls to fgets() (and indeed the calls to malloc() — another reason for using regular arrays is that you don't have to add that error checking). You can find my preferred error reporting mechanisms on GitHub (https://github.com/jleffler/soq/tree/master/src/libsoq) in the files stderr.h and stderr.c. That code reduces each error report to one line instead of four, which makes it less onerous and therefore less likely to be skipped.

Note the use of square brackets around the printed strings. They make it easier to spot problems (newlines, carriage returns, etc) in your data.

Example run

On my machine, al is a command that echoes its arguments, one per line. So, a sample run (of exec37) was:

$ ./exec37
Filename of splitter program: al
File name of data file: apocalypse-now.score
Number of strings to sort: 23
catprog: [al]
datafile: [apocalypse-now.score]
start: [1]
end: [11]
apocalypse-now.score
1
11
PID 93352 exited 0x0000
$

Using memory allocation for the arrays is not particularly sensible; you could write char catprog[max_line_length]; etc (and they wouldn't be variable length arrays (VLAs) if you make max_line_length into a compile-time integer constant — enum { max_line_length = 101 }; for example.

Upvotes: 1

Related Questions