[comp.lang.c] fopen

cs161fdu@sdcc10.ucsd.edu (Don't crash sdcc10) (01/21/90)

I have a question:

Is there a reason why fopen will not work in my subroutines that are in a 
different file? fopen works when it is contained in the main program, but
when I put a routine containing fopen in a separate file, statements coming
after the fopen statements are never reached.  Once fopen is reached, the
routine seems to start over again.  I've check the code and everything is
correct to my knowledge.

Is.  File roughly looks like this:

int function()
{
.
.
.
FILE *fp;
fp=fopen("Anyfile","r"0;); //where "Anyfile" can be anyfile: ie.  test.data

.
.
.
return something;
}

The problem is that program execution does not go past fp=fopen(....).
The code is the exact same code in the main routine, so it should work.
I've tried playing with it, but no success.

Any help would be appreciated...

Gary (cs161fdu)

tarvaine@tukki.jyu.fi (Tapani Tarvainen) (02/06/90)

Two days ago I had this little trouble with a program which, to make
it brief, reads a file into memory, does something to it and writes it
back, saving the original as a backup with another name (= an editor).
Normally it works just fine, but in this instance it complained "can't
open file for writing"; I then exited without saving and found that it
had destroyed the original file but failed to back it up.

The relevant code looks something like this:

	if (fexist(fname))
		/* backup */
	if (!(fp = fopen(fname,"w))
		/* error message */

I soon found out that fexist() returned 0 even though the file definitely
existed.  Here's what fexist() looked like (slightly trimmed):

int fexist(fname)
char *fname;
{
        FILE *fp;        
        if ((fp = fopen(fname, "r")) == NULL)
                return(0);
        fclose(fp);
        return(1);
}

I had to sleep over it before I figured out what's wrong:
fopen() needs memory for buffering, and there wasn't enough left.
I replaced fopen() and fclose() with open() and close(),
and presto! it worked.

I can't help wondering why the author had used buffered calls in the
first place, since nothing is read from the disk anyway; is there
some situation where open()/close() wouldn't work (are they less
portable perhaps)?

After fixing fexist() I'm now in a situation where the backup will
be created properly, but saving the file still doesn't succeed.
The fopen() fails, but open() inside it succeeds, effectively
destroying the file.
I can think of a couple of ways to handle this, but I don't
really like any of them:
(1) using unbuffered I/O for the writing (yuck!);
(2) malloc()ing the buffer earlier and writing my own buffered output
    routine (feels like re-inventing the wheel);
(3) malloc()ing enough memory earlier and free()ing it just before
    the fopen() (kind of silly, and where do I figure out how much
    memory fopen() needs -- portability is an issue);
(4) keeping a stream open throughout for all writes.

Suggestions, anyone?

(I am using TurboC 2.0 in MS-DOS, if it matters.)
-- 
Tapani Tarvainen    (tarvaine@jyu.fi, tarvainen@finjyu.bitnet)

peter@ficc.uu.net (Peter da Silva) (02/07/90)

If you're using UNIX, the following will work:

FILE   *safe_fopen(name, mode)
char   *name, *mode;
{
	static char buffer[BUFSIZ];
	FILE   *fp;
	char   *s;
	int     i, filepart;

	/*
	 * The following code creates a new file name in the same
	 * directory as the original.
	 */
	filepart = 0;
	for (i = 0; name[i]; i++)
	{
		buffer[i] = name[i];
		if (buffer[i] == '/')
			filepart = i + 1;
	}
	strcpy(&buffer[filepart], "tmpXXXXXX");
	mktmp(buffer);

	/* The following code opens the new file. It then attempts
	 * to slide the new file in under the old name, aborting
	 * if anything blows up and closing the new file.
	 */
	if (fp = fopen(buffer, mode))
	{
		if (unlink(name))
		{
			fclose(fp);
			fp = 0;
		}
		else
		{
			if (link(buffer, name))
			{
				fclose(fp);
				fp = 0;
			}
			unlink(buffer);
		}
	}

	return fp;
}

This will not have the exact same semantics as fopen. In particular,
it will happily overwrite a destination file that you don't have
write permission on. If you're root, it will happily delete a directory.
As I found out once upon a time with similar code. It would be a
good idea to do some more tests.
-- 
 _--_|\  Peter da Silva.+ 1 713 274 5180. <peter @ ficc.uu.net>.
/      \
\_.--._/ Xenix Support -- it's not just a job, it's an adventure!
      v  "Have you hugged your wolf today?" `-_-'

-- 

cs163wau@sdcc10.ucsd.edu (Tyron) (02/08/90)

>In article <6307@sdcc6.ucsd.edu>, cs161fdu@sdcc10.ucsd.edu (Don't crash sdcc10) writes:
>> 
>> int function()
>> {
>> FILE *fp;
>> fp=fopen("Anyfile","r"0;); //where "Anyfile" can be anyfile: ie.  test.data
>> return something;
>> }
>> 
>> Gary (cs161fdu)

The problem was with fopened calling a predefined open function.  My routine
was also called open.

 fp=fopen("Anyfile","r"0;); //where "Anyfile" can be anyfile: ie.  test.data
                       ^^
                       | does not belong there.  A typo during message entry.


---------------------------------------------------------------------
> Subject: fopen

Gary,

> Is there a reason why fopen will not work in my subroutines that are in a 
> different file? fopen works when it is contained in the main program, but
> when I put a routine containing fopen in a separate file, statements coming
> after the fopen statements are never reached.  Once fopen is reached, the
> routine seems to start over again.  I've check the code and everything is
> correct to my knowledge.

This is a shot in the dark.  Do you happen to have a function named
"open" somewhere in your program?  Maybe the very function containing the
call to "fopen"?  If you are not using an ANSI-conforming implementation
of C, this is likely to cause a problem.

The reason is that "fopen" may call other functions to do its work.
The names of those other functions may collide with names of your own
functions, causing your functions to be called instead of the ones
fopen wants.  The most likely culprit for a name clash when calling
"fopen" is "open".

This is called the "name space pollution" problem and is common
in pre-ANSI systems.  There is no way a programmer can be expected
to anticipate what names have to be avoided.  ANSI C solves the
problem by defining what names are allowed to be used by the
C library; all other names belong to the programmer.

Walter Murray
Hewlett-Packard, C language project
----------------------------------------------------------------------

Although I figured out the problem, I would like to thank Walter and
others who have written to me about this problem.


Gary (cs161fdu@sdcc10.ucsd.edu)

reznick@ribs.UUCP (Larry Reznick) (02/09/90)

> I had to sleep over it before I figured out what's wrong:
> fopen() needs memory for buffering, and there wasn't enough left.
> I replaced fopen() and fclose() with open() and close(),
> and presto! it worked.

Try using the stat() function, which gives directory information for a
pathname without opening the file.  Your fexist() function would use
stat() to determine if the file exists.

> I can't help wondering why the author had used buffered calls in the
> first place, since nothing is read from the disk anyway; is there
> some situation where open()/close() wouldn't work (are they less
> portable perhaps)?

The open()/close() functions are not standard; fopen()/fclose() are.
Using open()/close() may impact portability.

> Suggestions, anyone?
> 
> (I am using TurboC 2.0 in MS-DOS, if it matters.)

If you're really in a crunch for memory such that there is no room for
the file buffers (have you checked your config.sys FILES variable?),
you might consider fclose()ing one or more of stdin, stdout, stderr,
stdaux, & stdprn.  If you're not using them why keep them around?

-- 
Lawrence S. Reznick, KB6NSI	Systems Software Engineer
Alldata Corporation		Phone: (916) 684-5200  FAX: (916) 684-5225
9412 Big Horn Blvd, Suite 6	UUCP:  {sun!sacto,csusac}!ribs!reznick
Elk Grove, CA 95758		INTERNET: ribs!reznick@sacto.West.Sun.COM

chip@tct.uucp (Chip Salzenberg) (02/09/90)

According to reznick@ribs.UUCP (Larry Reznick):
>Try using the stat() function, which gives directory information for a
>pathname without opening the file.  Your fexist() function would use
>stat() to determine if the file exists.

However, Larry then goes on to mention:

>The open()/close() functions are not standard; fopen()/fclose() are.
>Using open()/close() may impact portability.

Using stat() will impact portability even more.  If you need to
conserve memory, just be sure to call "setbuf(fp, (char *)NULL)" after
the fopen() call(s) in question.  An endearing characteristic of
unbuffered streams is that they don't need to allocate buffers.  :-)
-- 
Chip Salzenberg at ComDev/TCT   <chip%tct@ateng.com>, <uunet!ateng!tct!chip>
          "The Usenet, in a very real sense, does not exist."

reznick@ribs.UUCP (Larry Reznick) (02/13/90)

chip@tct.uucp (Chip Salzenberg) mentioned:

>Using stat() will impact portability even more.

in response to my suggestion for using stat() to get assorted file
info without opening the file.  I'm sorry.  I could have sworn
remembering that stat() was included in the ANSI C Standard copy that
I have.  (Unfortunately, it isn't with me at this moment of writing.)
Consequently, I had no qualms about recommending it as a reasonably
portable option.  Since I'm waiting for the final standard doc to be
shipped, I don't really know if it is still in there.  I'll double
check my old draft.
-- 
Lawrence S. Reznick, KB6NSI	Systems Software Engineer
Alldata Corporation		Phone: (916) 684-5200  FAX: (916) 684-5225
9412 Big Horn Blvd, Suite 6	UUCP:  {sun!sacto,csusac}!ribs!reznick
Elk Grove, CA 95758		INTERNET: ribs!reznick@sacto.West.Sun.COM

tarvaine@tukki.jyu.fi (Tapani Tarvainen) (02/16/90)

Thanks to everybody who responded!  In case someone is interested,
here's a summary of the responses (attributions omitted, most ideas
were suggested by several people):

Checking for the existence of a file:

* open()/close() are less portable than fopen()/fclose() (the latter are in 
  ANS, the former in UNIX and lookalikes, including most MS-DOS compilers).

* stat() is probably the easiest if available, however it is no more 
  portable than open() and close().  (I decided to use 
  #ifdef MSDOS /* use stat() */ #else /* use fopen()/fclose */ #endif
  -- most other systems are likely to have enough and/or virtual memory
  anyway).

Ensuring that the original file won't get lost:

* Write to a temporary file first and rename it after success.  This is 
  actually what the program was trying to do, but first it tested if
  the file existed to see if this was necessary :-(.  This can of course 
  be done even when the file doesn't exist, obviating this problem.

* Backup can be done safely by simply trying rename() -- if it doesn't
  work, then the file probably doesn't exist.  The problem here is how
  to warn about other possible reasons of failure (write-protected
  directory or whatever).

Ensuring the ability to write the file when memory has ran out:

* Unbuffered I/O is unacceptably inefficient.

* Writing my own buffered output routine should work, but rather waste
  effort, and doing it in a portable way isn't easy either.

* malloc()ing the required space before hand and free()ing it before
  fopen() would probably work, but is rather, eh, yucky.

* Keeping a stream open throughout and using freopen() should work,
  and would probably even be quite portable.  (This is what I'll
  probably do if this really becomes a problem.  For now it's
  enough for me that the original can no longer be lost.)

* Somebody suggested closing stdin, stdout or stderr to make room.
  Unfortunately it doesn't work, as they may be unbuffered.
-- 
Tapani Tarvainen    (tarvaine@jyu.fi, tarvainen@finjyu.bitnet)