I have an assignment to write a program that can be used in the bash shell that mimics certain default Unix commands, and we are supposed to build them from scratch. One of these commands is the PS1 command, which is supposed to change the $ prompt to whatever argument the command is given. I have implemented this in the code below, and it works almost perfectly.
Prior to using the PS1 command, the prompt works correctly, it prints the $ and does not indent, rather it lets the user continue typing on the same line. However, after using the command, whenever a prompt is supposed to come up, the program will print the prompt, and then go to a new line. I need it to print the PS1 char* without going to a newline.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
int exit = 0;
char* PS1 = "$";
while(exit == 0){
char* token;
char* string;
char input[500];
printf("%s", PS1);
fgets (input, 500, stdin);
token = strtok(input, " ");
if(strncmp(token, "exit", 4) == 0){
exit = 1;
break;
}
else if(strncmp(token, "echo", 4) == 0){
token = strtok (NULL, " ");
while (token != NULL){
printf ("%s", token);
printf("%s", " ");
token = strtok (NULL, " ");
}
}
else if(strcmp(token, "PS1") == 0){
token = strtok (NULL, " ");
char temp[300];
strcpy(temp, &input[4]);
PS1 = temp; }
}
}
fgets retains the newline character at the end, so that gets printed. You could get rid of that after reading the line:
fgets (input, sizeof(input), stdin);
strtok(input, "\n");
Your code has other issues:
... else if (strcmp(token, "PS1") == 0) {
token = strtok (NULL, " ");
char temp[300];
strcpy(temp, &input[4]);
PS1 = temp;
}
The character array temp is local to the block in curly braces and will be invalid after the closing }. That means that PS1 is a handle to invalid memory. That is undefined bevaiour. It may not be visible right now, but it will bite you later, when you add more commands.
It might be better to make PS1 an array if chars that is visible throughout main and copy to that. (The array can be initialised to hold "$" at the beginning.)
You should also avoid the explicit index at &input[4]. Let the tokenisation with strtok handle this. After all, there might be additional white space and " PS1 Command: " is valid input.
Related
I am attempting to execute command lines given by user input but for some reason the execvp() function isn't executing the command. I read in a user input and split it, store it into an array and use the excevp() function to execute it. I even printed out the spots in the array to make sure it was placing the token into the right spot and it is. Here is my code in C
char b[100];
int i = 0;
char *token;
char *array[3];
printf("Please enter the command you want to use: ");
fgets(b, 100, stdin);
token = strtok (b, " ");
while (token != NULL){
array[i++] = token;
printf("%s\n",token);
token = strtok(NULL, " ");
}
printf("%s", array[0]);
printf("%s", array[1]);
execvp(array[0], array);
So for example if I were to type in "ls" into the command line in the program and hit enter it will just go to the next line and nothing will execute. Are there any recommendation to fix this because I am lost on where to begin?
The problem is array[0] is ls\n (as fgets reads newline character as well) and not ls you have to remove \n as well.
You can simply create an array of delimiters like this:
char delimiters[] = " \t\n";
and then simply do
token = strtok(b, delimiters); // Use delimiters instead of only " " (whitespace)
while (token != NULL) {
array[i++] = token;
printf("%s\n", token);
token = strtok(NULL, delimiters); // Use delimiters instead of only " " (whitespace)
}
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.
I am trying to read a file line by line and split it into words. Those words should be saved into an array. However, the program only gets the first line of the text file and when it tries to read the new line, the program crashes.
FILE *inputfile = fopen("file.txt", "r");
char buf [1024];
int i=0;
char fileName [25];
char words [100][100];
char *token;
while(fgets(buf,sizeof(buf),inputfile)!=NULL){
token = strtok(buf, " ");
strcpy(words[0], token);
printf("%s\n", words[0]);
while (token != NULL) {
token = strtok(NULL, " ");
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
}
After good answer from xing I decided to write my FULL simple program realizing your task and tell something about my solution. My program reads line-by-line a file, given as input argument and saves next lines into a buffer.
Code:
#include <assert.h>
#include <errno.h>
#define _WITH_GETLINE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define assert_msg(x) for ( ; !(x) ; assert(x) )
int
main(int argc, char **argv)
{
FILE *file;
char *buf, *token;
size_t length, read, size;
assert(argc == 2);
file = fopen(argv[1], "r");
assert_msg(file != NULL) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
token = NULL;
length = read = size = 0;
while ((read = getline(&token, &length, file)) != -1) {
token[read - 1] = ' ';
size += read;
buf = realloc(buf, size);
assert(buf != NULL);
(void)strncat(buf, token, read);
}
printf("%s\n", buf);
fclose(file);
free(buf);
free(token);
return (EXIT_SUCCESS);
}
For file file.txt:
that is a
text
which I
would like to
read
from file.
I got a result:
$ ./program file.txt
that is a text which I would like to read from file.
Few things which is worth to say about that solution:
Instead of fgets(3) I used getline(3) function because of easy way to knowledge about string length in line (read variable) and auto memory allocation for got string (token). It is important to remember to free(3) it. For Unix-like systems getline(3) is not provided by default in order to avoid compatibility problems. Therefore, #define _WITH_GETLINE macro is used before <stdio.h> header to make that function available.
buf contains only mandatory amount of space needed to save string. After reading one line from file buf is extended by the required amount of space by realloc(3). Is it a bit more "universal" solution. It is important to remember about freeing objects allocated on heap.
I also used strncat(3) which ensures that no more than read characters (length of token) would be save into buf. It is also not the best way of using strncat(3) because we also should testing a string truncation. But in general it is better than simple using of strcat(3) which is not recommended to use because enables malicious users to arbitrarily change a running program's functionality through a buffer overflow attack. strcat(3) and strncat(3) also adds terminating \0.
A getline(3) returns token with a new line character so I decided to replace it from new line to space (in context of creating sentences from words given in file). I also should eliminate last space but I do not wanted to complicate a source code.
From not mandatory things I also defined my own macro assert_msg(x) which is able to run assert(3) function and shows a text message with error. But it is only a feature but thanks to that we are able to see error message got during wrong attempts open a file.
The problem is getting the next token in the inner while loop and passing the result to strcpy without any check for a NULL result.
while(fgets(buf,sizeof(buf),inputfile)!=NULL){
token = strtok(buf, " ");
strcpy(words[0], token);
printf("%s\n", words[0]);
while (token != NULL) {//not at the end of the line. yet!
token = strtok(NULL, " ");//get next token. but token == NULL at end of line
//passing NULL to strcpy is a problem
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
}
By incorporating the check into the while condition, passing NULL as the second argument to strcpy is avoided.
while ( ( token = strtok ( NULL, " ")) != NULL) {//get next token != NULL
//if token == NULL the while block is not executed
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
Sanitize your loops, and don't repeat yourself:
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *inputfile = fopen("file.txt", "r");
char buf [1024];
int i=0;
char fileName [25];
char words [100][100];
char *token;
for(i=0; fgets(buf,sizeof(buf),inputfile); ) {
for(token = strtok(buf, " "); token != NULL; token = strtok(NULL, " ")){
strcpy(words[i++], token);
}
}
return 0;
}
I have a simple C-based code to read a file. Read the input line by line. Tokenize the line and prints the current token. My problem is, I want to print the next token if some conditions are satisfied. Do you have any idea how to do it. I really need your help for this project. Thank you
Here is the code:
main(){
FILE *input;
FILE *output;
//char filename[100];
const char *filename = "sample1.txt";
input=fopen(filename,"r");
output=fopen("test.st","w");
char word[1000];
char *token;
int num =0;
char var[100];
fprintf(output,"LEXEME, TOKEN");
while( fgets(word, 1000, input) != NULL ){ //reads a line
token = strtok(word, " \t\n" ); // tokenize the line
while(token!=NULL){ // while line is not equal to null
fprintf(output,"\n");
if (strcmp(token,"SIOL")==0)
fprintf(output,"SIOL, SIOL", token);
else if (strcmp(token,"DEFINE")==0)
fprintf(output,"DEFINE, DEFINE", token);
else if (strcmp(token,"INTEGER")==0){
fprintf(output,"INTEGER, INTEGER");
strcpy(var,token+1);
fprintf(output,"\n%s,Ident",var);
}
else{
printf("%s\n", token);
}
token = strtok(NULL, " \t\n" ); //tokenize the word
}}fclose(output);return 0;}
Continuing from my comment. I'm not sure I completely understand what you need, but if you have the string:
"The quick brown fox";
And, you want to tokenize the string, printing the next word, only if a condition concerning the current word is met, then you need to adjust your thinking just a bit. In your example, you want to print the next word "quick", only if the current word is "The".
The adjustment in thinking is how you look at the test. Instead of thinking about printing the next word if the current matches some condition, you need to save the last word, and only print the current if the last word matches some condition -- "The" in your example.
To handle that situation, you can make use of a statically declared character array of at least 47 characters (the longest word in Merriam-Websters Unabridged Dictionary is 46-character). I'll use 48 in the example below. You may be tempted to just save a pointer to the last word, but when using strtok there is no guarantee that the memory address returned by the previous iteration is preserved -- so make a copy of the word.
Putting the pieces together, you could do something like the following. It saves the prior token in last and then compares the current word to the last and prints the current word if last == "The":
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXW 48
int main (void) {
char str[] = "The quick brown fox";
char last[MAXW] = {0};
char *p;
for (p = strtok (str, " "); p; p = strtok (NULL, " "))
{
if (*last && strcmp (last, "The") == 0)
printf (" '%s'\n", p);
strncpy (last, p, MAXW);
}
return 0;
}
Output
$ ./bin/str_chk_last
'quick'
Let me know if you have any questions.
Test Explanation
As written in the comment *last is simply shorthand for last[0]. So the first part of the test, *last is just testing if ((last[0] != 0) && ... Since last was initially declared and initialized:
char last[MAXW] = {0};
All chars in last are 0 for the first pass through the loop. By including the check last[0] != 0, that just causes the printf to be skipped the first time the for loop executes. The longhand for the test would look like:
if ((last[0] != 0) && strcmp (last, "The") == 0)
printf (" '%s'\n", p);
Which in pseudo code just says:
if (NOT first iteration && last == "The")
printf (" '%s'\n", p);
Let me know if that doesn't make sense.
It is easy to achieve with strtok function. Note that if you put null pointer as the first argument, the function continues scanning the same string where a previous successful call to the function ended. So if you need next token just call
char* token = strtok(NULL, delimeters);
See small example below
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "The quick brown fox";
// split str by space
char* token = strtok(str, " ");
// if a token is found
if(token != NULL) {
// print current token
printf("%s\n", token);
// if token is "The"
if(strcmp(token, "The") == 0) {
// print next token
printf("%s\n", strtok(NULL, " "));
}
}
return 0;
}
The output will be
The
quick
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.