Input in C, special case? - c

Good day,
I've got the following code:
43 while (TRUE)
44 {
45 printf("Swipe Card: ");
46 scanf("%s;%s=%s", id, banner, cp);
47 printf("%s\n%s\n%s\n", id, banner, cp);
48 ProcessStudent(banner, file);
49
50 }
I've dynamically allocated id, banner, and cp, however when I try to print them (which I did just to check their contents) everything is taken into 'id' only. The string I'm trying to read looks like this %GRE068?;01540594=000331!

scanf doesn't try to do a full pattern match of the format string. %s input format simply reads everything up to the next whitespace (or EOF). After that, it looks for a ;, and since it doesn't find that it doesn't parse any of the other inputs.
If you want to stop at some other character, use [^char]
scanf("[^;];%[^=]=%s", id, banner, cp);

Related

How to combine hex values in real-time?

To give a context, I have an incoming stream of Hex values that is getting written to a CSV file which are in the format shown below.
20 5a 20 5e 20 7b 20 b1 20 64 20 f8 ...
I can not change the way the data is flowing in, but before it gets written to a CSV file I want it in this format below.
205a 205e 207b 20b1 2064 20f8 ...
As the data is coming, I need to process it and store it in the format shown above. One of the ways I tried was just bitshifting and doing logical OR which would store the result in a variable. But all I have here is a pointer pointing to a buffer where the data will be flowing into. I have something like this.
uint8_t *curr_ptr;
uint8_t* dec_buffer=(uint8_t*)calloc(4000,sizeof(uint8_t)*max_len);
init=dec_buffer;
curr_ptr=init+((count)*max_len);
for(int j=17;j<=145;j+=1){
fprintf(f_write[file_count],"%02x ", *(curr_ptr+j));
if(j>0 && j%145==0){
fprintf(f_write[file_count],"\n");
Effectively you want to remove every other space. Why not something like this?
for(int j=17;j<=145;j+=1){
fprintf(f_write[file_count], j%2 ? "%02x " : "%02x", *(curr_ptr+j));
Not sure if you should be printing spaces after the odd values of j or the even ones, but you can sort that out.

How to send image or binary data through HTTP POST request in C

I'm trying to POST a binary file to a web server with a client program written in C (Windows). I'm pretty new to socket programming, so tried POST requests using multipart/form-data with plain text messages, and text-based files (.txt, .html, .xml). Those seem to work fine. But when trying to send a PNG file, I'm running into some problems.
The following is how I read the binary file
FILE *file;
char *fileName = "download.png";
long int fileLength;
//Open file, get its size
file = fopen(fileName, "rb");
fseek(file, 0, SEEK_END);
fileLength = ftell(file);
rewind(file);
//Allocate buffer and read the file
void *fileData = malloc(fileLength);
memset(fileData, 0, fileLength);
int n = fread(fileData, 1, fileLength, file);
fclose(file);
I confirmed that all the bytes are getting read properly.
This is how I form my message header and body
//Prepare message body and header
message_body = malloc((int)1000);
sprintf(message_body, "--myboundary\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Disposition: form-data; name=\"myFile\"; filename=\"%s\"\r\n\r\n"
"%s\r\n--myboundary--", fileName, fileData);
printf("\nSize of message_body is %d and message_body is \n%s\n", strlen(message_body), message_body);
message_header = malloc((int)1024);
sprintf(message_header, "POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Content-Type: multipart/form-data; boundary=myboundary\r\n"
"Content-Length: %d\r\n\r\n", path, host, strlen(message_body));
printf("Size of message_header is %d and message_header is \n%s\n", strlen(message_header), message_header);
The connection and sending part also works fine as the request is received properly. But, the received png file is ill-formatted.
The terminal prints out the following for fileData if I use %s in printf
ëPNG
I searched around and came to know that binary data doesn't behave like strings and thus printf/ sprintf/ strcat etc. cannot be used on them. As binary files have embedded null characters, %s won't print properly. It looks like that is the reason fileData only printed the PNG header.
Currently, I send two send() requests to server. One with the header and the other with body and footer combined. That was working for text-based files. To avoid using sprintf for binary data, I tried sending one request for header, one for binary data (body) & one for footer. That doesn't seem to work either.
Also, found that memcpy could be used to append binary data to normal string. That didn't work either. Here is how I tried that (Not sure whether my implementation is correct or not).
sprintf(message_body, "--myboundary\r\n"
"Content-Disposition: form-data; name=\"text1\"\r\n\r\n"
"text default\r\n"
"--myboundary\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Disposition: form-data; name=\"myFile\"; filename=\"%s\"\r\n\r\n", fileName);
char *message_footer = "\r\n--myboundary--";
char *message = (char *)malloc(strlen(message_body) + strlen(message_footer) + fileLength);
strcat(message, message_body);
memcpy(message, fileData, fileLength);
memcpy(message, message_footer, strlen(message_footer));
I'm stuck at how I could send my payload which requires appending of string (headers), binary data (payload), string (footer).
Any advice/ pointers/ reference links for sending the whole file would be appreciated. Thank You!
How to print binary data
In your question, you stated you were having trouble printing binary data with printf, due to the binary data containing bytes with the value 0. Another problem (that you did not mention) is that binary data may contain non-printable characters.
Binary data is commonly represented in one of the following ways:
in hexadecimal representation
in textual representation, replacing non-printable characters with placeholder characters
both of the above
I suggest that you create your own simple function for printing binary data, which implements option #3. You can use the function isprint to determine whether a character is printable, and if it isn't, you can place some placeholer character (such as 'X') instead.
Here is a small program which does that:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void print_binary( char *data, size_t length )
{
for ( size_t i = 0; i < length; i += 16 )
{
int bytes_in_line = length - i >= 16 ? 16 : length - i;
//print line in hexadecimal representation
for ( int j = 0; j < 16; j++ )
{
if ( j < bytes_in_line )
printf( "%02X ", data[i+j] );
else
printf( " " );
}
//add spacing between hexadecimal and textual representation
printf( " " );
//print line in textual representation
for ( int j = 0; j < 16; j++ )
{
if ( j < bytes_in_line )
{
if ( isprint( (unsigned char)data[i+j] ) )
putchar( data[i+j] );
else
putchar( 'X' );
}
else
{
putchar( ' ' );
}
}
putchar( '\n' );
}
}
int main( void )
{
char *text = "This is a string with the unprintable backspace character \b.";
print_binary( text, strlen( text ) );
return 0;
}
The output of this program is the following:
54 68 69 73 20 69 73 20 61 20 73 74 72 69 6E 67 This is a string
20 77 69 74 68 20 74 68 65 20 75 6E 70 72 69 6E with the unprin
74 61 62 6C 65 20 62 61 63 6B 73 70 61 63 65 20 table backspace
63 68 61 72 61 63 74 65 72 20 08 2E character X.
As you can see, the function print_binary printed the data in both hexadecimal representation and textual representation, 16 bytes per line, and it correctly replaced the non-printable backspace character with the placeholer 'X' character when printing the textual representation.
Wrong printf conversion format specifier
The line
printf("\nSize of message_body is %d and message_body is \n%s\n", strlen(message_body), message_body);
is wrong. The return type of strlen is size_t, not int. The correct printf conversion format specifier for size_t is %zu, not %d. Using the wrong format specifier causes undefined behavior, which means that it may work on some platforms, but not on others.
Concatenating string with binary data
The following lines are wrong:
char *message = (char *)malloc(strlen(message_body) + strlen(message_footer) + fileLength);
strcat(message, message_body);
memcpy(message, fileData, fileLength);
memcpy(message, message_footer, strlen(message_footer));
The function strcat requires both function arguments to point to null-terminated strings. However, the first function argument is not guaranteed to be null-terminated. I suggest that you use strcpy instead of strcat.
Also, in your question, you correctly stated that the file binary data should be appended to the string. However, that is not what the line
memcpy(message, fileData, fileLength);
is doing. It is instead overwriting the string.
In order to append binary data to a string, you should only overwrite the terminating null character of the string, for example like this:
memcpy( message + strlen(message), fileData, fileLength );

c fscanf using and new line

I need to receive and put into variables text file.
For example:
/*
system_1
6
challenge_2 22 2
challenge_3 33 3
challenge_4 44 1
challenge_5 55 3
challenge_6 66 3
challenge_1 11 1
4
room_2 1 22
room_1 3 11 44 66
room_3 3 55 33 11
room_4 4 22 44 55 66
*/
I know that every one of the words (not line) is not more than 50 characters.
What will be the easy way to put each word in the appropriate (int,string...).
Also I need to know where the line is ending because the number of words in line is not constant.
I think fscanf is the most efficient, but I dont know how to use it for line ending and such...
I would love to see an example of using fscanf as I wanted.
Thanks in advance.
It's more efficient, because you do not know the format string to use fgets to return a line from the file, then to parse it yourself rather than using scanf.
An example would be like this:
FILE* file = fopen("somefile.txt", "r");
char buffer[51];
while (fgets(buffer, 50, file)) {
// Buffer now stores the line, lets see if it can be an integer.
int possible_num = 0;
// sscanf will return 1 if it read a possible integer from the buffer.
if (sscanf(buffer, "%d", &possible_num) == 1) {
// We read a number, so possible_num is now the number.
}
else {
// We read a string, buffer stores the string.
}
}
NOTE: The code will return a false positive if your string is something like "10hello", as sscanf will still read the 10. There are multiple ways of checking if a string is a valid integer out there in C, which I'm sure you will be able to find on StackOverflow, but this code should give you a place to get started.

C: Write to a specific line in the text file without searching

Hello I have file with text:
14
5 4
45 854
14
4
47 5
I need to write a text to a specific line. For example to the line number 4 (Doesn't matter whether I will append the text or rewrite the whole line):
14
5 4
45 854
14 new_text
4
47 5
I have found function fseek(). But in the documentation is written
fseek(file pointer,offset, position);
"Offset specifies the number of positions (bytes) to be moved from the location specified bt the position."
But I do not know the number of bites. I only know the number of lines. How to do that? Thank you
You can't do that, (text) files are not line-addressable.
Also, you can't insert data in the middle of a file.
The best way is to "spool" to a new file, i.e. read the input line by line, and write that to a new file which is the output. You can then easily keep track of which line you're on, and do whatever you want.
I will assume that you are going to be doing this many times for a single file, as such you would be better indexing the position of each newline char, for example you could use a function like this:
long *LinePosFind(int FileDes)
{
long * LinePosArr = malloc(500 * sizeof(long));
char TmpChar;
long LinesRead = 0;
long CharsRead = 0;
while(1 == read(FileDes, &TmpChar, 1))
{
if (!(LinesRead % 500)
{
LinePosArr = realloc(LinePosArr, (LinesRead + 500) * sizeof(long));
}
if (TmpChar == '\n')
{
LinePosArr[LinesRead++] = CharsRead;
}
CharsRead++;
}
return LinePosArr;
}
Then you can save the index of all the newlines for repeated use.
After this you can use it like so:
long *LineIndex = LinePosFind(FileDes);
long FourthLine = LineIndex[3];
Note I have not checked this code, just written from my head so it may need fixes, also, you should add some error checking for the malloc and read and realloc if you are using the code in production.

read and gather variables in a text file

I asked this a while ago but was really vague and I also made some changes to my code.
I have a file that I call "stats.txt" which I open with: (using "C" btw)
fopen("stats.txt", r+)
During the first run of my program, I will ask the user to fill in the variables used to write to the file:
fprintf(fp, "STR: %i(%i)\n", STR, smod);
fprintf(fp, "DEX: %i(%i)\n", DEX, dmod);
etc...
the file looks like this after the programs first run, with all the numbers corresponding to a variable in the program:
Level 1 Gnome Wizard:
STR: 8(-1)
DEX: 14(2)
CON: 14(2)
INT: 13(1)
WIS: 13(1)
CHR: 12(1)
APP: 11(0)
Fort save: 0
Reflex save: 0
Will save: 3
when the program closes and runs for a second time, I have an "IF" statement checking for and displaying text within the "stats.txt" file:
if (fgets(buf, 1000, fp) == NULL)
{
printf("Please enter in your base stats (no modifiers):\n");
enter_stats();
printf("Please indicate your characters level:\n");
printf("I am a level ");
level = GetInt();
Race_check();
spec_check();
printf("------Base saving throws (no modifiers)------\n");
saving_throws();
}
else
{
printf("%s",buf);
}
The problem that I am having is the fact that the program reads the file, but does not transfer any variable values it seems here:
Level 1 Gnome Wizard:
-------------------------
STR: 0(-5)
DEX: 0(-5)
CON: 0(-5)
INT: 0(-5)
WIS: 0(-5)
CHR: 0(-5)
APP: 0(-5)
-----Saving Throws------
Fortitude: 0
Reflex: 0
Will: 0
Can anyone give me their suggestions on how to read the variables as well?
PLease and Thank you
Computers only understand numbers - they don't understand text. This means that you have to write code to convert the numbers (that represent individual characters) back into the values you want and store them somewhere.
For example, you might load the entire file into an "array of char", then search that "array of char" for the 4 numbers that represent STR:, then skip any whitespace (between the STR: and the 0(0)), then convert the character/s 0 into the value 0 and store it somewhere, then check for a ( character, then convert the characters -1 into the value -1 and store it somewhere, then check for the ) character and the newline character \n.
More likely is to arrange the code as a "for each line" loop, where the first characters of a line determine how to process the other characters. E.g. if the first character is - then ignore the line; else if the first 5 characters are level call a function that processes the remainder of the line (1 Gnome Wizard); else if the first few characters are STR:, DEX:, CON, etc call a function to get both numbers (and check for the right brackets, etc); else...
In addition to all this, you should have good error handling. As a rough guide, about half of the code should be checks and error messages (like if( buffer[i] != '(' ) { printf("ERROR: Expecting left bracket after number on line %u", lineNumber); return -1;}).

Resources