Passing parameters a a string to execvp - c

I have the below code:
int main(int argc, char *argv[])
{
int i, a=9;
int length = 0;
const char fail[20] = "Missing Arguments\n";
char s1[512] = "";
char s2[15] = "./calc_prizes";
for (i=1; i<argc; i++) {
length += sprintf(s1+length, " %s", argv[i]);
}
strcat(s2, s1);
while(++a < argc) {
if(fork() == 0) {
char* arg[] = {s2, s1, NULL}; //this is the part that's wrong
execvp(arg[0],arg);
exit(1);
}
else
wait(NULL);
}
return 0;
}
S2 stores the name of the program, s1 the parameters collects the parameters. I can't seem to run the program with the parameters with execvp, what am I doing wrong?

One possible reason of execvp failing could be this:
strcat(s2, s1);
[I hope you have ensured that s2 is large enough to contain the concatenated resulting string otherwise its buffer overflow, which is a different but certainly a problem in your code.]
Here you are concatenating the s1 to s2 and s2 is the name of the program you want to execute. In the while loop, you are doing:
char* arg[] = {s2, s1, NULL};
The arg[0] pointing to s2 (concatenated string) and you are passing this as the first argument to execvp:
execvp(arg[0],arg);
execvp:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer. [emphasis mine]
Hence, to call execvp successfully you should have the first argument as the name of the executable file which is ./calc_prizes in your case.

Related

How to iterate through loop of a string to compare with another string's index?

I am currently facing a problem while coding. The problem is I want to loop through a string and compare each index with another string's index. And at the same time, copy the character to the other string if it does not have it yet. The code below is where I got stuck:
I compiled this and got the error: comparison between pointer and integer ('char' and 'string' (aka 'char *')) [-Werror,-Wpointer-integer-compare]
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[1])
{
string key = argv[1], key2[26];
for (int i = 0; key[i] != '\0' ; i++)
{
int j = 0;
if (key[i] != key2[j]) // I got an error here
{
key2[j] = key[i];
j++
}
}
printf("%s\n", key2);
}
An array of 26 char is needed, not 26 string
// string key = argv[1], key2[26];
string key = argv[1];
char key2[26];
key2[] not initialized
Unclear how OP wants to populate this. Perhaps from next argument?
Best to test argc first
if (argc < 2) {
fprintf(stderr, "Missing argument\n");
return EXIT_FAILURE;
}
string key = argv[1];
...
Drop unneeded 1
// int main(int argc, string argv[1])
int main(int argc, string argv[])
This shows why you shouldn't hide a pointer behind a typedef and why hiding how C works from novice programmers doesn't benefit anyone.
There is no string type in C. string here is just a typedef for a char *. The following statement:
string key = argv[1], key2[26];
allocates memory for a char *, and an array[26] of char *, which are not synonymous.
Or in other words:
It declares:
char *key = argv[1];
char *key2[26];
Note that the these statements only allocated memory for the pointers, not what they point to. In this case, you did initialize key, but forgot to initialize key2.
The statement:
if (key[i] != key2[j])
is then invalid. Because key[i] evaluates to a char, where key2[j] evaluates to a char * (that you didn't allocate memory for, nor initialize).
Fix:
Simply declare key2 to be a char *, not an array of char *s.
Now find the length of argv[1] before copying (it could result in an overflow elsewise).
size_t len = strlen (argv[1]);
Now allocate memory for key2:
char *key2 = malloc (len + 1); /* One for the '\0' byte */
if (!key2) {
/* ENOMEM, handle error accordingly */
}
Now copy the strings with strcpy() and continue on with the loop. And free() the memory when you're done with it.
Minor: You are accessing argv[1] before checking if its valid.
if (argc != 2) {
fprintf (stderr, "/* Print usage message here */.\n");
return EXIT_FAILURE;
}

Problems passing in a char[][] to execvp

I am trying to iterate over some input (which are commands and arguments), split the input into individual strings, then pass that input into execvp().
I am having trouble as execvp() wants a (char*, char*[]) as its arguments. I am passing in (char*, char[][]) which I thought was the same thing but it isn't liking it.
I would use a char*[] but I don't know how big the strings are prior to it running, so that is the reason I didn't use it. So obviously if I use char*[], I get a seg fault when I try and access elements of the char*'s.
Here's a snippet of the code
//input
char *line = "echo hello there what is";
int count = 0;
//store arguments
char args[6][10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word(string) size
int wordSize = 0;
/*
Here I iterate over the input, storing each string I find in args
all the above variables help me do that.
*/
execvp(args[0], args);
printf("Error: It didnt work\n");
Hopefully that is clear and a valid question, let me know if you want me to add the code of me turning the input into args.
An array of arrays of char is not the same as an array of pointers to arrays of char. execvp() expects the latter, with a null pointer as its last element, passing the former has undefined behavior.
You must construct an array of pointers, either allocated from the heap or defined with automatic storage (on the stack), initialize it with pointers to argument strings and pass this array to execvp().
Note also that echo is both a shell internal command and an executable file in the path.
Here is your code fragment modified accordingly (without the parse code, which is still yours to write):
//input "echo hello there what is";
//arguments array
char *args[6];
/*
* Here you should iterate over the input, storing each string you find
* on the command line into `args` and terminate with a null pointer...
*/
args[0] = "echo";
args[1] = "hello";
args[2] = "there";
args[3] = "what";
args[4] = "is";
args[5] = NULL;
execvp(args[0], args);
You can use two arrays:
char real_args[6][10]; // Six strings of up to nine characters each
...
char *args[] = {
real_args[0],
real_args[1],
real_args[2],
real_args[3],
real_args[4],
real_args[5],
NULL // Must be terminated by a null pointer
};
Use real_args for the actual arguments, and then pass args to execvp.
Ok I worked out how to use a char*[] instead of a char[][] and successfully put it into execvp().
Notes for the code below: I keep track of how long the current string is that I am iterating over in wordSize.
//input
char* line = "echo hello there what is";
int count = 0;
//store arguments
char* args[10];
int argCount = 0;
//store temp string from input
char temp[100];
int tempCount = 0;
//word size
int wordSize = 0;
while(line[count] != '\0')
{
if (line[count] == ' ' || line[count + 1] == '\0')
{
/*
As I don't know how big each string will be, I can
allocate it here as I know how big the string is through wordSize
*/
args[argCount] = malloc(sizeof(char)*(wordSize +1));
.
.
.
}
}
//terminate args with 0
args[argCount] = 0;
execvp(args[0], args);
printf("Error: It didnt work\n");

decrypt and encrypt text with command line arguments

I am trying to make a program that will encrypt and decrypt when user enters the string they want to encrypt/decrypt for argv[2] and enters either "encrypt" or "decrypt" for argv[3]. Here is the code I am trying to compile and run as of now
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int i;
// char *string;
char *key_ch;
char key_int;
char *string_ = calloc(80, 1);
string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
//char *key_ch;
//char key_int;
string = argv[1];
key_ch = argv[2];
key_int = atoi(key_ch);
if (argc < 3)
{
printf("Not enough arguments!\n");
exit (1);
}
if (strcmp(argv[3], "encrypt") == 0)
{
i = 0;
while(i <= strlen(string)-1)
{
encrypted_string[i] = string[i] + key_int;
i++;
}
// printf("Encrypted string: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", encrypted_string[i]);
i++;
}
printf("\n");
}
if (strcmp(argv[3], "decrypt") == 0)
{
i = 0;
while(i <= strlen(string) -1)
{
decrypted_string[i] = string[i] - key_int;
i++;
}
// printf("Decrypted String: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", decrypted_string[i]);
i++;
}
printf("\n");
}
return 0;
}
When I try to compile it without the -Wall command it compiles fine but when I run the program I am getting a segmentation fault, when I compile with -Wall I am getting
sam0.c:9:24: warning: 'string' is used uninitialized in this function [-Wuninitialized]
char encrypted_string[strlen(string)];
Can anyone possible shine some light on this error? Thank you
Edit:
Changed my code to your suggestions. I am not getting a compiling error at all even when using "-Wall" however somewhere in my program it is causing me to get a segmentation fault... any ideas? I put quotes around where I changed my code for reference in case I did it wrong.
warning: 'string' is used uninitialized in this function
[-Wuninitialized]
char *string; creates a pointer to char. At this point it is not yet a string, but you are using it as a string argument.
Before using char *string; it must have memory assigned, and should be initialized. Among other methoods, this can be done by:
char *string = calloc(80, 1);//initializes with known values (NULL).
Now string is usable, but has zero length. Values can be assigned via string functions:
strcpy(string, argv[1]);
sprintf(string, "%s", argv[1]);
strcat(string, argv[1]);
... more string functions
When using input from command line, argv, argc, malloc/calloc and string cpy functions can be avoided by using strdup. a value can be assigned like this:
if(argc == 2)
{
char *string = strdup(argv[1]);
if(!string) return -1;
...
EDIT (addressing your OP edit)
You are now using two different variables: string_ and string
char *string_ = calloc(80, 1);
^
string = argv[1];
Make them the same throughout your code and it should build and run.
When a meaningful value for the variable can be determined, try to declare and initialize the variable in one step:
char *string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
char *key_ch = argv[2];
char key_int = atoi(key_ch);
This will fix the warning: you were trying to get the length of an uninitialized string.
(here assuming C99. char encrypted_string[strlen(string)] is a VLA. If you're restricted to C89 only Rykker's answer works).
Also:
use const to prevent unwanted modifications, e.g.
const char *string = argv[1];
/* ... */
const char *key_ch = argv[2]
string is a constant so you can get its length just one time (do not recalculate the length every time... take a look at Shlemiel the painter's algorithm). So
const char *string = argv[1];
const int length = strlen(string);
char encrypted_string[length];
char decrypted_string[length];
const char *key_ch = argv[2];
char key_int = atoi(key_ch);
check inputs: what happen if
argc < 4
argv[3] isn't in ("encrypt", "decrypt")
key_ch is not a number
key_int is too big
you're printing encrypted_string / decryped_string one character at a time... and it works, but consider that if you want to manipulate them as 'strings' they aren't null terminated and their length isn't correct.
don't repeat yourself. The differences between the encryption and the decryption phases are minimal: you can use just one buffer and change key_int to a negative value to decrypt.
char *string;
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
This results in undefined behavior - you try to get the length of an uninitialized piece of memory (I'm amazed that your program doesn't crash). You may only call strlen after assigning something to string.
The easiest fix (assuming you don't want to start using malloc, strdup, etc.) would be moving the last two lines after string = argv[1];
Also, you need to check argc to make sure enough arguments have been passed!

Passing an argument using execvp

I have this in my parent.c file
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
for (i=1; i<=n; i++){
if((pid = fork()) == 0) {
execvp(command, NULL);
}
wait(&status);
}
My child.c file looks like this
int main(int argc, char *argv[]){
char *processnum = argv[0];
printf("This is the child %s\n", processnum);
return 0;
}
I basically just ran
gcc -o parent parent.c
gcc -o child child.c
./parent
This prints outs "This is the child (null)" 6 times, which is what I expect. But I want to be able to pass a parameter as I run child, in this case the process number.
So I changed my parent.c to look like this
for (i=1; i<=n; i++){
if(i == 1){
char* args = "1";
}
if(i == 2){
char* args = "2";
}
if(i == 3){
char* args = "3";
}
if(i == 4){
char* args = "4";
}
if(i == 5){
char* args = "5";
}
if(i == 6){
char* args = "6";
}
if((pid = fork()) == 0) {
execvp(command, args);
}
wait(&status);
}
What I thought would happen is that my program would print "This is the child 1", "This is the child 2" etc...
However, what actually happened is that the program seemed to run parent.c numerous times (I put a print statement at the start of parent.c and the output printed that statement like 20 times) instead of child.c
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
Thanks
here is the critical excerpt from the man page for execvp()
The execv(), execvp(), and execvpe() functions provide an array of
pointers to null-terminated strings that represent the argument list
available to the new program. The first argument, by convention,
should point to the filename associated with the file being executed.
The array of pointers must be terminated by a NULL pointer.
There are several issues with your program as it stands. First, as described by others, the second argument to execvp is a char ** type ... it ends up as the argv in the exec'ed program.
Second, the
if(i == 1){
char* args = "1";
}
...
code sets a variable args whose scope ends at the next line. You must declare the args in a scope in which you want to use it.
Third, converting a number to a string like this is very limited and tedious! You would do better using the standard C function sprintf (or even better snprintf).
Here is an updated parent.c:
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
char *child_args[2];
char number_buff[32]; // more than big enough to hold any number!
child_args[1] = NULL;
for (int i=1; i<=n; i++){
sprintf(number_buff, "%d", i);
child_args[0] = number_buff;
if((pid = fork()) == 0) {
execvp(command, child_args);
}
wait(&status);
}
return 0;
}
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
You should be passing a char* array to execvp, not a char*. As mentioned in the comments, the last element of the array should be a null pointer. The following should work:
char* args[] = {"1\0", NULL};
execvp(command, args);
I'm guessing that execvp is failing since it can't dereference args as a char**, and thus the forked process is continuing the loop as a parent. You should be checking the return value of execvp to see that the function call worked.
Also, command and args should be null-terminated. I suggest using the function itoa to convert an int to a c string.

Shell in c using strtok and an array of pointers

For this Shell program i'm using the functions strtok (see fragmenta.h code) to parsing a string which is introduced by user.
I need to remove the blanks with strotk function and introduce those on a struct of an array of pointers. This are made in fragmenta.h
In the main program (shell.c), is necessary to introduce the string, this one is passed to fragmenta and stored on char **arg. After that, i use the execvp function to execute the command.
The problem is that the program store the whole command, but only execute the first individual command. For example, if we introduce "ls -al", only execute the ls command so i understand that is a problem on the pointer.
Main program shell.c
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "fragmenta.h"
//
char cadena[50];
int pid;
int i, status;
char **arg;
pid_t pid;
//
main()
{
printf("minishell -> ");
printf("Introduce the command \n");
scanf("%[^\n]", cadena);
if (strcmp(cadena, "exit") == 0)
{
exit(0);
}
else
{
pid = fork();
if (pid == -1)
{
printf("Error in fork()\n");
exit(1);
}
else if (pid == 0) //child proccess
{
arg = fragmenta(cadena);
if (execvp(*arg, arg) < 0) /* execute the command */
{
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else /* for the parent: */
{
while (wait(&status) != pid);
}
}
}
int len;
char *dest;
char *ptr;
char *aux;
char **fragmenta(const char *cadena)
{
//
char *token;
int i = 0;
//
len = strlen(cadena);
char *cadstr[len + 1];
dest = (char *)malloc((len + 1) * sizeof(char));
strcpy(dest, cadena);
//printf("Has introducido:%s\n",dest);
token = strtok(dest, " ");
while ( token != NULL)
{
cadstr[i] = malloc(strlen(token) + 1);
strcpy(cadstr[i], token);
token = strtok(NULL, " ");
i++;
}
*cadstr[i] = '\0';
ptr = *cadstr;
i = 0;
while (cadstr[i] != NULL)
{
//printf("almacenado: %s\n",cadstr[i]);
i++;
}
return &ptr;
}
You've got at least two problems.
The first one is this:
ptr=*cadstr;
You've gone through all that trouble to create an array of arguments, and then you just copy the first argument and return a pointer to that copy.
You could just get rid of ptr and return cadstr, except that it's a local variable, so as soon as the function returns, it can be overwritten or deallocated.
Since you're storing everything else in the universe as globals for some reason, the obvious fix to that is to make cadstr global too. (Of course you can't use a C99 runtime-length array that way, but since you've written your code to guarantee a buffer overrun if the input is more than 50 characters, you can safely just allocate it to 50 strings.)
A better solution would be to initialize a new array on the heap and copy all of cadstr into it. Or just initialize cadstr on the heap in the first place.
Second, you never append a NULL to the end of cadstr. Instead, you do this:
*cadstr[i] = '\0';
That leaves the last element in cadstr pointing to whatever uninitialized pointer it was pointing to, but modifies the first byte of whatever that is to be a 0. That could corrupt important memory, or cause a segfault, or be totally harmless, but the one thing it can't do is set cadstr[i] to point to NULL.
When you check this:
i = 0;
while (cadstr[i] != NULL)
i++;
… you only get out of that loop because of luck; you read right past the end of the allocated array and keep going until some other structure or some uninitialized memory happens to be sizeof(void*) 0s in a row.
And when you pass the same thing to execvp, who knows what it's going to do.
You're also declaring main without a prototype, which is deprecated, so you'll probably get a warning for it from any compiler that accepted the rest of your code. To fix that, just do int main().

Resources