I'm making a custom C shell and currently splitting up commands using whitespace:
(Simplified example of what I'm doing currently)
char *buf[20];
char *tempVar;
tempvar = strtok(buf, " ");
For example:
sleep 5 would be split up into sleep and 5.
However, I'm wanting to split for & as well (to create background processes). So sleep 5& would be split into 3: sleep, 5 and &.
Any help would be greatly appreciated.
You can using a specific method to extract numbers, or just drop the character &. I've a thought here, maybe it's not the best solution. Hoping for further exchanges.
char buf[] = "sleep 5&";
char *cmd;
char *arg;
char *tmpArg;
int count = 0;
while (true) {
if (0 == count) {
cmd = strtok(buf, " ");
arg = strtok(NULL, " ");
} else {
cmd = strtok(NULL, " ");
arg = strtok(NULL, " ");
}
printf("%s\n", cmd);
if (NULL != cmd) {
if (strcmp("sleep", cmd) == 0) {
if (arg != NULL) {
// 1. introduce a method to extract digit or
// 2. using `&` to split the arg again, the shortcoming is that we omit character `&` here.
for (int i = 0; i < 2; ++i) {
if (0 == i) {
tmpArg = strtok(arg, "&");
} else {
tmpArg = strtok(NULL, "&");
}
printf("%s\n", tmpArg);
}
}
}
} else {
break;
}
++count;
}
Related
I am writing a program that parses input from stdin and calls functions according to the input.
The inputs my program is supposed to handle are the following:
end //stops the program
report //prints a specific output
addent "ent_id"
delent "ent_id"
addrel "ent_id1" "ent_id2" "rel_id"
delrel "ent_id1" "ent_id2" "rel_id"
The functions called by the input are not relevant to my issue, but do note the all the arguments that are passed to the functions are between quotation marks.
Here's the code
int main() {
const char Comando[6][7] = { "addrel", "addent", "delrel", "delent", "report", "end" };
const char spazio[2] = " ";
const char newline[3] = "\n";
const char quote[2] = "\"";
char sample[100];
char *temp;
char *comandoIN;
char *argomento1;
char *dest;
char *rel;
RelHead = NULL;
init_array();
char *str = fgets(sample, 100, stdin);
for (;;) {
if (strncmp(sample, Comando[5], 3) == 0) {
return 0;
} else if (strncmp(sample, Comando[4], 6) == 0) {
report();
} else {
temp = strtok(sample, newline);
comandoIN = strtok(temp, spazio);
argomento1 = strtok(NULL, quote);
if (strncmp(Comando[1], comandoIN, 7) == 0) {
addent(argomento1);
} else if (strncmp(Comando[3], comandoIN, 7) == 0) {
delent(argomento1);
} else {
temp = strtok(NULL, quote);
dest = strtok(NULL, quote);
temp = strtok(NULL, quote);
rel = strtok(NULL, quote);
if (strncmp(Comando[0], comandoIN, 7) == 0) {
addrel(argomento1, dest, rel);
} else if (strncmp(Comando[2], comandoIN, 7) == 0) {
delrel(argomento1, dest, rel);
}
}
}
char *str = fgets(sample, 69, stdin);
}
return 0;
}
The incorrect behavior is cause by the following input:
addrel "The_Ruler_of_the_Universe" "The_Lajestic_Vantrashell_of_Lob" "knows"
which causes the last two calls of strtok to return NULL instead of " " (whitespace) and "knows" respectively (without quotation marks).
Furthermore, if this is the first input given to the program, it behaves correctly, and if it's the last, the following cycle will put "knows" in the "comandoIN" variable. This is the only input I've found so far that causes this issue, and I think it has something to do with removing the newline character with the first call of strtok.
This is an assignment for uni, so we have several inputs to test the program, and my program passes the first 4 of these (the tests are about 200 inputs each), so I don't really understand what's causing the bug. Any ideas?
The problem here is that the input:
addrel "The_Ruler_of_the_Universe" "The_Lajestic_Vantrashell_of_Lob" "knows"
is 77 bytes long (76 characters plus terminating NULL).
At the end of your loop you have:
char *str = fgets(sample, 69, stdin);
where your state that your buffer is 69 long.
Why does it behave correctly if it is the first input?
Before the for loop you have:
char *str = fgets(sample, 100, stdin);
for(;;)
...
Here you use a size of 100, so it works if you first use the above input directly after starting the program.
Using strtok for parsing the command line with different sets of separators is confusing and error prone. It would be simpler to parse the command line with a simple loop and handle spaces and quotes explicitly, then dispatch on the first word.
Here is a more systematic approach:
#include <stdio.h>
char *getarg(char **pp) {
char *p = *pp;
char *arg = NULL;
while (*p == ' ')
p++;
if (*p == '\0' || *p == '\n')
return arg;
if (*p == '"') {
arg = ++p;
while (*p != '\0' && *p != '"')
p++;
if (*p == '"')
*p++ = '\0';
} else {
arg = p++;
while (*p != '\0' && *p != ' ' && *p != '\n')
p++;
if (*p != '\0')
*p++ = '\0';
}
*pp = p;
return arg;
}
int main() {
char sample[100];
char *cmd, *arg1, *arg2, *arg3;
RelHead = NULL;
init_array();
while (fgets(sample, sizeof sample, stdin)) {
char *p = sample;
cmd = getarg(&p);
arg1 = getarg(&p);
arg2 = getarg(&p);
arg3 = getarg(&p);
if (cmd == NULL) { // empty line
continue;
} else
if (!strcmp(cmd, "end")) {
break;
} else
if (!strcmp(cmd, "report")) {
report();
} else
if (!strcmp(cmd, "addent")) {
addent(arg1);
} else
if (!strcmp(cmd, "delent")) {
delent(arg1);
} else
if (!strcmp(cmd, "addrel")) {
addrel(arg1, arg2, arg3);
} else
if (!strcmp(cmd, "delrel")) {
delrel(arg1, arg2, arg3);
} else {
printf("invalid command\n");
}
}
return 0;
}
In here I am trying to tokenize the user input e.g. load sml.txt.
The load command works fine because it has 2 tokens, but if I try to use a single word input like display, it crashes and gives me a segfault. I assume its because the second token is NULL, but I have no idea how to circumvent this issue. Can you help?
For your reference COMMAND_LOAD = "load" and COMMAND_DISPLAY = "display".
int main(int argc, char **argv)
{
AddressBookList *addressBookList;
char input[BUFSIZ];
char load[BUFSIZ];
char fileN[BUFSIZ];
char *fileName;
char *token;
showStudentInformation();
do
{
printf("Enter your command: \n");
fgets(input, sizeof(input), stdin);
input[strlen(input) - 1] = '\0';
token = strtok(input, " ");
strcpy(load, token);
token = strtok(NULL, " ");
strcpy(fileN, token);
fileName = fileN;
if (strcmp(load, COMMAND_LOAD) == 0)
{
addressBookList = commandLoad(fileName);
}
else if (strcmp(load, COMMAND_UNLOAD) == 0)
{
/*commandUnload(fileName);*/
}
else if (strcmp(load, COMMAND_DISPLAY) == 0)
{
if (fileN == NULL)
{
printf("> No file loaded");
}
else
{
commandDisplay(addressBookList);
}
}
else
{
printf("> Invalid input\n\n");
}
} while (strcmp(load, COMMAND_QUIT) != 0);
return EXIT_SUCCESS;
}
strtok() returns NULL when there's no more tokens, you can check for this. If there's no token, I empty the target string by assigning '\0' to the first character, instead of calling strcpy().
do
{
printf("Enter your command: \n");
fgets(input, sizeof(input), stdin);
token = strtok(input, " \n");
if (token) {
strcpy(load, token);
} else {
load[0] = '\0';
}
token = strtok(NULL, " \n");
if (token) {
strcpy(fileN, token);
} else {
fileN[0] = '\0';
}
...
} while (strcmp(load, COMMAND_QUIT) != 0);
There's also no need to replace the last character in the string with \0. Just include \n in the strtok() delimiters, so it won't include the newline at the end in the token.
My code is:
getline(&cmd, &len, stdin);
cmd[strcspn(cmd,"\n")] = 0;
char *ncmd = (char*)malloc(strlen(cmd) + 1);
memset(ncmd, '\0', strlen(cmd));
strcpy(ncmd,cmd);
const char *tok = strtok(ncmd, " ");
// token is just string from stdin
if(!strcmp(tok, "bye")){
printf("bye");
} else if (!strcmp(tok, "help")){
printf("help");
} else if (!strcmp(tok, "list")){
......
}
ie. For input: help and bye
Result: TOK: help
strcmp(tok, "bye"): 6
strcmp(tok, "help"): 0
strcmp(tok, "list"): -10
Result: TOK: bye
strcmp(tok, "bye"): 0
strcmp(tok, "help"): -6
strcmp(tok, "list"): -4
Whatever I type, I can't seem to pull out the correct if statement.
How do I fix the "strcmp" function so it goes to the correct if statements?
As the code and if statement are correct, I think tok has extra characters, such as a newline. Then tok will never be equal to any of your words.
When creating a buffer to use with strtok(), often times the same buffer will be used several times. Therefore, instead of declaring it as:
const char *tok = strtok(ncmd, " "); //tok cannot be changed (const keyword)
Create it so it can be changed:
char *tok = strtok(ncmd, " ");//tok can be changed (removed const keyword)
Example:
char string[] = {"this is \n a string \t with embedded \r non-printables"};
char *tok = {0}; //declare and initialize
tok = strtok(string, " \n\r\t");
while(tok)
{
tok = strtok(NULL, " \n\r\t");//tok will be a different value each iteration
// 1 - this
// 2 - is
// 3 - a
// 4 - string
// and so on
}
...
I didn't understand your code properly, but I hope this is what you are trying to achieve in your code:
int main()
{
char list[] = "bye help list";
char *delim = " ";
char *pch = strtok(list,delim);
int count = 0;
while(pch) {
if(!strcmp(pch,"bye")) {
printf("i found bye at %d\n",count);
} else if (!strcmp(pch,"help")) {
printf("i found help at %d\n",count);
} else if (!strcmp(pch,"list")) {
printf("i found list at %d",count);
}
count++;
pch = strtok(NULL,delim);
}
printf("\n");
return 0;
}
I need to make a program that will emulate the terminal of Linux. Since some system calls requires 1,2 or more arguments, I want to make sure that the number of parameters given are correct. I'm using strtok() to separate the call name from the arguments, but I need to know how many tokens strtok() created to compare it.
Here's and example code:
char *comand = (char*) malloc(sizeof(char)*100);
char *token;
char *path1 = (char*) malloc(sizeof(char)*100);
char *path2= (char*) malloc(sizeof(char)*100);
fgets(comand, 100, stdin);
printf( "\nYou entered: %s \n", comand);
token = strtok(comand ," ");
//Check the number of tokens and add a condition in each IF to match
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cat",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cp",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("mv",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("find",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rm",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("mkdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rmdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("quit",token) == 0) {
exit(0);
}
else print("Number of parameters do not match);
the only thing strtok() does is look for the next occurance of the delimiter and overwrite that character with a \0 and return the pointer with the offset added. the pointer is kept in a static variable that's why a subsequent call to it with a NULL for the char * will perform it on the last string used from the offset that the last delimiter was found.
this page has a very nice example:
http://en.cppreference.com/w/c/string/byte/strtok
If you only want to count the arguments it would be easier to use strchr(), this function searches for a character and returns a pointer to its location. you could use it like this.
unsigned int i = 0;
char *temp = token;
while ( (temp = strchr(temp, '') != NULL) ) {
++i;
}
this has the added benefit of not modifying your original char array while strtok() does!
I would handle this within the functions you create for each command.
you pass all options to the function and there you parse it. either with strtok() or whatever else you want to use.
This keeps it nice and clean within the sub-routines and you will always know what to expect.
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token); // I would maybe change the path variable name to args
ret = lscmd(path1);
if (ret == -1) {
// invalid input detected
}
}
then you would have a ls function
int lsdcmd(char *args) {
// parse args for argumants you are looking for, return -1 if it fails
// do whatever you want to do.
}
You can count the arguments using strtok this way:
Example:
const char* delimiter = ",";
char* tokens[MAX_NUM_OF_ARGS];
unsigned int index = 0;
char* temp = strtok(buff,delimiter);
while (temp!=NULL){
if(index<MAX_NUM_OF_ARGS){
tokens[index]=temp;
}
index++;
temp = strtok(NULL,delimiter);
}
Then later you can iterate through the array of pointers (tokens) and compare them...
I am having trouble with the strtok function. I keep getting a 'bus error.' I wrote a function to return all the words within a line. Could somebody please point out my error?
NOTE: I am used to higher level languages
void extract_words(char tokens[WORD_MAX][WORD_LEN], char* line, int* sizePtr)
{
printf("in extract words"); //for debugging
char* chPtr = NULL;
chPtr = strtok(line, " ");
int size = 1; //words has one element
while(chPtr != NULL)
{
strcpy(tokens[size++], chPtr);
chPtr = strtok(NULL, " "); //continue to tokenize the string
}
*sizePtr = size;
}
Thanks in advance!
strtok modifies the string you pass to it, so it can't be a string literal. You should be able to do something like this:
void extract_words(char tokens[WORD_MAX][WORD_LEN], const char* line_arg, int* sizePtr)
{
char line[(WORD_LEN+1)*WORD_MAX];
char* chPtr = NULL;
int size = 0;
strcpy(line,line_arg);
printf("in extract words"); //for debugging
chPtr = strtok(line, " ");
while(chPtr != NULL)
{
strcpy(tokens[size++], chPtr);
chPtr = strtok(NULL, " "); //continue to tokenize the string
}
*sizePtr = size;
}
Note that I also initialized size to zero, since array indices start at zero.
Well...
Should you try to use a separator?
That case, I have source code.
int split(char *src, char *div, char **result,int *size)
{
int i, j, slen, dlen, key=0, start=0;
slen=strlen(src);
dlen=strlen(div);
for(i=0;i<slen;i++)
{
for(j=0;j<dlen;j++)
{
if(src[i]==div[j])
{
src[i]=0x00;
result[key] = src+start;
key++;
start=i+1;
}
}
}
result[key]=src+start;
*size=key+1;
return 0;
}
using
split(chatData, " ", cmpData, &tok);
" " : token
&tok : count split word
chatData : original data
by korean Dalsam.