C strtok() and strcmp() issues - c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int args, char* argv[])
{
const int CBUFF = 1024;
char input[CBUFF];
char wkdir[CBUFF];
char* command;
printf("Welcome to MyShell...\n");
while(1)
{
getcwd(wkdir, CBUFF);
printf("%s ? ", wkdir);
fgets(input, CBUFF, stdin);
command = strtok(input, " ");
if(strcmp(command, "cd") == 0)
{
char* path;
path = strtok(NULL, " ");
if(chdir(path) != 0)
{
printf("ERROR: COULD NOT CHANGE DIRECTORY TO SPECIFIED PATH");
}
}
if(strcmp(command, "exit") == 0) break;
}
return 0;
}
I am running into an issue creating a very simple command shell in C. The input is only being read the way I want it too when I add a space after my directive. I know that it has something to do with my improper use of the strtok() function but am not able to figure out what I am doing wrong. I have read the documentation of <string.h> and am turning up blank.
Behavior I want:
Directive "exit" to exit from program.
Current behavior:
Must add space after directive to get it to parse correctly ie. "exit " or "cd " is entered.

You left the trailing newline in the buffer. Get rid of it.
char *got = fgets(input, CBUFF, stdin);
if (!got) return ; /* EOF -- treat like exit */
size_t gotlen = strlen(got);
if (got[gotlen] == '\n') got[gotlen] = 0;

Related

Segmentation fault when I change directory in C

I want to make a program that imitates the use of cd in UNIX. Obviously the best option is chdir. However when the user types cd /home I get Segmentation fault. When I use gdb, it prints:
Program received signal SIGSEGV, Segmentation fault. __rawmemchr_avx2 () at ../sysdeps/x86_64/multiarch/memchr-avx2.S:61
61 ../sysdeps/x86_64/multiarch/memchr-avx2.S: The file of directory does not exist.
The code looks like this:
void cd(char buff[]){
if(chdir(buff)==-1){
printf("Problem with directory\n");
}
}
int main(int argc, char *argv[]){
char* token;
char buffer[256];
printf("Welcome user\n");
sscanf("%s",buffer);
token=strtok(buff," ");
if(strcmp(token,"cd")==0||strcmp(token,"cd\n")==0){
cd(strtok(NULL," ");
}
return 0;
}
Than you for your time.
You have made a few syntactic and logical mistakes inside the provided code, like for example buff instead of buffer in main()and sscanf instead of scanf for giving the input string.
Also, you wrote cd(strtok(NULL," "); but a closing brace is missing for this statement to compile: cd(strtok(NULL," "));.
I've corrected the code and the output file runs fine on my platform - with GCC on Ubuntu GNU/Linux.
Here is the corrected code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void cd(char buff[]){
if(chdir(buff)==-1){
printf("Problem with directory\n");
}
}
int main(int argc, char *argv[]){
char* token;
char buffer[256];
printf("Welcome user\n");
scanf("%s",buffer);
token=strtok(buffer," ");
if(strcmp(token,"cd")==0||strcmp(token,"cd\n")==0){
cd(strtok(NULL," "));
}
return 0;
}
Try it with this.
This code has several problems:
1) even if sscanf is swapped for scanf, it does not do what you probably expect it to (read the whole line inserted by the user). scanf with %s will read up to the first whitespace. So, your buffer will actually contain cd\0 instead of cd /home\0 like you expect it to. Use fgets instead.
2) You must check that strtok does not return a NULL pointer. This is what is currently causing your segfault.
Something like this:
void cd(char buff[]) {
if(chdir(buff) == -1) {
printf("Problem with directory\n");
}
}
int main(int argc, char *argv[]){
char* token;
char buffer[256];
printf("Welcome user\n");
// reads the whole line into buffer
fgets(buffer, 256, stdin);
token = strtok(buffer, " ");
if (strcmp(token, "cd") == 0){
token = strtok(NULL," ");
// we must check if there are any tokens left
if (token != NULL) {
// we must remove the trailing newline character that is included by fgets
cd(strtok(token, "\n"));
}
}
return 0;
}

Segmentation Fault With Strcmp With One Input

I am having an issue with the following code.
I have a global variable
char tokens[512][80];
Along with code:
int main(int argc, char** argv) {
char *input = malloc(sizeof(char) * 80);
while (1) {
printf("mini-shell>");
fgets(input, 80, stdin);
parse(input);
if (strcmp(tokens[0], "cd") == 0) {
cd();
}
else if (strcmp(tokens[0], "exit") == 0) {
exit(1);
}
}
}
void parse(char str[]) {
int index = 0;
char* str_ptr = strtok(str, " ");
while (str_ptr != NULL) {
strcpy(tokens[index], str_ptr);
str_ptr = strtok(NULL, " \0\r\n");
//printf("%d\n", index);
index = index + 1;
}
}
I found that if I enter exit for stdin I get a Segmentation fault, but if I enter cd .. for stdin I don't. Why is this so?
We don't know what the definition of the cd() function is, but there are a number of things that you may wish to consider in this program.
First, I don't believe there's any benefit to dynamically allocating 80 bytes of memory for the input buffer when you can easily do so automatically on the stack with char input[80]; - this is free and easy and requires no deallocation when you're done.
If you do this, you derive the size with fgets(input, sizeof input, stdin) where if you change the size of your input line from 80 to some other number, you only have to change it once: the sizeof on an array pulls the size directly.
Your parse() routine needs a little bit of help also. It's a really good idea to declare the function via the extern as shown so that when the compiler sees you call the function in the loop (right after the fgets), it knows the parameter and return types. Otherwise it has to make assumptions.
Because parse() is splitting apart the line you read from input, it's not required to copy the strings to some other place, so you can turn tokens from a multi-dimensional array into a simple array of pointers. As you run strtok() through the line to split up the parameters, you can store just the pointer, knowing that they will be pointing to stable data until the next fgets().
Also: your code does not strictly require or use this, but adding a NULL pointer to the end of the tokens list is a really good idea: otherwise, how does the caller know how many parameters were actually entered? This code checks whether the user entered just a blank line or not.
We've also change the loop around a little bit so the strtok() is called just once instead of twice, including the \n as noted in the comments.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *tokens[512];
extern void parse(char *str);
int main(int argc, char** argv) {
char input[80];
while (1) {
printf("mini-shell> "); fflush(stdout); // make sure user sees prompt
fgets(input, sizeof input, stdin);
parse(input);
if (tokens[0] == NULL) continue; // user entered blank line
if (strcmp(tokens[0], "cd") == 0) {
cd();
}
else if (strcmp(tokens[0], "exit") == 0) {
exit(1);
}
}
}
void parse(char *str) {
int index = 0;
char* str_ptr;
while ( (str_ptr = strtok(str, " \n")) != NULL)
{
tokens[index++] = str_ptr;
str = NULL; // for next strtok() loop
}
tokens[index] = NULL;
}

Reading in user input with space using scanf in a while loop?

I'm working on some code in which I am trying to read in the following commands, which will lead to certain functions in my program being called:
PRINT
INSERT 0,Folders,Folders for storing related papers,25
PRINT
QUIT
I have been trying different ways to read in this input, which comes from ./inventory test02/inventory02-actual.txt < test02/input02.txt > test02/actual02.txt, in which these commands shown above are in the file input-02.txt.
I've primarily been working with scanf, but have tried fgets, but I have had the most success to what I desire with scanf. I originally tried scanf("%s", command) and in this case, scanf does not take in whitespace so the program terminates.
//Keep re-prompting user for commands until you reach EOF{
while ((scanf("%[0-9a-zA-Z, ]s", command) == 1)) {
printf("====================\nCommand? ");
printf("%s\n", command);
if (strcmp(command, "PRINT") == 0) {
print(list);
} else if (strcmp(substring(command, START, INSERT_END), "INSERT") == 0) {
//Get the substring - this is the input after "INSERT"
char string[MAX_LEN_COMMAND];
strcpy(string, substring(command, OFFSET, strlen(command)));
insert(string, list);
} else if (strcmp(command, "PRINTREVERSE") == 0) {
printReverse(list);
} else {
printf("Invalid command passed.\n");
exit(EXIT_BAD_INPUT);
}
}
Currently, when I run my code, only the first command, "PRINT" is read in. It seems like I cannot read the next line of input from input-02.txt. Is there a way I can read in these commands properly? In addition, after my program reads in "INSERT", it then reads in "0,Folders,Folders for storing related papers,25" as a command, which it should not. It should go directly to the next command, which is "PRINT". I have tried using a continue statement after calling the insert method, but that did not work. Does anyone have any suggestions?
EDIT: Updating code with fgets.
Instead of posting all my functions that I called to above, I think passing a printf to show us what the command is might be simple enough for a reproducible example!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OFFSET 7
#define START 0
#define INSERT_END 5
static const char *substring(char command[], int start, int end);
int main(int argc, char **argv)
{
//Get user input for commands
char command[500];
//Keep re-prompting user for commands until you reach EOF{
while (fgets(command, sizeof(command), stdin) != NULL) {
printf("====================\nCommand? ");
printf("%s\n", command);
if (strcmp(command, "PRINT") == 0) {
printf("%s", command);
} else if (strcmp(substring(command, START, INSERT_END), "INSERT") == 0) {
printf("%s", command);
} else if (strcmp(command, "PRINTREVERSE") == 0) {
printf("%s", command);
} else {
printf("Invalid command passed.\n");
exit(1);
}
}
}
static const char *substring(char command[], int start, int end)
{
int i = 0;
int j = 0;
char *sub;
sub = (char *)malloc(500 * sizeof(char));
for (i = start, j = 0; i <= end; i++, j++) {
sub[j] += command[i];
}
sub[j] = '\0';
return sub;
}
The output I get is:
====================
Command? PRINT
Invalid command passed.
Since you are reading a line, you will have better success with fgets.
char command[101];
while (fgets(command, 100, stdin))
{
// rest of the code can be the same
}

How to chdir using C in Linux environment

I am new in c programming. How can I change directory like /home/jobs/$ans/xxx/ while I have $ans is a user string I can't chdir in c program.
My script is below:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char jdir;
printf("Enter job directory:"); /* user input for different directories */
scanf("jdir");
chdir("/home/jobs/%jdir/xxx");
system("ls -ltr");
return(0);
}
How to change directory with chdir?
Use something like:
char jdir[200]
scanf("%s", &jdir);
char blah[200];
snprintf(blah, 199, "/home/jobs/%s/xxx", jdir);
chdir(blah);
It seems mildly silly to write this program in C, but if there is a good reason to do so (for instance if it has to be setuid) then you should be a great deal more defensive about it. I would do something like this:
#define _XOPEN_SOURCE 700 /* getline */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
char *jobdir = 0;
size_t asize = 0;
ssize_t len;
fputs("Enter job directory: ", stdout);
fflush(stdout);
len = getline(&jobdir, &asize, stdin);
if (len < 0) {
perror("getline");
return 1;
}
jobdir[--len] = '\0'; /* remove trailing \n */
if (len == 0 || !strcmp(jobdir, ".") || !strcmp(jobdir, "..")
|| strchr(jobdir, '/')) {
fputs("job directory name may not be empty, \".\", or \"..\", "
"nor contain a '/'\n", stderr);
return 1;
}
if (chdir("/home/jobs") || chdir(jobdir) || chdir("xxx")) {
perror(jobdir);
return 1;
}
execlp("ls", "ls", "-ltr", (char *)0);
perror("exec");
return 1;
}
The edit history of this answer will demonstrate just how hard it is to get this 100% right - I keep coming back to it and realizing that I forgot yet another case that needs to be defended against.

Character arrays, and pointers how to use strtok and strcmp

I'm writing a linux shell for my operating systems class. I've knocked out the majority of it but I'm stuck on simple string comparisons. I've everything I can think of. strcmp should take in \0 terminated strings and return 0 for equal but that doesn't seem to be working and even stepping through the array and checking each char isn't working either. I currently have cmd[0] in the strcmp I know thats not right it needs to be null terminated but I've tried using strcpy and strcat \0 to another string. If someone could point out my mistake it would be much appreciated.
//Matthew Spiers
//CSC306
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
void ckCmd(char dir[]);
int main(){
pid_t pid;
char cdstr[4] = "cd";
char str[50];
char *cmd[3];
char *pstr;
char temp[50];
char dir[50] = "/bin/";
while(1){
pid = fork();
if(pid < 0){
fprintf(stdout, "Fork Failed");
}
else if(pid == 0){
fprintf(stdout, "\e[36m306.SH>\e[0m");
fgets(str, 50, stdin);
for(int i =0; i<50; i++){
if(str[i] == '\n'){
str[i] = '\0';
}
}
strcpy(temp, str); // Make a copy of original string
cmd[0] = strtok(str, " ");
for(int i =1; i<3; i++){
cmd[i] = strtok(NULL, " ");
}
strcat(dir, cmd[0]);
cout << cmd[0];
pstr = strtok(temp, " "); //pull out only first token
//Change Directory
if(!strcmp(pstr, "cd")){ //if first token is cd
//either branch to a routine just change directory
//ie branch and change directory
}
//ckCmd(temp);
execlp(dir, cmd[0], cmd[1], cmd[2], NULL);
_exit(0);
}
else{
wait(NULL);
}
}
}
void ckCmd(char str[]){
char *p;
p = strtok(str, " ");
if(p[0] == 'c'){
chdir("new");
}
}
enter code here
strtok is not reentrant/thread-safe!
You should use the RETURN-value from strtok:
p = strtok(str, " ");
if(p[0] == 'c'){
cmd[0] = strtok(str, " ");
...
if(!strcmp(cmd[0], "cd")){
If p/cmd[0] is NULL, it will crash.
What exactly isn't working? Can you show a smaller code sample?
The line:
strcat(dir, cmd[0]);
Are you aware that dir is the target here and not cmd[0]?
The line: !strcmp(cmd[0], "cd") by itself is correct, but it's unclear what exactly you're trying to do. Could you comment each group of line with your intentions?
Update: I suggest that you're trying too many things at once. To home in on your problem I recommend the following steps:
Understand how strtok works. It isn't very hard - read its manual and then try it in a separate file with some strings. This should give you a good idea.
Break parts of your code out and provide them with pre-set input (not from the user and without the forking). See where the behavior is as expected and where it drifts off.

Resources