So I was trying to make a very primitive kind-of-shell and I ended ip doing the follow. I just want an opinion
This is my header where I declare the available funtions and make an array of structs where each struct has the name of the function to call the number of arguments it needs and a pointer to the function to call
Code:
#ifndef UTILITIES_H
#define UTILITIES_H
/* ======================== FUNCTIONS ======================== */
int my_mkfs(int argc, char **argv);
int my_mount(int argc, char **argv);
int my_unmount(int argc, char **argv);
int my_quit(int argc, char **argv);
int my_help(int argc, char **argv);
int my_ls(int argc, char **argv);
int my_cp(int argc, char **argv);
int my_mv(int argc, char **argv);
int my_rm(int argc, char **argv);
int my_df(int argc, char **argv);
int my_cat(int argc, char **argv);
int my_echo(int argc, char **argv);
/* ======================== GLOBALS,STRUCTS,CONSTS ======================== */
//The following are mostly to simplify things for me as this is my first time trying something of this sort
//restriction of mine: each argument can have a length of 16 characters - max length of filenames of my filesystem
#define ARG_LENGTH 16
// restriction of mine: at most there can be 8 arguments
#define MAX_ARGS 8
/*
My struct where I store pointers to functions. As you can see it has the name of the function it calls, the number
of arguments it takes (including the command name - I thought of it like the arguments of main in c) and the
pointer to the function it calls which are declared above and are implemented in other files. They all have the
same format, return type int, arguments an int (number of arguments) and a char ** the arguments themseleves
*/
struct cmd_det{
const char *name;
int no_args;
int (*func_to_call) (int argc, char **argv);
};
extern const struct cmd_det function_index[];
extern int shell_running;
#endif
And here is the shell implementation
Code:
#include <stdio.h> //printf()
#include <stdlib.h> //malloc(),
#include <string.h> //strcmp(),
#include "utilities.h"
int break_cmd_line(char *line, char **argumentsR);
void clear_buffers(char *buf1, char **buf2);
int shell_running; //state of shell - when running will be 1 and when it turns 0 it will exit
//Here I create the array of all the function pointers
const struct cmd_det function_index[] = {
{"my_mkfs",3,&my_mkfs},
{"my_mount",1,&my_mount},
{"my_unmount",1,&my_unmount},
{"my_quit",1,&my_quit},
{"my_help",1,&my_help},
{"my_ls",1,&my_ls},
{"my_cp",3,&my_cp},
{"my_mv",3,&my_mv},
{"my_rm",2,&my_rm},
{"my_df",1,&my_df},
{"my_cat",2,&my_cat},
{"my_echo",3,&my_echo},
{NULL,-1,NULL}
};
int main ()
{
char cmd_line[ARG_LENGTH * MAX_ARGS]; //This is where I read the command typed by the user
char arguments[MAX_ARGS][ARG_LENGTH]; //In here after breaking the above string I store each argument
int args; //number of argument found in the cmd_line above
int i,found;
shell_running = 1;
//This is the shell. Basically an endless while loop - what did i tell ya; primitve
do
{
clear_buffers(cmd_line,arguments);
printf("os280fs> ");
//Here it gets the line from the user
if ( !fgets(cmd_line, ARG_LENGTH*MAX_ARGS , stdin) ) break;
/*
My function that breaks it. Again I considered using sscaf to break it but I think it's behaviour is unspecified
when the format has more parts than found. I thought of using it like this sscanf(cmd_line,"%s %s %s %s...",arg1,
arg2, ..., argMAX_ARGS) with the maximum arguments possible but like I said I didnt like it and to be honest I
am little prejudices against the scanf family
*/
args = break_cmd_line(cmd_line, arguments);
// If there are arguments try to find the aproppriate function
if ( args != 0 )
{
i = 0;
found = 0;
//iterate the array of structs to find the function to call
while (function_index[i].name != NULL )
{
//compare names
if ( strcmp(arguments[0],function_index[i].name) == 0 )
//compare number of arguments
if ( args == function_index[i].no_args )
{
/*
call the function from the pointer inside the struct with arguments the number ofarguments (args) returned by my
function and the arguments themselves, basically the pointer to the 2D array created inside my function where I
broke the cmd_line
*/
(*function_index[i].func_to_call) (args,arguments);
//This is nothing, just to know when a function wasnt fount and print a message
found = 1;
break;//no point to continue searching
}
i++;
}
if ( !found ) printf("No such command available\n") ;
}
}while ( shell_running ); //if my_quit is called shell_running changes to 0 and exits
return 0;
}
//My function to break the cmd_line
int break_cmd_line(char *line, char **argumentsR)
{
char *iter;
int i,j;
i = 0;
j = 0;
iter = line;
//if there are leading whitespaces ignore them
while ( *iter == ' ' || *iter == '\t' )
iter++;
//And then if it's followed by new line the exit. Basically the user typed whitespace
if ( *iter == '\n' )
return 0;
else
//but if not find the arfuments
{
int set = 0; //this is used to know when there is another argument following whitespace
while ( *iter != '\n' )
{
//if set - 1 then there was found whitespace and then not a newline which means that a new argument follows
if ( set ) { i++; set = 0; } //and so go to a new row
//Copy the argument per character to the arguments table
if ( *iter!= ' ' && *iter != '\t')
{
argumentsR[i][j] = *iter;
j++;
iter++;
}
else
//whitespace is found
{
set = 1;
j = 0;//new row so start from the beginning when copying the argument
//eat up all the whitespace
while ( *iter == ' ' || *iter == '\t' )
iter++;
}
}
}
return i+1; //return the number of arguments - +1 because arrays start from 0
}
void clear_buffers(char *buf1, char **buf2)
{
int i;
memset( buf1, 0, ARG_LENGTH * MAX_ARGS );
for ( i = 0; i < MAX_ARGS; i++ )
memset( buf2[i], 0, ARG_LENGTH );
}
If you have an editor then it is probably easier for you to read the code so I attached the corresponding files