Tokenize user input for execvp() in c - c

fgets(command, BUFFSIZE, stdin);
command[strlen(command)-1] = '\0';
char *p = strtok(command, " ");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " ");
}
char *commands[] = {*command_tok, NULL};
execvp(command_tok[0], commands);
I try to get user's input, tokenize it and store command and arguments in an array to pass it to execvp(). However, the arguments are never read. If the input is "ls -l", it is always executed as "ls".

This line:
// makes a two element array
char *commands[] = {*command_tok, NULL};
is wrong.
It's creating a new array consisting of just two elements, the first of which is command_tok[0]. Instead what you want is
command_tok[i] = NULL
execvp(command_tok[0], command_tok);
Also
command[strlen(command)-1] = '\0'
is nonsensical: strlen finds the length by searching for the null byte, which you then set to null again.
Here's my version:
#include <string.h>
#include <unistd.h>
#include <cstdio>
static const int BUFFSIZE=200;
int main(int argc, char* argv[]) {
char command[BUFFSIZE] = { 0 };
fgets(command, BUFFSIZE, stdin);
// remove trailing new line if present
commmand[strcspn(command, "\n")] = 0;
char *p = strtok(command, " ");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " ");
}
command_tok[i] = NULL;
execvp(command_tok[0], command_tok);
}
The one-liner for removing trailing new line came from Removing trailing newline character from fgets() input

After the -l you don't have the space, so, your option takes the '-l\n' as the argument. So you have to use the '\n' is also in the separator in the strtok function.
The below code will work as you expected.
fgets(command, BUFFSIZE, stdin);
command[strlen(command)-1] = '\0';
char *p = strtok(command, " \n");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " \n");
}
command_tok[i] = NULL;
execvp(command_tok[0], command_tok);
When you use fgets to read the input, If it is not an EOF, you have \n at last. So, using this you can choose the argument.

You provide to commands an array that consists exactly of two elements: the (first) element command_tok points to, and NULL. How should the compiler know that you mean all elements of an array command_tok, and how should it determine its size?

Related

Parsing tokens into char ** with user input fgets()

My problem is quite similar to the problem here: Trimming a trailing \0 from fgets() in C
However, the suggested solution buffer[strcspn(buffer, "\n")] = 0; as I am using char ** to store the tokens
Here's my code:
char str[1024];
fgets(str, 1024, stdin);
const char s[2] = " ";
char *token;
char ** token_arr = malloc(100 * sizeof(char*));
int pos = 0;
token = strtok(str, s);
while( token != NULL) {
token_arr[pos] = token;
printf( "%d %s\n", pos, token );
pos++;
token = strtok(NULL, s);
}
int i = 0;
while (token_arr[i]) {
printf("%d %s \n", i, token_arr[i]);
i++;
}
Input:
a b c d e
The printf in each loop is separated by a blank line, which I presume is due to the trailing \0 that is perhaps stored inside the token_arr.
How can I remove it?
Thanks a lot
ETA: What I meant it each print loop is printing an unintended extra blank line.
Contrary to gets(), fgets() keeps the newline that you typed in after your input.
Since the newline is not in your list of tokens it is kept as a part of the last token, hence the empty line.
just replace const char s[2] = " ";
with
const char s[3] = " \n";

Scan a sentence with space into *char array in C

I'm not good at using C language. Here is my dumb question. Now I am trying to get input from users, which may have spaces. And what I need to do is to split this sentence using space as delimiter and then put each fragment into char* array. Ex:
Assuming I have char* result[10];, and the input is: Good morning John. The output should be result[0]="Good"; result[1]="morning"; result[2]="John";I have already tried scanf("%[^\n]",input); and gets(input); Yet it is still hard to deal with String in C. And also I have tried strtok, but it seems that it only replaced the space by NULL. Hence the result will be GoodNULLmorningNULLJohn. Obviously it's not what I want. Please help my dumb question. Thanks.
Edit:
This is what I don't understand when using strtok. Here is a test code.
The substr still displayed Hello there. It seems subtok only replace a null at the space position. Thus, I can't use the substr in an if statement.
int main()
{
int i=0;
char* substr;
char str[] = "Hello there";
substr = strtok(str," ");
if(substr=="Hello"){
printf("YES!!!!!!!!!!");
}
printf("%s\n",substr);
for(i=0;i<11;i++){
printf("%c", substr[i]);
}
printf("\n");
system("pause");
return 0;
}
Never use gets, is deprecated in C99 and removed from C11.
IMO, scanf is not a good function to use when you don't know the number of elements before-hand, I suggest fgets:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[128];
char *ptr;
fgets(str, sizeof str, stdin);
/* Remove trailing newline */
ptr = strchr(str, '\n');
if (ptr != NULL) {
*ptr = '\0';
}
/* Tokens */
ptr = strtok(str, " ");
while (ptr != NULL) {
printf("%s\n", ptr);
ptr = strtok(NULL, " ");
}
return 0;
}
gets is not recommended to use, as there is no way to tell the size of the buffer. fgets is ok here because it will stop reading when the 1st new line is encountered. You could use strtok to store all the splited words in to an array of strings, for example:
#include <stdio.h>
#include <string.h>
int main(void) {
char s[256];
char *result[10];
fgets(s, sizeof(s), stdin);
char *p = strtok(s, " \n");
int cnt = 0;
while (cnt < (sizeof result / sizeof result[0]) && p) {
result[cnt++] = p;
p = strtok(NULL, " \n");
}
for (int i = 0; i < cnt; i++)
printf("%s\n", result[i]);
return 0;
}
As most of the other answers haven't covered another thing you were asking:
strtok will not allocate temporary memory and will use your given string to replace every separator with a zero termination. This is why Good morning John becomes GoodNULLmorningNULLJohn. If it wouldn't do this, each token would print the whole rest of the string on its tail like:
result[0] = Good morning John
result[1] = morning John
result[2] = John
So if you want to keep your original input and an array of char* per word, you need 2 buffers. There is no other way around that. You also need the token buffer to stay in scope as long as you use the result array of char* pointers, else that one points to invalid memory and will cause undefined behavior.
So this would be a possible solution:
int main()
{
const unsigned int resultLength = 10;
char* result[resultLength];
memset(result, 0, sizeof result); // we should also zero the result array to avoid access violations later on
// Read the input from the console
char input[256];
fgets(input, sizeof input, stdin);
// Get rid of the newline char
input[strlen(input) - 1] = 0;
// Copy the input string to another buffer for your tokens to work as expected
char tokenBuffer[256];
strcpy(tokenBuffer, input);
// Setting of the pointers per word
char* token = strtok(tokenBuffer, " ");
for (unsigned int i = 0; token != NULL && i < resultLength; i++)
{
result[i] = token;
token = strtok(NULL, " ");
}
// Print the result
for (unsigned int i = 0; i < resultLength; i++)
{
printf("result[%d] = %s\n", i, result[i] != NULL ? result[i] : "NULL");
}
printf("The input is: %s\n", input);
return 0;
}
It prints:
result[0] = Good
result[1] = morning
result[2] = John
result[3] = NULL
result[4] = NULL
result[5] = NULL
result[6] = NULL
result[7] = NULL
result[8] = NULL
result[9] = NULL
The input is: Good morning John

How to pass a character string to a function in C

I am trying to process a character string in order to change something in a file. I read from a file a character string which contains a command and an argument, separated by a space character. I separated this array in tokens.
Now I want to pass the second token, which is the argument to a function. My problem is that when I run my program, the screen freezes and nothing happens. Here is my separating way and the call to the function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void create_file(char *argument)
{
//some code goes here
}
int main()
{
int i = -1;
char *token[5];
char command[20];
const char delim[1] = " ";
FILE *fin;
fin = fopen("mbr.op", "r");
while(fscanf(fin, "%[^\n]", command) == 1)
{
i = -1;
token[++i] = strtok(command, delim);
while(token[i] != NULL)
token[++i] = strtok(NULL, delim);
if(strcmp(token[0], "CREATE_FILE") == 0)
create_file(token[1]);
}
fclose(fin);
return 0;
}
You have a few errors, first command[20] is an uninitialised string and that will cause undefined behaviour. Second, you failed to check the first arg as well as the second, so I added a test where commented. Also, the strings are not long enough so I removed the length. Lastly I test for a NULL pointer passed to the function.
Edit code was added to the question to show that command[20] was initialised, but it is still too short to take the command and a reasonable file name (thanks to #ameyCU).
#include <stdio.h>
#include <string.h>
void create_file(char *argument)
{
if(argument == NULL)
printf("NULL pointer\n");
else
printf("Arg: %s\n", argument);
}
int main(void)
{
int i = -1;
char *token[5];
char command[] = "CREATE_FILE myfile.txt";
const char delim[] = " ";
token[++i] = strtok(command, delim);
while(token[i] != NULL)
token[++i] = strtok(NULL, delim);
if(token[0] != NULL && strcmp(token[0], "CREATE_FILE") == 0) // added test
create_file(token[1]);
return 0;
}
Program output
Arg: myfile.txt
The first error is present in array definition:
const char delim[1] = " ";
In C "" is a string - an array of characters delimited by '\0'. This means that what stands to the right of "=" is a string of two chars:
// ' ' + '\0'
//0x20 0x00
Therefore this should be an array of two chars:
const char delim[2] = " ";
or
const char delim[] = " ";

split string value in arrays

I have to split a string-input value where-ever there is a blankspace and output the result.
eg: input:
I am a noob at C
output:
>>I
>>am
>>a
>>noob
>>at
>>C
Code:
void splitText(){
char str[100];
char sep[] = " \r\n";
char *res; //if i remove this
fgets(str,sizeof str,stdin);
if (fgets(str, sizeof str, stdin) == NULL) {
printf("error");
}
char *p = strchr(str, '\n');
if (p) *p = 0;
res = strtok(str, sep); //and this
printf("%s\n",res); //and change this to str
}
Working code for anyone encountering the same problem:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void splitText() {
char str[100];
char sep[] = " \n";
char *res;
fgets(str,sizeof str, stdin);
if ( fgets(str, sizeof str, stdin) == NULL ) {
printf("Error");
break;
}
res = strtok(str, sep);
while(res != NULL){
printf("Splitted String: \"%s\"\n",res);
res = strtok(NULL,sep);
}
}
Thanks to everyone who contributed in helping me with this issue!
The problem with
char str[100] = scanf("%s",str);
is that you are assigning an int to a char array.
scanf() returns the number of items successfully scanned. The actual reading of chars into the array is done by scanf() itself. So you just need to call scanf() separately.
if (scanf("%s",str) != 1) { /* error */}
But scanf() is not the right tool here since you want to read a whole line. scanf() would stop at the first whitespace (after reading non-whitespace chars).
So when you type "I am a noob at C", scanf() will only read the I and ignore the rest.
What you want is to use the fgets() function to read a line:
char str[100];
if (fgets(str, sizeof str, stdin) == NULL) {
/* error */
}
/* rest of the code */
fgets() would read the newline as well if there's space in the buffer. If this is undesirable, then you can remove it:
char *p = strchr(str, '\n');
if (p) *p = 0; //remove the trailing newline.
Note: strtok() is not a thread safe function. POSIX provides strtok_r() as a thread-safe alternative. This is something to be aware of even if it doesn't matter in this specific case.
Here's a self contained example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char str[100];
char sep[] = " \n";
char *res;
if ( fgets(str, sizeof str, stdin) == NULL ) {
exit(1);
}
res = strtok(str, sep);
while(res != NULL){
printf("Splitted String: \"%s\"\n",res);
res = strtok(NULL,sep);
}
return 0;
}
That is not how scanf() works.
Change the code to
char str[100];
scanf("%s",str);
A little note about scanf()
You should check for return values, like here for scanf().
if (scanf("%s", str) != 1)
{
printf("scanf failed");
exit(0);
}
You should also mention the number of chars to be read by scanf() to avoid buffer overflow.
scanf("%99s", str)
For a char str[100] of size 100, one should give 99 to keep place for the null character \0.

c how to cut of the first x words of a string?

I have a char array:
char tmp[2048];
I want to cut of the first x words of tmp. I define a word as a sequence of characters that does not include whitespaces. I tried something like this (should cut of the first 3 words):
sscanf(tmp, "%*s %*s %*s %s", tmp);
My problem is, that '%s' stops at the first whitespace it finds. I want the new string to end at the end of the old string, not at the first whitespace.
I'm also open for other suggestions how to cut of the first x words of a string. I define a word as a sequence of characters that doesn't contain whitespaces.
Here's a rough implementation:
const char* TrimWords(const char* input, int nWords)
{
while (nWords)
{
if (!isspace(*input) && isspace(*(input + 1)))
{
nWords--;
}
input++;
}
return input;
}
TrimWords("One Two Three Four Five", 3);
// returns " Four Five" after the first 3 words are trimmed.
Detailed input validation and error checking is left to the OP.
This is just a good starting point.
use strncpy(tmp, n, tmp+m); where m and n are ints
char tmp[20] = "abcdef";
strncpy(tmp, tmp + 3, 2);
for exmaple: code above will result in decdef
You can use strtok to tokenize strings by whitespace. Something similar to this could do what you're trying to achieve:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
// Number of words to trim
const int numWords = 2;
char tmp[1024];
char buffer[1024];
sprintf(tmp, "this is a\tsentence.");
strcpy(buffer, tmp);
char* token = strtok(buffer, " \t");
for (int i = 0; i < numWords && token; i++) {
token = strtok(NULL, " \t");
}
if (token) {
size_t len = 1024 - (token - buffer);
memmove(tmp, tmp + (token - buffer), len);
}
else {
memset(tmp, '\0', 1024);
}
// Prints "a sentence."
printf("%s", tmp);
return 0;
}
However, the use of strtok is tricky at best. I would suggest using an approach similar to that of abelenky's answer.

Resources