I'm writing a shell and I'm using getline() with stdin from the keyboard to take commands. I'm having trouble tokenizing the inputs though. I tried using \n as a delimiter in the strtok() function, but it seems not to be working.
For example, I included an if statement to check if the user typed "exit" in which case it will terminate the program. It's not terminating.
Here's the code I'm using:
void main() {
int ShInUse = 1;
char *UserCommand; // This holds the input
int combytes = 100;
UserCommand = (char *) malloc (combytes);
char *tok;
while (ShInUse == 1) {
printf("GASh: "); // print prompt
getline(&UserCommand, &combytes, stdin);
tok = strtok(UserCommand, "\n");
printf("%s\n", tok);
if(tok == "exit") {
ShInUse = 0;
printf("Exiting.\n");
exit(0);
}
}
if (tok == "exit")
tok and exit are pointers, so you are comparing two pointers. This leads to an undefined behavior, since they don't belong to the same aggregate.
This is not the way to compare strings. Use rather strcmp.
if (strcmp (tok, "exit") == 0)
As #Kirilenko stated, you can't compare strings using the == operator.
But that's not it. If you're using getline() you don't need to split the input to lines anyway as getline() only reads a single line. And if you did want to split the input to other delimiters, you'd have call strtok() in a loop till it returns NULL.
Related
I have been trying to make some kind of "my own shell". So, what I have been trying to do is get input with fgets() and execute it with execvp().
If I use execvp with an array made by me, it works as expected. However, if I try to do it with the results of fgets then I get no output.
main() {
char str[64];
char *array[sizeof(str)];
char *p = NULL;
int i = 0;
printf("my_shell >");
fgets(str, sizeof(str), stdin); // Use fgets instead of gets.
p = strtok(str," ");
while (p != NULL) {
array[i++] = p;
p = strtok(NULL, " ");
}
execvp(str, array);
}
As commented by user3386109, the solution was:
First, the array must have a NULL pointer at the end. Second, the delimiter string passed to both strtok should be " \n" (that's a space followed by a newline). You need the newline because fgets will put a newline character into your buffer, and you don't want that newline added to the array as an argument. Finally, put a perror("execvp failed"); after the execvp so that you get some indication of the problem when the execvp fails.
Here is the format of the file that is being read:
type Extensions
application/mathematica nb ma mb
application/mp21 m21 mp21
I am able to read each entry from the file.
Now I want to make a key-value pair where the output for the above entries would be like
{nb: application/mathematica}
{ma:application/mathematica} and so on.
this is my current code to simply read through the entries
char buf[MAXBUF];
while (fgets(buf, sizeof buf, ptr) != NULL)
{
if(buf[0] == '#' || buf[0] == '\n')
continue; // skip the rest of the loop and continue
printf("%s\n", buf);
}
"How to split the a string into separate words in C, where the space is not constant?"
A simple answer would be using strtok() (string.h library), but keep it mind that this function affects your initial string. So, if you want to use it again, you should use an temporary variable equal to your initial string.
char *p = strtok(char *str, const char *delim)
where, in place of delim you should place : " ".
Basically, strtok splits your string according to given delimiters.
Here, i let u an example of strtok:
char* p = strtok(str," ");
while(p != NULL){
printf("%s\n",p);
p=strtok(NULL," ");
}
I am writing a simple command line interpreter. My code reads a string using scanf and parses it using the function getArgs() shown below, and then uses that array as an argument to execvp to perform a command such as ls. It works if I call only 'ls' but when I call 'ls -la', it gives the same result as 'ls'.
void getArgs(char* command, char* args[]){
int i = 0;
char* p = strtok(command, " ");
args[i] = p;
while(p != NULL){
i++;
p = strtok(NULL, " ");
args[i] = p;
}
}
Here is my main function which includes the initialization of the arguments given:
int main(){
char *args[1024];
char example[30];
char exit[5] = {'q', 'u', 'i', 't', '\0'};
int f1;
int status;
size_t n = sizeof(args)/sizeof(args[0]);
while(strncmp(example, exit, 30) !=0){
printf(">>>");
scanf("%s", example);
getArgs(example, args);
int x = strncmp(args[0], exit, 30);
if (x != 0){
f1 = fork();
if (f1 != 0){
/* wait for child process to terminate */
waitpid(f1, &status, 0);
}
else{myExec(args);}}
else{
return 0;}}
return 0;
}
My guess as to the problem is that my argument array, args, is not null terminated and so when I attempt to use it in myExec():
void myExec(char* args[]){
execvp(args[0], args);
}
this does not work. So my question is, can I set the item after the last non-empty part of my array to null to try to get this to work? If so, how can I do that? Is there a better way to solve this?
The -la is being ignored because scanf("%s", example); will stop at the first space. I suggest
scanf(" %29[^\n]", example);
Which will
Ignore whitespace left in the buffer from the previous command.
Restrict the string input from overflowing.
Allow space seperators in the command.
Note too that in the first execution of while(strncmp(example, exit, 30) !=0) the example is an uninitialised variable, so that needs to be
char example[30] = "";
The %s directive stops scanning at the first whitespace character, so it won't properly capture any commands with spaces (such as ls -la). You should use fgets to get user input if you want to preserve any whitespace:
if ( fgets( example, sizeof example, stdin ) )
{
getArgs( example, args);
...
}
fgets will read up to sizeof example - 1 characters into example (including the newline!) and 0-terminate the string. You may want to take that newline into account with your strtok call.
I have use strtok to assign data to a variable, cmd, from a user input. How can i detect if cmd is empty? Similarly to checking if cmd is q as seen below:
void readcmd() {
char read_input[50];
char* cmd;
char* param;
scanf("%[^\n]%*c",read_input);
cmd = strtok(read_input, " ");
param = strtok(NULL, " ");
if (strcmp(cmd, "q") == 0) {
printf("quitting\n");
exit(0);
}
run(cmd, param);
}
The strtok() function will return NULL if no tokens can be obtained from the input string.
I'm assuming that you want to know whether there's any point in calling strtok() at all, in other words does the buffer contain any data for tokenising?
You could set a nul (\0) byte in the first character of the string:
read_input[0] = '\0';
Then call scanf() - after scanf() returns you can check to see if the first byte of the buffer is still a nul byte:
if (read_input[0] != '\0') {
/* call strtok() */
} else {
/* buffer is empty. */
}
The \0 won't cause any problems when you pass it to say, printf() or strtok(), you could still use printf() to output the empty string since the \0 byte simply tells functions like strtok() and printf() that they've reached the end of a string.
Example, you can hand-pack a string:
read_input[0] = 'a';
read_input[1] = 'b';
read_input[2] = 'c';
read_input[3] = '\0';
printf("%s", read_input);
if(strcmp(cmd,"")==0){
...
}
I'm writing a C program where the user can input a string of 1-3 digits followed by a backslash and then another 1-3 digits or they can enter 1-3 digits, followed by a comma, then another 1-3 digits and there is no limit to how many times they can iterate this.
I need to determine whether the input delimiter is a backslash or comma (to determine what to do with the numbers) and put the numbers into an array.
The way I was thinking of doing this was to using strtok as follows. The string is inputted as char *token.
op_tok1 = strtok(token, "\\");
if(op_tok1 != NULL)
{
/* Process numbers */
return;
}
op_tok2 = strtok(token, ",");
if(op_tok2 != NULL)
{
/* Process other numbers */
return;
}
This works for anything delimetered with a backslash, but not with a comma. I believe this is because strtok messes with the token variable. Is this true? Is there a better way to go about this? thanks!
There are certainly ways I'd consider better. If you can depend reasonably well on the format of the input (i.e., really being three digits followed by one of the allowed delimiters), you could do something like:
char *pos = 0;
while (2 == sscanf(input+pos, "%d%c", &number, &delimiter)) {
if ('\\' == delimiter)
process_backslash(number);
else if (',' == delimiter)
process_comma(number);
else
error_invalid_delimiter(delimiter);
pos += 4;
}
Others have posted better solutions - strtok is not really suitable for this task. However, answering the first question - is strtok changing the underlying string, Yes (it's evil in my mind how it works. Many a young player has fallen into this trap):
strtok replaces token with \0 (Null terminator) and passes the start of the string. Subsequent calls to strtok(NULL, <token>) continue scanning the string, looking for the next token, which does not need to be the same.
Therefore you could do:
op_tok1 = strtok(token, "\\");
if(op_tok1 != NULL)
{
/* Process numbers */
return;
}
op_tok2 = strtok(NULL, ",");
if(op_tok2 != NULL)
{
/* Process other numbers */
return;
}
Also beware it is not thread safe.
Why not just use scanf()?
~/tmp$ cat test.c
#include <stdio.h>
int main(int argc, char ** argv) {
int i;
char c;
while (2 == scanf("%d%[\\.]",&i,&c)) {
printf("Int %d\nChar %c\n", i, c);
}
}
... worked for me.
~/tmp$ gcc test.c && echo "123.456\789.4" | ./a.out
Int 123
Char .
Int 456
Char \
Int 789
Char .
~/tmp$