1

I'm trying to get input from the user in the console, but I'm having problems with the function getline() in my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <memory.h>
#include <sys/wait.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    //check that the number of arguments given is valid
    if (argc != 2){
        printf("Error: arguments. \nThe file should only take one argument which is the name of the level\n");
        exit(1);
    }
    char test[5];
    int nb_lettres=strlen(argv[1]);
    strncpy(test,argv[1]+nb_lettres-4,4);
    //check that the file given is either a .tgz or a .tar
    test[4]='\0';
    if(strcmp(test,".tar")!=0 && strcmp(test,".tgz")!=0)
    {
        printf("Error: arguments. \nThe argument should be a file having the extension .tar or .tgz \n");
        exit(2);
    }
    int status; //START OF THE PART CONTAINING THE PROBLEM
    pid_t pid;
    //create the folder then move to it, then extract the tar
    if((pid=fork())!=0){
        if(fork()){
            execlp("mkdir","mkdir","leaSHdir",NULL);
        }
        //waiting to make sure we don't try to go in the folder before it's fully created
        wait(&status);
        execlp("tar","tar", "-xf", argv[1], "-C", "leaSHdir/",NULL);
    }
    waitpid(pid,&status,0);
    printf("Extracting the files..\n");
    sleep(1);   //END OF THE PART CONTAINING THE PROBLEM
    //Read the meta file
    FILE *file;
    chdir("./leaSHdir");

    file=fopen("meta","r");
    if (file==NULL){
        //  printf("Oh dear, something went wrong with read()! %s\n", strerror(errno));
        printf("Error: meta. \nImpossible to read the meta file. Please check that it does exist (without looking in, vile cheater)\n");
        exit(3);

}
char *line=NULL;
size_t len=0; 
        //Saving the commands which will be used by the user
char *result=NULL;
char **commands = malloc(5 * sizeof *commands);
int i=0;
if(commands==NULL){
    printf("Error: memory. \nA problem occured with the memory while creating a pointer\n");
    exit(4);
}
while(getline(&line,&len,file)!=-1){
    if(strstr(line,"$")!=NULL){
        commands[i]=(malloc(strlen(line)));
        strcpy(commands[i],line+2); 
                    //if the array is full,we add space for the next incoming command
        if(i >= 4){
            commands=realloc(commands,sizeof *commands *(i+2));
        }
        i++;
    }
    if(line[0]=='>'){
        result=malloc(strlen(line));
        strcpy(result,line+2);
        }
}
int a = 0;
for (a = 0;a<i;a++){
    printf("%s",commands[a]);
}
printf("%s",result);
printf("Erasing meta..");
unlink("meta");
printf("meta erased.\n");
int c;

    while((c = getchar()) != '\n' && c != EOF){
        printf("rien\n");
    }
ssize_t r = getline(&line,&len,stdin); 
printf("%d '%s' %d",(int) r, line, c);
char machin[2555];
scanf("%s",machin);
printf("%s test",machin);
free(commands);
free(result);
return 0;
}   

When I execute this code, the last getline is completely skipped (the first one is working without any problem), which I don't see why. I've also tried using different functions (fgets, scanf) and both were also skipped. Thanks in advance for any help which can be provided :)

Edit: Changed the faulty getline line with ssize_t r = getline(&line,&len,stdin); printf("%d '%s' %d",(int) r, line, c);, here's the result:

cat
ls
man
Bravo! C'est ici!//this line and the 3 other lines before are the lines read by the first getline which is working
Erasing meta..meta erased.
-1 '> Bravo! C'est ici! 
' -1

So basically, I don't even have time to type anything, I get this result directly without entering anything. Also the content of line isn't changed after the second getline considering it still contains the result from the first getline. Edit 2 : Okay, I think I found from where comes the problem: Basically, it's from a part of the code i didn't put in the extract there because I though it was not related at all with my current problem, so I've edited the whole extract to put it fully. I've put two comments to mark the part containing the problem. Although I don't see what could be causing it, considering this part contains only forks. Although, sorry for the trouble, guys, should have put the whole code at the start

Last edit: Figured out what was the problem: if((pid=fork())!=0){ which means that once my forks ended I was working on the child process and not on the father as I thought. Once I've changed it to if((pid=fork())==0){ everything worked fine. Thanks for the help :)

jilako
  • 65
  • 1
  • 2
  • 7
  • [Don't cast the result of malloc (and friends)](http://stackoverflow.com/q/605845). – Deduplicator Oct 30 '14 at 15:52
  • Suggest: Change `char c;` -- > `int c;` so the `EOF` difference is maintained from the other 256 values returned from getchar(). – chux - Reinstate Monica Oct 30 '14 at 15:57
  • Change `getline(&line,&len,stdin); printf("%s",line);` to `if (getline(&line,&len,stdin) > 0); printf("%s",line);` If EOF occurs, there is nothing to print. – chux - Reinstate Monica Oct 30 '14 at 16:01
  • @Deduplicator that question really needs an update. What has been said in 2009 isn't totally true today. – Igor Pejic Oct 30 '14 at 16:04
  • 1
    @Igor: It's still true that explicitly casting a `void*` is at best bad style, a waste of time and unneccessary clutter for readers. Also, the advice to use `sizeof` on expressions/variables instead of types also still holds. What exactly should be "updated"? – Deduplicator Oct 30 '14 at 16:08
  • What text are you inputting such that "the last getline is completely skipped"? IOW, what do you see that resulted in concluding that getline is completely skipped? Suggest using `ssize_t r = getline(&line,&len,stdin); printf("%d '%s' %d",(int) r, line, c);` to get a better idea of what is happening in the end. – chux - Reinstate Monica Oct 30 '14 at 16:24
  • @Deduplicator The second point of the highest upvoted answer is rubbish: "It can hide an error, if you forgot to include . This can cause crashes, in the worst case." – Igor Pejic Oct 30 '14 at 16:36
  • @Igor: Depends on your warning/error settings. If you use sane settings (`-Wall -Wextra -pedantic` + standard-selection), implicit declaration will certainly ring the warning bell. Doesn't make itwrong though. – Deduplicator Oct 30 '14 at 16:40
  • @Deduplicator Won't it ring the warning bell even if you don't have those settings on the newest gcc compiler? – Igor Pejic Oct 30 '14 at 16:47
  • 1
    @Igor: There's not only gcc, and certainly not only the newest version. And I don't know (Though I suspect yes). – Deduplicator Oct 30 '14 at 16:49
  • @Deduplicator Removed the casts on the mallocs, thanks for telling me about the fact it's bad to do it :) – jilako Oct 30 '14 at 16:55
  • @Deduplicator Yes, the newest gcc will. So the response (or at least a part of it) is outdated – Igor Pejic Oct 30 '14 at 16:57
  • 1
    @Igor: You are still ignoring the facts that 1) gcc is not the only compiler there is, and 2) the newest version is not all there is. Actually, sometimes (embedded mostly, though 16-bit desktop probably too), you can be forced to use a severly dated version, or even a completely different compiler. – Deduplicator Oct 30 '14 at 17:02
  • @jilako: Did you also see how you can use `sizeof` in a less error-prone way, by avoiding types? – Deduplicator Oct 30 '14 at 17:03
  • @Deduplicator You mean by replacing for exemple `malloc(sizeof(char)*strlen(line))` with `malloc(sizeof * strlen(line)` ? – jilako Oct 30 '14 at 17:10
  • @jilako For strings, use `malloc(strlen(line) + 1)`. Else consider style `commands = realloc(commands, N * sizeof *commands);`. – chux - Reinstate Monica Oct 30 '14 at 17:17
  • 1
    No, more like `malloc(1+strlen(line))`. (You don't use `sizeof` on `char`, because a char is never more or less than one char big). But, e.g. `T* x = malloc(count * sizeof*x)` instead of `T* x = malloc(count * sizeof(T))`. See the dereference I did? – Deduplicator Oct 30 '14 at 17:18
  • I see, though, what is the kind of error which could happen by using the types in it? – jilako Oct 30 '14 at 17:30
  • @jilako Using c_orrect_ types in `malloc()` is not a problem, but using @Deduplicator suggestion, code is, IMO, _less likely_ to have a mis-matched size calculation. Example `char **commands=malloc(sizeof(char**)*5);` should be `char **commands=malloc(sizeof(char*)*5);`, In this case, the `sizeof(char *)` is likely the same as `sizeof(char **)`, but by using `char **commands = malloc(5 * sizeof *commands);` is certainly correct, easier to code and to follow. – chux - Reinstate Monica Oct 30 '14 at 18:11

2 Answers2

1

[Edit]

After some edits:
OP's problem occurs when using stdin after fork().

Similar to Two processes reading the same stdin


Code is unable to take in additional input as stdin has all ready reached the EOF condition or a rare IO error. That is the -1 return values from getline() and getchar(). line simply retains its former contents as nothing was changed.

char c;
while((c = getchar()) != '\n' && c != EOF);
ssize_t r = getline(&line,&len,stdin); //getline not working
printf("%d '%s' %d",(int) r, line, c);

-1 '> Bravo! C'est ici!
' -1


Additional issue: realloc() too late.

strcpy(commands[5],line+2); writes to unallocated memory.

char **commands=malloc(sizeof(char**)*5);
int i=0;
...
// In loop, i becomes 5.
...
strcpy(commands[i],line+2); 
if (i > 5){
  commands=realloc(commands,sizeof(char**)*(i+2));
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I see, but how would I then able to get input from stdin, considering the last `getline` in the code is the only time where I try to get input from stdin? Isn't `while((c = getchar()) != '\n' && c != EOF);` supposed to flush the stdin? – jilako Oct 30 '14 at 17:34
  • 1
    Hmmm. Thinking code may be faulty in other places and the the `getline()` issues results from that. E.g. (in 2 places) `commands[i]=(malloc(sizeof(char)*strlen(line))); strcpy(commands[i],line+2);` is a problem as `line+2` could be beyond the end of `line`. I'll look more. IAC, `getline()` is working, it is just that `stdin` has read EOF. BTW: after seeing "meta erased.", do you type anything? Are you using redirected input? – chux - Reinstate Monica Oct 30 '14 at 18:08
  • @jilako Check appended answer. – chux - Reinstate Monica Oct 30 '14 at 18:23
  • I corrected the realloc so it happens earlier, also changed the mallocs following your and deduplicator's instructions, but the problem still happens. After "meta erased" appears on screen, i don't have time to type anything, it prompts me directly with the result from the next printf, then the program ends. – jilako Oct 30 '14 at 18:47
  • The `line+2` shouldn't be a problem because line is supposed to contain text only having this format "$ text" or "> text" (I know it's not really safe, but I'm mostly concerned by the problem with the `getline()` first) – jilako Oct 30 '14 at 18:50
  • I've edited my code so it contains the whole code,considering the problem seems to have come from a part I thought unrelated. It would seem the problem is caused by one of the two execlp in my forks, though I don't see how they can have effect on stdin.. – jilako Oct 30 '14 at 19:34
  • Are you using redirected input? – chux - Reinstate Monica Oct 30 '14 at 19:48
  • `char test[4]; ... test[4]='\0';` is bad as `test` is only 4 `char` long. Further `strncpy()` is not used correctly. Try char test[4+1]; strncpy(test, ..., 4); test[4]='\0';` – chux - Reinstate Monica Oct 30 '14 at 19:50
  • I've modified the variable test. As for redirected input, as far as i know, I'm not using any, but I'm not used to execs functions =/ – jilako Oct 30 '14 at 20:33
  • `char test[4]; ... test[5]='\0';` is bad. Suggest `char test[4+1]; ... test[4]='\0';` Recall index addressing is 0-based. – chux - Reinstate Monica Oct 30 '14 at 20:43
  • Woops, my bad there, changed wrong position, should be right now – jilako Oct 30 '14 at 20:47
  • Hmmm, Maybe your forking does not allow multiple processes to share `stdin`? – chux - Reinstate Monica Oct 30 '14 at 20:54
  • Would seem to be the problem, if I fork , with no instructions but an exit(0) in the children, the problem do happen, but if I comment the whole part with the forks, no problem.. Now I'll have to figure out either how to do it with no fork, or a way to keep stdin working with my forks.. Thanks for the help :) – jilako Oct 30 '14 at 21:06
0

Put somtehing before getilne to consume the trailing '\n' character. For example:

while ( getchar() != '\n' );
getline(&line,&len,stdin); 
Igor Pejic
  • 3,658
  • 1
  • 14
  • 32
  • Code may need to check against `'\n'` and `EOF` to avoid infinite loop with `while ( getchar() != '\n' );` – chux - Reinstate Monica Oct 30 '14 at 15:54
  • Thing is there is already a `while((c = getchar()) != '\n' && c != EOF);` just before, the getline, and it's still getting skipped. I also tried putting a `if` on the `getline` , didn't change anything. – jilako Oct 30 '14 at 16:08