I would like to copy the value stored in char *command to a char *command_copy, because I am performing tokenization on command but still would like to use the initial command (which is why I am creating a copy). The problem is that every time I try something, the whole program goes crazy and I just can't find where to start or what to do.
Here is my code:
int main(void)
{
init_ui();
hist_init(100);
char *command;
while (true) {
signal(SIGINT, SIG_IGN);
command = read_command();
if (command == NULL) {
break;
}
char *command_copy;
command_copy = (char *) malloc(1000);
memcpy(command_copy, command, sizeof(*command));
char *args[4096];
int tokens = 0;
char *next_tok = command;
char *curr_tok;
while((curr_tok = next_token(&next_tok, " \t\n\r")) != NULL) {
if(strncmp(curr_tok, "#", 1) == 0){
break;
}
args[tokens++] = curr_tok;
}
args[tokens] = NULL;
if(args[0] == NULL) {
continue;
}
hist_add(command);
int builtin_status = handle_builtins(tokens, args);
if(builtin_status == 0) {
continue;
}
pid_t child = fork();
if(child == -1){
perror("fork");
}
...
I would like the hist_add() function to take command_copy instead of command because command at that point in the code is only the first word of the whole initial command, and I would like hist_add() to have the whole (original) command.
read_command(void):
char *read_command(void)
{
if(scripting == true) {
ssize_t read_sz = getline(&line, &line_sz, stdin);
if(read_sz == -1){
perror("getline");
free(line);
return NULL;
}
line[read_sz - 1] = '\0';
return line;
}
else {
return readline(prompt_line());
}
}
To make a copy of a char* string - so long as that string is properly nul-terminated - you can use the strdup function. This, essentially, is a combination of malloc and strcpy.
So, you can use something like this:
while (looping) {
char* original = getstring();
char* copy = strdup(original);
// ...
// do whatever you want with "original" - "copy" is left alone!
//
free(copy); // When you're done, free the copy
}
The strdup call is equivalent to the following:
char* copy = malloc(strlen(original) + 1);
strcpy(copy, original);
Related
I'm writing a basic shell in C, and I want it to be the case where if the entered command contains an &, then it will run in the background and the user can still execute other commands.
But for some reason, I can get the program to run in the background, so I know that the IF statement works (where it checks for the '&') but I can't get it to remove the '&' sign from the command.
Here is the relevant code, feel free to ask questions:
int main(void)
{
Command cmd;
int n;
while (!done) {
char *line;
line = readline("> ");
//This should check if 'line' contains an &, and remove it if so.
if (strchr(line, "&") != NULL) {
line[strlen(line) - 1] = '\0';
char **cmds;
cmds = separateCmd(line);
if (!line) {
/* Encountered EOF at top level */
done = 1;
} else {
stripwhite(line);
if(*line) {
add_history(line);
/* execute it */
n = parse(line, &cmd);
PrintCommand(n, &cmd);
executeBgCmd(cmds);
}
}
if(line) {
free(line);
}
} else {
char **cmds;
cmds = separateCmd(line);
if (!line) {
/* Encountered EOF at top level */
done = 1;
} else {
stripwhite(line);
if(*line) {
add_history(line);
/* execute it */
n = parse(line, &cmd);
PrintCommand(n, &cmd);
executeCmd(cmds);
}
}
if(line) {
free(line);
}
}
}
return 0;
}
Any help would be much appreciated. Thank you in advance :D
When I compile your code and look at the warnings I see:
warning: passing argument 2 of ‘strchr’ makes integer from pointer without a cast [-Wint-conversion]
So the compiler tells me that your code is wrong! Looking up strchr (e.g. http://man7.org/linux/man-pages/man3/strchr.3.html), I see that strchr expects the second argument to be the character to search for. But your code pass a string (or more precise: a pointer to the first character of a string):
if (strchr(line, "&") != NULL) {
^^^
ups
Try this instead
if (strchr(line, '&') != NULL) {
From the man-page I can also see that strchr returns a pointer to the character if it's found. Therefore it could make sense to write the code like:
char* pHit = strchr(line, '&');
if (pHit != NULL) {
*pHit = '\0'; // Terminate string at first &
}
so that the & doesn't have to be the last character.
A simple test program could be:
int main()
{
char line[] = "hello & world";
printf("Before: %s\n", line);
char* pHit = strchr(line, '&');
if (pHit != NULL) {
*pHit = '\0';
}
printf("After: %s\n", line);
return 0;
}
Output:
Before: hello & world
After: hello
So to start off, this program has two main parts. The first takes input from the command line using fgets and then makes an argv array with a function called makeargv. The second, takes that argv array and runs it using execvp. The problem I am having is that the program will only run system programs such as "ls", "pwd", "vim", etc. but will not run any program when the directory is specified, such as "./program". I have already tried different versions of exec but the only difference that has made is that then my program will no longer run any commands.
For the below program I cut out all the code that was not relevant to the question to avoid confusion.
#ifndef MAX_CANON
#define MAX_CANON 8192
#endif
int makeargv(const char *s, const char *delimiters, char ***argvp);
int main (int argc, char *argv[]) {
char cmd[MAX_CANON];
char delim[] = "\t";
char **myargv;
printf("Beginning program...\nEnter a command to execute:\n");
while(fgets(cmd, MAX_CANON, stdin) != NULL){ // Here's where I get input from the command line
/* Remove newline from end of command */
if (*(cmd + strlen(cmd) - 1) == '\n' || *(cmd + strlen(cmd) - 1) == ' ' )
*(cmd + strlen(cmd) - 1) = 0;
/*---- Child Code ----*/
if((p = fork()) == 0){
if (makeargv(cmd, delim, &myargv) == -1) { // Here is where I make the argv array
fprintf(stderr, "Child failed to construct an argument array for %s\n", &cmd[0]);
return 1;
}
fprintf(stderr, "Command is: %s\n", *&myargv[0]);
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
return 0;
}
/*---- Parent Code ----*/
Here is the makeargv code
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int makeargv(const char *s, const char *delimiters, char ***argvp) {
int error;
int i;
int numtokens;
const char *snew;
char *t;
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
errno = EINVAL;
return -1;
}
*argvp = NULL;
snew = s + strspn(s, delimiters); /* snew is real start of string */
if ((t = malloc(strlen(snew) + 1)) == NULL)
return -1;
strcpy(t, snew);
numtokens = 0;
if (strtok(t, delimiters) != NULL) /* count the number of tokens in s */
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
/* create argument array for ptrs to the tokens */
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
error = errno;
free(t);
errno = error;
return -1;
}
/* insert pointers to tokens into the argument array */
if (numtokens == 0)
free(t);
else {
strcpy(t, snew);
**argvp = strtok(t, delimiters);
for (i = 1; i < numtokens; i++)
*((*argvp) + i) = strtok(NULL, delimiters);
}
*((*argvp) + numtokens) = NULL; /* put in final NULL pointer */
return numtokens;
}
Edit:
Swapped fprintf for perror.
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
fprintf(stderr, "Error: Failed to execute command!\n");
return 1;
}
if(execvp(myargv[0], &myargv[0]) == -1){ // Here is where the error keeps appearing
perror("Error: Failed to execute command!\n");
return 1;
}
I am now getting a "No such file or directory" error.
FIXED:
The makeargv program was using "\t" as it's delimeter instead of " " so it was not creating the array correctly.
Changing:
char delim[] = "\t";
To:
char delim[] = " ";
Fixes the problem.
There is 2 type of exec:
the first need a path as the environment PATH who contain a location (where find sys executables)
and the others how take real file location.
Form the man page (https://linux.die.net/man/3/exec):
The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character.
So this mean that you need to use an absolute location, who always begin with /.
You can use getwd() and strcat() to concatenate the 2 strings.
For execvp:
int execvp(const char *file, char *const argv[]); // From the man (I <3 mans)
So:
char argv[3][] = { "/bin/ls", "/", NULL};
if (execvp("/bin/ls", argv) == -1)
return (1);
For evecv:
int execv(const char *path, char *const argv[]);
There is a problem, it need the system PATH. If you don't know what it is type "echo $PATH" in bash. It's a list of directories where the system can find binaries like "ls" in "/bin", concatenate with ":" as sparator. I find the exact definition here.
For your makeargv:
I don't understand why you deference it and take the address after.
execvp(myargv[0], &myargv[0])
char **myargv; //That's we have
char *const argv[]; //that's we need
//so you should try simply:
execvp(myargv[0], myargv);
//or if there is a cast problem
execvp(myargv[0], (char *const[])myargv);
Good luck!
Trying to build a shell implementation in linux for an assignment and am having trouble. I get the following error after running 3/4 times into the loop
* Error in `./1': realloc(): invalid next size: 0x000000000132c150 *
Aborted (core dumped)
I have left parts of the code out as it's long and I know there's no issues with them. If there's other parts you need I can add them too.
Thanks in advance :)
Note: argc and argv are global variables
int argc = 0;
char **argv = NULL;
int main(void)
{
while(1)
{
catch_signal();
printDate();
readCommand();
if(strcmp(argv[0], "cd") == 0)
{
changeDirectory();
}
else
{
executeCommand();
}
//free(argv);
}
}
void readCommand()
{
char *buffer = NULL;
char *token = " ";
char *p;
size_t len = 0;
int i = 0;
getline(&buffer, &len, stdin);
p = strtok(buffer, "\n");
p = strtok(buffer, token);
while(p)
{
argv = realloc(argv, sizeof(char *) * ++i);
argv[i - 1] = p;
p = strtok(NULL, token);
}
argc = i;
argv[argc] = NULL;
free(p);
}
void executeCommand()
{
int status;
pid_t pid;
if((pid = fork()) < 0)
{
printf("Error: forking child process failed.");
}
else if(pid == 0)
{
if(execvp(argv[0], argv) < 0)
{
printf("error");
exit(1);
}
}
else
{
while(wait(&status) != pid);
}
}
You are reallocating the argv array 1 pointer too short. There is not enough space for the final NULL pointer. Consider replacing the while(p) loop with this code:
if (!p) {
/* deal with empty command line */
}
while (p) {
argv = realloc(argv, sizeof(char *) * (i + 2));
argv[i++] = p;
p = strtok(NULL, token);
}
p should start as NULL if the user entered an actual command. But what if buffer contains an empty string or just a blank one? strtok will return NULL directly in these cases. You should ignore such command lines.
Hi guys I got some problems with using a function with a reference to a string as argument. I read that you should use e double pointer for this, but I can't get it to work.
This is (partly) my code.
enum errCode { ERR_NONE = 0, ERR_EMPTY, ERR_FULL, ERR_MEM, ERR_INIT, ERR_COMMAND, ERR_UNDEFINED };
typedef enum errCode ErrCode;
typedef enum {
no = 0, add, del, src, show, exit
} Command;
int main(void) {
char stringval[50];
char stringval2[50];
ErrCode err;
Command currentCommand = no;
printf("Enter a command\n");
if (fgets(stringval, 50, stdin) != NULL) {
char *p;
if ((p = strchr(stringval, '\n')) != NULL)
*p = '\0';
}
ErrHandler(
extractCommand(¤tCommand, stringval, &stringval2)
);
printf("stringval 2 = %s.\n", stringval2);
return 0;
}
ErrCode extractCommand(Command *command, char *inputString, char **outputString) {
char *strTemp;
char *strTemp2;
//Get the first word of the string
strTemp = strtok(inputString, " ");
strTemp2 = strtok(NULL, " ");
*outputString = strTemp2;
//Check if it equals a command
if (strcmp(strTemp, "exit") == 0) {
*command = exit;
return ERR_NONE;
} else if (strcmp(strTemp, "add") == 0) {
*command = add;
return ERR_NONE;
} else if (strcmp(strTemp, "del") == 0) {
*command = del;
return ERR_NONE;
} else if (strcmp(strTemp, "src") == 0) {
*command = src;
return ERR_NONE;
} else if (strcmp(strTemp, "show") == 0) {
*command = show;
return ERR_NONE;
} else {
*command = no;
printf("%s", strTemp);
return ERR_COMMAND;
}
}
This is what my output looks like:
Enter a command
add this is a test
stringval 2 = z˜ˇøÀo‡èK‡èT¯ˇø.
I obviously want to have the second word of the inputted string, but I'm doing something wrong.
Thx for the help!
stringVal2 is not initialised and is never populated: that is reason junk is being printed. There is no need in this case to pass a char**, passing a char* will work. However, this:
outputString = strTemp2;
does not copy the content of strTemp2 to outputString: it makes outputString point to the same address as strTemp2: use strcpy().
A double pointer, char** for example, is commonly passed to a function when that function allocates a new buffer for the argument (which is not the case in the posted code):
char* buf = NULL;
my_alloc(&buf);
void my_alloc(char** p)
{
*p = malloc(10);
}
I am trying to get command completion working but it seems like its not working properly..
Please have a look at my code and tell me how I can fix it..
Thanks in advance...
char store_commands() {
char *newEnv;
DIR * dir;
char *new ;
struct dirent * entry;
char *env = getenv("PATH");
do {
newEnv = strsep(&env, ":");
if(newEnv != NULL)
if(strlen(newEnv) > 0) {
dir = opendir(newEnv);
if( dir == NULL ) break;
if(flag == 1) {
flag = 0;
while((entry = readdir(dir)) != NULL) {
new = malloc(strlen(entry->d_name) + 1) ;
new = strcpy(new, entry->d_name);
commands[++count] = new; // add possible commands into an array
printf("---%i %s\n", count ,commands[count]);
}
}
closedir(dir); // close directory
}
} while(newEnv);
return **commands;
}
static char** my_completion( const char * text , int start, int end){
char **matches;
store_commands();
matches = (char **)NULL;
if (start == 0)
matches = rl_completion_matches ((char*)text, &my_generator);
return matches;
}
char * dupstr (char* s) {
char *r;
r = (char*) malloc ((strlen (s) + 1));
strcpy (r, s);
return (r);
}
char* my_generator(const char* text, int state) {
int index, len;
char *comm;
if (!state) {
index = 0;
len = (int)strlen (text);
}
while ( (comm = commands[index])) {
index++;
if (strncmp (comm, text, len) == 0)
return (dupstr(comm));
}
return NULL;
}
int main (int argc, char * argv[]) {
char *command;
using_history();
rl_readline_name = basename(argv[0]);
rl_attempted_completion_function = my_completion;
while ( (command = readline(" $ "))!= NULL ) { // scan stdin
rl_bind_key('\t',rl_complete);
if(strlen(command) > 0)
add_history(command);
}
return 0;
}
Some test cases
l (tab)
Display all 1281 possibilities? (y or n) // all possibilities come up when I put one letter *** all possibilities wrong actually what I meant was all commands including the ones dont start with l
ls (tab)
ls lsbom lsdistcc lsm lso lsvfs // seems alright here
however if I press enter
comm[0]: 'ls' and comm[1]: '(null)' // execution of the command fails!!! WHY????
Execution of the command is failed
: No such file or directory
If I use a static array like this one char *test[7] = {"ls","cat","lso", "mk", "mkd", "mkdir",""}; everything seems fine including execution of the command..
Where are the definitions/declarations for commands[] and count ?
Also: your style is incoherent, the program is barely readable.
Why do you cast the return from malloc() at one place and not at another place?
If (x == 0) {} and if ( !x ) {} are equivalent. Make your choice and stick with it.
The ugly do { ... } while ( ... ); loop can be replaced by a for( ... ; ... ; ...) {} loop, saving you two levels of indentation.