char *wcat(char *str, size_t n, FILE *fp){
if (fp == NULL) {
printf("wcat cannot open file\n");
fclose(fp);
perror("File cannot be opened");
return NULL;
exit(1);
}else{
if ((str = fgets(str,n,fp)) != NULL){
printf("%s",str);
return str;
exit(0);
}
}
}
Terminal:
gcc -o wcat wcat.c
Error: wcat.c:36:1: warning: control may reach end of non-void function [-Wreturn-type]
The fp already equals fopen(...).
I am not sure why this is happening. I wanted to create this wcat file to work like:
./wcat file1.c file2.c
Your else clause also needs an else, or at least a default return. Your ifs don't cover every possible case. The warning says exactly what the problem is.
char *wcat(char *str, size_t n, FILE *fp){
if (fp == NULL) {
printf("wcat cannot open file\n");
fclose(fp);
perror("File cannot be opened");
return NULL;
//exit(1);
}
else if (fgets(str,n,fp))
{
printf("%s",str);
return str;
// exit(0);
}
return NULL; /// or whatever it is that you expect to happen here.
}
Neither of the calls to exit makes sense. They'll never be executed. It looks like you're trying to use those to return some sort of success/failure flag, but:
they never execute because they follow a return
exit terminates the program.
The parameter is passed back to the calling process. In my experience, this is basically never used unless you're writing a console utility.
Do you really understand what exit does? And return?
There's a lot wrong with this one. I suggest stepping through in your debugger.
the following changes, with comments, is the correct way to handle this function:
char *wcat(char *str, size_t n, FILE *fp){
// note: this check should have been handled
// where/when 'fopen()' was called
if (fp == NULL) {
// this changes the value in 'errno'
// so 'perror() will not display the right message
//printf("wcat cannot open file\n");
//fclose(fp); // <-- never open'd so cannot close
perror("File cannot be opened");
return NULL;
//exit(1); // <-- will never be executed
}else{
if (fgets(str,n,fp)){
printf("%s",str);
return str;
//exit(0); // <-- will never be executed
}
return NULL; // <-- need to handle when 'fgets()' fails
}
}
after applying the corrections AND moving the check for a failure of 'fopen()', the code would look like the following:
char *wcat(char *str, size_t n, FILE *fp)
{
if ( fgets( str, n, fp ) )
{
printf("%s",str);
return str;
}
return NULL;
}
Related
I have got the following code:
#include <stdio.h>
#include "config.h"
#include <errno.h>
char buffer[50];
long long bufSize = 50;
FILE* fptr;
char* readConfig(char* buffer, size_t bufSize) {
fptr = fopen("config.txt", "rt");
if (fptr == NULL) {
return "error opening config file: %s", strerror(errno);
} else {
if ((fgets(buffer, bufSize, fptr)) == NULL) {
fclose(fptr);
return "error reading config file: %s", strerror(errno);
}
else {
fclose(fptr);
return buffer;
}
}
}
For test purposes I deleted the config.txt file so that the fopen() function should return NULL.
What wonders me is that fopen("config.txt", "rt"); fails, but when debugging the code it just skips the "if (fptr == NULL) {...}" part and directly jumps out of the function.
When going on with the debugging process, I get the following error when trying to work with the return Value of readConfig() "0xC0000005: Access violation reading location 0xFFFFFFFFA4E0EB70"
Cannot compile your code as you shared a snippet (no main()) and didn't include config.h.
#include <string.h> for strerror().
Suggest caller passes in local variables instead of global variables.
Instead of hard-coding the size in both buffer[50] and bufSize = 50; use sizeof to determine the size of the array. The other good alternative is to define a constant.
The fopen() mode "t" isn't standard, so either leave it out or tag your program with windows or whatever.
As you return on error, eliminate the unnecessary else & indentation.
The expression return "error opening config file: %s", strerror(errno); doesn't work the way you expect, it will evaluate the first part in void context then return the 2nd part strerror(errno). I was not able to otherwise reproduce any ill effects.
fgets() return NULL on eof or error but not appear to set errno. You can use feof() and/or ferror() to tell which if needed.
After the call to fgets() you call fclose() prior to inspecting errno, so it have the status of the fclose() call instead.
It's a bad design to return either error message or the value you read from the file as you cannot tell them apart. Changed to return NULL on success.
#include <errno.h>
#include <stdio.h>
#include <string.h>
char *readConfig(char *buffer, size_t bufSize) {
FILE* fptr = fopen("config.txt", "r");
if(!fptr)
return strerror(errno);
if(!fgets(buffer, bufSize, fptr)) {
fclose(fptr);
return "fgets eof/error";
}
fclose(fptr);
return NULL;
}
int main(void) {
char b[50];
const char *error = readConfig(b, sizeof b);
if(error) {
printf("error: %s\n", error);
return 1;
}
printf("%s", b);
}
Consider having caller open the file and pass in the FILE *. It gives you the flexibility, for instance, to use stdin as the file handle.
I prefer using goto instead of the multiple returns when resource cleanup is required. It's a bit more verbose here but each error case is handled the same way. When you swap the arguments you can document how they are related with recent compilers:
char *readConfig(size_t bufSize, char buffer[bufSize]) {
char *r = NULL;
FILE* fptr = fopen("config.txt", "r");
if(!fptr) {
r = strerror(errno);
goto out;
}
if(!fgets(buffer, bufSize, fptr)) {
r = "fgets eof/error";
goto out;
}
out:
fptr && fclose(fptr);
return r;
}
Here is my basic code that I'm trying to work with.
void test(){
FILE *input;
input = fopen("input.txt.", "r");
}
So I'm trying to check if file was already opened before, meaning that void test() function was called once before. I realy have no idea how to do that, i tried it with while and if.
Like this.
void test(){
FILE *input;
int open = 0;
while (open == 0){
input = fopen("input.txt", "r");
if (input == NULL){
printf("File wasnt opened.\n");
}
if (input != NULL){
printf("File is opened.\n");
}
open = open + 1;
}
if(open!=0){
printf("file is already opened.\n");
}
}
Use a local static variable.
void test (void)
{
static bool called_before = false;
if(called_before)
{
do_this();
}
else
{
do_that();
called_before = true;
}
}
Supposing that your intent is for test to open the file just once but to read from the file each time it is called, you can make input static:
void test(void)
{
/* Since input is static, it will be initialized with NULL when the
program starts and we will retain its value between calls to
the function.
*/
static FILE *input = NULL;
// If input has not been set for an open file yet, try to open it.
if (!input)
{
input = fopen("input.txt", "r");
if (!input)
{
fprintf(stderr, "Error, unable to open input.txt.\n");
exit(EXIT_FAILURE);
}
}
for (int c = fgetc(input); c != EOF && c != '\n'; c = fgetc(input))
putchar(c);
putchar('\n');
}
Note static objects should generally be avoided, as they complicate program state and so may lead to more bugs. It is okay to play with them in student programs to learn how they work, but their use in real-world applications should be limited.
Say, I have code like this:
int foo() {
...
int buff_size;
scanf("%d", &buff_size);
FILE * fp = fopen("./file", "a+");
char *buff = malloc(buff_size*sizeof(char));
char *buff2 = malloc(buff_size*sizeof(char));
char *buff3 = malloc(buff_size*sizeof(char));
while (!feof(fp)) {
/*do something, say read, write etc.*/
if (/*error case 1*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -1;
}
else if (/*error case 2*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -2;
}
...
else if (/*error case k*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -k;
}
}
...
free(buff);
free(buff1);
free(buff2);
fclose(fp);
return 0;
}
for C doesn't provide try...throw...finally syntax, I have to close file descriptors and free heap memories pointer that I created before I return a error integer code. It produces some duplicated code that makes the code ugly.
Do anyone know how should I modifly this kind of code to make it looks more brief?
Your code, as written is equivalent to
FILE * fp = fopen("./file", "a+");
int return_value = 0; /* assume zero means no error */
char *buff = malloc(1024*sizeof(char));
char *buff2 = malloc(1024*sizeof(char));
char *buff3 = malloc(1024*sizeof(char));
while (feof(fp))
{
/*do something, say read, write etc.*/
if (/*error case 1*/)
{
return_value = -1;
}
else if (/*error case 2*/)
{
return_value = -2;
}
// ...
else if (/*error case k*/)
{
return_value = -k;
}
}
if (return_value == 0) /* if no error has occurred, we can still do stuff */
{
...
}
/* I have assumed all buffers need to be released
and files closed as the function returns
*/
free(buff);
free(buff1); /* you probably intend buff2 and buff3 here, to match the malloc() calls */
free(buff2);
fclose(fp);
return return_value;
}
I have not, however, addressed two critical errors in your code. You need to.
fopen() can fail, and return NULL. If it does, passing it to feof() or to fclose() gives undefined behaviour. Your code is not checking that at all, and needs to both before call of feof() and fclose().
A loop of the form while (!feof(fp)) {read_something_from(fp); use_something();} is a bad idea. Have a look at Why is “while ( !feof (file) )” always wrong? for more info.
Less critical - for fixed size arrays (size fixed at compile time) you probably don't need to use malloc() which also means you don't need to use free(). However, you still need to deal with potential failures of fopen().
For this sort of thing with common cleanup code, I'd use a return value variable and a goto:
int foo() {
...
// append extended mode ... what are you doing with this?
FILE * fp = fopen("./file", "a+");
char buff[1024];
char buff2[1024];
char buff3[1024];
int ret = 0;
while (/* file I/O is valid on fp */) {
/*do something, say read, write etc.*/
if (/*error case 1*/) {
ret = -1;
goto cleanup;
}
else if (/*error case 2*/) {
ret = -2;
goto cleanup;
}
...
else if (/*error case k*/) {
ret = -k;
goto cleanup;
}
}
...
cleanup:
free(buff);
free(buff2);
free(buff3);
fclose(fp);
return ret;
}
This also prevents having to copy-paste code, or retyping with errors (as your current code has).
I am trying to read proc file /proc/stat at periodic interval but I want to avoid having to open and close the proc file each time I want to access it.
I want to open the file in sort of init function, and then keep using it in some other function, then close it later on.
It seems file handles opened by a function gets closed when the function exits
How can I keep it open ?
Please let me know if I should be doing it in some other manner
Sample of what I am trying to do:
#include <stdio.h>
int printer(FILE* fin)
{
/* I am getting fin as NULL */
if(!fin)
return 1;
char buf[16*1024];
rewind(fin);
size_t sz = fread(buf, 1, sizeof(buf), fin);
if (sz) {
buf[sz]=0;
printf(buf);
}
return 0;
}
int opener(FILE *fin)
{
fin = fopen("/proc/stat", "r");
if (!fin) {
perror("fopen");
return 1;
}
return 0;
}
int main() {
FILE *fin;
/*
* I know it works if I open the file handle in here instead of
* in another function but I want to avoid this
*/
if(opener(fin))
{
printf("ERROR1\n");
return 0;
}
while(1) {
if(printer(fin))
{
printf("ERROR2\n");
break;
}
sleep(1);
}
return 0;
}
Functions in c are pass by value. So when you pass a file handle to a function, it receives a copy of that handle and will update it locally. If you want those updates to propagate to your caller, you need pass file handle pointers. So your open would look like:
int opener(FILE **fin)
{
*fin = fopen("/proc/stat", "r");
if (!(*fin)) {
perror("fopen");
return 1;
}
return 0;
}
And you would call it like:
int main() {
FILE *fin;
/*
* I know it works if I open the file handle in here instead of
* in another function but I want to avoid this
*/
if(opener(&fin))
{
printf("ERROR1\n");
return 0;
}
/...
}
You need to pass a reference to the pointer to fin in order to keep it in main.
if(opener(&fin)) {}
pass it as double pointer :
int opener(FILE **fin) {}
and use it with derefencing
*fin = fopen("/proc/stat", "r");
otherwise you initiate it everytime you call your subfonction.
The C language passes arguments by value, so the fin that opener has is a copy of the fin that main has. Changing fin in opener has no effect on main's copy.
One solution is to use a temporary file pointer in opener and then return that pointer. To indicate an error, return NULL.
FILE *opener( char *name )
{
FILE *temp = fopen( name, "r" );
if ( !temp )
{
perror( "fopen" );
return( NULL );
}
return( temp );
}
int main( void )
{
FILE *fin = opener( "/proc/stat" );
if ( !fin )
printf( "failed\n" );
else
printf( "fin=%p\n", fin );
}
I created a function to print the contents of a file:
void readFile(char* filename)
{
int c ;
file = fopen(filename, "r");
printf("The contents of the file are:\n");
while((c = fgetc(file)) != EOF)
{
printf("%c", c);
}
return;
}
where file is a global variable. GDB gives output as follows:
_IO_getc (fp=0x0) at getc.c:39
39 getc.c: No such file or directory.
(gdb) bt
#0 _IO_getc (fp=0x0) at getc.c:39
#1 0x000000000040075e in readFile ()
#2 0x00000000004006d4 in main ()
However, the file is present and I get the SEGFAULT after printing the contents of the file. It might be because the buffer here (c) is small but I am not sure. Also, I don't know how do I fix this even if that were the case. Can anyone suggest how do I proceed?
EDIT
I call the readFile function only once. Here is my calling function:
int main(int argc, char* argv[])
{
char * filename;
filename = argv[1];
readFile(filename);
printf("File Handler: %ld", (long)file);
fclose(file);
return 0;
}
You're passing in a filename that doesn't exist or for some other reason cannot be opened. Get rid of the segfault by checking for errors (you'll need to #include <errno.h> and <string.h> too for this:
void readFile(char* filename)
{
int c ;
file = fopen(filename, "r");
if (file == NULL) {
printf("Cannot open file '%s' : %s\n", filename, strerror(errno));
return;
}
printf("The contents of the file are:\n");
while((c = fgetc(file)) != EOF)
{
printf("%c", c);
}
return;
}
Most likely your file is NULL and you are still trying to read it.
I simulated this behaviour (SEG fault) when I deleted this file.
If file exists then your code works fine.
Check what path you are passing.. If you are using single \ try with \\ and see if this works. First \ will work as escape sequence and final path will be send as D:\temp\use.dat to fopen.
readFile("D:\\temp\\user.dat");
Before you do anything with a file, you must ensure that you opened it successfully. This is done by checking that the file pointer received by calling fopen is not NULL.
Once you do this, you read using whatever function you choose until it returns a value that indicates failure to read — a NULL pointer for fgets, 0 or EOF for fscanf, or EOF for fgetc.
In any case, you challenge these return values in two ways. The first way is to check for read errors using ferror. The other way is to check whether the end of the file was reached using feof.
A complete program that should work, based upon your code:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
enum { OPEN_ERROR = 1, READ_ERROR };
enum { PARAM_EXIT = 1, OPEN_EXIT, READ_EXIT };
FILE *file = NULL;
int readFile(char* filename)
{
int c;
file = fopen(filename, "r");
if (file == NULL)
return OPEN_ERROR;
printf("The contents of file '%s' are:\n", filename);
while((c = fgetc(file)) != EOF)
printf("%c", c);
/*
* fgetc returns EOF on end of file and when an error occurs.
* feof is used to determine whether the end of the file was reached.
* Otherwise, we encountered a read error.
*/
if (feof(file))
c = 0;
else
c = READ_ERROR;
return c;
}
int main(int argc, char *argv[])
{
int status = 0;
if (argc == 1) {
fprintf(stderr, "usage: %s file\n", argv[0]);
return PARAM_ERROR;
}
/* Check that <program ""> wasn't used... */
if (argv[1][0] == '\0') {
fprintf(stderr, "error: empty filename detected, exiting. . .\n");
return PARAM_ERROR;
}
switch (readFile(argv[1])) {
case 0:
break;
case OPEN_ERROR:
fprintf(stderr, "error: file open failed - %s\n", strerror(errno));
status = OPEN_EXIT;
break;
case READ_ERROR:
fprintf(stderr, "error: file read failed - %s\n", strerror(errno));
status = READ_EXIT;
break;
default:
fprintf(stderr, "error: unknown error occurred, aborting...\n");
abort();
}
if (file != NULL)
fclose(file);
return status;
}
Of course, normally you would close the file in the same function in which it was opened (e.g. something like filep = openFile(...); readFile(filep); fclose(filep);, except error handling would be used of course).
I am completely changing my answer
Actually, the file that I was reading was open in gedit (which might explain why I was getting "NULL" even after printing the file contents. I closed the file and removed my NULL comparison code and it works perfectly fine.
Ok, from everybody's comments I got to know that you basically get a SEGFAULT when you read the contents of file that has NULL contents. I just made a simple fix in my while loop:
while((c != EOF))
{
printf("%c", c);
c = fgetc(file);
if(c == NULL)
break;
}
Problemo solved! (Although, the compiler gives me a warning of "comparison between pointer and integer".)