I am learning how to write a simple CGI page with C language. I tried with Apache on both Linux and Windows. I compiled my scripts on 2 different computers that run different OSes.
Firstly, I created a simple CGI page for getting a static plain-text content:
#include
int main()
{
FILE *fp = fopen("plain_text.txt", "r"); // text-mode only.
if (fp)
{
int ch;
printf("content-type: text/plain\n\n");
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
}
fclose(fp);
}
return 0;
}
I compiled it into an executable and put it in cgi-bin directory. When I browse it with my web-browser, it returns the plain-text content correctly (both Linux and Windows).
Then, I modified above script for getting a simple JPEG content.
(I understand that: every JPEG picture is a binary file)
#include
int main()
{
FILE *fp = fopen("cat_original.jpg", "rb"); // with binary-mode.
if (fp)
{
int ch;
printf("content-type: image/jpg\n\n");
while (((ch = fgetc(fp)) != EOF) || (!feof(f1))) // can read whole content of any binary file.
{
printf("%c", ch);
}
fclose(fp);
}
return 0;
}
I compiled it into an executable and put it in cgi-bin directory, too.
I can get the correct returned-image with Linux compiled-executable files; but, the Windows does not.
To understand the problem, I downloaded the returned-image with Windows compiled-execute files.
(I named this image: cat_downloaded_windows.jpg)
Then, I used VBinDiff for compare 2 images: cat_original.jpg (68,603 bytes) and cat_downloaded_windows.jpg (68,871 bytes).
There are many lines in cat_downloaded_windows.jpg (like the row I marked) have a character which cat_original.jpg does not have.
VBinDiff
So, I guess that the Windows OS causes the problem (Windows add some characters automatically, and Linux does not)
(Apache and web-browsers do not cause problem)
So, I posted this topic into StackOverflow for getting your helps. I have 2 questions:
Is there any problem with the printf("%c", ch); (in my script) on Windows?
Is there any way to print binary content into stdout, both Linux and Windows?
I am learning programming myself, and this is the first time I ask on StakOverflow.
So, if my question is not clear, please comment below this question; I will try to explain it more.
Thank you for your time!
When you use printf() to write to standard output, it is working in text mode, not binary mode, so every time your program encounters a newline \n in the JPEG file, it writes \r\n on Windows, which corrupts the JPEG file.
You'll need to know how to put standard output into binary mode and you'll need to ensure that you generate \r\n in place of \n in the headers.
The MSDN documentation says you can use _setmode(), and shows an example (setting stdin instead of stdout):
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
int main(void)
{
int result;
// Set "stdin" to have binary mode:
result = _setmode(_fileno(stdin), _O_BINARY);
if (result == -1)
perror("Cannot set mode");
else
printf("'stdin' successfully changed to binary mode\n");
}
Related
CS50 Lab4 code which change volume of .wav file:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
const int HEADER_SIZE = 44;
uint8_t header[HEADER_SIZE];
int16_t buffer;
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
float factor = atof(argv[3]);
fread(header, 1, HEADER_SIZE, input);
fwrite(header, 1, HEADER_SIZE, output);
int n = 0; // for debug purpose
while (fread(&buffer, 2, 1, input))
{
buffer = buffer * (float)factor;
fwrite(&buffer, 2, 1, output);
if (n == 2115) // for debug purpose
{
if (n == 2111) // for debug purpose
;
}
printf("%d\n", n++); // for debug purpose
}
// Close files
fclose(input);
fclose(output);
}
The thing is.. That it works perfectly in CS50 Codespace IDE:
but all my local compilers (I've tried: bcc32, cpp32, tcc, gcc, clang) gives the same result - output of this broken file (must be 345kb file, but it's 5 kb):
https://cdn.discordapp.com/attachments/792992622196424725/964833387363852308/output.wav
I've tried some debug:
According debug it always stops at 2117 step (4608 buffer value).
Again I wanna point that in CS50 IDE it works alright and it goest through all 176399 steps :)
feof(input) and ferror() debug:
Please help to solve this puzzle! I can't rest until I understand whats wrong there..
On Windows, you must open binary files with mode rb (or wb for writing). On Unix, you should do that for portability, but in practice it works either way. (And it has nothing to do with the compiler).
The reason is that in text files, Windows treats a byte with value 0x1A (which is Ctl-Z) as an EOF indicator. Unix doesn't do this; on Unix, the end of the file is where the file ends.
Also, Windows uses a two-character end-of-line indicator (\r\n), which must be translated to a single \n because the C standard requires that multi-character end-of-line indicators in a text file be translated to a single newline character (and translated back when you write to the file). That doesn't happen on Unix either, because Unix line endings are already a single newline character.
So on Windows, if you read a binary file without specifying the b-for-binary open mode, then the read will stop at the first 0x1A in the file. In your case, that seems to have happened on the 2117th character read, but note that that might not be the 2117th character in the file because of newline translation. You could try looking at your file with a binary editor, but the bottom line is that if you think your program might be run under Windows, then you should always use rb and wb for binary files. Unix ignores the b and it tells Windows to stop messing with your file.
just installed eclipse on my linux and trying working with files.
I wanted to use fgetc function but it seems that its not working..
while debugging: even if Im using step over its crush, and while letting it run its just dont do anything.
its happen also for every function related to files like fscanf,fgets etc..
the error messege is:
Can't find a source file at "/build/glibc-OTsEL5/glibc-2.27/libio/getc.c"
Locate the file or edit the source lookup path to include its location.
any ideas?
thnk's in advanced
and this is my code:
#include <stdio.h>
#include <stdlib.h>
int main(){
func();
return 0;
}
void func(){
int ch;
int fd = open("out.txt", O_RDONLY);
if(fd < 0)
perror("fd");
ch = fgetc(fd);
printf("%d",ch);
}
The error message comes from the debugger. It indicates that whoever built glibc for your system did not add source files to the debugging information. As a result, stepping through system library functions such as fgetc is very confusing. But this is independent of your actual problem.
You cannot mix file descriptor functions like open with file stream functions like fgetc. The compiler will have print a type mismatch warning; you really should not ignore these.
Something like this should fix the type error:
File *fp = fopen("out.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
ch = fgetc(fp);
If you want to keep using unbuffered I/O and open, you will have to use the read function instead of fgetc to read bytes.
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
for(i=1; i<=255; i++)
{
printf("%d %c\n",i,i);
}
}
Hey i am working my way out from i/o redirection, and i got stuck in outputting ascii table from command prompt i done this.
C:\New folder\practice> main.exe > temp.txt
C:\New folder\practice> type temp.txt
and after hitting enter (after type temp.txt) it only outputs first 26 numbers. My question is why?
Also can someone explain me how to just copy the code into text file using redirection I know how to do using FILE I/O.
Because you're using MS-DOS... er MS WinDOS, and there ASCII number 26/^Z is the end-of-text-file mark.
The feature exists so that the environment is compatible with the CP/M operating system of the early 1970s, in case you'd need to use some files that originate from that. As you've noticed, only type works like that, but more would display more... (no pun intended).
No kidding.
It is very dangerous to write non ASCII characters in a text stream. 0x10 is \n and and can be changed into the underlying system end of line which is \r\n on Windows.
The correct way is to open a file in binary mode:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
FILE *fd = fopen("temp.txt", "wb");
if (NULL == fd) {
perror("Error opening file");
return 1;
}
for(i=1; i<=255; i++)
{
fprintf(fd, "%d %c\n",i,i);
}
fclose(fd);
return 0;
}
That being said, commands expecting text files may stop when they read a SUB control character (CtrlZ code 0x1A), which is you current problem...
I´m beginning with C and I´m willing to understand certain conditions.
I have a text file, generated by notepad or direct via shell by echo in a windows os.
When running this the output show extra chars. What I ´m doing wrong? How I can read text files in a secure way char by char?
Using codeblocks with minggw.
file.txt:
TEST
C program
void main()
{
int i;
FILE *fp;
fp = fopen("file.txt","r");
while ((i = fgetc(fp)) != EOF)
{
printf("%c",i);
}
}
Output
■T E S T
Your code has issues, but the result is fine.
Your file is likely UTF-8 with a (confusingly enough) byte order mark in the beginning. Your program is (correctly) reading and printing the bytes of the BOM, which then appear in the output as strange characters before the proper text.
Of course, UTF-8 should never need a byte order mark (it's 8-bit bytes!), but that doesn't prevent some less clued-in programs from incuding one. Window's Notepad is the first program on the list of such programs.
UPDATE: I didn't consider the spacing between your letters, which of course indicate 16-bit input. That's your problem right there, then. Your C code is not reading wide characters.
Try this code
void main()
{
int c,i;
FILE *fp;
fp = fopen("file.txt","r");
while ((i = fgetc(fp)) != EOF)
{
printf("%c",i);
}
}'
I am working on TTCN-3 (Testing and Test Control Notation) scripting language. I wanted to prepare on guideline checker for this code files.
For that I want to read lines of TTCN-3 script file( some thing like file.ttcn ) one by one into a buffer. But for me fopen / sopen / open / fgetc / fscanf are not able to work properly and are not reading the file correctly. It is giving NULL. Is there any way I can read characters of it into a buffer. I think C cannot read files with more than three extension characters (like .ttcn). Forgive me if my assumption is wrong.
My Environment is Turbo C on windows.
Edit:
Yes I checked those errors also but they are giving unknown error for read()
and no such file or directory exists.
My code is as follows
#include <errno.h>
#include <io.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <process.h>
#include <share.h>
#include <stdio.h>
int main(void)
{
int handle;
int status;
int i=0;
char ch;
FILE *fp;
char *buffer;
char *buf;
clrscr();
handle = sopen("c:\\tc\\bin\\hi.ttcn", O_BINARY, SH_DENYNONE, S_IREAD);
/here even I used O_TEXT and others/
if (!handle)
{
printf("sopen failed\n");
// exit(1);
}
printf("\nObtained string %s #",buf);
close(handle);
fp=fopen("c:\\tc\\bin\\hi.ttcn","r"); \\sorry for the old version of one slash
if(fp==NULL) \\I was doing it with argv[1] for opening
{ \\user given file name
printf("\nCannot open file");
}
ch=fgetc(fp);
i=0;
while(i<10)
{
printf("\ncharacter is %c %d",ch,ch);
i++; //Here I wanted to take characters into
ch=fgetc(fp); //buffer
}
getch();
return 0;
}
The most likely culprit is your Turbo C, an ancient compiler. It's techincally a DOS compiler, not Windows. That would limit it's RunTme Library to 8.3 filenames. Upgrade to something newer - Turbo C++ seems like a logical successor, but Microsoft's VC++ Express would work as well.
Your assumption is wrong about extensions. If fopen is returning NULL, you should output the result of strerror(errno) or use the perror() function to see why it failed.
Edit: The problem is probably because you have "c:\tc\bin\hi.ttcn". in C, "\t" is interpreted as tab, for example.
You could do
"c:\\tc\\bin\\hi.ttcn"
But this is extremely ugly, and your system should accept:
"c:/tc/bin/hi.ttcn"
MS-DOS does not know about long file names, thos including files with extensions longer than 3 characters. Therefore, the CRT provided by Turbo C most probably does not look for the name you are providing, but a truncated one - or something else.
Windows conveniently provides a short (i.e. matching the 8.3 format, most of the time ending in ~1 unless you play with files having the same 8-character prefix) file name for those; one way to discover it is to open a console window and to run "dir /x" in the folder your file is stored.
Find the short name associated to your file and patch it into your C source file.
Edit: Darn, I'll read the comments next time. All credits to j_random_hacker.
Now that you've posted the code, another problem comes to light.
The following line:
fp=fopen("c:\tc\bin\hi.ttcn","r");
Should instead read:
fp=fopen("c:\\tc\\bin\\hi.ttcn","r");
In C strings, the backslash (\) is an escape character that is used to encode special characters (e.g. \n represents a newline character, \t a tab character). To actually use a literal backslash, you need to double it. As it stands, the compiler is actually trying to open a file named "C:<tab>c<backspace>in\hi.ttcn" -- needless to say, no such file exists!