Shell endlessly loops when EOF is entered C - c

I am a new C programmer who is attempting to create their own shell. The shell itself works well and processes my commands correctly, but when the user enters the EOF character as input into the command line, my shell just infinite loops. My code is posted below as well as what I've already attempted (I'm also new to using GDB and Valgrind but neither is seeming to help me locate the issue).
What I have tried already:
The current implementation below attempts to capture the return value of getline and handle the case where it returns -1 (when EOF is read). However this just causes the shell to endlessly loop the prompt
I replaced my function call completely with:
if (fgets(command_line, MAX_CANON, stdin) == NULL) {
printf("\nTo quit, please use the exit command: exit.\n");
}
To my knowledge the above replacement should handle an EOF character input by the user. However this implementation using fgets also causes an endless command prompt loop.
Below is my current implementation which is referred to in #1 above:
Function called in main to read input from user:
char *read_command_line(void)
{
//Declare an integer to hold the length of the string, a line to hold our output, and a variable getline can use to hold the generated buffer
int len;
char *line = NULL;
ssize_t bufsize = 0;
//Get the line from stdin
int retval = getline(&line, &bufsize, stdin);
if(retval == -1)
{
line = NULL;
return line;
}
//Determine the length of the line and set a null terminating byte to end the string and get rid of the trailing return
len = strlen(line);
line[len - 1] = '\0';
//Finally return the read in line
return line;
}
Beginning of my shell while loop where line is read in:
//BEGIN SHELL
while (go)
{
//Signals are handled in the main.c
//Print the prompt
char cwd_loop[max_buf_size];
getcwd(cwd_loop, sizeof(cwd_loop));
printf("\n%s [%s]:> ", prompt_prefix, cwd_loop);
commandline = read_command_line();
if(commandline == NULL)
{
continue;
}

From your code
commandline = read_command_line();
if(commandline == NULL)
{
continue;
}
If read_command_line returns a null pointer, which it does if there's an error like EOF, then you continue the loop, letting it iterate again. This time read_command_line will again return a null pointer, and you continue like that forever.
You should break out of the loop if read_command_line returns a null pointer.

You should not continue to prompt and read further input when the input stream has been closed as indicated by getline() returning -1 or fgets() returning NULL. Just break out of the loop as if an exit command had been entered.

Related

Multi line input from stdin using fgets()

I am trying to input something like this
line 1
this is line 2
now line 3
Using fgets and from the user stdin
My snippet:
char *line;
while ((line = fgets(text, BUF_SIZE, stdin)) != NULL) {
//save and process line
However, upon runnnig this I am stuck at stdin until I do Ctrl+C force quit.
How do I check the 2-enter key aka '\n' using this? so that the code can proceed to the next lines following the 'read from stdin section'
I am open to using getc or getline() if that is a simpler solution.
You can check for '\n' and break while loop
your while loop should look something like this
while ((line = fgets(text, 100, stdin)) != NULL) {
if(text[0] == '\n'){
break;
}
/* do your work here */
}
fgets returns NULL when it hits the end of file marker aka EOF. However, unlike a standard text file, stdin doesn't automatically encounter the EOF marker since there is no pre-designated end of file location that will be automatically run into like a standard text file. If you want to send the end of file marker to stdin, you have to type in a command unique to each operating system. For example, on windows you enter Crtl-Z to send EOF to stdin. Anytime you are stuck on something like this, try to look up the documentation of the function you're using (in this case fgets).
Also, there's no need to save the return value of fgets into "line". You can just compare the return value of the function directly to NULL like
while (fgets(text, BUF_SIZE, stdin) != NULL) {
// do stuff
}

Terminate C program which stdin is linked to output of other program

I have a program x, which I want to cleanly terminate.
You can run it by simply doing ./x and use the terminal to write lines to stdin directly and terminate it by writing exit.
However, if you use: cat file.txt | ./x, the stdin is now piped from the file and therefore you can never type exit.
The best way to end this program would be for it to automatically terminate once the last line was read from the file.
Alternatively, I'd like to re-route stdin back to the terminal if that is at all possible, to further allow manual input as before.
Here is some sample code:
int main() {
// ...
while (ongoing) {
size_t n = 0;
char* ln = NULL;
getline(&ln, &n, stdin);
strtok(ln, "\n");
strtok(ln, "\r");
if (strcmp("exit", ln) == 0) {
break;
}
//...
}
}
you're using getline to read your input. getline returns -1 on EOF. This makes it easy.
if (-1==getline(...))
break;
When you have read all the input from a pipe, EOF will be raised up to indicate that the full input has been reached.
In your example, this will be rougly equivalent with exit, so you can also check the return value of getline to see if the EOF has reached (in which case -1 will be returned).

C - Print lines from file with getline()

I am trying to write a simple C program that loads a text-file, prints the first line to screen, waits for the user to press enter and then prints the next line, and so on.
As only argument it accepts a text-file that is loaded as a stream "database". I use the getline()-function for this, according to this example. It compiles fine, successfully loads the text-file, but the program never enters the while-loop and then exits.
#include <stdio.h>
#include <stdlib.h>
FILE *database = NULL; // input file
int main(int argc, char *argv[])
{
/* assuming the user obeyed syntax and gave input-file as first argument*/
char *input = argv[1];
/* Initializing input/database file */
database = fopen(input, "r");
if(database == NULL)
{
fprintf(stderr, "Something went wrong with reading the database/input file. Does it exist?\n");
exit(EXIT_FAILURE);
}
printf("INFO: database file %s loaded.\n", input);
/* Crucial part printing line after line */
char *line = NULL;
size_t len = 0;
ssize_t read;
while((read = getline(&line, &len, database)) != -1)
{
printf("INFO: Retrieved line of length %zu :\n", read);
printf("%s \n", line);
char confirm; // wait for user keystroke to proceed
scanf("%c", &confirm);
// no need to do anything with "confirm"
}
/* tidy up */
free(line);
fclose(database);
exit(EXIT_SUCCESS);
}
I tried it with fgets() -- I can also post that code --, but same thing there: it never enters the while-loop.
It might be something very obvious; I am new to programming.
I use the gcc-compiler on Kali Linux.
Change your scanf with fgetline using stdin as your file parameter.
You should step through this in a debugger, to make sure your claim that it never enters the while loop is correct.
If it truly never enters the while loop, it is necessarily because getline() has returned -1. Either the file is truly empty, or you have an error reading the file.
man getline says:
On success, getline() and getdelim() return the number of
characters
read, including the delimiter character, but not including the termiā€
nating null byte ('\0'). This value can be used to handle embedded
null bytes in the line read.
Both functions return -1 on failure to read a line (including end-of-
file condition). In the event of an error, errno is set to indicate
the cause.
Therefore, you should enhance your code to check for stream errors and deal with errno -- you should do this even when your code works, because EOF is not the only reason for the function
to return -1.
int len = getline(&line, &len, database);
if(len == -1 && ferror(database)) {
perror("Error reading database");
}
You can write more detailed code to deal with errno in more explicit ways.
Unfortunately handling this thoroughly can make your code a bit more verbose -- welcome to C!

Segmentation fault in c in my code.

I am creating a simple unix shell using c. Where there are two commands: "set prompt" which changes the user prompt and "quit" which exits the program, any other commands are taken care of by the system. I have the following code but I keep getting segmentation fault. What is it that I am accessing incorrectly. Please Help.
int main(int argc, char *argv[])
{
char cmdLine[BUFSIZ];
char *cmdPrompt = "$PROMPT:";
{
if(argc!=1)
{
printf("error: Incorrect number of arguments on command line");
}
else
{
while(1) //This creates an infinite loop as 1 will never be equals 0
{
printf("%s", cmdPrompt); //Prints the current Prompt on the screen
fgets(cmdLine, sizeof(cmdLine), stdin); //puts the user input into the cmdLine array
char *token = strtok(cmdLine, " \n"); //tokenizes the user input with delimitters space or enter
if(strcasecmp(token, "QUIT")==0) //checks if the user input is "quit"
{
exit(EXIT_SUCCESS); //successfully exits program
}
else if(strcasecmp(token, "SET")==0) //checks if the first part of user input is "set"
{
token = strtok(NULL, " \n");
if(strcasecmp(token, "PROMPT")==0) //checks to see if the next part is promt
{
token = strtok(NULL, "\n");
cmdPrompt = token; //changes the user prompt
}
else
{
system(cmdLine); //all other commands taken care of by the system
}
}
}
}
}
}
From the manual page (man strtok in a Unix shell):
RETURN VALUE
The strtok() and strtok_r() functions return a pointer to the next token, or NULL if there are no more tokens.
Which means you need to make sure that the returned value (token) is not a NULL pointer before using it.
Also the system() call may not like it if the cmdLine variable includes a '\n' character.
Also, fgets() can return NULL on errors:
RETURN VALUE
gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read.
You should be fine on that one though.

fopen() always returns NULL

int main()
{
int i;
FILE *list,*file;
char temp[30];
list=fopen("filelist","rb");
while(fgets(temp,30,list)!=NULL)
{
file=fopen(temp,"r");
{
fclose(list);
return 0;
}
This is my code I basically want to open all files in filelist but my fopen call (exept the first one always returns a NULL am i missing something also this is my filelist
file1
file2
file3
file4
also i dont use file extensions and files exist in the same directory wtih executable.
fgets() stores the new-line character into the buffer it is populating so you need to remove it before calling fopen() within the while.
From the linked reference page for fgets():
Reads at most count - 1 characters from the given file stream and stores them in str. The produced character string is always NULL-terminated. Parsing stops if end-of-file occurs or a newline character is found, in which case str will contain that newline character.
Example code to remove the new-line:
char* nl = strrchr(temp, '\n');
if (nl) *nl = 0;
fgets leaves the newline on the end of the string, which you can plainly see if you add the following line afterwards:
printf ("[%s]\n", temp);
You'll see something like:
[file1
]
You need to remove it before use, which you can do this with something like:
size_t sz = strlen (temp);
if (sz > 0)
if (temp[sz-1] == '\n')
temp[sz-1] = '\0';
You can see this effect in action in the following complete program:
#include <stdio.h>
#include <string.h>
int main (void) {
size_t sz;
char temp[30];
printf ("\n> ");
while (fgets (temp, sizeof(temp), stdin) != NULL) {
printf ("before: [%s]\n", temp);
sz = strlen (temp);
if (sz > 0) {
if (temp[sz-1] == '\n') {
temp[sz-1] = '\0';
}
}
printf ("after : [%s]\n", temp);
printf ("\n> ");
}
return 0;
}
It basically uses your exact method to get a line using fgets (but from standard input) and then outputs the result both before and after removal of the trailing newline. A sample run follows:
pax> ./testprog
> hello
before: [hello
]
after : [hello]
> goodbye
before: [goodbye
]
after : [goodbye]
> [CTRL-D]
pax> _
You may also want to look at a few other things in that code segment:
the use of an open brace { at the end of the while loop.
the fact that you're opening the files within the loop and not doing anything with them (including closing them).
the use of "rb" open mode. Usually this is unnecessary, it's certainly unnecessary if you know it's a text file.
you should always check the return codes of functions that can fail (like fopen) before using them.
the canonical form of main in C where no arguments are needed is int main (void).
I'll state my case of which I am still uncertain: I thought my problem was with "fopen", but after trying every single solution, I ran into the extension problem, which I'm facing in Windows 10. It appears that Windows puts ".txt" automatically but, if you put ".txt" as extension, the name becomes ".txt.txt" at the end. So I left the file name with no extension, and put "file.txt" as argument of "fopen", and that was the only way it has worked for me.

Resources