I was writing a function to parse command line as char* arguments array to another program but then I faced a problem allocating and/or reading the resulting buffer. I am stuck on that for about 2 days and 1000+ google searches later and I just can't figure it out all by myself.
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> //for malloc, realloc
char** parse_cmdline(const char* cmdline) {
int wrdc = 0; //word count
int wrd_len = 0; //current word length
char** args = NULL; //result buffer, filled with arguments
int i; //counter of characters read from cmdline
for(i = 0; ; ++i){
if(cmdline[i] == '\n') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("EOL found\n");
wrd_len = 0;
}
break;
}
else if(cmdline[i] == ' ') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("space found!\n");
wrd_len = 0;
}
}
else if(cmdline[i] > 32 && cmdline[i] < 127) {
++wrd_len;
printf("char found !\n");
}
//FOR DEBUGGING
printf("i = %d, wrdc = %d, wrd_len = %d\n", i, wrdc, wrd_len);
}
printf("%d words in command\n", wrdc);
return args;
}
int main(int argc, char* argv[]) {
char buffer[200];
while(read(STDIN_FILENO, buffer, 200) > 0) {
char** data = parse_cmdline(buffer);
printf("%s\n", data[0]);
memset(buffer, 0, 200);
free(data);
}
return 0;
}
else if(cmdline[i] == ' ') {
if(wrd_len > 0) {
++wrdc;
args = realloc(args, wrdc * sizeof(char*));
memcpy((void*)&args[wrdc - 1], (void*) &cmdline[i - wrd_len], wrd_len);
printf("space found!\n");
wrd_len = 0;
}
}
Here the pointer stored in args[wrdc-1] is uninitialized and pointed to somewhere unknown. You shouldn't just memcpy() the cmdline into args[wrdc-1].
Allocate the memory for one single argument before memcpy():
args[wrdc-1] = calloc(wrd_len+1, sizeof(char));
Note the +1 and calloc() for terminating NULL character. Remember to free them in main().
Related
I am trying to write my own Shell in C. I have a problem. I wrote my own _strtok function that uses strtok but returns all the tokens as an array of strings. For testing I use the string "ls -laR" defined in the main function. I get the valgrind error "Invalid write of size 8" when trying to malloc the number of chars in the second pointer in the array of strings named "Doubl". Why is it doing this? I am allocating the proper number of pointers to strings in the doubl array. Any insight or help would be appreciated
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
char **_strtok(char *str, char *delim)
{
char **doubl;
char *s = str;
char *string;
int i = 0;
while (*s)
{
if (*s == *delim)
i++;
s++;
}
doubl = malloc(sizeof(char *) * i + 1);
i = 0;
string = strtok(str, delim);
while (1)
{
doubl[i] = malloc(sizeof(char) * strlen(string) + 1);
strcpy(doubl[i], string);
i++;
if (string == NULL)
break;
string = strtok(NULL, delim);
}
return (doubl);
}
char *get_path(char **env)
{
char **check = env;
char *path = NULL;
char pth[] = "PATH";
int i, j, stop = 0;
for (i = 0; check[i] && stop == 0; i++)
{
for (j = 0; j < 4 && stop == 0; j++)
{
if (check[i][j] != pth[j])
break;
if (check[i][j] == pth[j] && j == 3)
{
path = malloc(strlen(check[i]));
strcpy(path, check[i]);
stop = 1;
}
}
}
return (path);
}
char **cmd_to_arg(char **cmd, char **env)
{
/* FREE PATH BEFORE END */
char *path = get_path(env);
char *slash = "/";
char **args = NULL, **check = _strtok(path, ":"), **checkStart = check, **cmdStart = cmd;
int status = -1, i = 0, j;
while (*checkStart)
{
strcat(*checkStart, slash);
strcat(*checkStart, cmd[0]);
status = access(*checkStart, F_OK | X_OK);
printf("%s\n", *checkStart);
if (status == 0)
break;
checkStart++;
}
for(;*cmdStart; i++, cmdStart++)
printf("%d\n", i);
args = malloc(sizeof(char *) * i);
args[0] = malloc(strlen(*checkStart));
strcpy(args[0], *checkStart);
puts(args[0]);
for (j = 1; j < i && cmd[j] != NULL; j++)
{
//printf("%d\n", j);
args[j] = malloc(strlen(cmd[j]) * sizeof(char));
strcpy(args[j], cmd[j]);
puts(args[j]);
}
return (args);
}
int main(int ac, char **av, char **env)
{
(void)ac, (void)av, (void)env;
char line[] = "ls laR";
//size_t size = 0;
char **cmd; //**cmdStart;
//int i = 0, j = 0;
cmd = _strtok(line, " ");
cmd = cmd_to_arg(cmd, env);
return (0);
}
my function is reading character by character so that's why it is not working with multiple buffer size and l need help in fixing it so that it can work with any buffer size, not just 1.
so the main issue is it's supposed to work with buffer size not character by character
l have tried to read changing the buffer but it skips some characters
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFF_SIZE 32
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
int get_next_line(int fd, char **line){
static char * s_line = NULL;
static int s_max = 0;
char character[BUFF_SIZE + 1];
if(s_line == NULL)
{
s_line = (char *)malloc((BUFF_SIZE + 1) * sizeof(char));
s_max = BUFF_SIZE;
}
int len = 0;
int ret;
while ((ret = read(fd, character, BUFF_SIZE)) > 0)
{
if (character[0] == '\n'){
break;
}
s_line[len] = character[0];
len = len + 1;
if (len >= s_max)
{
char *tmp;
s_max = s_max + BUFF_SIZE;
tmp = (char *) malloc((s_max + 1) * sizeof(char));
s_line[len] = '\0';
strcpy(tmp, s_line);
free(s_line);
s_line = tmp;
}
}
if (ret < 0) { //read error, free memory and return -1
free(s_line);
return -1;
}
if (len == 0){
free(s_line); //required to release the memory after reading the entire file
s_line = NULL;
return 0;
}
s_line[len] = '\0';
*line = s_line;
return 1;
}
int main(int argc, char **argv)
{
char *txt;
int fd;
fd = open(argv[1], O_RDONLY);
while (get_next_line(fd, &txt) > 0)
{
printf("%s\n", txt);
}
printf("Done\n");
if (txt != NULL)
{
free(txt);
}
return (0);
}
l expect the output of reading the whole file, not skipping characters
like what it is doing
The character reads the string of size BUFF_SIZE. So you need to iterate over each element in the string if BUFF_SIZE > 1 and must copy it into the s_line array to copy the complete string. Otherwise the characters will be skippped.
while ((ret = read(fd, character, BUFF_SIZE)) > 0)
{
for(i=0;i<BUFF_SIZE;i++){ // for loop is added to visit every element of the character array
if (character[i] == '\n'){
s_line[len] = '\n';
break;
}
s_line[len] = character[i];
len = len + 1;
if (len >= s_max)
{
char *tmp;
s_max = s_max + BUFF_SIZE;
tmp = (char *) malloc((s_max + 1) * sizeof(char));
s_line[len] = '\0';
strcpy(tmp, s_line);
free(s_line);
s_line = tmp;
}
}}
i am coding a minishell, and i want to store my arguments in a char** and then call the execvp function with my char**
int main(){
char* buffer;
printf(">");
buffer = readbuff();
printf("buffer :%s\n",buffer);
exec(buffer);
//printf("buffer :%s\n",buffer);
free(buffer);
}
my function to read user input in a buffer
char* readbuff(){
char* buff = malloc(sizeof(char)*BUFFER_SIZE);
char* ptr = buff;
fgets(buff,BUFFER_SIZE,stdin);
int i;
for(i = 0;*ptr != '\0' && *ptr != '\n';++i){
ptr++;
}
memset(ptr,'\0',1);
return buff;
}
exec to execute the shell command
void exec(char* buff){
char* ptr = buff;
char command[30];
int i = 0,j = 0;
for(i=0;*ptr != ' ';++i){
ptr++;
}
strncpy(commande,buff,i);
command[i] = '\0';
ptr++;
char* ptr2 = ptr;
while(*ptr2 != '\0'){
if(*ptr2 == ' ')
j++;
ptr2++;
}
j `will contain the number of arguments
char** args = malloc(sizeof(char*)*j);
char** ptrarg = args;
for(int i = 0;i < j;i++){
*ptrarg = ptr;
while(*ptr != ' ')
ptr++;
ptr++;
ptrarg ++;
}
pid_t son = fork();
if(son == 0){ //son
execvp(command,args);
free(buff);
free(args);
}
free(args);
}
My problem is the conversion of char* to char**
Input:
char input[] = "Lorem ipsum dolor sit amet";
Steps to create arguments to execvp():
Count the blanks: 5
Replace the blanks by '\0's.
Create an array of 5+1 char* (one more to be able to mark the end of the array):
char ** ppc = malloc(6 * sizeof *ppc);
Make the 1st point to the first word's 1st character:
ppc[0] = input;
Make the other 4 elements of ppc point the 1st char after each '\0'
Mark the end of the array by setting its the last (the additional element) to NULL:
ppc[5] = NULL;
call execvp like this
execvp(ppc[0], ppc);
Complete code:
#include <stdlib.h> /* for malloc() and EXIT_xxx macros */
#include <stdio.h> /* for perror() */
#include <string.h> /* for strchr() and strtok() */
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int result = EXIT_SUCCESS;
char input[] = "Lorem ipsum dolor sit amet";
size_t words = 0;
{
char * pc = input;
while ((pc = strchr(pc, ' ')))
{
++pc;
if (!*pc && *pc != *(pc - 1)) /* Skip successive blanks. */
{
++words;
}
}
}
{
char ** ppc = malloc((words + 1) * sizeof *ppc);
if (NULL == ppc)
{
perror("malloc() failed");
exit(EXIT_FAILURE);
}
{
size_t i = 0;
ppc[i] = strtok(input, " ");
while (NULL != (ppc[++i] = strtok(NULL, " ")));
}
{
pid_t pid = fork();
if (-1 == pid)
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if (0 == pid)
{
execvp(ppc[0], ppc);
perror("execvp() failed");
result = EXIT_FAILURE;
}
}
free(ppc);
}
return result;
}
basically the same answer as above, implemented slight differently. quick answer to your problem "My problem is the conversion of char* to char**" => you have to create(malloc) a char* array to the count of individual char* elements. then for each element in char** you have to malloc char array and copy the chars. which is implemented below. cheers !
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define BUFFER_SIZE 100
char* readbuff()
{
char* buff = malloc(sizeof(char)*BUFFER_SIZE);
char* ptr= buff;
memset(ptr,'\0',BUFFER_SIZE);
fgets(buff,BUFFER_SIZE,stdin);
// int i;
//for(i = 0;*ptr != '\0' && *ptr != '\n';++i){
//ptr++;
//}
//memset(ptr,'\0',1);
return buff;
}
void exec(char* buff)
{
char* ptr = buff;
char command[30];
int i = 0,j = 0;
for(i=0;*ptr != ' ';++i){
ptr++;
}
strncpy(command,buff,i);
command[i] = '\0';
//ptr++;
char* ptr2 = ptr;
ptr++;
while(*ptr2 != '\0'){
if(*ptr2 == ' ')
j++;
ptr2++;
}
char** args = malloc(sizeof(char*)*j);
//char** ptrarg = args;
int k=0;
char* ptrstart = ptr;
for(int i = 0;i < j;i++)
{
//*ptrarg = ptr;
ptrstart = ptr;
k=0;
while((*ptr != ' ') && (*ptr != '\0') )
{
k++;
++ptr;
}
args[i] = malloc(sizeof(char)*(k));
strncpy(args[i], ptrstart, k-1);
args[i][k]= '\0';
ptr++;
//ptrarg ++;
}
printf("command %s", command);
printf("args[0] %s", args[0]);
pid_t son = fork();
if(son == 0){ //son
execvp(command,args);
//free(buff);
//free(args);
}
free(args);
}
int main()
{
char* buffer;
printf(">");
buffer = readbuff();
printf("buffer :%s\n",buffer);
exec(buffer);
//printf("buffer :%s\n",buffer);
free(buffer);
}
When I run my code the first printParams() call works perfectly. But every call after fork() the struct loses all of its char array values. I'm not that knowledgeable of pointers, but I can tell that the root of this problem is probably pointer-based. For example, the first printParams() will print out all of the values assigned in the Parse() function. But after the fork(), all of the integer values such as background and argumentCount are displayed but none of the string values associated with inputRedirect or the string values held in the vectorArguments array.
![Here is a photo of my output]]1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "parse.h"
void printParams(Param_t * param);
struct PARAM
{
char *inputRedirect; /* file name or NULL */
char *outputRedirect; /* file name or NULL */
int background; /* ethier 0 (false) or 1 (true) */
int argumentCount; /* number of tokens in argument vector
*/
char *argumentVector[MAXARGS]; /* array of String */
};
typedef struct PARAM Param_t;
int main(int argc, char *argv[]){
int i;
int debug;
pid_t pid;
if(argc>1){
if(!strcmp(argv[1], "-debug"))
debug = 1;
}
Param_t * testParam = Parse();
if(debug == 1){
printParams(testParam);
}
pid = fork();
printParams(testParam);
if(pid == 0){
exit(1);
}
return 0;
}
void printParams(Param_t *param)
{
int i;
printf("InputRedirect: [%s]\n", (param->inputRedirect != NULL) ? param-
>inputRedirect: "NULL");
printf("OutputRedirect: [%s]\n", (param->outputRedirect != NULL) ?
param->outputRedirect: "NULL");
printf ("Background: [%d]\n", param->background);
printf ("ArgumentCount: [%d]\n", param->argumentCount);
for (i = 0; i < param->argumentCount; i++)
printf("ArgumentVector[%2d]: [%s]\n", i, param->argumentVector[i]);
}
Param_t* Parse(){
char *toke[MAXARGS];
int i = 0;
char str[MAXSTRLENGTH];
int j;
int k=0;
Param_t* testParam = malloc(sizeof(Param_t));
testParam->argumentCount = 0;
printf("Enter your commands:\n");
fgets(str, MAXSTRLENGTH, stdin);
toke[i] = strtok(str, " ");
//Tokenizes the user input into the toke array
while(toke[i] != NULL){
//printf("%s\n", toke[i]);
++i;
toke[i] = strtok(NULL, " ");
}
i=0;
char c;
while(toke[i] != NULL){
c = toke[i][0];
if(c == '<')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->inputRedirect = toke[i];
}
else if(c == '>')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->outputRedirect = toke[i];
}
else if(c == '&')
{
testParam->background = 1;
//background
}
else
{
testParam->argumentVector[k] = toke[i];
k++;
//save as cmd vector
}
++i;
}
testParam->argumentCount = k;
return testParam;
}
The reason you loose all char * values is because strtok() function doesn't create buffer. Basically all your char* consist of an address pointing into str variable you read with fgets(). The str variable has scope only to end of Parse() function.
Solution for this:
Replace:
testParam->inputRedirect = toke[i];
with:
testParam->inputRedirect = malloc(MAXSTRLENGTH);
memset( testParam->inputRedirect, 0, MAXSTRLENGTH);
memcpy( testParam->inputRedirect, toke[i], strlen(toke[i]) );
But please note, that this leads to memory leak since there is no free().
Sugestion:
Create static instance of structure in main and give its pointer to Parse function.
Param_t testParam;
Parse( &testParam );
Let the Parse function fill it. And at the end of main call free for all char * buffers inside testParam
I need to return a char** but when I try to do this, the compiler tells me that I want to return the address of a local variable. How can I do that? I know that I should allocate space for this variable but how? Here is my code, but the second printf doesn't appear and the function returns nothing:
char** parse_cmd(const char* cmdline) {
char** arguments = (char**)malloc(100);
int i;
int j=0, k=0;
printf("%s\n", cmdline);
for(i=0; i<100; i++) {
arguments[i] = malloc(100);
}
for(i = 0; i < strlen(cmdline); i ++) {
if(cmdline[i] != ' ') {
arguments[j][k] = cmdline[i];
k++;
} else {
arguments[j][k] = '\0';
j++;
k = 0;
}
}
printf("%s\n", arguments[1]);
return arguments;
}
You need to do multiple allocations. The first for the char** and then for each of the char*. E.g. something like
char **args = (char**)malloc(100);
int i;
for (i=0; i<100; i++)
args[i] = malloc(100);
// Rest of program
return args;
Here's the code I assembled - and tested. It uses dynamic memory allocation for both the argv argument list and for each argument as it is assembled. The function release_cmd() releases the allocated space. The function cleanup() is internal and releases allocated space on a failure, before returning a null double-pointer. This simplifies the error handling. There's a minimal test harness in the prompt() function and main(). I haven't run in under valgrind, but the MacOS X implementation of malloc() quite often spots problems so I'm moderately confident there's no gross memory abuse - but there could be an off-by-one leak. I haven't tested the cleanup() code by faking an allocation failure.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void release_cmd(char **argv)
{
for (size_t i = 0; argv[i] != 0; i++)
free(argv[i]);
free(argv);
}
static char **cleanup(size_t argc, char **argv)
{
argv[argc] = 0;
release_cmd(argv);
return 0;
}
char **parse_cmd(const char* cmdline)
{
size_t argc = 2;
char **argv = malloc(argc * sizeof(char *));
if (argv == 0)
return 0;
size_t j = 0; // Index into argv
size_t len = strlen(cmdline);
for (size_t i = 0; i < len; i++)
{
while (isspace(cmdline[i]))
i++;
if (cmdline[i] == '\0')
break;
if (j > argc - 2)
{
size_t newc = (argc * 2);
char **newv = realloc(argv, newc * sizeof(char *));
if (newv == 0)
return cleanup(argc, argv);
argv = newv;
argc = newc;
}
size_t argl = 2; // Length of argument string
argv[j] = malloc(argl);
size_t k = 0; // Index into argv[j]
while (cmdline[i] != '\0' && !isspace(cmdline[i]))
{
if (k > argl - 2)
{
size_t newl = argl * 2;
char *news = realloc(argv[j], newl);
if (news == 0)
return cleanup(argc, argv);
argv[j] = news;
argl = newl;
}
argv[j][k++] = cmdline[i++];
}
argv[j][k] = '\0';
argv[j] = realloc(argv[j], k+1); // Shrink to fit!
j++;
}
argv[j] = 0;
argv = realloc(argv, (j+1)*sizeof(*argv)); // Shrink to fit!
return argv;
}
static int prompt(const char *prompt, char *buffer, size_t bufsiz)
{
printf("%s", prompt);
return (fgets(buffer, bufsiz, stdin) != 0);
}
int main(void)
{
char line[1024];
while (prompt("cmd? ", line, sizeof(line)) != 0)
{
char **argv = parse_cmd(line);
char **args = argv;
while (*args)
puts(*args++);
release_cmd(argv);
}
putchar('\n');
return 0;
}