snprintf() return value when size=0 - command

Thanks first for your time spent here. I have a question with snprintf() when size=0, with code below:
#include <stdio.h>
#include <stdlib.h>
int main(int ac, char **av)
{
char *str;
int len;
len = snprintf(NULL, 0, "%s %d", *av, ac);
printf("this string has length %d\n", len);
if (!(str = malloc((len + 1) * sizeof(char))))
return EXIT_FAILURE;
len = snprintf(str, len + 1, "%s %d", *av, ac);
printf("%s %d\n", str, len);
free(str);
return EXIT_SUCCESS;
}
when I run:
momo#xue5:~/TestCode$ ./Test_snprintf
The result is:
this string has length 17
./Test_snprintf 1 17
What confuses me is in the code, the size to be written is 0, why displayed 17?
What did I miss?
Thanks~~

The solution can be found in the man page under Return value;
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available.
This is so that you can do exactly what you do, a "trial print" to get the correct length, then allocate the buffer dynamically to get the whole output when you snprintf again to the allocated buffer.

Related

Writing value 0 to a binary file

I am generating a binary file from a SystemVerilog simulation environment. Currently, I'm doing the following:
module main;
byte arr[] = {0,32, 65, 66, 67};
initial begin
int fh=$fopen("/home/hagain/tmp/h.bin","w");
for (int idx=0; idx<arr.size; idx++) begin //{
$fwrite(fh, "%0s", arr[idx]);
end //}
$fclose(fh);
$system("xxd /home/hagain/tmp/h.bin | tee /home/hagain/tmp/h.txt");
end
endmodule : main
The problem is, that when b has the value of 0, nothing is written to the file. xxd output is:
0000000: 2041 4243 ABC
Same result when casting to string as follows:
$fwrite(fh, string'(arr[idx]));
I tried to change the write command to:
$fwrite(fh, $sformatf("%0c", arr[idx]));
And then I got the same value for the first two bytes ('d0 and 'd32):
0000000: 2020 4142 43 ABC
Any idea on how to generate this binary file?
You cannot have a null(0) character in the middle of a string, it is used to terminate the string.
You should use the %u format specifier for unformated data.
module main;
byte arr[] = {0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9};
int fh, c, tmp;
initial begin
fh = $fopen("h.bin","wb");
for (int idx=0; idx<arr.size; idx+=4) begin
tmp = {<<8{arr[idx+:4]}};
$fwrite(fh, "%u", tmp);
end
$fclose(fh);
fh = $fopen("h.bin","r");
while ((c = $fgetc(fh)) != -1)
$write("%d ",c[7:0]);
$display;
end
endmodule : main
Note that %u writes a 32-bit value in least-significant bytes first order, so I reversed the bytes being written with the streaming operator {<<8{arr[idx+:4]}}. If the number of bytes is not divisible by 4, it will just pad the file with null bytes.
If you need the exact number of bytes, the you will have to use some DPI C code
#include <stdio.h>
#include "svdpi.h"
void DPI_fwrite(const char* filename,
const svOpenArrayHandle buffer){
int size = svSize(buffer,1);
char *buf = (char *)svGetArrayPtr(buffer);
FILE *fp = fopen(filename,"wb");
fwrite(buf,1,size,fp);
}
And then import it with
import "DPI-C" function void DPI_fwrite(input string filename, byte buffer[]);
...
DPI_fwrite("filename", arr);

Fread/fwrite unexpected behavior

I have created a file 'meta.dat' in my current directory and want the code below to give me this output
The character B
Number of items read 1
int main() {
FILE* fp = fopen("meta.dat", "wb");
char j = 'B';
fwrite(&j, sizeof(j), 1, fp);
fclose(fp);
FILE* fp1 = fopen("meta.dat", "rb");
char i = '\0';
int n = fread(&i, sizeof(i), 1, fp1);
printf("The character %c\n", &i);
printf("Number of items read %d\n", &n);
}
However what I get is this output in my console (I use Windows):
The character &
Number of items read 6422304
What's wrong with the code? And what's happening behind the scenes, why am I seeing this strange output?

strncpy functions produces wrong file names

I am new in C and writing a code to help my data analysis. Part of it opens predetermined files.
This piece of code is giving me problems and I cannot understand why.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLOGGERS 26
// Declare the input files
char inputfile[];
char inputfile_hum[MAXLOGGERS][8];
// Declare the output files
char newfile[];
char newfile_hum[MAXLOGGERS][8];
int main()
{
int n = 2;
while (n > MAXLOGGERS)
{
printf("n error, n must be < %d: ", MAXLOGGERS);
scanf("%d", &n);
}
// Initialize the input and output file names
strncpy(inputfile_hum[1], "Ahum.csv", 8);
strncpy(inputfile_hum[2], "Bhum.csv", 8);
strncpy(newfile_hum[1], "Ahum.txt", 8);
strncpy(newfile_hum[2], "Bhum.txt", 8);
for (int i = 1; i < n + 1; i++)
{
strncpy(inputfile, inputfile_hum[i], 8);
FILE* file1 = fopen(inputfile, "r");
// Safety check
while (file1 == NULL)
{
printf("\nError: %s == NULL\n", inputfile);
printf("\nPress enter to exit:");
getchar();
return 0;
}
strncpy(newfile, newfile_hum[i], 8);
FILE* file2 = fopen(newfile, "w");
// Safety check
if (file2 == NULL)
{
printf("Error: file2 == NULL\n");
getchar();
return 0;
}
for (int c = fgetc(file1); c != EOF; c = fgetc(file1))
{
fprintf(file2, "%c", c);
}
fclose(file1);
fclose(file2);
}
// system("Ahum.txt");
// system("Bhum.txt");
}
This code produces two files but instead of the names:
Ahum.txt
Bhum.txt
the files are named:
Ahum.txtv
Bhum.txtv
The reason I am using strncpy in the for loop is because n will actually be inputted by the user later.
I see at least three problems here.
The first problem is that your character array is too small for your strings.
"ahum.txt", etc. will need to take nine characters. Eight for the actual text plus one more for the null terminating character.
The second problem is that you have declared the character arrays "newfile" and "inputfile" as empty arrays. These also need to be a number able to contain the strings (at least 9).
You're lucky to have not had a crash from overwriting memory out the program space.
The third and final problem is your use of strcpy().
strncpy(dest, src, n) will copy n characters from src to dest, but it won't copy final null terminator character if n is equal or less than size of the src string.
From strncpy() manpage: https://linux.die.net/man/3/strncpy
The strncpy() function ... at most n bytes of src are copied.
Warning: If there is no null byte among the first n bytes of src,
the string placed in dest will not be null-terminated.
Normally what you would want to do is have "n" be the size of the destination buffer minus 1 to allow for the null character.
For example:
strncpy(dest, src, sizeof(dest) - 1); // assuming dest is char array
There are a couple of problems with your code.
inputfile_hum, newfile_hum, need to be to be one char bigger for the trailing '\0' on strings.
char inputfile_hum[MAXLOGGERS][9];
...
char newfile_hum[MAXLOGGERS][9];
strncpy expects the first argument to be a char * region big enough to hold the expected results, so inputfile[] and outputfile[] need to be declared:
char inputfile[9];
char outputfile[9];

Creating a packet as a string and then extracted its fields in C

I need to implement my own packets to send over UDP. I decided that I would do this by sending a char buffer which has the sequence number, checksum, size, and the data of the packet which is bytes from a file. The string i'm sending separates each field by a semicolon. Then, when I receive the string (which is my packet) I want to extract each felid, use them accordingly (the sequence number, size, and checksum) and write the bytes to a file. So far I have wrote a method to create 100 packets, and I'm trying to extract and write the bytes to a file (I'm not doing it in the receiver yet, first I'm testing the parsing in the sender). For some reason, the bytes written to my file are incorrect and I'm getting "JPEG DATATSTREAM CONTAINS NO IMAGE" error when I try to open it.
struct packetNode{
char packet[1052]; // this is the entire packet data including the header
struct packetNode *next;
};
This is how I'm creating my packets:
//populate initial window of size 100
for(i = 0; i < 100; i++){
memset(&data[0], 0, sizeof(data));
struct packetNode *p; // create packet node
p = (struct packetNode *)malloc(sizeof(struct packetNode));
bytes = fread(data, 1, sizeof(data), fp); // read 1024 bytes from file into data buffer
int b = fwrite(data, 1, bytes, fpNew);
printf("read: %d\n", bytes);
memset(&p->packet[0], 0, sizeof(p->packet));
sprintf(p->packet, "%d;%d;%d;%s", s, 0, numPackets, data); // create packet
//calculate checksum
int check = checksum8(p->packet, sizeof(p->packet));
sprintf(p->packet, "%d;%d;%d;%s", s, check, numPackets, data); //put checksum in packet
s++; //incremenet sequence number
if(i == 0){
head = p;
tail = p;
tail->next = NULL;
}
else{
tail->next = p;
tail = p;
tail->next = NULL;
}
}
fclose(fp);
and this is where I parse and write the bytes to a file:
void test(){
FILE *fpNew = fopen("test.jpg", "w");
struct packetNode *ptr = head;
char *tokens;
int s, c, size;
int i = 0;
char data[1024];
while(ptr != NULL){
memset(&data[0], 0, sizeof(data));
tokens = strtok(ptr->packet,";");
s = atoi(tokens);
tokens = strtok(NULL, ";");
c = atoi(tokens);
tokens = strtok(NULL, ";");
size = atoi(tokens);
tokens = strtok(NULL, ";");
if(tokens != NULL)
strcpy(data, tokens);
printf("sequence: %d, checksum: %d, size: %d\n", s,c,size);
int b = fwrite(data, 1, sizeof(data), fpNew);
ptr = ptr->next;
i++;
}
fclose(fpNew);
}
Since there is transfer of binary data, a JPEG stream, this data cannot be treated as a string. It's better to go all binary. For instance, instead of
sprintf(p->packet, "%d;%d;%d;%s", s, 0, numPackets, data); // create packet
you would do
sprintf(p->packet, "%d;%d;%d;", s, 0, numPackets);
memcpy(&p->packet[strlen(p->packet)], data, bytes);
but this leads to parsing problems: we would need to change this:
tokens = strtok(NULL, ";");
if(tokens != NULL)
strcpy(data, tokens);
to something like this:
tokens += 1 + ( size < 10 ? 1 : size < 100 ? 2 : size < 1000 ? 3 : size < 10000 ? 4 : 5 );
memcpy(data, tokens, sizeof(data));
#Binary Protocol
It's easier to use a binary packet:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma push(pack,1)
typedef struct Packet {
int seq, maxseq, size;
unsigned short cksum;
unsigned char payload[];
} Packet;
#pragma pop(pack)
typedef struct PacketNode{
struct PacketNode * next;
Packet packet;
} PacketNode;
PacketNode * allocPacketNode(int maxPayloadSize) {
void * ptr = malloc(sizeof(PacketNode) + maxPayloadSize); // FIXME: error checking
memset(ptr, 0, sizeof(PacketNode) + maxPayloadSize); // mallocz wouldn't cooperate
return (PacketNode*) ptr;
}
PacketNode * prepare(FILE * fp, int fsize, int chunksize)
{
PacketNode * head = allocPacketNode(chunksize);
PacketNode * pn = head;
int rd, seq = 0;
int maxseq = fsize / chunksize + ( fsize % chunksize ? 1 : 0 );
while ( ( rd = fread(pn->packet.payload, 1, chunksize, fp ) ) > 0 )
{
printf("read %d bytes\n", rd);
pn->packet.seq = seq++;
pn->packet.maxseq = maxseq;
pn->packet.size = rd + sizeof(Packet);
pn->packet.cksum = 0;
pn->packet.cksum = ~checksum(&pn->packet, pn->packet.size);
if ( rd == chunksize )
pn = pn->next = allocPacketNode(chunksize);
}
return head;
}
int checksum(unsigned char * data, int len)
{
int sum = 0, i;
for ( i = 0; i < len; i ++ )
sum += data[i];
if ( sum > 0xffff )
sum = (sum & 0xffff) + (sum>>16);
return sum;
}
void test( PacketNode * ptr ) {
FILE *fpNew = fopen("test.jpg", "w");
while (ptr != NULL)
{
printf("sequence: %d/%d, checksum: %04x, size: %d\n",
ptr->packet.seq,
ptr->packet.maxseq,
ptr->packet.cksum,
ptr->packet.size - sizeof(Packet)
);
int b = fwrite(ptr->packet.payload, ptr->packet.size - sizeof(Packet), 1, fpNew);
ptr = ptr->next;
}
fclose(fpNew);
}
void fatal( const char * msg ) { printf("FATAL: %s\n", msg); exit(1); }
int main(int argc, char** argv)
{
if ( ! argv[1] ) fatal( "missing filename argument" );
FILE * fp = fopen( argv[1], "r" );
if ( ! fp ) fatal( "cannot open file" );
fseek( fp, 0, SEEK_END );
long fsize = ftell(fp);
fseek( fp, 0, SEEK_SET );
printf("Filesize: %d\n", fsize );
test( prepare(fp, fsize, 1024) );
}
The #pragma push(pack,1) changes how the compiler aligns fields of the struct. We want them to be compact, for network transport. Using 1 is byte-aligned. The #pragma pop(pack) restores the previous setting of the pack pragma.
A note on the checksum method
First we calculate the sum of all the bytes in the packet:
int sum = 0, i;
for ( i = 0; i < len; i ++ )
sum += data[i];
Since the packet uses an unsigned short (16 bits, max value 65535 or 0xffff) to store the checksum, we make sure that the result will fit:
if ( sum > 0xffff ) // takes up more than 16 bits.
Getting the low 16 bits of this int is done using sum & 0xffff, masking out everything but the low 16 bits. We could simply return this value, but we would loose the information from higher checksum bits. So, we will add the upper 16 bits to the lower 16 bits. Accessing the higher 16 bits is done by shifting the int to the right 16 bits, like so: sum >> 16. This is the same as sum / 65536, since 65536 = 216 = 1 << 16.
sum = (sum & 0xffff) + (sum>>16); // add low 16 bits and high 16 bits
I should note that network packet checksums are usually computed 2 bytes (or 'octets' as they like to call them there) at a time. For that, the data should be cast to an unsigned short *, and len should be divided by 2. However! len may be odd, so in that case we'll need to take special care of the last byte. For instance, assuming that the maximum packet size is even, and that the len argument is always <= max_packet_size:
unsigned short * in = (unsigned short *) data;
if ( len & 1 ) data[len] = 0; // make sure last byte is 0
len = (len + 1) / 2;
The rest of the checksum method can remain the same, except that it should operate on in instead of data.

How to let fscanf stop reading after a new line

#include <stdio.h>
#define MAX 1000
int line_counter (FILE *file, char buf[]);
int main(int argc, char *argv[]) {
FILE *ptr_file;
char buf[MAX];
ptr_file = fopen("alice-eg.txt", "r");
if (!ptr_file) {
return 1;
}
int count = 0;
while (fscanf(ptr_file, "%s", buf) == 1) {
printf("%s", buf);
if (buf == '\n') {
return count;
}
else {
count += 1;
}
}
printf("The number of words in this line is: %d", count);
return 0;
}
I want to do something along the lines of this but I have no idea how to make it work as the buf is just a pointer to an array of letters (correct me if I'm wrong I just started with C and my understanding of pointers is still quite bad).
fscanf write the line from the file (separated by ENTER) to the buff array and so if it will read an empty line buff[0] = '\n' so that should be your condition.
Secondly:
while (fscanf(ptr_file, "%s", buf) == 1)
Is wrong since fscanf returns the number of read character and so for the line "abcd" form the file it will return 4 and your loop will stop right away instead of reading the entire file and so your condition should be:
while (fscanf(ptr_file, "%s", buf) != EOF)
since fscanf will return EOF when it will reach the end of the file