Hi I have a program that needs to compare a string array to a predefined string but when I use the variable args[0] it works in my function strcmp as below
int gash_execute(char **args){
int i;
if(args[0] == NULL){
return 1;
}
for(i = 0; i < gash_command_num(); i++){
if(strcmp(args[0], functions[i]) == 0){
return(*function_address[i])(args);
}
}
return gash_launch(args);
}
However when trying to strcmp args[i] such as below I get a seg fault. Can anyone help me find a solution to this problem?
int gash_execute(char **args){
int i;
if(args[0] == NULL){
return 1;
}
for(i = 0; i < gash_command_num(); i++){
if(strcmp(args[i], functions[i]) == 0){
return(*function_address[i])(args);
}
}
return gash_launch(args);
}
args[] is an array of strings that were previously separated by spaces, this program is for a custom shell, so pretend in my shell command line I input "cat echo ls" args[0] would be "cat" and so on. However now I need to implement i/o redirection. So i need to check every element of args to check if they represent the symbols "<" ">" "|" and if one of them is we can take it from there
Without seeing all the code, or a report from a tool such as valgrind, I can't say for sure. But I can tell you that this loop is full of potential problems.
for(i = 0; i < gash_command_num(); i++){
if(strcmp(args[i], functions[i]) == 0){
return(*function_address[i])(args);
}
}
It's iterating through three arrays (args, functions, and function_address) based on some function call which takes none of those as variables (gash_command_num()) which has an unknown relation to how many elements are actually in those arrays.
And it's using two global variables (functions and function_addresses) which could contain anything.
If all those things are related, I would suggest making that explicit... but I suspect they're not. I suspect your loop logic is wrong. It's comparing args[i] with functions[i]. I suspect gash_command_num() is actually the size of functions and so the loop is walking off args.
What I suspect you really want to do is to see if args[0] matches any function name in functions and then call the related function. If args[0] is ls then you want to check if there's a built in shell function for ls and call it with all the arguments.
Rather than searching a list over and over again, and having to manage two parallel lists, this would be much better served with a hash table. The keys are the function names, the values are the function pointers. C doesn't have a hash table built in, but there's plenty of libraries for that. Gnome Lib is a solid choice for this and many other basic functionality that C is missing.
Using a hash table, and eliminating the globals, your code reduces to this:
/* For encapsulation */
typedef int(*shell_function_t)(char **);
int gash_execute(char **args, GHashTable *shell_functions){
int i;
if(args[0] == NULL){
return 1;
}
shell_function_t func = g_hash_table_lookup(shell_functions, args[0]);
if( func ) {
return(*func)(args);
}
else {
return gash_launch(args);
}
}
Scanning for pipes is now a separate problem. For that you do want to loop through args looking for special characters. That is made much simpler if, when you create args, you ensure it ends with a null pointer.
for(int i = 0; args[i] != NULL; i++) {
...check if args[i] is special...
}
Related
My code is really messy right now, so I think it would be easier to convey what I'm trying to do by just describing it.
I'm working on a shell for homework that needs to be able to redirect output to a file, just like the default shell. We were provided a preexisting shell and asked to modify it. The shell already sets up an argv for execve, but in order to implement redirection I need to remove the last two entries from the argv the program built (> and the file name), and after testing that by just freeing up the last two entries I think it's probably better to just make a copy, minus those two entries, as this program handles freeing up the argv and if I try to do that at some point before the predesignated time to do so I run into problems when I try to run another command.
My point is I'm having a hard time copying part of an array of strings that's going to serve as an argv. I've seen a couple of solutions posted, but they're all in C++, and I'm asked to do this in C.
Alternatively, I suppose it would also be sufficient if I could properly empty part of an argv. Here's the code I tried:
for(i=0;i<10;i++)
{
if(my_argv[i] == NULL)
{
break;
}
if(strcmp(my_argv[i], ">") == 0)
{
if(my_argv[i+1] != NULL)
{
strncpy(fileName, my_argv[i+1], strlen(my_argv[i+1]));
strncat(fileName, "\0", 1);
//bzero(my_argv[i+1], strlen(my_argv[i+1])+1);
//my_argv[i+1] = NULL;
//free(my_argv[i+1]);
} else {
printf("no file name given\n");
return;
}
//bzero(my_argv[i], strlen(my_argv[i])+1);
//my_argv[i] = NULL;
//free(my_argv[i]);
redirectOutput(cmd, fileName);
return;
}
}
The commented sections are where I copied in code from the function that empties my_argv to attempt to free up the contents of argv where the > and file name are. It still runs without those lines, but then I kick the can down the road with having to deal with the extra entries in my redirectOutput() function, which is an absolute train wreck. The free_argv() function looks like this:
void free_argv()
{
int index;
for(index=0;my_argv[index]!=NULL;index++) {
bzero(my_argv[index], strlen(my_argv[index])+1);
my_argv[index] = NULL;
free(my_argv[index]);
}
}
You're going to much more work than you need to do. You can safely do whatever you want to the pre-prepared argv, as long as you do it after the fork(), in the child (before the execve(), of course). No such modifications will affect the parent, and you don't need to worry about any cleanup because the exec replaces the old process image with the new one.
Example:
/* these are already provided: */
char *filename = /* ... */;
char **child_argv = /* ... */;
char **child_env = /* ... */;
pid_t pid = fork();
if (pid == 0) {
/* the child */
char **arg;
for (arg = child_argv; *arg && strcmp(*arg, ">"); arg += 1) { /* empty */ }
*arg = NULL; /* terminate the arg list at the ">", if present */
/* no need to clean up anything before execve() */
execve(filename, child_argv, child_env);
exit(1); /* execve() failed */
} else if (pid < 0) {
/* handle error */
}
/* the parent continues ... */
I am building a Linux Shell, and my current headache is passing command line arguments to forked/exec'ed programs and system functions.
Currently all input is tokenized on spaces and new lines, in a global variable char * parsed_arguments. For example, the input dir /usa/folderb would be tokenized as:
parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb
parsed_arguments tokenizes everything perfectly; My issue now is that i wish to only take a subset of parsed_arguments, which excludes the command/ first argument/path to executable to run in the shell, and store them in a new array, called passed_arguments.
so in the previous example dir /usa/folderb
parsed_arguments[0] = dir
parsed_arguments[1] = /usa/folderb
passed_arguments[0] = /usa/folderb
passed_arguments[1] = etc....
Currently I am not having any luck with this so I'm hoping someone could help me with this. Here is some code of what I have working so far:
How I'm trying to copy arguments:
void command_Line()
{
int i = 1;
for(i;parsed_arguments[i]!=NULL;i++)
printf("%s",parsed_arguments[i]);
}
Function to read commands:
void readCommand(char newcommand[]){
printf("readCommand: %s\n", newcommand);
//parsed_arguments = (char* malloc(MAX_ARGS));
// strcpy(newcommand,inputstring);
parsed = parsed_arguments;
*parsed++ = strtok(newcommand,SEPARATORS); // tokenize input
while ((*parsed++ = strtok(NULL,SEPARATORS)))
//printf("test1\n"); // last entry will be NULL
//passed_arguments=parsed_arguments[1];
if(parsed[0]){
char *initial_command =parsed[0];
parsed= parsed_arguments;
while (*parsed) fprintf(stdout,"%s\n ",*parsed++);
// free (parsed);
// free(parsed_arguments);
}//end of if
command_Line();
}//end of ReadCommand
Forking function:
else if(strstr(parsed_arguments[0],"./")!=NULL)
{
int pid;
switch(pid=fork()){
case -1:
printf("Fork error, aborting\n");
abort();
case 0:
execv(parsed_arguments[0],passed_arguments);
}
}
This is what my shell currently outputs. The first time I run it, it outputs something close to what I want, but every subsequent call breaks the program. In addition, each additional call appends the parsed arguments to the output.
This is what the original shell produces. Again it's close to what I want, but not quite. I want to omit the command (i.e. "./testline").
Your testline program is a sensible one to have in your toolbox; I have a similar program that I call al (for Argument List) that prints its arguments, one per line. It doesn't print argv[0] though (I know it is called al). You can easily arrange for your testline to skip argv[0] too. Note that Unix convention is that argv[0] is the name of the program; you should not try to change that (you'll be fighting against the entire system).
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return 0;
}
Your function command_line() is also reasonable except that it relies unnecessarily on global variables. Think of global variables as a nasty smell (H2S, for example); avoid them when you can. It should be more like:
void command_Line(char *argv[])
{
for (int i = 1; argv[i] != NULL; i++)
printf("<<%s>>\n", argv[i]);
}
If you're stuck with C89, you'll need to declare int i; outside the loop and use just for (i = 1; ...) in the loop control. Note that the printing here separates each argument on a line on its own, and encloses it in marker characters (<< and >> — change to suit your whims and prejudices). It would be fine to skip the newline in the loop (maybe use a space instead), and then add a newline after the loop (putchar('\n');). This makes a better, more nearly general purpose debug routine. (When I write a 'dump' function, I usually use void dump_argv(FILE *fp, const char *tag, char *argv[]) so that I can print to standard error or standard output, and include a tag string to identify where the dump is written.)
Unfortunately, given the fragmentary nature of your readCommand() function, it is not possible to coherently critique it. The commented out lines are enough to elicit concern, but without the actual code you're running, we can't guess what problems or mistakes you're making. As shown, it is equivalent to:
void readCommand(char newcommand[])
{
printf("readCommand: %s\n", newcommand);
parsed = parsed_arguments;
*parsed++ = strtok(newcommand, SEPARATORS);
while ((*parsed++ = strtok(NULL, SEPARATORS)) != 0)
{
if (parsed[0])
{
char *initial_command = parsed[0];
parsed = parsed_arguments;
while (*parsed)
fprintf(stdout, "%s\n ", *parsed++);
}
}
command_Line();
}
The variables parsed and parsed_arguments are both globals and the variable initial_command is set but not used (aka 'pointless'). The if (parsed[0]) test is not safe; you incremented the pointer in the previous line, so it is pointing at indeterminate memory.
Superficially, judging from the screen shots, you are not resetting the parsed_arguments[] and/or passed_arguments[] arrays correctly on the second use; it might be an index that is not being set to zero. Without knowing how the data is allocated, it is hard to know what you might be doing wrong.
I recommend closing this question, going back to your system and producing a minimal SSCCE. It should be under about 100 lines; it need not do the execv() (or fork()), but should print the commands to be executed using a variant of the command_Line() function above. If this answer prevents you deleting (closing) this question, then edit it with your SSCCE code, and notify me with a comment to this answer so I get to see you've done that.
I am to program a simple shell in C for my project that can implement environment variables. I looked up on how to use the getenv, setenv, putenv. So far so good i've tried to use the getenv to show the shell variables...well... with some succes . But I have a feeling that my reasoning is flawed. I have a char** argv which contains parsed input from the user input. I now check if argv starts with the command "echo" and then if any of the following inputs starts with a $ sign or not. Here's my code:
int executeVariables(char** arguments){
int i = 0;
if(strcmp(arguments[i], "echo") == 0){
char *variable;
for(i = 1; arguments[i] != NULL; i++){
char *str = arguments[i];
if( *(str + 0) == '$'){
variable = getenv(str + 1);
}else{
variable = getenv(str);
}
if(!variable){
//puts("not a variable");
printf("%s ", arguments[i]);
}else{
//puts("a variable");
printf("%s ", variable);
}
}
printf("\n");
exit(0);
}
return 1;
}
I think that normal linux shell finds the $ sign, it expands the variable before invoking the echo command. My shell isn't following this principle, it's expanding variables inside the echo command itself. Any idea as to how I can implement this? Thanks.
EDIT:
A problem I have is: echo $HOME and echo HOME gives me the same result which is wrong.
EDIT:
After various tests everything works well. But to really test it i'll need to create a local variable then echo this value. I tried it using putenv function but it doesn't create the local variable.
i = 0;
char** temp = malloc(sizeof (*temp));
if(strstr(userInput, "=") != NULL){
//puts("we got equals");
puts(userInput);
if(putenv(userInput) == 0){
printf("doing putenv(%s)\n", userInput);
exit(0);
}
else{
puts("couldnt putenv");
exit(1);
}
}
userInput: char *userInput is the input gotten from the command line using fgets()
You're specifically asking the code to do getenv() for the string, even if $ isn't found. That's why it will lookup $HOME or HOME. Just remove the else case for not finding the dollar-sign, and make sure to initialize variable to NULL at its declaration, and put it inside the loop.
Something like so:
// First, perform replacements
int executeVariables(char** arguments){
int i = 0;
for(i = 0; arguments[i] != NULL; i++){
char *str = arguments[i];
if(*str == '$'){
// make sure the result isn't NULL, though I'm not sure a real shell does
char *tmp = getenv(str + 1);
if (tmp != NULL) {
arguments[i] = getenv(str + 1); // save off the argument
}
}
}
// Then actually execute the function. This would be like bash's echo builtin
if (strcmp(arguments[0], "echo") == 0) {
int i;
for (i = 1; arguments[i] != NULL; i++) {
printf("%s ", arguments[i]);
}
printf("\n");
}
// Other functions could go here
return 1;
}
Edit: As far as your methodology goes, why do you specifically check for echo? Why not make it generic, and check all the arguments, including the first one? You actually probably want to substitute all the potential environment variables, so if you had MYECHO=echo somewhere, the following would work. To make it more generic, you'd have this function, which would then execute the stuff based on the expanded variables. You could make it all nice and have separate functions, but to fit it all in here, I've updated the code above accordingly, though I haven't tested it. ;)
Edit: That being said, werewindle's comment about doing this earlier does apply -- replace the arguments, like here, and then use that updated arguments array to have a separate function do whatever it needs to do. :)
> $MYVAR totally awesome $HOME
totally awesome /home/user
Edit: As for the putenv() situation, you'll want something structured like the following. This way, it will set it for the shell, and any other processes you run in the shell.
void do_args_env(char *args[])
{
// do putenv, etc.
}
// inside main loop in shell process
while (1) { // just an example
if (check_args_syntax(args) != 0) {
// error
}
do_args_env(args);
// fork and do other stuff
}
(Hopefully final) Edit: As an explanation, processes generally don't (perhaps can't?) affect the environment of processes above them in their hierarchy; only the other way around. So if you putenv() in a child, that child's siblings (i.e. other processes forked from its parent) won't get the environment change.
Glad I could be of help!
Yes, normal shells expand variables during parsing command line. So you need substitute variables in function, that produces array that "contains parsed input from the user input". In this case code for echo (and other commands) will be much shorter.
I am to program a simple shell in linux that can implement various stuffs including environment variables. I tried printing these variables using getenv but I have some problems. getenv always return NULL even if the user types a correct variable like $HOME for example. Here is my code
int i = 0;
if(strcmp(cmdArgv[i], "echo") == 0){
char *variable;
for(i = 1; cmdArgv[i] != NULL; i++){
variable = getenv(cmdArgv[i]);
if(!variable){
puts("not a variable");
printf("%s ", cmdArgv[i]);
}else{
puts("a variable");
printf("%s ", variable);
}
}
printf("\n");
exit(0);
}
It doesn't enter into the else condition. For example if the user types echo ls $HOME. This input is parsed into the cmdArgv which is a char **. Then the output I have is
not a variable
ls
not a variable
$HOME
BUT $HOME is a variable so maybe my implementation of getenv isn't right. Any ideas as to what seem to be the problem? Thanks.
The variable is called HOME, not $HOME. (The latter is your shell's syntax for expanding the variable.)
Suppose I want my Lex and Yacc program to parse the command line arguments like:
./a.out show memory
I want lex to parse the string "show memory". How do I accomplish this?
You'll need to concatenate all the arguments into a big string, by inserting whitespace between them. Then feed the remaining text buffer to Lex/Yacc, by re-defining the YY_INPUT macro so it reads input from your text buffer.
The start could be something like:
#include <stdio.h>
#include <string.h>
char *argbuf;
size_t arglen;
int main(int argc, char *argv[])
{
int i;
// Compute total length of all arguments, with a single space between.
arglen = 0;
for(i = 1; argv[i] != NULL; i++)
arglen += 1 + strlen(argv[i]);
// Allocate buffer space.
argbuf = malloc(arglen);
if(argbuf == NULL)
{
fprintf(stderr, "No memory for argument buffer, aborting");
exit(1);
}
// Concatenate all arguments. This is inefficient, but simple.
argbuf[0] = 0;
for(i = 1; argv[i] != NULL; i++)
{
if(i > 1)
strcat(argbuf, " ");
strcat(argbuf, argv);
}
// Here we should be ready to call yyparse(), if we had implemented YY_INPUT().
return 0;
}
int main(int argc, char **argv) {
if(argc > 1) {
if(argv[1])
yy_scan_string(argv[1]);
}
yyparse();
return 0;
}
What's wrong with doing it the old fashioned way?:
if(argc > 1 && !strcmp(argv[1],"show"))
{
if(argc > 2)
{
if(!strcmp(argv[2],"memory"))
...
else if(!strcmp(argv[2],"cpu"))
...
else ...
}
}
Besides, getopt() and friends are more appropriate.
My blog article Parsing command line parameters with Yacc & Flex explains this with a working example. There is no need to concatenate the argument string. The reason is given in the article.
The blurb is:
Every once in a while someone comes along and asks how to parse
command line parameters with Yacc & Flex. This is rather straight
forward, but requires some knowledge of the generated code to get
right.
Here we present a source template that does this. The user only has to
edit the grammar and scanning rules. Some knowledge of C, Yacc and
Flex is assumed.
The code is WTFPL licensed
The template is written for Berkeley Yacc and the reflex variant of
Flex. It may be made to work with GNU Bison and SourceForge Flex,
possibly with a few changes.
What you get is a template where you can just insert your lexical and grammar specification.
Please ask questions about the using and adapting the template itself to the blog comments.