Reputation: 95
My problem is when I try to print text with a "\n", this special characters are invisible for printf and puts after echoing it to file, and reading it again.
#include <stdio.h>
#include <string.h>
int main()
{
FILE *f;
char *s = (char*) malloc (2919);
strcpy(s, "printf 'H4sIAIM4aFYCAwtJLS6JyQsBklwAMrDLnwsAAAA=' | base64 -d | gunzip > r"); //Test\nTest after decoding
system(s);
f = fopen("r", "r");
fseek(f, SEEK_SET, 0);
fread(s, 2919, 1, f);
printf("%s", s); //puts(s); gives the same result
fclose(f);
system("rm r");
free(s);
return 0;
}
Output should look like:
Test
Test
and it looks like Test\nTest
. What am I doing wrong?
Learning purpose, so please be nice.
Upvotes: 4
Views: 145
Reputation: 145317
Your code has the following problems:
fseek(f, SEEK_SET, 0);
serves no purpose, the file opened with fopen
is at position 0 by default.
fread(s, 2919, 1, f);
: you do not store the number of bytes read. You cannot properly null terminate the buffer for printf to stop at the last decoded byte. How do you know the file size anyway?
The encoded string H4sIAIM4aFYCAwtJLS6JyQsBklwAMrDLnwsAAAA=
decodes as Test\nTest
, with a litteral \
character followed by an n
, not a linefeed character. Reading these characters from file r
with fread
will not convert escape sequence \n
to the actual linefeed character. Such conversion is a feature of the compiler when parsing string and character literals. If you intend to do that with your file contents, you have to hand code the conversion yourself.
Here is a corrected version:
#include <stdio.h>
#include <string.h>
int main(void) {
FILE *f;
char *s = malloc(2919 + 1);
char *p;
int nread;
strcpy(s, "printf 'H4sIAIM4aFYCAwtJLS6JyQsBklwAMrDLnwsAAAA=' | base64 -d | gunzip > r"); //Test\nTest after decoding
system(s);
f = fopen("r", "r");
nread = fread(s, 2919, 1, f);
if (nread >= 0) {
s[nread] = '\0';
while ((p = strstr(s, "\\n")) != NULL) {
/* converting \ n sequences to linefeed characters */
*p = '\n';
memmove(p + 1, p + 2, strlen(p + 2) + 1);
}
printf("%s", s); //puts(s); will not give the same result
}
fclose(f);
system("rm r");
free(s);
return 0;
}
Upvotes: 3
Reputation: 29362
The C compiler transforms the "\n" sequence into a newline character (ASCII code 10) ONLY IF it is encountered in a literal character string (a constant presented in the source code), not in any character string variable.
Examples:
char s1[] = "TEST\nTEST";
printf(s1); // ---> TEST newline TEST.
char s2[] = "TEST\\nTEST"; // s2 = "TEST\nTEST"
printf(s2); // ---> TEST\nTEST (the characters \ and n are present inside the string)
As you can see, in the case of s1
, the string was first parsed by the C compiler, which then transformed \n
into newline. In the second case, the escape character \
prevented the interpretation so the string s2
was exactly TEST\nTEST
, but now this is no longer a literal (given explicitly in the code), but a real character string residing in memory. Hence it will no longer be interpreted.
Therefore, printf(s2)
is not like printf("TEST\nTEST")
because in the former case there is no interpretation, while in the latter case, the C compiler sees a literal and will interpret it, replacing \n
by newline
.
Upvotes: 2
Reputation: 3592
@lurker has hit the nail on the head...
The encoding evidently has a literal backslash and nothing in your pipeline of system commands interprets it. One way to get it to interpret the escapes is: "echo -e $(printf 'H4sIAIM4aFYCAwtJLS6JyQsBklwAMrDLnwsAAAA=' | base64 -d | gunzip) r"
To be a little more verbose in answering the question - specifically what's happened is you've compressed the literal ASCII (or UTF-8) characters:
T e s t \ n T e s t
Given your question, you were (presumably) meaning to have either compressed a new line character (often interpreted by tools from the string \n
) rather than the two literal characters \
and n
.
You could create a different gzipped input. That's one option. I don't know how you generated the current gzipped binary data - if you post that, we can take a look and propose a fix.
Alternatively as @lurker said - you can do something on the output to convert any occurrences of \
and n
to newline characters - and there's lots of ways to do that.
But it really depends on what you were trying to do. If you thought you had zipped this text:
Test
Test
...then it's the input that's gone wrong.
If you thought you had zipped this text:
Test\nTest
...(as in literally a back-slash-and-n), then you presumably are trying to add some output-processing to convert it to a real new line character.
Does that make sense?
Upvotes: 2
Reputation: 91159
Well, obviously, you have encoded the bytes '\'
and 'n'
at the respective places in the string.
If you don't want that, you can
either compress and encode the correct string:
$ echo $'Test\nTest' | gzip | base64
H4sIAEs+aFYAAwtJLS7hCgERAF0muOIKAAAA
$ echo $'Test\nTest' | gzip -n | base64
H4sIAAAAAAAAAwtJLS7hCgERAF0muOIKAAAA
or somehow interpret the \n
in the string as wanted. But this makes everything more complicated.
Upvotes: 2
Reputation: 225817
The text you've encoded looks like this:
Test\nTest
This is a 10 character string with "\" for the fifth character and "n" for the sixth. This is different from this:
char str[]="Test\nTest";
Which is a 9 character string with a newline for the fifth character.
If you want to print a newline, your encoded string needs to contain it. Either that, or you have to parse the resulting string and perform the newline substitution manually.
Upvotes: 4