I am writing a custom shell in c, and have encountered a strange little bug, which can be seen in the following testcase:
#include <stdio.h>
#include <stdbool.h>
#include <readline/readline.h>
#include <readline/history.h>
int main(int argc, char **argv, char **env) {
while(true) {
fprintf(stdout, "input: ");
char *line = readline(NULL);
if(line) {
add_history(line);
}
}
return 0;
}
The program spits out the prompt, and if I type something like this:
input: foo bar baz
And then go to erase the line, input: gets erased as well (once I hit the first character I inputted). Is there anyway to fix this? The only thing I could think to do was fflush(stdout) but that didn't work.
Tell readline() about the prompt:
#include <readline/readline.h>
#include <readline/history.h>
int main(void)
{
char *line;
while ((line = readline("input: ")) != 0)
add_history(line);
return 0;
}
When you do the prompting yourself, readline() doesn't know about the presence of the prompt, and therefore mishandles redrawing the line when you erase the last character you entered (moving the cursor back to the beginning of the line). When it knows about the prompt, it doesn't erase it.
Related
When I run this c snippet, it outputs something really random every time, and then segfaults...
Code:
#include <stdio.h>
#include <stdlib.h>
int parse(void) {
int i = 0;
int system(const char *command);
char line[1024];
scanf("%[^\n]", line);
system(line);
do {
line[i] = "\0";
i++;
} while (i != 1024);
parse();
}
int main(void) {
parse();
return 0;
}
What I expected was a prompt, and when any shell command is entered (I used pwd for my testing), the output of the command prints and the prompt returns. And this is what actually happened:
Output:
> pwd
/home/runner/c-test
sh: 1: �: not found
sh: 1: : not found
sh: 1: ׀: not found
signal: segmentation fault (core dumped)
Print prompt
Do not use scanf
Use static storage duration buffer
#include <stdio.h>
#include <stdlib.h>
int parse(void)
{
static char line[1024];
while(1)
{
printf(">>>");
if(!fgets(line, 1024, stdin)) return 1;
system(line);
}
}
int main(void) {
parse();
}
https://www.onlinegdb.com/wmPB3ZGNQ
The reason for your crash is most likely explained by the following:
scanf("%[^\n]", line);
means keep reading until there is a newline in the input stream. So once it completes the next character in the input stream is a newline.
The next time you do
scanf("%[^\n]", line);
that newline is still the first character, so the scanf will return without waiting for the user to type any input.
Since you are doing recursion this will happen again and again and again.... Then most likely the system crashes due to stack overflow after a while.
Solution: Read that newline from the input stream before calling scanf again.
Besides that you should:
Remove int system(const char *command);
Put a maximum field width on scanf
Check the return value of scanf
Change "\0" to '\0' (or delete the whole do-while loop as you don't need it)
Use a while(1) { ... } loop instead of recursive calls
I'm using execvp() to run some system calls. Program works great for valid commands, and fails for any command that doesn't exist, which is perfect.
The program is, when I use execvp() on a command that needs extra arguments(like cat) and I don't provide arguments, the program just infinitely reads from input.
I'm not sure how to get around this issue, as I don't know how to 'tell' if a command is incomplete. Any ideas?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
char command[1000];
printf("Enter command: ");
scanf("%[^\n]s", command);
char *temp = strtok(command, " ");
char *commandList[100];
int index = 0;
while (temp != NULL) {
commandList[index] = temp;
index++;
temp = strtok(NULL, " ");
}
commandList[index] = NULL;
execvp(commandList[0], commandList);
printf("Failed");
}
The ideal result would be a print of "Command incomplete" and the process ending.
One of the ideas from the comment completely answered my question (To the exact needs I had). Not sure how to give him credit on here, though.
The solution is to simply close stdin right before I use execvp(). If the command is not completed on the first scanf, the program throws an error, which is perfect.
Since I'm running the main program I'm using this on in a loop, I can use dup and dup2 to save and reload stdin later.
The code I used to test if it'll work:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
char command[1000];
int stdinput = dup(STDIN_FILENO);
close(STDIN_FILENO);
dup2(stdinput, STDIN_FILENO);
printf("Enter command: ");
scanf("%[^\n]s", command);
printf("%s\n", command);
}
I'm trying to read in inputs from the terminal, I want to stop reading inputs if there is a blank line and enter is pressed. Here is what I have at the moment.
#include <stdio.h>
#include <string.h>
int main(int argc,char const *argv[]){
char input[1024];
while(fgets(input,1024,stdin)!=NULL){
printf("%s", input);
}
if(EOF){
printf("EOF");
}
return 0;
}
One easy thing that you could do is to check the length of the string read in by fgets. The newline character at the end of the input string is considered a valid character by fgets. That means that the length of the string you're testing would be 1 if you just entered a newline So, you want to move the test inside the loop instead of trying to test for EOF outside of the loop. So, your code should look like this:
#include <stdio.h>
#include <string.h>
int main(void) {
char input[1024];
while(fgets(input,1024,stdin)!=NULL) {
printf("%s", input);
if(strlen(input) == 1) {
printf("EOF\n");
break;
}
}
return 0;
}
Also note, to get the EOF test to work you wouldn't hit enter, instead you'd send the end of file key, like CTRL-D on linux to your program. In this case you wouldn't have a strlen test inside the loop.
I'm trying to create a simple shell program which execute the program specified in input. There are two main function: scanner() (use strtok to split the input in token) and execute() (fork the process and execute the program).
Unfortunately it doesn't work... I've tried to print string[0] at the end of scanner() and at the beginning of execute(). The first time the output is correct but the second time string[] seems to be modified in a sequence of random numbers so execvp() doesn't work...
I really can't figure out why the values of string[] changes, probably is a very stupid error but I can't see it. I really need your help! Thanks in advice.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define DIM 256
int scanner(char*[]);
int execute(char*[]);
int main()
{
char* string[DIM];
scanner(string);
execute(string);
}
/* scan: read the input in token*/
int scanner(char* string[])
{
char input[1024];
char delimit[]=" \t\r\n\v\f";
int i = 0;
if(fgets(input, sizeof input, stdin)) {
string[i] = strtok(input, delimit);
while(string[i]!=NULL){
i++;
string[i]=strtok(NULL,delimit);
}
return 0;
}
return 1;
}
/* execute: execute the command*/
int execute(char* string[])
{
int pid;
printf("%s\n", string[0]);
switch(pid = fork()){
case -1:
return 1;
case 0:
execvp(string[0], string);
return 1;
default:
wait((int*)0);
return 0;
}
}
The string variable input in scanner is a local variable, with storage class "auto". That means that when that function returns, that variable disappears, and the memory it occupied can be re-used for other things. That is unfortunate, since strtok returns pointers into that string variable.
My program reads and stores standard input into a char array cmd and then call system(cmd). I printed out cmd and its content is what I expected. But system(cmd) does not save the content in report.log. I tried with a literal string stored in cmd2, and this time it worked. So what is wrong with system(cmd)?
And I am using Windows 8.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#define MAXLEN 100
char *now(){
time_t t;
time(&t);
return asctime(localtime(&t));
}
int main(int argc, char *argv[])
{
char comment[80];
char cmd[120];
fgets(comment, 80, stdin);
sprintf(cmd, "echo '%s %s' >> report.log", comment, now());
printf("%s", cmd); // content of cmd is what I expect
system(cmd); // does not work, why?
char cmd2 = "echo 'Hello world' >> report.log";
system(cmd2); // work
return 0;
}
Your problem maybe with the excess \ns present in your inputs to sprintf().
fgets() scans and stores the \n from the stdin. You need to get rid of that \n and replace that with a null.
asctime() returns a ctime() return style string, again ended with a newline \n. You need to remove (replace) that too.
You can check the following code for your reference.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#define MAXLEN 100
char *now(){
time_t t;
time(&t);
return asctime(localtime(&t));
}
int main(int argc, char *argv[])
{
char comment[80] = {0};
char cmd[120] = {0};
char * timestring = NULL; //initialize local variables, good practice
fgets(comment, 80, stdin);
comment[ strlen(comment) -1] = 0; //reomve the trailing \n taken by fgtes(), replace by null
timestring = now();
timestring[strlen(timestring)-1] = 0; //remove the \n from ctime() return style string, replace b null
sprintf(cmd, "echo '%s %s' >> report.log", comment, timestring);
printf(">> The string is : %s\n", cmd);
system(cmd); // should work now.. :-)
return 0;
}