I’m trying to make some sort of simple console text editor program to get better with C. I’m having trouble with what I thought would be a somewhat simple task:
How do I get each line from my char* buffer, which contains all of my text, so I can output each line, including empty lines, with the correct line number in front of it.
I tried several different ways already, but none have stuck. I tried strtok(), which is what is currently pushed to my repo, and it ignores whitespace. I tried strchr() but did not have the slightest idea how that function worked and got an infinite loop. I tried doing my own function to create an array of lines but that lead to a segmentation fault which was not fixed by mallocing the array. I am at a loss here, I’m not sure what I can do.
Here is the repo: https://codeberg.org/Mister_Bones/txt-ed
Here is the offending code:
// Print contents of file
int print_file(char* buffer) {
// Print a new line
printf("\n");
// Print each line with line number
// Set first line
int line_num = 1;
// Get individual line from buffer
char* line = strtok(buffer, "\n");
// Loop through and print lines
// TODO: Don't ignore whitespace
while (line != NULL) {
printf("%4d\t%s\n", line_num, line);
line = strtok(NULL, "\n");
line_num++;
}
return 0;
}


the strtok issue is, per the man page, that strtok returns only “non empty” tokens with your given delimiters.
Given only one delimiter, the strchr approach is likely what I’d use. understanding strchr (and the similar strstr) is a pretty useful thing to have anyway, so it’s a good time to learn it. strchr makes no modifications and isn’t aiming to make tokens for you. It is looking for the first instance of a given byte in a C string and, provided it found it, returning you a pointer to that byte. as such, if it returns you something other than NULL, you’ve got a pointer to a byte. assuming you’re fine with the function modifying the buffer, you can convert that byte to a nul byte (\0), then print buffer, then a new line. You then advance your buffer pointer to the byte past the now-nul byte. Continue until strchr returns NULL, which is your last line - assuming the buffer is nul-terminated
Edit: example, now that I’ve got a real keyboard:
I’ve put together code that copies the buffer and then prints it exactly as it would appear in a text editor. Since I used
malloc()to allocate space for the copy, I figured I would need to use afree(), but it seems I can’t do that. Is it fine to not free strings, or is there a special way to free them?So there’s a couple errors I can see in there: one which causes your issue here, and one that will cause another painful issue, unless you’re lucky.
malloc(strlen(buffer + 1));is not the same asmalloc(strlen(buffer) + 1);. Your allocation is actually one byte shorter than your input string. This means yourstrcpywill copy a byte past the allocated buffer. Only bad things can come out of that, and they may not be noticeable at first.mallocis giving you back an address - call it0x0001- and promises to have at least the available size you requested. You promise to at some pointfreethat address. That address is stored intoline.strchrreturns a different address - one later in the string, specifically. Your loop continuously advanceslineto that new address plus one. So, at the end, you’ve been given an address, 0x0001, and you’re trying to free some other address - e.g. 0x10de. That’s why you can’t free; you’re freeing something other than what you malloc’d. You need to hold onto the actual address returned by malloc in another variable, and free that address specifically.For #1, I’d personally recommend taking a look at
strdupinstead ofmalloc+strcpy. It’s identical functionally, so I invite you to wholly understand all three functions and understand when/why they’re used, but when copying a string exactly,strdupis the way to go - and it removes the possibility of an errant+ 1. As part of the understanding, understand that the result ofstrdupdoes still need to be given tofreeeventually.As a general note, to provide maybe some clarity on “is it fine to not free strings” - strings don’t really exist in C. That is, there’s not some special thing that the system sees as a string. They’re by convention - a string is a blob of non-
'\0'bytes that ends with'\0'. malloc and free do not know or care what you did with the bytes you received, and whenever you see a man page say that it does something with a string (copy, search, etc.), you can confidently substitute string with “blob of non-nul bytes ending with a nul byte”. “string” just sounds nicer, and by convention, that’s what we know a “C string” to be.I see , thank you for the example, I think I’ve come up with a way I can use
strchrfor my purposes.Also, I’ve heard of there being man pages for C library functions before, is there a way I can run the man command with a C library function from the linux terminal?
Most linux distributions either include C man pages by default or add them in when you install dev tools. I’d be surprised if
man strchrdidn’t already come up with something for you.If you’re going down this path, it’s worth recognizing the different meanings of the man sections - shell commands in section 1 means that e.g.
man chownis going to show you shell instead of the C function you’ll see fromman 2 chown. In the same vein, section 3 has library functions, which are sometimes more relevant for you than section 2’s syscalls.This is very useful, thank you! It should be alot easier to figure out how these functions work without relying on modern search engines.