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.
Related
I am a newbie in C and for an exercise I have to write a program, where I can read in strings. If my reserved memory (length BUFFER_SIZE) isn't enough, it should reserve memory in increments of +=BUFFER_SIZE, as long as needed to read the string. I tried to write some functions to get this done, but it doesn't work. Can somebody please help me?
My code:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
size_t string_length(char *string)
{
size_t i = 0;
while(string[i] != '\0')
{
i++;
}
return i;
}
void string_concatenate(char *string, char *string_to_chain)
{
size_t length = string_length(string);
for(size_t i = 0; *(string_to_chain + i) != '\0'; i++, length++)
{
string[length] = string_to_chain [i];
}
string[length] = '\0';
}
char *string_search(char *string, char character)
{
do
{
if (*string == character)
{
return (char*)string;
}
} while (*string++);
return NULL;
}
char *get_line()
{
int line_size = BUFFER_SIZE;
char* line = malloc(line_size * sizeof(char));
if(line == NULL)
{
return NULL;
}
printf("Bitte Text eingeben: \n");
fgets(line, BUFFER_SIZE, stdin);
char *new_line_character = string_search(line, '\n');
while(new_line_character == NULL)
{
line_size += BUFFER_SIZE;
line = realloc(line, (line_size * sizeof(char)));
if(line == NULL)
{
return NULL;
}
char *new_line = line + BUFFER_SIZE - 1;
fgets(new_line, line_size, stdin);
new_line_character = string_search(line, '\n');
}
*new_line_character = '\0';
return line;
}
}
int main(void) {
char *string = get_line();
printf("s%\n", string);
}
Here is a problem I am trying to solve. I have a task to create a function, which has to return newline from the file(accessing with fd). It has to use only functions read,malloc,free and the functions I wrote.
I used linked lists to add and save the data from the files. 1 node represents 1 specific file.
Please help me find out what is the problem in this case. It works well in all cases except when BUFFER_SIZE is 1 and we gave him more than 2 file descriptors.
In the firs call of get_next_line function it reads well for first 2 file descriptors and returns the line properly. For the 3rd fd it returns 2nd file descriptors data. At the second call it returns 2nd line of each fd(1st and 2nd) and returns 1st line of 3rd file.
Here is the way I have to compile my files for using this function.
gcc -Wall -Wextra -Werror -D BUFFER_SIZE=1 get_next_line_bonus.c get_next_line_utils_bonus.c get_next_line.h main.c
./a.out
get_next_line.h
# define GET_NEXT_LINE_H
# include <unistd.h>
# include <stdlib.h>
typedef struct s_info
{
int fd;
char *info;
int check;
struct s_info *next;
} t_info;
int get_next_line(int fd, char **line);
t_info *ft_lstnew(int fd);
char *get_line(char *str);
int ncheck(char *str);
unsigned long ft_strlen(const char *s);
char *get_save(char *save);
void *ft_memmove(void *dest, const void *src, size_t len);
char *ft_strjoin(char const *s1, char const *s2);
t_info *ft_find(int fd, t_info **start);
int ft_lstdelone(t_info **start, int fd);
#endif
get_next_line.c
size_t ft_strlen(const char *s)
{
int i;
i = 0;
if (!s)
return (0);
while (s[i] != '\0')
i++;
return (i);
}
char *ft_strjoin(char const *s1, char const *s2)
{
size_t s1_len;
size_t s2_len;
size_t stot_len;
char *rtn;
if (!s1 && !s2)
return (0);
s1_len = ft_strlen((char *)s1);
s2_len = ft_strlen((char *)s2);
stot_len = s1_len + s2_len + 1;
rtn = malloc(sizeof(char) * stot_len);
if (!rtn)
return (0);
ft_memmove(rtn, s1, s1_len);
ft_memmove(rtn + s1_len, s2, s2_len);
rtn[stot_len - 1] = '\0';
free((char *)s1);
return (rtn);
}
t_info *ft_lstnew(int fd)
{
t_info *i;
if ((i = (t_info*)malloc(sizeof(t_info))) == 0)
return (0);
i->fd = fd;
if ((i->info = malloc(sizeof(char*) * (1 + BUFFER_SIZE))) == 0)
return (0);
i->check = 1;
i->next = NULL;
return (i);
}
t_info *ft_find(int fd, t_info **start)
{
t_info *finder;
t_info *new;
finder = *start;
while (finder)
{
if (finder->fd == fd)
return (finder);
finder = finder->next;
}
if ((new = ft_lstnew(fd)) == 0)
return (0);
if (!(*start))
{
*start = new;
return (new);
}
new->next = *start;
*start = new;
return (new);
}
int get_next_line(int fd, char **line)
{
static t_info *start = NULL;
t_info *tmp;
char *buff;
if (fd < 0 || !line || BUFFER_SIZE <= 0 ||
((tmp = ft_find(fd, &start)) == 0) ||
!(buff = malloc(sizeof(char) * (BUFFER_SIZE + 1))))
return (-1);
while (!ncheck(tmp->info) && tmp->check != 0)
{
if ((tmp->check = read(fd, buff, BUFFER_SIZE)) == -1)
{
free(buff);
return (-1);
}
buff[tmp->check] = '\0';
tmp->info = ft_strjoin(tmp->info, buff);
}
free(buff);
*line = get_line(tmp->info);
tmp->info = get_save(tmp->info);
if (tmp->check == 0)
return (ft_lstdelone(&start, fd));
return (1);
}
get_next_line_utils.c
char *get_save(char *save)
{
char *rtn;
int i;
int j;
i = 0;
j = 0;
if (!save)
return (0);
while (save[i] && save[i] != '\n')
i++;
if (!save[i])
{
free(save);
return (0);
}
if (!(rtn = malloc(sizeof(char) * ((ft_strlen(save) - i) + 1))))
return (0);
i++;
while (save[i])
rtn[j++] = save[i++];
rtn[j] = '\0';
free(save);
return (rtn);
}
char *get_line(char *str)
{
int i;
char *rtn;
i = 0;
if (!str)
return (0);
while (str[i] && str[i] != '\n')
i++;
if (!(rtn = malloc(sizeof(char) * (i + 1))))
return (0);
i = 0;
while (str[i] && str[i] != '\n')
{
rtn[i] = str[i];
i++;
}
rtn[i] = '\0';
return (rtn);
}
void *ft_memmove(void *dst, const void *src, size_t len)
{
char *d;
char *s;
d = (char *)dst;
s = (char *)src;
if (dst == src)
return (dst);
if (s < d)
{
while (len--)
*(d + len) = *(s + len);
return (dst);
}
while (len--)
*d++ = *s++;
return (dst);
}
int ncheck(char *str)
{
int i;
i = 0;
if (!str)
return (0);
while (str[i])
{
if (str[i] == '\n')
return (1);
i++;
}
return (0);
}
int ft_lstdelone(t_info **start, int fd)
{
t_info *finder;
t_info *tmp;
finder = *start;
if (finder->fd == fd)
{
free(finder->info);
*start = (*start)->next;
free(finder);
}
else
{
while (finder->next->fd != fd && finder->next)
finder = finder->next;
free(finder->next->info);
tmp = finder->next;
finder->next = tmp->next;
free(tmp);
}
return (0);
}
Here is the behaviour of my test main I used.
c1r6s3% gcc -Wall -Wextra -Werror -D BUFFER_SIZE=1 get_next_line_bonus.c get_next_line_utils_bonus.c get_next_line.h main.c && ./a.out
1st call
buffer1=t morning; should have arrived at 6:46, but train w
buffer2=1.Aasdasndmbc this is 1st line of 2nd file
buffer3=1.Aasdasndmbc this is not what supposed to be in 3rd file
2nd call
buffer1=as anhour late. Buda-Pesth seems a wonderful place, from the glimpse which Igot of it from the train
buffer2=2.Dcccsadkajskldjaskld
buffer3=1.Abc 1st line of 3rd file
buffer1=and the little I could walk through thestreets. I feared to go very far from the station, as we had arr
buffer2=3.Saksadklasjdlkasjd
buffer3=2.Dccc
buffer1=ivedlate and would start as near the correct time as possible. Theimpression I had was that we were leaving the West and entering theEast; the most western of splendid bridges over the Danube, which ish
buffer2=4.Sakdljlkasjdlkasjsadlkasjdklasjd
buffer3=3.Sak
During debug process I found out my nodes took the previous node's info.
I wrote a small check in my get_next_line function.
if(fd > 3 && BUFFER_SIZE == 1)
// means this is not the first file to read && the specific case for BUFFER_SIZE
tmp->info = NULL; It will not look at the same value as the previous so we can give him another value in our calls.
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.
In this function, I have leak of memory
static int read_buffer(int const fd, char **buffer)
{
char buff[BUFF_SIZE + 1];
int ret;
ret = read(fd, buff, BUFF_SIZE);
if (ret > 0)
{
buff[ret] = 0;
if (!(*buffer = ft_strjoin(*buffer, buff)))
return (-1);
}
return (ret);
}
I tried to do this and more solution again.. But I get also leak of memory
static int read_buffer(int const fd, char **buffer)
{
char buff[BUFF_SIZE + 1];
char *tmp;
int ret;
ret = read(fd, buff, BUFF_SIZE);
tmp = *buffer;
if (ret > 0)
{
buff[ret] = 0;
if (!(*buffer = ft_strjoin(*buffer, buff)))
return (-1);
free(tmp);
}
return (ret);
}
Here is ft_strjoin function: (can't modify)
char *ft_strjoin(char const *s1, char const *s2)
{
size_t size_s1;
size_t size_s2;
char *strjoin;
size_s1 = ft_strlen(s1);
size_s2 = ft_strlen(s2);
if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
return (NULL);
ft_strcpy(strjoin, s1);
ft_strcat(strjoin, s2);
return (strjoin);
}
How I can resolve this ?
Thank you !
Anytime a function returns malloc'd memory, you've got a hot potato. You have to free that memory and cannot pass it on unless you've documented you're doing so. And you mustn't lose hold of (e.g. reuse) the pointer before freeing it. Below is how I interpret how read_buffer() needs to work. Since "buffer length is unknown" I'm assuming it comes from malloc and we can use realloc:
static int read_buffer(int const fd, char **buffer)
{
char local_buffer[BUFF_SIZE + 1];
int bytes_read = read(fd, local_buffer, BUFF_SIZE);
if (bytes_read > 0)
{
local_buffer[bytes_read] = '\0';
char *joined = ft_strjoin(*buffer, local_buffer);
if (joined != NULL)
{
*buffer = realloc(*buffer, strlen(joined) + 1);
if (*buffer != NULL)
{
ft_strcpy(*buffer, joined);
}
else
{
bytes_read = -1;
}
free(joined);
}
else {
bytes_read = -1;
}
}
return bytes_read;
}
Note that there are a lot of potential pitfalls that you need to test for and recover from.
If you're reading from the user directly, and don't want newlines as part of the concatenation of strings, you can replace this line:
local_buffer[bytes_read] = '\0';
with something like:
if (local_buffer[bytes_read - 1] == '\n')
{
local_buffer[bytes_read - 1] = '\0';
}
else
{
local_buffer[bytes_read] = '\0';
}
Assuming the above newline fix, here's a small test program I wrote:
int main() {
char *buffer = malloc(1);
*buffer = '\0';
for (int i = 0; i < 3; i++)
{
(void) read_buffer(0, &buffer);
puts(buffer);
}
free(buffer);
return 0;
}
RESULT
> ./a.out
abcdefg
abcdefg
hijklmn
abcdefghijklmn
opqrstu
abcdefghijklmnopqrstu
>
I'm trying to parse through some CSV log files extracting only the n'th field (dismissing the others for speed). My function works as expected when I use a buffer size with fread greater than the size of the input.
The problem is when I read in part of the input and try to continue where I left off the next time the function is called. I believe the problem lies in how I'm handling the null terminator and setting my globals, but I just can't seem to figure it out.
Any help with understanding what I'm doing wrong greatly appreciated!
Code
#include <stdio.h>
#include <time.h>
int gcomc = 0;
int gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
case '\0':
gcomc = comc;
gpos = pos;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
return;
}
int main(int argc, char* argv[])
{
FILE *fin=fopen("test.txt", "rb");
char buffer[100 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, 100, fin)) > 0)
{
buffer[bsz] = '\0';
test(buffer, bsz);
}
return 1;
}
Input
A;B;C;D;E;F;G;H
I;J;K;L;M;N;O;P
Q;R;S;T;U;V;W;X
Y;Z;1;2;3;4;5;6
Output with buffer size of 100 (101)
D L T 2
Output with buffer size of 10 (11)
D P
Q X
Segmentation fault (core dumped)
Edit:
Thank you for the comments and code, I've reworked my (rather dumb written) code - any further criticism is welcome (constructive or destructive, I learn from it all):
#include <stdio.h>
#include <time.h>
void test(char *str, int len);
int gcomc, gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
gcomc++;
break;
default:
if (gcomc == 3) {
ch = ptr[i];
so[gpos++] = ch;
}
if (gcomc == 7) {
so[gpos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
gcomc = 0;
gpos = 0;
}
}
}
return;
}
extern int main()
{
FILE *fin=fopen("test.txt", "rb");
char buffer[10 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
return 1;
}
There are at least two problems in your code to be able to read the file in chunks.
First, the so array is automatic: it has no reason to keep its values from one call to the others. You should declare it global (outside the test function) or static.
Next, you only copy the local state to global one when you find a null. But the null is at position len, and you exit the loop just before (for(i = 0; i < len; i++) note the <) so on next call you start again with 0, 0. You should choose one method to indicate the end of the buffer, either passing a length, of writing a null marker, but mixing both is error prone. As you use fread, my advice is to stick to a length:
In main use:
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
(that way, you only write the size of the buffer once)
and in test:
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
so[pos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
gcomc = comc; /* store the state to globals */
gpos = pos;
return;
}
But as you were said in comments mixing local and globals like that is error prone. It looks like you started coding before designing the structure of the program and identifying what actually needed to be global. You didn't, did you? ;-)
The state of the parser inside test() need to survive the multiple call. You took care of this partly only make the counters global. Globals are bad practise. Also you miss to save the state (its content) of so.
Encapsulate the state in a structure.
#include <stdlib.h>
#include <stdio.h>
#define SO_SIZE (10)
struct state
{
size_t comc;
size_t pos;
char so[SO_SIZE + 1]; /* Add 1 for the 0-terminator. */
}
and pass it to each call of the parser (test() here).
Adjust the parser like this:
int test(struct state * pstate, const char *str, size_t len)
{
int result = 0; /* be optimistic. */
char ch;
size_t i;
for (i = 0; i <= len; i++)
{
ch = str[i];
switch (ch)
{
case ';':
pstate->comc++;
break;
default:
if (pstate->comc == 3)
{
ch = str[i];
if (SO_SIZE <= pstate->pos)
{
result = -1; /* overflow */
break;
}
pstate->so[pstate->pos++] = ch;
}
if (pstate->comc == 7)
{
printf(" %s ", pstate->so);
pstate->comc = 0;
pstate->pos = 0;
}
}
}
return result;
}
Then call it like this:
#define BUFFER_SIZE (100)
int main(void)
{
FILE *fin = fopen("test.txt", "rb");
if (NULL == fin)
{
perror("fopen() failed");
return EXIT_FAILURE;
}
{
char buffer[BUFFER_SIZE + 1] = {0};
size_t bsz;
struct state state = {0};
int result;
while (0 < (bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin))
&& (0 == result))
{
result = test(&state, buffer, bsz);
}
return result ?EXIT_FAILURE :EXIT_SUCCESS;
}
}