realloc(): invalid next size: 0x00000000 - c

looking for some advice on a problem I've been trying to solve for hours.
The program reads from a text file and does some formatting based on commands given within the file. It seems to work for every file I've tried except 2, which are both fairly large.
Here's the offending code:
/* initalize memory for output */
output.data = (char**)calloc(1,sizeof(char*));
/* initialize size of output */
output.size = 0;
/* iterate through the input, line by line */
int i;
for (i = 0; i < num_lines; i++)
{
/* if it is not a newline and if formatting is on */
if (fmt)
{
/* allocate memory for a buffer to hold the line to be formatted */
char *line_buffer = (char*)calloc(strlen(lines[i]) + 1, sizeof(char));
if (line_buffer == NULL)
{
fprintf(stderr, "ERROR: Memory Allocation Failed\n");
exit(1);
}
/* copy the unformatted line into the buffer and tokenize by whitespace */
strcpy(line_buffer, lines[i]);
char* word = strtok(line_buffer, " \n");
/* while there is a word */
while (word)
{
/* if the next word will go over allocated width */
if (current_pos + strlen(word) + 1 > width)
{
/* make ze newline, increase output size */
strcat(output.data[output.size], "\n");
output.size++;
------->>>>> output.data = (char**)realloc(output.data, sizeof(char*) * (output.size + 1));
Using gdb I've figured out the error is on the line with the arrow pointing to it, only thing is I can't figure out why it occurs. It only happens when the text file that is being formatted is large (716 lines), and it seems to happen on the final iteration (num_lines = 716). Any thoughts would be hugely appreciated. Thanks!
EDIT: Sorry folks, should have mentioned that I'm pretty new to this! Fixed some of the errors.

The most immediate problem is:
strncat(output.data[output.size], "\n", 2);
as pointed out by BLUEPIXY. Currently output.data[output.size] is a null pointer 1, so you cannot strncat to it.
To fix this you could allocate some space:
output.data[output.size] = malloc(2);
if ( NULL == output.data[output.size] )
// error handling...
strcpy(output.data[output.size], "\n");
However there might be another solution that fits in better with the rest of your function, which you haven't shown. (Presumably you allocate space somewhere to store word).
It would be helpful to update your post and show the rest of the function. Also make sure you are posting the exact code, as (output.size + ) does not compile. I guess this is a typo you introduced when trying to put those big arrows on your line.
1 Actually it is all bits zero, which is a null pointer on common systems but not guaranteed to be so for all systems.

Related

I am trying to create a code polisher program in C

I am trying to create the function delete_comments(). The read_file() and main functions are given.
Implement function char *delete_comments(char *input) that removes C comments from program stored at input. input variable points to dynamically allocated memory. The function returns pointer to the polished program. You may allocate a new memory block for the output, or modify the content directly in the input buffer.
You’ll need to process two types of comments:
Traditional block comments delimited by /* and */. These comments may span multiple lines. You should remove only characters starting from /* and ending to */ and for example leave any following newlines untouched.
Line comments starting with // until the newline character. In this case, newline character must also be removed.
The function calling delete_comments() only handles return pointer from delete_comments(). It does not allocate memory for any pointers. One way to implement delete_comments() function is to allocate memory for destination string. However, if new memory is allocated then the original memory in input must be released after use.
I'm having trouble understanding why my current approach is wrong or what is the specific problem that I'm getting weird output. I'm approaching the problem by trying to create a new array where to copy the input string with the new rules.
#include "source.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* Remove C comments from the program stored in memory block <input>.
* Returns pointer to code after removal of comments.
* Calling code is responsible of freeing only the memory block returned by
* the function.
*/
char *delete_comments(char *input)
{
input = malloc(strlen(input) * sizeof (char));
char *secondarray = malloc(strlen(input) * sizeof (char));
int x, y = 0;
for (x = 0, y = 0; input[x] != '\0'; x++) {
if ((input[x] == '/') && (input[x + 1] == '*')) {
int i = 0;
while ((input[x + i] != '*') && (input[x + i + 1] != '/')) {
y++;
i++;
}
}
else if ((input[x] == '/') && (input[x + 1] == '/')) {
int j = 0;
while (input[x + j] != '\n') {
y++;
j++;
}
}
else {
secondarray[x] = input[y];
y++;
}
}
return secondarray;
}
/* Read given file <filename> to dynamically allocated memory.
* Return pointer to the allocated memory with file content, or
* NULL on errors.
*/
char *read_file(const char *filename)
{
FILE *f = fopen(filename, "r");
if (!f)
return NULL;
char *buf = NULL;
unsigned int count = 0;
const unsigned int ReadBlock = 100;
unsigned int n;
do {
buf = realloc(buf, count + ReadBlock + 1);
n = fread(buf + count, 1, ReadBlock, f);
count += n;
} while (n == ReadBlock);
buf[count] = 0;
return buf;
}
int main(void)
{
char *code = read_file("testfile.c");
if (!code) {
printf("No code read");
return -1;
}
printf("-- Original:\n");
fputs(code, stdout);
code = delete_comments(code);
printf("-- Comments removed:\n");
fputs(code, stdout);
free(code);
}
Your program has fundamental issues.
It fails to tokenize the input. Comment start sequences can occur inside string literals, in which case they do not denote comments: "/* not a comment".
You have some basic bugs:
if ((input[x] == '/') && (input[x + 1] == '*')) {
int i = 0;
while ((input[x + i] != '*') && (input[x + i + 1] != '/')) {
y++;
i++;
}
}
Here, when we enter the loop, with i = 0, input + x is still pointing to the opening /. We did not skip over the opening * and are already looking for a closing *. This means that the sequence /*/ will be recognized as a complete comment, which it isn't.
This loop's also assumes that every /* comment is properly closed. It's not checking for the null character which can terminate the input, so if the comment is not closed, it will march beyond the end of the buffer.
C has line continuations. In ISO C translation stage 2, all backlash-newline sequences are deleted, converting one or more physical lines into logical lines. What that means is that a // comment can span multiple physical lines:
// this is an \
extended comment
You can see, by the way, that StackOverflow's automatic language detector for syntax highlighting is getting this right!
Line continuations are independent of tokenization, which doesn't happen until translation stage 3. Which means:
/\
/\
this is an extended \
comment
That one has defeated StackOverflow's syntax highlighting.
Furthermore, a line continuation can happen in any token, possibly multiple times:
"\
this is a string literal\
"
If you really want to make this work 100% correctly, you need to parse the input. By "parse" I mean a more formal, rigorous detection routine that understands what it is reading, in the context it is reading it.
For example, there are many times where this code could be defeated.
printf("the answer is %d // %d\n", a, b);
would likely trip your // detection and strip the end of the printf.
There are two general approaches to the problem above:
Find every corner case where comment-like characters could be used, and write conditional statements to avoid them before stripping.
Fully parse the language, so you will know if you are within a string or some other context that's wrapping comment like characters, or if you are in the top level context where the characters really mean "this is a comment"
To learn about parsing, I generally recommend "The Dragon Book" but it is a hard read, unless you have studied a bit of Discrete Mathematics. It covers a lot of different parsing techniques, and in doing so it doesn't have many pages left for examples. This means that it's the kind of book where you have to read, think, and then program a mini-example. If you follow that path, there is no input you can't tackle.
If you are pragmatic in your solution, and it is not about learning parsing, but about stripping comments, I recommend that you find a well constructed parser for C, and then learn how to walk the Abstract Syntax Tree in an Emitter, which fails to emit the comments.
There are some projects that do this already; but, I don't know if they have the right structure for easy modification. lint comes to mind, as well as other "pretty-printers" GCC certainly has the parsing code in there, but I've heard that GCC's Abstract Syntax Tree isn't easy to learn.
Your solution has several problems:
The worst issue
As the first instruction in delete_comments() you overwrite input with a new pointer returned by malloc(), which points to memory of random contents.
In consequence the address to the real input is lost.
Oh, and please check the returned value, if you call malloc().
Failing to increment the scanned position in comments correctly
You are scanning the input by the index x, but if you detect a comment, you don't change it.
You are actually advancing y but this is only used for the copying.
Think about lines like these:
int x; /* some /* weird /* comment */
///////////////////////////////
for (;;) { }
Ignoring character and string literals
Your solution should take character and string literals into account.
For example:
int c_plus_plus_comment_start = '//'; /* multi character constant */
const char* c_comment_start = "/*";
Note: There are more. Learn to use a debugger, or at least insert lots of printf()s in "interesting" places.

output formatted text using width and margin

I am trying to write a program that will take the following input and will format it and output it to a text file.
Here is a picture of how it should work
?mrgn left: Each line following the command will be indented left spaces from
the left-­­hand margin. Note that this indentation must be included in the page
width. If this command does not appear in the input file, then the value of left
is 0 (zero).
I did the following so far:
while (fgets(line, MAX_LINE_LEN-1, infile) != NULL){/*Read the first line if it is not empty*/
char last[MAX_WORD_LEN] = {0};
char *p;
for (p = strtok(line, " "); p; p = strtok(NULL, " ")){
if(*last && strcmp(last, width)==0){
iwidth = atoi(p);
printf("width = %d\n", iwidth);
}
if(*last && strcmp(last, margin)==0){
imargin = atoi(p);
printf("margin = %d\n", imargin);
}
strncpy (last, p, MAX_WORD_LEN);
if(iwidth != 0 || imargin != 0){
printf("%s ", p);
}else{
printf("%s", line);
}
}
}
I am able to store the value of width and margin to a variable. I am now stuck on how I can specify the required formatting. I did some research but I couldn't find what I want. Please help!
Thank you!
Cheers!
After you have had several hours to work on the problem, let me give you a few pointers (no pun intended) that may help simplify your approach to the problem. While you can certainly use strtok to parse your option and width from the input file, there really is no need to tokenize when you know the line format containing the option will be "?name value".
A simplified approach, knowing your file contains the option as the first line, would simply be to read the entire line (using fgets or POSIX getline), verify the first character is '?' and then parse the option name and value from the line with sscanf. (you can either include the '?' in your format-string, or simply begin the parse at the 2nd character. (my choice) To accomplish this you could start with something similar to:
#include <stdio.h>
#include <string.h> /* for strcmp, strlen */
#define OPT 32 /* if you need constants, #define one (or more) */
#define WDTH 78
#define MAXC 1024
void str_word_wrap (char *buf, int n); /* wrap function prototype */
int main (void) {
char buf[MAXC] = "", /* buffer to hold words in file */
*p = buf, /* pointer to buf */
opt[OPT] = ""; /* buffer to hold option found in file */
int width = WDTH, /* variable holding width from option */
used = 0, /* number of chars used in buf */
val = 0; /* temp value to read option value from file */
/* option on line 1, read entire line */
if (!fgets (buf, MAXC, stdin) || *buf != '?')
fputs ("error: unexpected file format, using default width.\n\n",
stderr);
/* parse option and value, compare option is "width", use val as width */
if (sscanf (buf + 1, "%s %d", opt, &val) == 2) {
if (strcmp (opt, "width") == 0)
width = val;
}
...
At this point in your code, buf contains the first line, opt contains the option name, and width contains the width specified in the file (or the default width WDTH (78) in the event the first line did not contain the "?width val" information). Ideally, if the first line was not a valid option/value line, you would simply eliminate the excess whitespace from buf, add an ending ' ' and continue, but that code is left to you.
(note: I just redirect the file to stdin so I read from stdin instead of a file here -- but your infile is fine as well. You would just substitute infile where I read stdin)
Since you simply want to eliminate all the additional whitespace from your input file leaving a normally formatted paragraph which you will wrap to the specified width, using fscanf with the "%s" format specifier can handle the whitespace removal automatically. (with the scanf family, "%s" and numeric format specifiers ignore leading whitespace, "%c" and "%[..]" do not). So reading the remainder of the file into your buffer is simply a matter of reading each word in the file, keeping track of how many characters in your buffer you have used (so you know the next word will fit), and adding a ' ' (space) between each word as you add them to your buffer.
You can use strcat if that helps, or you can simply use a pointer and write a ' ' at one past the end of the current buffer and then a nul-terminating character past that on each iteration. Either way, just keep track of how many characters you have used so far and the len (length) of what you are adding, and then update your used count with the length of each word as you go. You could do something like the following:
while (scanf ("%s", p + used) == 1) { /* read each word, ignore WS */
size_t len = strlen (p + used); /* get length of word */
if (used + len + 2 >= MAXC) { /* make sure it fits with ' ' */
fputs ("warning: file truncated.\n", stderr);
break; /* note you can refine to save 1-char space at end */
}
*(p + used + len++) = ' '; /* add space at end of word */
*(p + used + len) = 0; /* nul-termiante after space */
used += len; /* update used with len */
}
*(p + --used) = 0; /* overwrite final ' ' with nul-character */
At this point you could write out your width value and the contents of your filled buffer for a check before you wrap the lines to width. I simply write the width used out before outputting the wrapped lines which completes the main() function of the program, e.g.
printf ("Wrapping file at width: %d\n\n", width);
str_word_wrap (buf, width); /* wrap buffer at width chars/output */
return 0;
}
All that remains is finishing the function to wrap the buffer to no more that width characters per line as you output your buffer. I provided the prototype above for str_word_wrap function and details in my original comment concerning the approach to wrapping the buffer by simply using a sliding-window of width length to work down your buffer, outputting the words that fit within the sliding window each time it is moved down the buffer.
To accomplish the task you generally use three pointers (I name then p the pointer to the current char, sp the start pointer for the window, and ep the end pointer for the window. The scheme is this, you begin with all three initialized to the beginning of your buffer, you then iterate over each char with p until p points to a space between the words, setting the end-pointer ep = p; each time a space is encountered. On each iteration you check if p - sp >= width, where p - sp is simply the current pointer address minus the start pointer address which tells you how many characters you have moved from the start. If that equals or exceeds your width, you know you last set ep (your end-pointer) to the last whitespace in the windows marking the end of the last word to output.
All that remains is outputting the line up to the end-pointer, (and a '\n') and then setting your new start-pointer to the next character after end-pointer and you can set your end-pointer to one after the current pointer (which slides your window forward) and you repeat. Nothing fancy is needed. Something like the following works fine:
void str_word_wrap (char *buf, int n)
{
char *p = buf, /* pointer to current char */
*sp = buf, /* pointer to start of line to print */
*ep = buf; /* pointer to end of line to print */
for (; *p && *p != '\n'; p++) { /* loop over each char (omit '\n')*/
if (*p == ' ') /* if space, set ep */
ep = p;
if (p - sp >= n) { /* if wrap length 'n' reached */
while (sp < ep) /* loop outputting chars sp -> ep */
putchar (*sp++);
putchar ('\n'); /* tidy up with '\n' */
sp = ++ep; /* set start to next after end */
ep = ++p; /* set end to next after current */
}
}
while (*sp && *sp != '\n') /* output last line of chars */
putchar (*sp++);
putchar ('\n'); /* tidy up with final '\n' */
}
Putting that altogether will handle your case, exactly:
Example Input File
$ cat dat/taggedparagraph.txt
?width 30
While there are enough characters here to
fill
at least one line, there is
plenty
of
white space that needs to be
eliminated
from the original
text file.
Now simply running the program using the file as input or redirecting the file to the programs stdin gives:
Example Use/Output
$ ./bin/wrapped_tagged_p < dat/taggedparagraph.txt
Wrapping file at width: 30
While there are enough
characters here to fill at
least one line, there is
plenty of white space that
needs to be eliminated from
the original text file.
Look things over and let me know if you have questions. This all boils down to basic pointer arithmetic and keeping track of where you are within a buffer while iterating over each character in the buffer to extract whatever specific information you need from it. You will often hear that referred to as "walking-a-pointer" over or down the buffer. Using a sliding-window is nothing more than walking-a-pointer while keeping track of the fixed point you started from and limiting the walk to no more than some fixed width of characters, and doing whatever it is you need to over-and-over again until you reach the end.
Help "Learn About Pointers"
Since in your comment below your question you mentioned you were "going to learn about pointers", start with the basics:
A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a;.
To reference the value at the address held by a pointer you dereference the pointer by using the unary '*' character before the pointer name. E.g., b holds the address of a (e.g. b point to a), so to get the value at the address held by b, you simply dereference b, e.g. *b.
It works the same way regardless what type of object the pointer points to. It is able to work that way because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 point to the next byte, for an int * pointer (normal 4-byte integer), pointer+1 will point to the next int at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)
When you are dealing with strings in C, you can iterate from the beginning to the end of the string checking each character and stopping when you reach the nul-terminating character at the end of every string. This nul-character serves as a sentinel for the end of the string. You will see it represented as '\0' or just plain 0. Both are equivalent. The ASCII character '\0' has the integer value 0.
A simple example of walking a pointer may help cement the concept:
#include <stdio.h>
int main (void) {
char buf[] = "walk-a-pointer down buf", /* buffer */
*p = buf; /* initialize p to point to buffer */
/* dereference the pointer to get the character at that address */
while (*p) { /* while *p != 0, or (*p != '\0') */
putchar (*p); /* output each character */
p++; /* advance pointer to next char */
}
putchar ('\n'); /* then tidy up with a newline */
return 0;
}
Example Use/Output
$ ./bin/walkpointer
walk-a-pointer down buf

Invalid Argument Reported By getdelim

I'm trying to use the getdelim function to read an entire text file's contents into a string.
Here is the code I am using:
ssize_t bytesRead = getdelim(&buffer, 0, '\0', fp);
This is failing however, with strerror(errno) saying "Error: Invalid Argument"
I've looked at all the documentation I could and just can't get it working, I've tried getline which does work but I'd like to get this function working preferably.
buffer is NULL initialised as well so it doesn't seem to be that
fp is also not reporting any errors and the file opens perfectly
EDIT: My implementation is based on an answer from this stackoverflow question Easiest way to get file's contents in C
Kervate, please enable compiler warnings (-Wall for gcc), and heed them. They are helpful; why not accept all the help you can get?
As pointed out by WhozCraig and n.m. in comments to your original question, the getdelim() man page shows the correct usage.
If you wanted to read records delimited by the NUL character, you could use
FILE *input; /* Or, say, stdin */
char *buffer = NULL;
size_t size = 0;
ssize_t length;
while (1) {
length = getdelim(&buffer, &size, '\0', input);
if (length == (ssize_t)-1)
break;
/* buffer has length chars, including the trailing '\0' */
}
free(buffer);
buffer = NULL;
size = 0;
if (ferror(input) || !feof(input)) {
/* Error reading input, or some other reason
* that caused an early break out of the loop. */
}
If you want to read the contents of a file into a single character array, then getdelim() is the wrong function.
Instead, use realloc() to dynamically allocate and grow the buffer, appending to it using fread(). To get you started -- this is not complete! -- consider the following code:
FILE *input; /* Handle to the file to read, assumed already open */
char *buffer = NULL;
size_t size = 0;
size_t used = 0;
size_t more;
while (1) {
/* Grow buffer when less than 500 bytes of space. */
if (used + 500 >= size) {
size_t new_size = used + 30000; /* Allocate 30000 bytes more. */
char *new_buffer;
new_buffer = realloc(buffer, new_size);
if (!new_buffer) {
free(buffer); /* Old buffer still exists; release it. */
buffer = NULL;
size = 0;
used = 0;
fprintf(stderr, "Not enough memory to read file.\n");
exit(EXIT_FAILURE);
}
buffer = new_buffer;
size = new_size;
}
/* Try reading more data, as much as fits in buffer. */
more = fread(buffer + used, 1, size - used, input);
if (more == 0)
break; /* Could be end of file, could be error */
used += more;
}
Note that the buffer in this latter snippet is not a string. There is no terminating NUL character, so it's just an array of chars. In fact, if the file contains binary data, the array may contain lots of NULs (\0, zero bytes). Assuming there was no error and all of the file was read (you need to check for that, see the former example), buffer contains used chars read from the file, with enough space allocated for size. If used > 0, then size > used. If used == 0, then size may or may not be zero.
If you want to turn buffer into a string, you need to decide what to do with the possibly embedded \0 bytes -- I recommend either convert to e.g. spaces or tabs, or move the data to skip them altogether --, and add the string-terminating \0 at end to make it a valid string.

memcpy vs strcat

Seems to be a basic question but I would rather ask this to clear up than spend many more days on this.I am trying to copy data in a buffer which I receive(recv call) which will be then pushed to a file. I want to use memcpy to continuously append/add data to the buffer until the size of buffer is not enough to hold more data where I than use the realloc. The code is as below.
int vl_packetSize = PAD_SIZE + (int)p_size - 1; // PAD_SIZE is the size of char array sent
//p_size is the size of data to be recv. Same data size is used by send
int p_currentSize = MAX_PROTO_BUFFER_SIZE;
int vl_newPacketSize = p_currentSize;
char *vl_data = (char *)malloc(vl_packetSize);
memset((char *)vl_data,'\0',vl_packetSize);
/* Allocate memory to the buffer */
vlBuffer = (char *)malloc(p_currentSize);
memset((char *)vlBuffer,'\0',p_currentSize);
char *vlBufferCopy = vlBuffer;
if(vlBuffer==NULL)
return ERR_NO_MEM;
/* The sender first sends a padding data of size PAD_SIZE followed by actual data. I want to ignore the pad hence do vl_data+PAD_SIZE on memcpy */
if((p_currentSize - vl_llLen) < (vl_packetSize-PAD_SIZE)){
vl_newPacketSize +=vl_newPacketSize;
char *vlTempBuffer = (char *)realloc(vlBufferCopy,(size_t)vl_newPacketSize);
if(vlTempBuffer == NULL){
if(debug > 1)
fprintf(stdout,"Realloc failed:%s...Control Thread\n\n",fn_strerror_r(errno,err_buff));
free((void *)vlBufferCopy);
free((void *)vl_data);
return ERR_NO_MEM;
}
vlBufferCopy = vlTempBuffer;
vl_bytesIns = vl_llLen;
vl_llLen = 0;
vlBuffer = vlBufferCopy+vl_bytesIns;
fprintf(stdout,"Buffer val after realloc:%s\n\n",vlBufferCopy);
}
memcpy(vlBuffer,vl_data+PAD_SIZE,vl_packetSize-PAD_SIZE);
/*
fprintf(stdout,"Buffer val before increment:%s\n\n",vlBuffer);
fprintf(stdout,"vl_data length:%d\n\n",strlen(vl_data+PAD_SIZE));
fprintf(stdout,"vlBuffer length:%d\n\n",strlen(vlBuffer));
*/
vlBuffer+=(vl_packetSize-PAD_SIZE);
vl_llLen += (vl_packetSize-PAD_SIZE);
vl_ifNotFlush = 1;
//fprintf(stdout,"Buffer val just before realloc:%s\n\n",vlBufferCopy);
}
Problem: Whan ever I fputs the data into the file later on. Only the first data recv/added to buffer is gets into the file.
Also when I print the value of vlBufferCopy(which points to first location of data returned by malloc or realloc) I get the same result.
If I decrease the size by 1, I see entire data in the file, but it somehow misses the new line character and hence the data is
not inserted in the proper format in the file.
I know it is because of trailing '\0' but some how reducing the size by 1
(vlBuffer+=(vl_packetSize-PAD_SIZE-1);)
misses the new line character. fputs while putting the data removes the trailing null character
Please let me know what I am missing here to check or in the logic
(Note: I tried using strcat:
strcat(vlBuffer,vl_data+PAD_SIZE);
but I wanted to use memcpy as it is faster and also it can be used for any kind of buffer and not only character pointer
Thanks
strcat and memcpy are very different functions.
I suggest you read the documentation of each.
Mainly, there are two differences:
1. memcpy copies data where you tell it to. strcat finds the end of the string, and copies there.
2. memcpy copies the number of bytes you request. strcat copies until the terminating null.
If you're dealing with packets of arbitrary contents, you have no use for strcat, or other string functions.
You need to write to the file in a binary-safe way. Check how to use fwrite instead of fputs. fwrite will copy all the buffer, even if there's a zero in the middle of it.
const char *mybuff= "Test1\0Test2";
const int mybuff_len = 11;
size_t copied = fwrite(mybuff, mybuff_len, 1, output_file);

Cannot read binary video files in GNU/Linux

I'm stuck with an apparently harmless piece of code. I'm trying to read a whole flv video file into a uint8_t array, but by no reason only the 10 first bytes are read.
contents = malloc(size + 1);
if (read(fd, contents, size) < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
I've tried with fopen and "rb" also, but seems that Glibc ignores that extra 'b' or something. Any clues?
Thanks in advance.
Edit: Maybe it reads a EOF character?
PS. 'size' is a variable containing the actual file size using stat().
It seems the original code correctly reads the entire content.
The problem seems to be in making use of that binary data - printing it out will truncate at the first null, making it appear that only 10 bytes are present.
You can't use any methods intended for strings or character arrays to output binary data, as they will truncate at the first null byte, making it appear the array is shorter than it really is.
Check out some other questions related to viewing hex data:
how do I print an unsigned char as hex in c++ using ostream?
Converting binary data to printable hex
If you want to append this to a string - in what format? hex? base64? Raw bytes won't work.
Here's the original code I posted. A few minor improvements, plus some better diagnostic code:
int ret, size = 4096; /* Probably needs to be much bigger */
uint8_t *contents;
contents = malloc(size + 1);
if(contents == NULL)
{
log_message(WARNING, __func__, EMSG_MEMORY);
return (NULL);
}
ret = read(fd, contents, size);
if(ret < 0)
{
/* Error reading file */
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
for(i = 0;i < ret;++i)
{
printf("%c", contents[i]);
/* printf("%0.2X", (char) contents[i]); /* Alternatively, print in hex */
}
Now, is ret really 10? Or do you just get 10 bytes when you try to print the output?
The 'read()' function in the C library doesn't necessarily return the whole read in one shot. In fact, if you're reading very much data at all, it usually doesn't give it to you in a single call.
The solution to this is to call read() in a loop, continuing to ask for more data until you've got it all, or until read returns an error, indicated by a negative return value, or end-of-file, indicated by a zero return value.
Something like the following (untested):
contents = malloc(size + 1);
bytesread = 0;
pos = 0;
while (pos < size && (bytesread = read(fd, contents + pos, size - pos)) > 0)
{
pos += bytesread;
}
if (bytesread < 0)
{
free(contents);
log_message(WARNING, __func__, EMSG_READFILE);
return (NULL);
}
/* Go on to use 'contents' now, since it's been filled. Should probably
check that pos == size to make sure the file was the size you expected. */
Note that most C programmers would do this a little differently, probably making 'pos' a pointer which gets moved along, rather than offsetting from 'contents' each time through the loop. But I thought this approach might be clearer.
On success, read() returns the number of bytes read (which may be less than what you asked for, at which point you should ask for the rest.) On EOF it will return 0 and on error it will return -1. There are some errors for which you might want to consider re-issuing the read (eg. EINTR which happens when you get a signal during a read.)

Resources