I started coding in C a few months ago. I wrote this code to return each line from a file. It has to work with any buffer size, which right now it does, and it should return each line present in the file. However if I don't have a newline character at the end of my line, my code does not return anything.
So far I believe the issue is in my get_next_line function. I can see that I can get the line without the newline character into the line variable, as well as the stash variable. However, after the code cleans the stash, I believe it enters the if condition (stash[0] == '\0'), therefore ending my program before returning my line which is in my line variable. I added this condition as a way to terminate my main while loop, so if I remove it the code gets stuck on an infinite loop. I have tried other approaches and I managed to get all lines but then it only worked with buffer sizes greater than the size of my file, I did this by changing the i variable to be equal to find_nl(line) + (line[0] != '\0');. In this scenario, for smaller buffers I would get the lines but they would be "broken".
I'm at the very beginning of learning C so I would be grateful for any inputs to improve this code. Thank you in advance.
Here is the program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 100
int find_nl(char *stash);
char *get_line(char *stash, char *line);
void ft_clean(char *stash);
char *get_next_line(int fd)
{
static char stash[BUFFER_SIZE + 1];
char *line;
int n;
int i;
if (fd < 0 || BUFFER_SIZE <= 0)
return (NULL);
i = 0;
n = 0;
line = NULL;
while (!i)
{
if (!stash[0])
n = read(fd, stash, BUFFER_SIZE);
if (n == -1)
return (NULL);
line = get_line(stash, line);
i = find_nl(stash) + (line == NULL);
if (stash[0] == '\0')
return (NULL);
ft_clean(stash);
}
return (line);
}
int find_nl(char *stash)
{
size_t i;
if (stash == NULL)
return (0);
i = 0;
while (stash[i])
{
if (stash[i] == '\n')
return (1);
i++;
}
return (0);
}
char *get_line(char *stash, char *line)
{
size_t len;
size_t i;
size_t j;
char *nline;
len = 0;
j = 0;
while (stash[len] && stash[len] != '\n')
len++;
if (line == NULL)
i = 0;
else
while (line[i])
i++;
nline = (char *)malloc((len + i + 1) * sizeof(char));
if (nline == NULL)
return (NULL);
while (line && line[j])
{
nline[j] = line[j];
j++;
}
i = 0;
while (i < len)
{
nline[j] = stash[i];
i++;
j++;
}
nline[j] = '\0';
return (nline);
}
void ft_clean(char *stash)
{
size_t stash_len;
size_t len;
size_t i;
len = 0;
stash_len = 0;
i = 0;
if (stash == NULL)
return ;
while (stash[len])
{
if (stash[len] == '\n')
{
len++;
break ;
}
len++;
}
while (stash[stash_len] != '\0')
stash_len++;
while (i < stash_len - len + 1)
{
stash[i] = stash[i + len];
i++;
}
stash[i] = '\0';
}
int main(void) {
char *line;
while ((line = get_next_line(0)) != NULL) {
printf("[%s]\n", line);
free(line);
}
return 0;
}
It works if called as follows:
printf 'abc\n' | ./a
But the following doesn't output anything:
printf 'abc' | ./a
Not a direct answer, but i think there is a better approach. Your current code is hard to understand, not very modular and buggy.
You are using
malloc()andfree()in your code, normally there is nothing wrong with that, but since you have the requirement ofYou can't use them.
Instead of using
staticvariables inside the function (get_next_line()in your case forstashand potentially more since that would be required for a better implementation), i would suggest using astructand passing the pointer to thatstructwhen calling this functions. This would also allow you to use this functions for more than a single file at (quasi) the same time.Something like this:
When you
read()from a file, you may receive'\0'bytes butread()itself does not add a'\0'at the end, i.e.read()does not fill a string but a buffer that can contain any data. You need to use the return value ofread()to check how many characters where read. I suggest, you read to thebufferafter the last valid character (which was set from the last time youread()or at 0) and you can at the amount of read characters tovalidChars. Also note thatread()returns assize_ttype and notint.To avoid
malloc()you can search for'\n'in thebuffer(till you reachvalidChars, after that you either have to read more or have to decide that there is no space left in the buffer or you read the last line, then you have to use the next char after thevalidCharsposition) and replace it with a'\0', setcurrentLineEndto 1 character after that and return a pointer to the start. Whenfile_getNextLine()is called next time, move everything valid fromcurrentLineEndtillvalidCharsto the beginning (withmemmove()or your own implementation of it) and repeat the process.Edit:
If you need to handle lines of arbitrary length, without setting
BUFFER_SIZEso large, you could allocate new memory for the string to store and don't usecurrentLineEnd. Since you don't want to usemalloc(), you can reserve a buffer withmmap(). Like this:Don't forget to
munmap()it later.