basic CLI program in C - c

Okay so overall im trying to complete a basic CLI C program which will complete functions such as clear, quit, cd, ls, help (bring up the unix man) etc.. i altered my code and so far i have this, im getting segmination error when trying to execute the cd command part of the program, (im very new to c btw);
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main (int argc, char *argv[])
{
char input[] = " ";
char *argument;
while(strcmp(input, "quit")!= 0)
{
printf("$");
scanf ("%s", input);
if(strcmp(input,"clear") == 0)
{
printf("\e[1;1H\e[2J");
}
else if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
else if(strcmp(input, "echo") == 0)
{
char str[50];
scanf("%[^\n]+", str);
printf(" %s", str);
}
}
}

input is declared as a ' ' (space) character. It will never match 'cd'.
This is probably more along the lines of what you want to achieve, where the first parameter is the command (cd), and the second will be the directory:
int main (int argc, char *argv[])
{
char *argument;
if(strcmp(argv[1],"cd") == 0)
{
if(chdir(argv[2]) == -1)
{
printf("\n directory does not exists");
}
}
Edit Also please note that there is no need for the else satement. If chdir does not return an error, it will change the directory, thus no need to call it again in an else.
Additionally, another tip for using system calls in general, it would be of great help if you print the error number returned by the system upon a failure in system call. This will make things easier when things start going wrong. To do this simply include <errno.h>' and modify the printf to printerrno` which gives specific details about the error:
printf("Chdir error: %d", errno);
For instance chdir() does not only return an error when the directory does not exist, but also for example if you do not have permissions to view the contents of the directory. See the man page for a list of possible errors.

To implement your own shell, you need to take input directly from stdin, not from command-line arguments (argv) from another shell. The basic pattern is like this:
Read input
Execute command
Print results
Loop back to step 1

Related

issue executing unix commands using system() in c

I'm currently trying to write a program to accept user input to and execute commands in a unix system, the code compiles but when I run it I get a segmentation fault. I think it might be related to the input data type for the system() function but I can't seem to figure it out
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
char argument[1024];
main() {
fgets(argument, 1024, stdin);
strtok (argument, "\n");
char *command = strcat("cd ", argument);
int response = system(command);
if(response == -1)
{
printf("error executing command");
}
}
I apologise if this seems trivial I don't have much experience with c
You are programming in C in an Unix system.
The language C was written just to write Unix itself. system() was written in C, Unix, Windows, Linux, Android, MacOS, Python, java, everything was written in C at first and still is at the most part.
And you have many shells in Unix and derivatives that just run commands as the user types them.
So to use system() to run a command does not add much. Just another level of indirection, an a big security hole, since your program can be used to do unexpected things by not so good people. Using YOUR program.
An alternative
This is the output of the program below
Current directory is: /home/testing/projects/tcursor
directory changed to '/tmp'
Enter name of directory to create: thing
'/tmp/thing' created
Now changing to newly created folder
Current directory is: '/tmp/thing'
The program just
cwd to /tmp
prompt for the name a folder to create
creates the folder
cd to it
shows current dir
returning 1,2,3,4 in case of error, 0 in case of success
So you can see some C code to cd, pwd and mkdir
int main(void)
{
char asw[30];
char buffer[1024];
char* p = buffer;
const char* temp = "/tmp";
p = getcwd(p,1024);
printf("Current directory is: %s\n",p);
int n = chdir(temp);
if ( n != 0 ) return(1);
printf("directory changed to '%s'\n", temp);
printf("Enter name of directory to create: ");
fgets(asw,30,stdin);
asw[strlen(asw)-1] = 0;
n = mkdir(asw, 0777 );
if ( n != 0 ) return(2);
printf("'%s' created\n", buffer);
printf("Now changing to newly created folder\n");
sprintf(buffer,"%s/%s", temp, asw);
n = chdir(buffer);
if ( n != 0 ) return(4);
p = getcwd(p,1024);
printf("Current directory is: '%s'\n",p);
return 0;
Do not use system()

How do you generally scan/parse commands in interactive REPL programs in C, is there a standard way?

I am writing an interactive REPL program in c.
Some examples of commands (lines starting with >) I would like to handle are:
$ ./my_program // run the program
> add user
id: 123 // this is the output of above command
> update user 123 name "somename"
> remove user 123
> quit
So basically the command is a line with multiple strings.
This is how I am trying to handle the commands.
scan the whole line
parse the command and get a corresponding int value unique to command
do whatever needs to be done for the command
#include <stdio.h>
int parse_cmd(const char *buffer)
{
// parse command
}
int main(int argc, const char **argv)
{
// init code
char buffer[100];
int cmd;
while (1) {
printf("> ");
scanf("%[^\n]%*c", buffer);
cmd = parse_cmd(buffer);
if (cmd < 0) {
printf("error: invalid command\n");
continue;
}
switch (cmd) {
// handle commands
}
}
// deinit code
}
There are a lot of cli programs I have seen that take command inputs in similar way.
I wonder if there is a general way of writing cli programs?
I can write code to parse the commands, just wanted to know the standard approach for such situations?
While there's no real standard way, quite a lot of opensource console tools with an interactive mode use the GNU readline library (https://tiswww.case.edu/php/chet/readline/rltop.html).
It's actually quite easy to use, even simpler than implementing everything 100% correctly by yourself.
Your example rebased on readline:
int main(int argc, const char **argv)
{
// init code
int cmd;
char* line;
while (1) {
line = readline("> ");
if (line) {
cmd = parse_cmd(line);
switch (cmd) {
// handle commands
default:
printf("error: invalid command\n");
}
free(line);
} else {
break;
}
}
// deinit code
}
This isn't any more complex than your example, but you immediately gain:
command line editing at the interactive prompt, with correct handling of each and every possible terminal
correct handling of EOF (important if stdin is redirected)
unlimited input line size
And it's not very hard to add a command history, with arrow-up and down to repeat previous lines, incremental search, optionally persisted to a file, et et.
There's not really a standard way to do it. This is not a 100% fair comparison, but your question is kind of like if there is a standard way to construct a compiler, because you are in fact constructing a language, although a very simple one.
But one reasonably common way that works fairly well for simple programs is this approach. Let's assume that we have two commands add and del. Create a function for both these commands. First we search for one of the strings "add " or "del ". Notice the spaces. Put a pointer on the next character and call the corresponding function with the rest of the line as argument and allow them to determine things.
Here is some pseudo:
parse(bufferptr)
word = getFirstWord(bufferptr)
ptr = bufferptr + strlen(word)
if word == "add"
return add(ptr)
else if word == "del"
return del(ptr)
return -1
add(bufferptr)
word = getFirstWord(bufferptr)
if userExist(word)
return -1
else
return addUser(word)
del(bufferptr)
word = getFirstWord(bufferptr)
if not userExist(word)
return -1
else
return delUser(word)
buffer = input()
int res = parse(buffer)

Create simple shell in C with a "Set prompt" command

#include <string.h>
#include <errno.h>
#define MAX_LENGTH 1024
#define SPACES " \t\r\n"
int main(int argc, char *argv[]){
char line[MAX_LENGTH];
char *cmd;
char *PROMPT = "SUPER SHELL!!";
while(1){
printf(PROMPT);
if (!fgets(line, MAX_LENGTH, stdin)) break;
//parse and execute commands
if((cmd = strtok(line, SPACES))){
// clean errors
errno=0;
if (strcmp(cmd, "quit") ==0){
break;
}
else if(strcmp(line, "Set Prompt") == 0){
char *arg = strtok(0, SPACES);
if (!arg){
fprintf(stderr, "You were missing a prompt. \n");
}
else {PROMPT =arg;
}
}
else system(line);
if(errno) perror ("Command failed. sorry");
}
return 0;
}
I am writing a program in which I have to write my own shell in C.
There must be two commands : quit, which quits the program, and Set Prompt which changes the prompt.
For some reason quit works but Set prompt does not.
How can I fix this?
Thank you
Your program indeed seems to have a lot of problems.
Trying to give you some hints, where to improve without solving the whole exercise for you, here some points I stumbled across:
Gemeral problems
The program doesn't compile!
missing closing brace (most probably on line 35) resulting in:
error: expected declaration or statement at end of input
missing include of stdio.h resulting in:
error: ‘stdin’ undeclared (first use in this function)
...
the last one rather a result of the -Werror switch used in my compilation attempt:
error: format not a string literal and no format arguments [-Werror=format-security]
Nevertheless it might not be the worst idea to change printf(PROMPT); on line 13. (see: http://en.wikipedia.org/wiki/Uncontrolled_format_string)
Logic problems
the block following
else if(strcmp(line, "Set Prompt") == 0){ on line 24
will never be executed as the prior call to strtok() will have replaced any input of the form "Set Prompt whatever..." with "Set\0Prompt whatever..." not(!) with "Set Prompt\0whatever..."
Even after fixing this issue there will still be some spooky behavior left.
To reproduce try changing the set prompt command to "SetPrompt" in your code and start a session. I'd be surprised if you came up with a different result than me:
SUPER SHELL!!SetPrompt myprompt:
myprompt:what the hell is going on here?
sh: 1: what: not found
ell is going on here?
quit
Hint: the simple assignment on line 29 else {PROMPT =arg; will not do, as the memory arg is pointing to will be overwritten the next time fgets(line, MAX_LENGTH, stdin) gets called on line 14.
Try to reserve some extra memory for the prompt string and look up strcpy()

Problems with system() in C

I'm quite new at C programming. Im using a Mac and I just created this app program with Matlab. This app what it does is, that creates a .txt based on some data. When I run it directly from the terminal it creates my .txt file, but when I try to execute this app using the following code (First test code) :
int main(int argc, const char * argv[])
{
char test_Exe[BUFSIZ];
// Build a buffer, and execute the commands within it
strcpy (test_Exe, "/Users/pepe1503/Documents/Pepe/Stage_Bruker/HS_Conversion_Topspin_Bucket_Matlab/matlab/MP_ToolBox/small_toolbox/test_Exe_D2O/distrib/run_test_Exe_D2O.sh");
strcat (test_Exe, " /Applications/MATLAB/MATLAB_Compiler_Runtime/v715 /Users/pepe1503/Documents/Pepe/Stage_Bruker/HS_Conversion_Topspin_Bucket_Matlab/matlab/MP_ToolBox/small_toolbox/D20/nmr/Agrifood_Caviar_Sturgeon_D2O/4/pdata");
printf ("Executing your Matlab program: %s\n", test_Exe);
system (test_Exe);
return 0;
}
I got the same printed output as the terminal but it doesn't build my .txt file.
Does anyone know the reason?
TY!
Following your suggestions I recently changed my code to (keeping my last program in another code just in case) :
int main(int argc, const char * argv[])
{
char* arg_list[] = {
"run_test_Exe_D2O.sh",
"/Applications/MATLAB/MATLAB_Compiler_Runtime/v715",
"/Users/pepe1503/Documents/Pepe/Stage_Bruker/HS_Conversion_Topspin_Bucket_Matlab/matlab/MP_ToolBox/small_toolbox/D20/nmr/Agrifood_Caviar_Sturgeon_D2O/4/pdata",
NULL
};
pid_t child_pid;
child_pid = fork();
if ( child_pid != 0 ) {
// This is the parent process.
return child_pid;
}
else {
// Now execute PROGRAM, searching for it in the path.
fprintf(stdout, "\n");
fprintf(stdout, "Executing your Matlab program:\n");
execvp("/Users/pepe1503/Documents/Pepe/Stage_Bruker/HS_Conversion_Topspin_Bucket_Matlab/matlab/MP_ToolBox/small_toolbox/test_Exe_D2O/distrib/run_test_Exe_D2O.sh", arg_list);
// The execvp function returns only if an error occurs.
fprintf (stderr, "an error occurred in execvp\n");
abort ();
}
return 0;
}
It still executes the application, now not printing the messages of the application as with system(), but it stills not create my .txt that I can obtain by executing it from my terminal. To be more precise with system() it does create my .txt but it erase it after the program execution.
Any sugestions of why it doesn't print my app messages and why it does create it but erase it ?
You are trying to execute the following program:
"/Users/pepe1503/Documents/Pepe/Stage_Bruker/HS_Conversion_Topspin_Bucket_Matlab/matlab/MP_ToolBox/small_toolbox/test_Exe_D2O/distrib"
Are you sure this is what you intended?
Try to use "bash run_test_Exe_D2O.sh" or "sh run_test_Exe_D2O.sh" instead raw script filename. Also, are you sure that whitespaces in script file name are OK?
Ok I found the answer. The problem was with system that it creates I think a temporary file each time Xcode is lunched. In which it was storing my .txt files.

Unable to use "execve()" successfully

The aim of the program is to fork a new child process and execute a process which also has command line arguments. If I enter /bin/ls --help, I get the error:
shadyabhi#shadyabhi-desktop:~/lab/200801076_lab3$ ./a.out
Enter the name of the executable(with full path)/bin/ls --help
Starting the executable as a new child process...
Binary file to be executed: /bin/ls
/bin/ls: unrecognized option '--help
'
Try `/bin/ls --help' for more information.
Status returned by Child process: 2
shadyabhi#shadyabhi-desktop:~/lab/200801076_lab3$
What would be the right argument to execve()?
#include<stdio.h>
#include<string.h> //strcpy() used
#include<malloc.h> //malloc() used
#include<unistd.h> //fork() used
#include<stdlib.h> //exit() function used
#include<sys/wait.h> //waitpid() used
int main(int argc, char **argv)
{
char command[256];
char **args=NULL;
char *arg;
int count=0;
char *binary;
pid_t pid;
printf("Enter the name of the executable(with full path)");
fgets(command,256,stdin);
binary=strtok(command," ");
args=malloc(sizeof(char*)*10);
args[0]=malloc(strlen(binary)+1);
strcpy(args[0],binary);
while ((arg=strtok(NULL," "))!=NULL)
{
if ( count%10 == 0) args=realloc(args,sizeof(char*)*10);
count++;
args[count]=malloc(strlen(arg));
strcpy(args[count],arg);
}
args[++count]=NULL;
if ((pid = fork()) == -1)
{
perror("Error forking...\n");
exit(1);
}
if (pid == 0)
{
printf("Starting the executable as a new child process...\n");
printf("Binary file to be executed: %s\n",binary);
execve(args[0],args,NULL);
}
else
{
int status;
waitpid(-1, &status, 0);
printf("Status returned by Child process: %d\n",WEXITSTATUS(status));
}
return 0;
}
The first entry in the args array should be the program name again. Your code calls /bin/ls with --help as the process name.
Please check to make sure args is not getting clobbered by the realloc call. See here on SO regarding realloc
Edit:
Also the loop looks funny....
You called strtok like this:
binary=strtok(command," ");
Change the loop construct to use binary instead as shown...
char *tmpPtr;
while (binary != NULL){
if ( count%10 == 0) tmpPtr=realloc(args,sizeof(char)*10);
if (tmpPtr != NULL) args = tmpPtr;
count++;
args[count-1]=malloc(strlen(binary)+1);
strcpy(args[count-1],binary);
binary = strtok(command, " ");
}
And use the binary for copying the string....
Hope this helps,
Best regards,
Tom.
Your program has some obvious errors. For instance, declaring char **args=NULL; and then args=realloc(args,sizeof(char)*10); (since it's char**, you should be alloc-ing to char*, no?..).
Since sizeof(char*) is usually 4 while sizeof(char) is usually 1, you end up with some serious memory management problems around there (you alloc less than you use, and you end up writing where you shouldn't). From there on, all hell breaks loose and you can't expect your program's behavior to make any sense.
I'd suggest that you run your program through an util such as Valgrind to figure out memory leaks and correct the program appropriately. Probably your execve problems will disappear as soon as the memory problems are corrected.

Resources