C - Replace $$ in string with ID from getpid()? - c

If I have a string like the following:
char* exampleString = "test$$";
Let's say that getpid() returns 1587.
How can I replace the $$ in the string with the result of getpid() such that the result would be a string "test1587"?

Since "test$$" is immutable and a pointer to it should better be char const* instead of char* you'd have to copy it to an array where you then can replace "$$".
Possible solution:
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char const *input = "foo$$bar";
pid_t pid = 1234;
size_t format_length = strlen(input);
char *format = calloc(format_length + 2, 1);
if (!format) {
fputs("Couldn't allocate memory :(\n\n", stderr);
return EXIT_FAILURE;
}
// copy input to format replacing "$$" with "%ld" in the process
bool replacement_done = false;
for (size_t i = 0; i < format_length + replacement_done; ++i) {
if (!replacement_done && i + 1 < format_length &&
input[i] == '$' && input[i + 1] == '$')
{
format[ i] = '%'; // just
format[++i] = 'l'; // being
format[++i] = 'd'; // safe.
replacement_done = true;
continue;
}
format[i] = input[i - replacement_done];
}
if (!replacement_done) {
free(format);
fputs("Nothing to do :(\n\n", stderr);
return EXIT_FAILURE;
}
char *result = malloc(1);
if (!result) {
free(format);
fputs("Couldn't allocate memory :(\n\n", stderr);
return EXIT_FAILURE;
}
// there is no guesswork needed, snprintf() will tell the needed size
int bytes_needed = snprintf(result, 1, format, (long)getpid());
if (bytes_needed < 0) {
free(format);
free(result);
fputs("snprintf() failed :(\n\n", stderr);
return EXIT_FAILURE;
}
char *temp = realloc(result, ++bytes_needed);
if (!temp) {
free(format);
free(result);
fputs("Couldn't allocate memory :(\n\n", stderr);
return EXIT_FAILURE;
}
result = temp;
int written = snprintf(result, bytes_needed, format, (long)getpid()); // long should be big enough
if(written < 0 || written >= bytes_needed ) {
free(format);
free(result);
fputs("snprintf() failed :(\n\n", stderr);
return EXIT_FAILURE;
}
puts(result); // done.
free(format);
free(result);
}

You can't do a direct substitution because your string (character array) is not long enough to hold the pid, and of course, if declared as a string literal is also not mutable.
There are a couple of ways you could go with this, but here's a reasonably elegant one:
/* Modify the existing string to be a pattern string for snprintf */
int len = strlen(exampleString) - 1; /* Can you see why I might do this? */
char* formatString = strdup(exampleString); /* Because we can't modify a literal */
int newLen = len + 12; /* How about this? */
char *pidString = malloc(newLen);
int i;
for (i = 0; i < len; i++) {
if (formatString[i] == '$' && formatString[i+1] == '$') {
formatString[i] = '%';
formatString[i+1] = 'd';
break;
}
}
snprintf(pidString, newLen - 1, formatString, getpid());
Can you follow how this works?
How would you enhance this to fail gracefully if the exampleString does not contain $$ ?

Related

Memory leak in C with static variable and i dont know how to fix it

As an exercise i'm writing a C function to read content from a file descriptor one line at a time.
In the exercise i'm only allowed to use read(), malloc() and free() from the standard library, while using one static variable. The function works but i keep getting a persistent memory leak when i reach the last line in the file that i cant seem to solve.
Leak:
Direct leak of 1 byte(s) in 1 object(s) allocated from:
#0 0x7f37f253a4bf in __interceptor_malloc (/usr/lib/gcc/x86_64-pc-linux-gnu/12/libasan.so.8+0xbd4bf)
#1 0x5644d5df4735 in offset /home/practical/Documents/Code/42/get_next_line/get_next_line.c:71
#2 0x5644d5df49ba in get_next_line /home/practical/Documents/Code/42/get_next_line/get_next_line.c:116
#3 0x5644d5df4a8b in main /home/practical/Documents/Code/42/get_next_line/get_next_line.c:138
#4 0x7f37f22cd189 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s).
Code:
#include "get_next_line.h"
#include <stdlib.h>
#include <unistd.h>
// needed for main:
#include <stdio.h>
#include <fcntl.h>
// finds the first line ending in \n from buffer
// this is the line for get_next_line to return
char *next_line(char *a)
{
int i;
char *buf;
if (!a || !a[0])
return (NULL);
i = 0;
while (a[i] && a[i] != '\n')
i++;
if (a[i] == '\n')
i++;
buf = (char *)malloc((sizeof(char) * i) + 1);
if (!buf)
return (NULL);
i = 0;
while (a[i] && a[i] != '\n')
{
buf[i] = a[i];
i++;
}
if (a[i] == '\n')
buf[i++] = '\n';
buf[i] = '\0';
return (buf);
}
// finds the end of the line we just returned in the buffer
// moves it to the end of new buffer
// changes the \n before it to '\0', so our new buffer does not contain it
// this offsets our buffer, the buffer now starts
// at the next line to be printed
char *offset(char *a)
{
char *buf;
int i;
int x;
i = 0;
x = 0;
while (a[i] && a[i] != '\n')
i++;
if (a[i] == '\0')
{
free(a);
return (NULL);
}
if (a[i] == '\n')
i++;
buf = (char *)malloc(ft_strlen(a) - i + 1);
if (!buf)
return (NULL);
while (a[i + x])
{
buf[x] = a[i + x];
x++;
}
buf[x] = '\0';
free(a);
return (buf);
}
// static buffer str starts at the next line to be printed
// we use strchr to find if there is a \n in the buffer
// if theres not, we read from the file into the buffer
// if \n is present, this is the line to print
// next_line then gets the first line ending in '\n' from the static buffer
// we return this line
// then we offset the static buffer by the line we just returned
char *get_next_line(int fd)
{
static char *str;
char *buf;
int i;
if (fd < 0 || BUFFER_SIZE <= 0)
return (NULL);
buf = (char *)malloc((sizeof(char) * BUFFER_SIZE) + 1);
if (!buf)
return (NULL);
i = 1;
while (!(ft_strchr(str, '\n')) && i != 0)
{
i = read(fd, buf, BUFFER_SIZE);
if (i == -1)
{
free(buf);
return (NULL);
}
buf[i] = '\0';
str = ft_strjoin(str, buf);
}
free(buf);
buf = next_line(str);
str = offset(str);
return (buf);
}
int main(int argc, char *argv[])
{
char *line;
int fd;
if (argc < 2)
{
printf("Usage: %s <file>\n", argv[0]);
return (1);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
{
perror("Failed to open file");
return (1);
}
while (line != NULL)
{
line = get_next_line(fd);
printf("%s", line);
free(line);
}
close (fd);
return (0);
}
ft_strjoin
char *ft_strdup(char *src)
{
char *dst;
int i;
i = 0;
dst = malloc(ft_strlen(src) + 1);
if (!dst)
return (NULL);
while (src[i])
{
dst[i] = src[i];
i++;
}
dst[i] = '\0';
return (dst);
}
char *ft_strcat(char *dest, char *src)
{
int i;
int l;
i = 0;
l = ft_strlen(dest);
while (src[i] != '\0')
{
dest[i + l] = src[i];
i++;
}
dest[i + l] = '\0';
return (dest);
}
char *ft_strjoin(char const *s1, char const *s2)
{
char *str;
size_t len;
if (!s1 && !s2)
return (ft_strdup(""));
if (s1 && !s2)
return (ft_strdup((char *)s1));
if (!s1 && s2)
return (ft_strdup((char *)s2));
len = ft_strlen((char *)s1) + ft_strlen((char *)s2);
str = malloc(sizeof(char) * (len + 1));
if (!str)
return (NULL);
str[0] = '\0';
ft_strcat(str, (char *)s1);
ft_strcat(str, (char *)s2);
return (str);
}
Problem
I went over the code and it seems that i am freeing everything.
I think the issue may be with returning buf in the get_next_line function, which gets allocated by the next_line function. I cant free this because i am returning it, but i am freeing the string later in the main().
Maybe i should be freeing the static variable after i reach the end of the file i'm reading from the descriptor?
Any suggestions appreciated, thanks
"seems that i am freeing everything" --> code does not free str in get_next_line() when done.

Getting valgrind errors with static char * [duplicate]

I have to recode an implementation of the getline() function, but using the file descriptor of the file and not a FILE *. I am only allowed to use malloc() and free(), along with 5 functions being 25 lines long at most.
I think I've done correctly the project although I am a beginner in C and my code isn't probably good.
When I run it, it works fine, but valgrind shows that I definetely lost x bytes, x depending of the file length and the READ_SIZE (macro defined in the header).
According to valgrind's --leak-check=full, I have a memory leak in the str_realloc_cat function, when I malloc dest. I tried but couldn't find where should I free / do something else?
Here below is my code:
char *get_next_line(const int fd)
{
static char *remaining = "";
char *buffer;
ssize_t cread;
size_t i;
i = 0;
if (remaining == NULL)
return (NULL);
if ((buffer = malloc(SOF(char) * READ_SIZE + 1)) == NULL ||
(cread = read(fd, buffer, READ_SIZE)) < 0)
return (NULL);
buffer[cread] = 0;
remaining = str_realloc_cat(remaining, buffer);
while (remaining[i])
{
if (remaining[i] == 10)
{
remaining[i] = 0;
buffer = str_create_cpy(remaining);
remaining = remaining + i + 1;
return (buffer);
}
i++;
}
return (check_eof(fd, buffer, remaining, cread));
}
char *str_realloc_cat(char *rem, char *buf)
{
size_t i;
size_t dest_i;
char *dest;
i = (dest_i = 0);
if ((dest = malloc(SOF(char) * (str_len(rem) + str_len(buf) + 1))) == NULL)
return (NULL);
while (rem[i])
{
dest[dest_i] = rem[i];
dest_i++;
i++;
}
i = 0;
while (buf[i])
{
dest[dest_i] = buf[i];
dest_i++;
i++;
}
dest[dest_i] = 0;
free(buf);
return (dest);
}
char *check_eof(const int fd, char *buffer, char *remaining, ssize_t cread)
{
if (cread == 0)
return (NULL);
if (cread < READ_SIZE)
{
buffer = remaining;
remaining = NULL;
return (buffer);
}
return (get_next_line(fd));
}
char *str_create_cpy(const char *src)
{
char *dest;
size_t i;
i = 0;
if ((dest = malloc(sizeof(char) * str_len(src) + 1)) == NULL)
return (NULL);
while (src[i])
{
dest[i] = src[i];
i++;
}
dest[i] = 0;
return (dest);
}
int str_len(const char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
And a main functon if you would like to test:
#define SOF(x) sizeof(x) // Why in the comments
int main(int ac, char **av)
{
int fd;
char *s;
UNUSED(ac);
if (!av[1])
return 1;
fd = open(av[1], O_RDONLY);
while ((s = get_next_line(fd)))
{
printf("%s\n", s);
free(s);
}
close(fd);
}
Your algorithm is bad:
You keep the buffer in a allocate memory
You don't use a structure to regroup your variable
You use magic number remaining[i] == 10
You use recursive you can stack overflow return get_next_line(fd). Never mind, I didn't read well you have a tail recursive, just be sure to have the optimization on your compile for it.
You have Spaghetti code.
etc.
You should rewrite your whole function with a better logic first use this structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GNL_SIZE 4096
struct gnl_context {
char buffer[GNL_SIZE];
size_t i;
size_t read;
};
char *get_next_line_r(int fd, struct gnl_context *gnl_context);
char *get_next_line(int fd);
static char *read_buffer(struct gnl_context *gnl_context, char *str,
size_t *size) {
size_t i = gnl_context->i;
while (i < gnl_context->read && gnl_context->buffer[i] != '\n') {
i++;
}
size_t j = i - gnl_context->i;
char *ret = realloc(str, *size + j + 1);
if (ret == NULL) {
return NULL;
}
memcpy(ret + *size, gnl_context->buffer + gnl_context->i, j);
*size += j;
ret[*size] = '\0';
gnl_context->i = i;
return ret;
}
char *get_next_line_r(int fd, struct gnl_context *gnl_context) {
char *str = NULL;
size_t size = 0;
loop:
if (gnl_context->i == gnl_context->read) {
ssize_t ret = read(fd, gnl_context->buffer, GNL_SIZE);
if (ret <= 0) {
return str;
}
gnl_context->read = (size_t)ret;
gnl_context->i = 0;
}
char *tmp = read_buffer(gnl_context, str, &size);
if (tmp == NULL) {
return str;
}
if (gnl_context->i != gnl_context->read) {
gnl_context->i++;
return tmp;
}
str = tmp;
goto loop;
}
char *get_next_line(int fd) {
static struct gnl_context gnl_context;
return get_next_line_r(fd, &gnl_context);
}
int main(void) {
char *str;
while ((str = get_next_line(0)) != NULL) {
printf("%s\n", str);
free(str);
}
}
I am concerned about this line:
remaining = remaining + i + 1;
remaining is a pointer to the allocated buffer. On this line, you destroy it, which means that you cannot free() it anymore.

Coding a getline() implementation - Valgrind errors

I have to recode an implementation of the getline() function, but using the file descriptor of the file and not a FILE *. I am only allowed to use malloc() and free(), along with 5 functions being 25 lines long at most.
I think I've done correctly the project although I am a beginner in C and my code isn't probably good.
When I run it, it works fine, but valgrind shows that I definetely lost x bytes, x depending of the file length and the READ_SIZE (macro defined in the header).
According to valgrind's --leak-check=full, I have a memory leak in the str_realloc_cat function, when I malloc dest. I tried but couldn't find where should I free / do something else?
Here below is my code:
char *get_next_line(const int fd)
{
static char *remaining = "";
char *buffer;
ssize_t cread;
size_t i;
i = 0;
if (remaining == NULL)
return (NULL);
if ((buffer = malloc(SOF(char) * READ_SIZE + 1)) == NULL ||
(cread = read(fd, buffer, READ_SIZE)) < 0)
return (NULL);
buffer[cread] = 0;
remaining = str_realloc_cat(remaining, buffer);
while (remaining[i])
{
if (remaining[i] == 10)
{
remaining[i] = 0;
buffer = str_create_cpy(remaining);
remaining = remaining + i + 1;
return (buffer);
}
i++;
}
return (check_eof(fd, buffer, remaining, cread));
}
char *str_realloc_cat(char *rem, char *buf)
{
size_t i;
size_t dest_i;
char *dest;
i = (dest_i = 0);
if ((dest = malloc(SOF(char) * (str_len(rem) + str_len(buf) + 1))) == NULL)
return (NULL);
while (rem[i])
{
dest[dest_i] = rem[i];
dest_i++;
i++;
}
i = 0;
while (buf[i])
{
dest[dest_i] = buf[i];
dest_i++;
i++;
}
dest[dest_i] = 0;
free(buf);
return (dest);
}
char *check_eof(const int fd, char *buffer, char *remaining, ssize_t cread)
{
if (cread == 0)
return (NULL);
if (cread < READ_SIZE)
{
buffer = remaining;
remaining = NULL;
return (buffer);
}
return (get_next_line(fd));
}
char *str_create_cpy(const char *src)
{
char *dest;
size_t i;
i = 0;
if ((dest = malloc(sizeof(char) * str_len(src) + 1)) == NULL)
return (NULL);
while (src[i])
{
dest[i] = src[i];
i++;
}
dest[i] = 0;
return (dest);
}
int str_len(const char *str)
{
size_t i;
i = 0;
while (str[i])
i++;
return (i);
}
And a main functon if you would like to test:
#define SOF(x) sizeof(x) // Why in the comments
int main(int ac, char **av)
{
int fd;
char *s;
UNUSED(ac);
if (!av[1])
return 1;
fd = open(av[1], O_RDONLY);
while ((s = get_next_line(fd)))
{
printf("%s\n", s);
free(s);
}
close(fd);
}
Your algorithm is bad:
You keep the buffer in a allocate memory
You don't use a structure to regroup your variable
You use magic number remaining[i] == 10
You use recursive you can stack overflow return get_next_line(fd). Never mind, I didn't read well you have a tail recursive, just be sure to have the optimization on your compile for it.
You have Spaghetti code.
etc.
You should rewrite your whole function with a better logic first use this structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GNL_SIZE 4096
struct gnl_context {
char buffer[GNL_SIZE];
size_t i;
size_t read;
};
char *get_next_line_r(int fd, struct gnl_context *gnl_context);
char *get_next_line(int fd);
static char *read_buffer(struct gnl_context *gnl_context, char *str,
size_t *size) {
size_t i = gnl_context->i;
while (i < gnl_context->read && gnl_context->buffer[i] != '\n') {
i++;
}
size_t j = i - gnl_context->i;
char *ret = realloc(str, *size + j + 1);
if (ret == NULL) {
return NULL;
}
memcpy(ret + *size, gnl_context->buffer + gnl_context->i, j);
*size += j;
ret[*size] = '\0';
gnl_context->i = i;
return ret;
}
char *get_next_line_r(int fd, struct gnl_context *gnl_context) {
char *str = NULL;
size_t size = 0;
loop:
if (gnl_context->i == gnl_context->read) {
ssize_t ret = read(fd, gnl_context->buffer, GNL_SIZE);
if (ret <= 0) {
return str;
}
gnl_context->read = (size_t)ret;
gnl_context->i = 0;
}
char *tmp = read_buffer(gnl_context, str, &size);
if (tmp == NULL) {
return str;
}
if (gnl_context->i != gnl_context->read) {
gnl_context->i++;
return tmp;
}
str = tmp;
goto loop;
}
char *get_next_line(int fd) {
static struct gnl_context gnl_context;
return get_next_line_r(fd, &gnl_context);
}
int main(void) {
char *str;
while ((str = get_next_line(0)) != NULL) {
printf("%s\n", str);
free(str);
}
}
I am concerned about this line:
remaining = remaining + i + 1;
remaining is a pointer to the allocated buffer. On this line, you destroy it, which means that you cannot free() it anymore.

C char* array allocation

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().

Append a character to function input?

I need the send and integer to a function and then append that to the end of a constant character.
int main (void)
{
append(1);
}
int append(int input)
{
const char P = 'P';
//This where I want to append 1 to P to create "P1"'
}
No matter what you do, you need to convert the number to a string, otherwise you can't create a string containing both numbers.
You can actually combine both the concatenation and the int-to-string conversion in one function call: sprintf:
char output[16];
sprintf(output, "P%d", input);
I'm not an expert on C, but I don't believe constants should be changed once they are defined.
Not sure if you can add something to a const chat (since its a const).
But why not:
char p[3];
sprintf(p, "P%d",input);
You cannot assign more than one character value to a char. For doing that you would have to take a string. Maybe like this.
int append(int input)
{
const char P = 'P';
//This where I want to append 1 to P to create "P1"
char app[2] ; //extend that for your no. of digits
app[0] = P '
app[1] = (char) input ;
}
This is for one digit. You can allocate dynamic memory for big integers and do the same in a loop.
What about using strncat?
See a working example on codepad: http://codepad.org/xdwhH0ss
I would convert the number to a string (assuming you have access to a function called itoa in this example and concatenate it to the character. If you don't have access to itoa you could sprintf instead.
itoa method:
#include <stdio.h>
#include <stdlib.h>
char *foo(const char ch, const int i)
{
char *num, *ret;
int c = i;
if(c <= 0) c++;
if(c == 0) c++;
while(c != 0)
{
c++;
c /= 10;
}
c += 1;
if(!(num = malloc(c)))
{
fputs("Memory allocation failed.", stderr);
exit(1);
}
if(!(ret = malloc(c + 1)))
{
fputs("Memory allocation failed.", stderr);
free(num);
exit(1);
}
itoa(i, num, 10);
ret[0] = ch;
ret[1] = 0x00;
strcat(ret, num);
free(num);
return ret;
}
int main(void)
{
char *result;
if(!(result = foo('C', 20))) exit(1);
puts(result);
free(result);
return 0;
}
sprintf method:
#include <stdio.h>
#include <stdlib.h>
char *foo(const char ch, const int i)
{
char *num, *ret;
int c = i;
if(c <= 0) c++;
if(c == 0) c++;
while(c != 0)
{
c++;
c /= 10;
}
c += 1;
if(!(num = malloc(c)))
{
fputs("Memory allocation failed.", stderr);
exit(1);
}
if(!(ret = malloc(c + 1)))
{
fputs("Memory allocation failed.", stderr);
free(num);
exit(1);
}
sprintf(num, "%d", i);
ret[0] = ch;
ret[1] = 0x00;
strcat(ret, num);
free(num);
return ret;
}
int main(void)
{
char *result;
if(!(result = foo('C', 20))) exit(1);
puts(result);
free(result);
return 0;
}
I compiled and tested both of these and they seem to work quite nicely. Good luck.

Resources