snprintf() only reads until new line character? - c

I'd like to send a message over a socket that looks like so: "USER anonymous\r\n".
In order to create a formatted string instead of a constant string, I used snprintf(). Unfortunately, it does not seem to copy the newline character \n, but just the carriage return \r.
#define USERNAME_ANONYMOUS "anonymous"
[...]
// Inside a function.
int sz = snprintf(NULL, 0, "USER %s\r\n", USERNAME_ANONYMOUS);
char* username = NULL;
if ((username = calloc(sz + 1, sizeof(char))) == NULL) {
perror("Could not allocate memory");
return;
}
snprintf(username, sz, "USER %s\r\n", USERNAME_ANONYMOUS);
for (int i = 0; i <= sz; i++) {
printf("%c %d\n", username[i], username[i]);
}
The output:
U 85
S 83
E 69
R 82
32
a 97
n 110
o 111
n 110
y 121
m 109
o 111
u 117
s 115
13
0
0

From this snprintf (and family) reference:
At most bufsz - 1 characters are written.
The size you provide must be including the terminator. You need to use sz + 1 to print the full string.

Well, if you allocate a buffer of size sz + 1 then pass that buffer size to the second snprintf call, which is sz + 1 and not sz, which is the number you pass.
The second call to snprintf should read:
snprintf(username, sz + 1, "USER %s\r\n", USERNAME_ANONYMOUS);
or it will overwrite the last \n with the null terminator, because it has not enough space for the whole string you want to print.

Related

How read a variable number of int from a string

I have the following text file
0 0 0 debut
1 120 0 permis exploitation
2 180 1 1 piste 6km
3 3 1 2 installation sondeuses
4 30 1 2 batiments provisoires
5 60 1 2 groudronnage piste
6 90 1 4 adduction eau
7 240 2 3 4 campagne sondage
8 180 3 5 6 7 forage 3 puits
9 240 3 5 6 7 construction bureaux logements
10 30 2 8 9 transport installation matériel
11 360 2 8 9 traçage du fond
12 240 2 8 9 construction laverie
13 0 3 10 11 12 fin des travaux
Each line is the representation of a task and is described as followed: the first number is and ID, the second is the duration, the third is the number of previous tasks that are required, and all the numbers afterward are the IDs of the required tasks. Finaly the string in the end is the title of the string.
I'm trying to fill an array of those struct by reading this file. Here is the struct:
typedef struct{
int id;
int duration;
int nbPrev; /* number of required previous tasks */
int prev[NMAXPREV]; /*array of required previous tasks*/
char title[LGMAX];
}Task ;
Here is my code to read the file
int readTasksFile(char* file_name, Task t[])
{
FILE* f;
char line[256] = {'\0'};
int i = 0;
char c[1] = {0};
if((f = fopen(file_name, "r")) == NULL)
{
perror("The file couldn't be opened");
exit(EXIT_FAILURE);
}
while (fgets(line, 256, f) != EOF)
{
sscanf_s(line, "&d &d &d", &(t[i].id), &(t[i].duration), &(t[i].nbPrev));
i++;
}
fclose(f);
return 0;
}
How can I read all the previous tasks number in a line considering it is variable and still be able to read the title afterward ?
How can I read all the previous tasks number in a line considering it is variable and still be able to read the title afterward ?
The 3rd int should be the number of following ints.
Use "%n" to record scan offset.
After reading the .prev[], copy the rest of the line to .title.
Add error checking. This is very important, especially for complex input.
// Untested code to get OP started
// while (fgets(line, 256, f) != EOF) Comparing against EOF is incorrect
while (fgets(line, sizeof line, f)) {
int offset = 0;
// Use %d, not &d
if (sscanf(line, "%d %d %d %n",
&t[i].id, &t[i].duration, &t[i].nbPrev, &offset) != 3) {
// Handle bad input, for now, exit loop
break;
}
if (t[i].nbPrev < 0 || t[i].nbPrev > NMAXPREV) {
// Handle bad input, for now, exit loop
break;
}
char *p = line + offset;
int prev;
// Populate t[i].prev[]
for (prev = 0; prev < t[i].nbPrev; prev++) {
if (sscanf(p, "%d %n", &t[i].prev[prev], &offset) != 1) {
break;
}
p += offset;
}
if (prev != t[i].nbPrev) {
// Handle bad input, for now, exit loop
break;
}
// remaining text
int len = strlen(p);
if (len > 0 && p[len-1] == '\n') p[--len] = '\0'; // consume potential trailing \n
if (len >= LGMAX) {
// Handle bad input, for now, exit loop
break;
}
strcpy(t[i].title, p);
i++;
}
return i; // Let caller know of successful lines parsed.
Advanced: robust code would use strtol() instead of "%d" and sscanf().
readTasksFile() should also pass in the max number of Task t[] that can be read.
You could also scan by line and assign the two first numbers to id and duration, then do an int analysis and add the rest of the elements to nbPrev until you encounter a letter.
I don't know if this would be the best way to do it, but it's how I would do it.
Why don't you create also a list each time you register in struct nbPrev?
Like, instead of nbPrev being of type int, make it of type list?

How to write and read dynamic arrays from binary file

I have this struct
struct _recipe
{
char name[50];
char** ingredients;
char diff[12];
int time;
int calories;
char** procedure;
} recipe;
And I want to copy all the data inside a binary file.
First of all I've allocated dynamically the memory for both the ingredients and procedure and I have written all I needed. But then I need to write all in a binary file. I know that they are both pointers, so this mean that if I use
fwrite(&recipe,sizeof(recipe),1,fbr);
I'll write inside the file the address and not the actually value which is I need. I've tried writing every single field of the struct inside the file this way
fwrite(recipe.name,sizeof(recipe.name),1,fbr);
fgets(recipe.ingredients[j],30,stdin);
strcpy(buff,recipe.ingredients[j]);
len = strlen(buff);
fwrite(buff,sizeof(recipe.ingredients[0]),len,fbr);
fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
fwrite(&recipe.time,sizeof(recipe.time),1,fbr);
fwrite(&recipe.calories,sizeof(recipe.calories),1,fbr);
fgets(recipe.procedure[i],1000,stdin);
strcpy(buff,recipe.procedure[i]);
len = strlen(buff);
fwrite(buff,sizeof(recipe.procedure[0]),len,fbr);
I'm not sure it's the right way but I tried putting the string inside an other one and then copying it on the file. The problem is, I'm not sure it worked because I don't know what kind of command should I put for reading all the values I stored. With the name of course it worked, I didn't have problems with that but then when I was about to read the ingredients I blocked myself because I wrote the value inside an other string and I don't know what lenght should I put in reading. Maybe I'm missing something, probably I'm messing with writing in the first place but I don't know what to do at this point.
You want to do something like this to write out your recipe:
fwrite(recipe.name,sizeof(recipe.name),1,fbr);
for (int i = 0; i < num_ingredients; i++) {
int len = strlen(recipe.ingredients[i]) + 1;
int num_elements = fwrite(recipe.ingredients[i],sizeof(char),len,fbr);
printf("wrote %d elements for %s\n", num_elements, recipe.ingredients[i]);
}
fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
for (int i = 0; i < num_procedures; i++) {
int len = strlen(recipe.procedure[i]) + 1;
int num_elements = fwrite(recipe.procedure[i],sizeof(char),len,fbr);
printf("wrote %d elements for %s\n", num_elements, recipe.procedure[i]);
}
You also need to write out the calories and time of course. I would also do this before reading anything into the struct:
memset(&recipe, 0, sizeof(struct _recipe));
If you use fprintf to write out the calories and time with a \n for the separator, your recipe file would look like this:
$ od -c recipe.bin
0000000 m u f f i n s \0 \0 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0000060 \0 \0 f l o u r \0 s u g a r \0 e g
0000100 g s \0 v a n i l l a \0 n u t s \0
0000120 n o d i f f \0 \0 \0 \0 \0 m i x \0
0000140 b a k e \0 r e s t \0 6 0 \n 2 5 0
0000160 \n
You can design a simple storage protocol for writing/reading.
For example:
| struct recipe | t l v | t l v | ... | t l v | struct recipe | t l v | ... | t l v |
|<- a complete struct recipe data ->|
| description | t(ype) | l(ength) | v(alue)|
| ingredient | i | strlen() | string |
| procedure | p | strlen() | string |
| data end | e | 0 | <null> |
Read a recipe data:
1. Read a struct recipe.
2. Read a t, t=='i' -- go (3.), t=='p' -- go (4.), t=='e' -- go (5.).
3. Read a l, then read a string whose length is l as an ingredient. Go (2.).
4. Read a l, then read a string whose length is l as a procedure. Go (2.).
5. Finish.
Write a recipe data:
1. Write a struct recipe.
2. Write ingredients to `i strlen(ingredient) ingredient` one by one.
3. Write procedures to `p strlen(procedure) procedure` one by one.
4. Write `e 0`.
5. Finish.
Pseudo code: (just a demo, please design your own protol-)
void write_recipe(recipe *r, FILE *fp)
{
fwrite(r, sizeof(recipe), 1, fp);
// ...
for ()
{
sprintf(buff, "i %5d %s", strlen(r->ingredients[i]), r->ingredients[i]);
fwrite(buff, strlen(buff), 1, fp);
}
sprintf(buff, "e 0");
fwrite(buff, strlen(buff), 1, fp);
//...
for ()
{
sprintf(buff, "p %5d %s", strlen(r->procedure[i]), r->procedure[i]);
fwrite(buff, strlen(buff), 1, fp);
}
sprintf(buff, "e 0");
fwrite(buff, strlen(buff), 1, fp);
// ...
}
void read_recipe(FILE *r, FILE *fp)
{
fread(r, sizeof(recipe), 1, fp);
// ...
while (true)
{
fread(&t, sizeof(char), 1, fp);
if (t == 'e')
{
// ...
break;
}
fread(buff, sizeof(char), 7, fp);
sscanf("%d", &len);
fread(buff, sizeof(char), len, fp);
buff[len] = 0;
ingredient = malloc(sizeof(char) * (len + 1));
strcpy(ingredient, buff);
// ...
}
// ...
while (true)
{
fread(&t, sizeof(char), 1, fp);
if (t == 'e')
{
// ...
break;
}
fread(buff, sizeof(char), 7, fp);
sscanf("%d", &len);
fread(buff, sizeof(char), len, fp);
buff[len] = 0;
procedure = malloc(sizeof(char) * (len + 1));
strcpy(procedure, buff);
// ...
}
// ...
}

How to print unsigned char data?

Im learning C so i have a little problem.
How to print: unsigned char *tlv_buffer = NULL;
In main function:
unsigned char *tlv_buffer = NULL;
int size = 1;
int len = 0;
int result;
tlv_buffer = BKS_MALLOC(size);
result = append_bertlv_data(&tlv_buffer, &size, &len, 0xDF04, 2,
"\x34\x56");
result = append_bertlv_data(&tlv_buffer, &size, &len, 0xDF81, 3, "ref");
BKS_TRACE("-------- success : %d --------- \n", result);
BKS_TRACE("======== %u =======", &tlv_buffer);
(I cannot see what happens in append_bertlv_data)
It should print df 04 02 34 56 df 81 03 72 65 66 ,
but it does not show like that.
My result is 3204447612
You can use the following:
for (int i = 0 ; i < strlen(tlv_buffer); i++)
printf("%02x ",*(tlv_buffer + i));
It will print each byte in hex.
edit:
use a space to separate and if you want the specific length bytes then specify the length instead of size. best is to use strlen.

C - Appending strings at beginning and end of while loop

I'm trying to construct a string - for every 80 chars in the loop, to add 7 tabs to the beginning of the line, and a new line at the end.
It should print out 7 tabs, then 80 chars, then 1 new line and so on.
However, something strange is happening. It's printing a new line straight after the first 2 chars and then everything is skewed from then on.
I'm also not sure why I need % 40 rather than % 80 - is it because there are 2 bytes?
I think generally im getting confused by 2 bytes.
void do_file(FILE *in, FILE *out, OPTIONS *options)
{
char ch;
int loop = 0;
int sz1,sz2,sz3;
int seeker = offsetof(struct myStruct, contents.datas);
//find total length of file
fseek(in, 0L, SEEK_END);
sz1 = ftell(in);
//find length from beggining to struct beginning and minus that from total length
fseek(in, seeker, SEEK_SET);
sz2 = sz1 - ftell(in);
int tabs = (sz2 / 80) * 8;// Total size / size of chunk * 8 - 7 tabs and 1 new line char
sz3 = ((sz2 + 1 + tabs) * 2); //Total size + nuls + tabs * 2 for 2 bytes
char buffer[sz3];
char *p = buffer;
buffer[0] = '\0';
while (loop < sz2)
{
if(loop % 40 == 0){
//Add 7 tabs to the beginning of each new line
p += sprintf(p, "%s", "\t\t\t\t\t\t\t");
}
fread(&ch, 1, 1, in);
//print hex char
p += sprintf(p, "%02X", (ch & 0x00FF));
if(loop % 40 == 0){
//Add a new line every 80 chars
p += sprintf(p, "%s", "\n");
}
strcat(buffer, p);
loop++;
}
printf("%s", buffer);
}
However, something strange is happening. It's printing a new line straight after the first 2 chars and then everything is skewed from then on.
It's because of the initial value of loop, try with int loop = 1;
I'm also not sure why I need % 40 rather than % 80 - is it because there are 2 bytes?
I think generally im getting confused by 2 bytes.
The point is, for each character you read in input file, you write two characters in buffer, because you decided to print characters as two bytes (%02X).
Now what is your need:
insert LF and tabs each 80 characters of the input file? (Is that your expecting?)
insert LF and tabs each 80 characters of the output? (That is what you coded)

Parsing issue in C

I have a string in the format:
char *sampleLine = "name1: 251 name2: 23 name3: -67 name4: 0.00 name5: 310 name6: 410 name7: 54001 name8: 332 name9: SOME_TEXT name10: 3 name1: 181 235 237 name11: 11 name12: 240 241 242 243 244 245 246 247 248 249 250 name13: 0 name14: 2 name15: 1 name16: 0 name17: 6 name18: 0 name19: 500 name20: 200 name21: 64 name22: 1 name23: 6 name24: 0 name25: 0";
One of the issues with the string is that some of the names are repeated but the basic pattern seemed to be name: value. So I wrote an algorithm that would take a name and return a value but it doesn't seem to work and does not take into account the issue with a name being repeated.
So for example: if I pass in name1, I would expect to get 251 ,etc.
Here is the code with a sample main:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
char* extractValue(char* name, char* buffer)
{
char* begining = strstr(buffer,name);
begining += strlen(name) + 2;
if (begining != NULL)
{
char* end = strstr(begining,":");
if (end != NULL)
{
end += 1;
for (int i=0; i < strlen(end); i++)
{
if (end[i] != ':')
{
i++;
} else {
char namevalue[200];
bzero(namevalue,200);
strncpy(namevalue,begining,i);
for (int x=strlen(namevalue); x>0; x--)
{
if (namevalue[x] == ' ')
{
char* value = (char*)malloc(200);
bzero(value,200);
strncpy(value,namevalue,strlen(namevalue) - (strlen(namevalue) - x));
return value;
}
}
break;
}
}
}
}
return NULL;
}
int main (int argc, char** argv)
{
char *sampleLine = "name1: 251 name2: 23 name3: -67 name4: 0.00 name5: 310 name6: 410 name7: 54001 name8: 332 name9: SOME_TEXT name10: 3 name1: 181 235 237 name11: 11 name12: 240 241 242 243 244 245 246 247 248 249 250 name13: 0 name14: 2 name15: 1 name16: 0 name17: 6 name18: 0 name19: 500 name20: 200 name21: 64 name22: 1 name23: 6 name24: 0 name25: 0";
char* value1 = extractValue("name1", sampleLine);
char* value3 = extractValue("name3", sampleLine);
char* value17 = extractValue("name17", sampleLine);
printf("value 1 = %s\n",value1);
printf("value 3 = %s\n",value3);
printf("value 17 = %s\n",value17);
return 0;
}
When I run it, I get:
$ gcc -Wall -std=c99 -o parse parse.c && ./parse
value 1 = 251 name2: 23
value 3 = -67 name4: 0.00
value 17 = 6 name18: 0 name19: 500 name20: 200 name21:
Instead of expected
value 1 = 251
value 3 = -67
value 17 = 6
Two subtle errors.
First,
if (end[i] != ':')
{
i++;
} else ..
By manually incrementing i here, you are skipping a character, as i already gets incremented by the for-loop. It appears to have no side effects, but only because ...
Second: the actual cause of your problem is you are measuring the length of the wrong string. You locate the beginning of the name value (begining) and then its end by scanning forward from that position for the next :. Then, you track backwards to find the previous space, which should be the end of the value. But ...
for (i=0; i < strlen(end); i++)
checks forwards from the (already determined) end of the string! Of course, for your test names, you are bound to find a colon somewhere further along--but its position has nothing to do with the string you are interested in, which is between begining and end.
Change your i loop to
for (i=0; i < end-begining; i++)
and change the : check to
if (begining[i] == ':')
{
char namevalue[200];
... (etc.)
(discarding the i++ line).
Loose notes
A slightly faster way to look for a single character is strchr:
char* end = strchr(begining,':');
You might want to find a better strategy for locating the name. If you are looking for name1, it could find name12 as well as noname1.
One way would be to split a string into tokens using strtok. Then anything ending with a : is a potential name, and the next token is the value you are looking for. Omitted, because it's a nice exercise in its own. (And if you are going to try this: strtok modifies the original string!)
Finally, you can do without all those loops :-)
char* extractValue(char* name, char* buffer)
{
char *start, *end, *ret_val;
int length;
char* begining = strstr(buffer,name);
if (!begining)
return NULL;
start = begining + strlen(name);
if (start[0] != ':')
return NULL;
start++;
// skip initial spaces, if any
while (*start == ' ')
start++;
// ptr points to the start of data. Find end.
end = start;
while (*end && *end != ' ')
end++;
// we have the correct string in start..end
length = end-start;
// it's a zero-terminated string so add 1 for the zero
ret_val = (char *)malloc(length+1);
strncpy (ret_val, start, length);
// put the zero where it belongs
ret_val[length] = 0;
return ret_val;
}

Resources