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.
Related
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.
Okay so overall im trying to complete a basic CLI C program which will complete functions such as clear, quit, cd, ls, help (bring up the unix man) etc.. i altered my code and so far i have this, im getting segmination error when trying to execute the cd command part of the program, (im very new to c btw);
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main (int argc, char *argv[])
{
char input[] = " ";
char *argument;
while(strcmp(input, "quit")!= 0)
{
printf("$");
scanf ("%s", input);
if(strcmp(input,"clear") == 0)
{
printf("\e[1;1H\e[2J");
}
else if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
else if(strcmp(input, "echo") == 0)
{
char str[50];
scanf("%[^\n]+", str);
printf(" %s", str);
}
}
}
input is declared as a ' ' (space) character. It will never match 'cd'.
This is probably more along the lines of what you want to achieve, where the first parameter is the command (cd), and the second will be the directory:
int main (int argc, char *argv[])
{
char *argument;
if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
Edit Also please note that there is no need for the else satement. If chdir does not return an error, it will change the directory, thus no need to call it again in an else.
Additionally, another tip for using system calls in general, it would be of great help if you print the error number returned by the system upon a failure in system call. This will make things easier when things start going wrong. To do this simply include <errno.h>' and modify the printf to printerrno` which gives specific details about the error:
printf("Chdir error: %d", errno);
For instance chdir() does not only return an error when the directory does not exist, but also for example if you do not have permissions to view the contents of the directory. See the man page for a list of possible errors.
To implement your own shell, you need to take input directly from stdin, not from command-line arguments (argv) from another shell. The basic pattern is like this:
Read input
Execute command
Print results
Loop back to step 1
Sorry for the bad title, but i didn't know a better one!
Target: I'm trying to make an command handler. So I'm printing out via printf("cmd: ") and listing on stdin via fgets(). If theres an Input I'm check on commands via if .. else if. So my Problem now: If there is no input on stdin it should repeat the function and print cmd!
int cmd_handler()
{
printf("cmd: ");
char command[LINE_MAX];
fgets(command, LINE_MAX, stdin);
if(command != NULL)
{
if(strcmp(command, "xyz"))
{
xyz();
}
}
return 0;
}
I really don't know how i can arrange that. simple call cmd_handler() on else isn't working. Maybe someone can give me a tip how to solve it.
EDIT:
It should look like this, if there is no input(2x for example) on stdin:
cmd:
cmd:
cmd:
THIS CODE ISN'T THE REAL ONE!
regards
You need a loop. I would suggest a while loop with an exit condition, perhaps set by an "exit" command.
int run = 1;
while (run) {
printf("cmd: ");
...
else if (strcmp(command, "exit") == 0) {
run = 0;
}
}
You need to check whether fgets() returns NULL, not whether command is NULL:
if (fgets(command, LINE_MAX, stdin) != NULL)
Then you can add an 'else' clause to handle the error condition, as you were trying to do.
The way you check for empty string is not going to work.
if(command != NULL)
This condition will always evaluate to true as command, in the expression, gets converted to a pointer and is always non-null.
To check if there are any alpha-numeric characters, use isalnum() from <ctype.h> and ensure there's no whitespace characters in command. You seem to want to use recursion whereas a loop is probably more suited.
char command[LINE_MAX];
int alnum=0;
while(1) {
alnum=0;
printf("cmd: ");
fgets(command, LINE_MAX, stdin);
for(i=0;i<strlen(command);i++)
if(isalnum(command[i])) {
alnum=1;
break;
}
if(!alnum) continue;
if(strcmp(command, "xyz"))
{
xyz();
}
....
break;
}
This way, you can ensure it handles any whitespace you may input. But rest of your strcmp(command"xyy") will fail if user inputs " xyz". So it may suffice to check whether user simply hits ENTER:
if(command[0] == '\n') continue;
instead of the above check using alnum().
After succesfully reading a re-directed file to my program from the console, I ask a user to enter a word, then use scanf() to read in the word.
The problem i'm having is that scanf() is immediately reading in junk characters and then the program continues. It doesn't even pause to let the user enter anything in the console. It doesn't happen when I don't open a file. EVERYTHING else works perfectly. What could be the issue:
**I tried everything suggested, still can't get it to work. I've made a new project that is just for getting this part to work, here it is. Ignore that scanf is only looking for a single character, even though I ask for a word. I did this just to see if the program would actually pause and allow me to enter something, but it doesn't. Just enters some garbage and program ends.
main(){
int n,i;
char ch;
char line[80];
while(fgets(line, 80, stdin) != NULL){
for(i=0;i<80;i++){
ch=line[i];
if(ch=='\n'){
printf("%c",ch);
break;
}
else{
printf("%c",ch);
}
}
}
printf("Please enter a word: ");
scanf("%c",&ch);
}
You can't re-direct stdin from a file and then also use the keyboard for input (that I know of). If you want to do that, it's simpler to have the program take the input file as a command-line argument and then run it like so: prog myfile.txt. Also, leave yourself a pad with fgets() -- use one less than the allocated array for maxlen. It's always safest with C char arrays to use one less than the allocated length for anything requiring a maximum length in case the maximum length is not including the '\0' terminating character.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char *argv[])
{
FILE *f;
int i;
char line[80];
if (argc<2)
{
printf("Usage: %s <inputfile>\n",argv[0]);
exit(10);
}
/* Open file and echo to stdout */
f=fopen(argv[1],"r");
if (f==NULL)
{
printf("Cannot open file %s for input.\n",argv[1]);
exit(20);
}
while (fgets(line, 79, f) != NULL)
printf("%s",line);
fclose(f);
/* Get user input from stdin */
printf("Please enter a word: ");
if (fgets(line,79,stdin)==NULL)
{
printf("Nothing entered. Program aborted.\n");
exit(30);
}
/* Remove CR/LF from end of line */
for (i=strlen(line)-1;i>=0 && (line[i]=='\n' || line[i]=='\r');i--)
;
line[i+1]='\0';
printf("The word entered is: '%s'\n",line);
return(0);
}
sscanf is used to input from a stream or a buffer, and in unix stdin is considered as file so u are supposed to use fscanf which inputs from a file so use fscanf(stdin,"%s",testword);
I'm trying to redirect the input of my program.
Here is the command I'm typing in the terminal:
./hello < name
"name" is a file containing a single string.
hello is a compiled C program consisting of the following code:
int main(int argc, char *argv[])
{
char message[100] = "Hello ";
if(argc>1)
{
strcat(message, argv[1]);
strcat(message, "\n");
}
else
{
strcat(message, "there\n");
}
printf("%s", message);
return 0;
}
As far as I've understood the argument should now be the content of the name file. However in the program I can't detect any arguments (and prints out "Hello there").
That is going to read the file name and put it in your standard input, not in the first argument.
You can read it using fgets, getchar, scanf, etc.
The equivalent:
int c;
printf("Hello ");
while((c = getchar()) != EOF) {
putchar(c);
}
puts("\nthere");
Giving input redirection is different than providing argument.
Input redirection means the provided file will work as standard input.
so if you need filename as an argument, just write like this
./hello name
but by looking at your program, I will say you might have made a logical error. By running the command which I have mentioned above, you will get 'Hello name' printed on your screen. The actual name which you have saved in a file named 'name' will not get printed.