Getting the file name from a FILE *

B

Bartc

jacob said:
fopen() will save the file name and an accessor function
will return the file name given a FILE *.
What would be the best name for this function?
char *fname(FILE *); // lower case, short, similar to other
// file functions

Reading this thread it is amazing how much negative reaction there is to
this idea.

Every possible objection has been raised against it (you'd think they had
been the ones asked to implement it!). The main one was that the 'filename'
was meaningless.

Various alternatives have been proposed which mostly involved the programmer
in doing extra work and which in some cases could have meant revising
millions of lines of existing code. In this case presumably the 'filename'
is no longer meaningless!

As I understand it:

* This feature requires the programmer to do nothing different
* There is available a new /optional/ function such as fname()

So the programmer can use fname() /or/ can do whatever he does now.

Use of fname() allows retrieval of a filename from any file opened with
fopen() from many millions of lines of existing code with /no changes/ to
those lines (which in some cases may not be accessible anyway).

So what is the problem? (I understand that fname() may be restricted to a
particular implementation.)
 
S

Serve Lau

Richard Heathfield said:
Serve Lau said:



That won't help if you don't have the authority to request a change, or if
the library writer has gone out of business leaving you with a legacy lib
and no budget to rewrite it.


Not if you're being paid to do other stuff.


Strawman. Now consider a library written by someone else, one that
provides
you with a file pointer but where you don't get to provide the filename.
Consider, for example:

FILE *open_config_file(int subprogramID);

where the decision about which config file to open is taken from you.

No problem.

printf("Could not read from config file opened by subProgramID %d\n",
subprogramID);

Then check documentation what file "subprograms" use or just use your brain
given the context. But I guess for the sake of argument we dont have
documentation now and we had way too much to drink yesterday and lost our
brain...then call the people who made the library...if their phones are
suddenly broken e-mail them what files that id could open. But yeah they use
outlook which is from microsoft so it sucks and never works then install
filemon which can show you the file that you tried to read.
Its a silly discussion. Once again, storing the filename in the FILE struct
is nothing more than moving the job of application programmers into system
libraries
 
B

Ben Bacarisse

Bartc said:
If you're distributing a library of some kind (it could be dynamic or
static), and want to update one of the functions so that it can make use of
the filename of a file-handle, then your solution would involve all the
thousands of users of your library in updating their code.

That is entirely true. In fact it shows that, given the right
restrictions (library using code needs only a re-link), it is
impossible even in simple systems. Jacob's point seemed to that it
was the complexity that made it impossible.
 
J

jacob navia

Tor said:
Now, notice that the output files are closed, then deleted, so your
_fname() will not help much. I assume you free the file name on
fclose(), right?

name = strdup(fname(fp));
fclose(fp);
delete(name);
free(name);
Finally, this GW run on Solaris... so using _fname() will break the
program.

Names beginning with underscore are reserved for the implementation.
OTOH, if using lcc-win, program will be bloated by 4x extra malloc's, 4x
extra free's, and more memory usage.... for no gain at all.

Why 4?

Just one
For "complex" systems, this is a useless extension.

Do not think so
 
B

Bartc

Richard said:
Serve Lau said:


Yes, you can find a solution, but it isn't a solution that gives you
access to the filename, whereas Mr Navia's solution could give you
that access:

fp = open_config_file(42);
if(NULL == fp)
{
printf("Could not open configuration file %s.\n", _fname(fp));

Since fp is NULL at this point, fname will need to be pretty clever.
 
R

robertwessel2

every time there's an error reading a file.

Error: inifile_read.c line 230: parse error reading "BLOB_COUNT"


Actually that illustrates the objection. It's not the FILE object
that's the center of this process, it's the C compiler's read_a_line
function, which is part of the read_source_file_xxx group of
functions. These may happen to use fopen, and fread or fgets to
actually physically retrieve in the input line, but they have to
maintain other stuff as well. For example the line number so that you
can produce error messages. Then the read_a_line function will
provide the higher levels of the compiler some way to associate a
particular point in the input stream with that data, so that a
meaningful error message can be produced.
 
C

Chris Torek

[I dropped comp.compilers.lcc from the newsgroups line.]

Reading this thread it is amazing how much negative reaction there is to
this idea.

Possibly because of the person who proposed it, but possibly also
or instead because the idea has been tried before and found wanting. :)
* This feature requires the programmer to do nothing different
* There is available a new /optional/ function such as fname()

So the programmer can use fname() /or/ can do whatever he does now.

Use of fname() allows retrieval of a filename from any file opened with
fopen() from many millions of lines of existing code with /no changes/ to
those lines (which in some cases may not be accessible anyway).

So what is the problem? (I understand that fname() may be restricted to a
particular implementation.)

There is nothing fundamentally wrong with the idea. As at least
a few people have pointed out, it helps out in the case of files
that were opened by "bad" code ("bad" is somewhat subjective of
course) that fails to save the name even though the name is useful
later. If the implementation saves the name for you, you can work
around the bad code -- rather than fixing it -- in those places
were you want to retrieve the name by which the file was opened.

This part is, I think, straightforward enough and something on
which everyone agrees.

The objections are:

- The name may be "out of date" (due to file renames and so on).
This is of course true, but not really relevant: in a situation
in which you would have saved the name, then printed the one
*you* saved, you would be printing that same out-of-date name.

- The name may be unavailable, because the "fopen()" equivalent
was done in a way that did not provide a name (e.g., via fdopen()
on a system that supports it -- this includes many Windows-based
systems -- or the operation is done on stdin, stdout, or stderr).
This is also true, but again perhaps not so relelvant: in
a situation in which you would have saved the name, then printed
the one *you* saved, you might well have the same problem. (Or
you might not, in the case of fdopen() for instance.) So this
objection is slightly more relevant.

- The "implementation question", as I will call it: how does the
implementation actually implement the name-saving?

A typical stdio "FILE *" pointer points to a structure with a
number of fields that are accessed via macros and/or functions:

typedef struct __sFILE FILE;
struct __sFILE {
... various fields ...
};

The "obvious" way to implement _fname is:

FILE *fopen(const char *restrict name, const char *restrict mode) {
FILE *retval;

... all the usual work ...
... assume we are now ready to succeed here ...
retval->__sname = name;
return retval;
}

const char *_fname(FILE *fp) {
return fp->__sname;
}

The problem with this implementation is that the pointer saved
in fopen(), and returned by _fname(), is valid only as long as
the storage for the name-string passed to fopen(). If -- as is
typical -- the name is in an automatic-duration buffer, the
pointer _fname() will return becomes invalid as soon as that
automatic-duration variable is destroyed. In something reasonably
close to half the calls to fopen(), this has happened by the
time you (the programmer who would use _fname()) would get
around to calling _fname(). So *this* implementation will not
help you.

The implementation can, of course, simply use something similar
to the (nonstandard) strdup() function:

retval->__sname = strdup(name);
return retval;
}

but now the cost is less trivial: the implementor now must not
only add a pointer to each FILE object, but also allocate space
to hold the name. The implementor must also make sure to free()
the space when the FILE is fclose()d.

This objection is not so much an "objection" as a "point of
view". Is it reasonable to have the C library routine do the
strdup() (or equivalent)? If not, is the pointer-only version
of fname() going to be useful enough?

- Lastly, there is the "utility question", which is something of
an expanded version of the previous question. Even if the C
library fopen() routine does do a strdup()-or-equivalent, so
that a non-NULL return from _fname() actually points to a
useable string, is knowing the name alone sufficient? In many
(but not all) cases, a diagnostic that prints only the name of
the file is not terribly useful. If you want to print a file
name and line number, or file name and character offset, or
file name and nearby contents, you need a bit more. The
character offset can be (non-portably, alas) obtained via
fseek(), ftell(), or fgetpos(), but the others require more
forethought and design -- and if you apply this forethought
and design, you do not need _fname().

(Note that when using forethought and design, the programmer
can save the name even when using fdopen(), tell whether or
not there is any need to strdup() the name, update it if the
file is renamed on purpose, and so on. The code also works on
every implementation, not just those that provide _fname().
So this option is a clear winner, if it is an option at all.)

So it really boils down to: a quick-and-dirty save-only-the-pointer
implementation is cheap, but not very useful. A strdup()-the-string
implementation is less cheap, but more useful. In my opinion, the
second version still does not reach the "90% useful" level that makes
it worth adding. The first has lower cost, but does not even make
it to "40% useful" (again in my opinion).
 
N

Nick Keighley

Nonsense.  You open the file, saving the name you used.  Another
process deletes the filename you used.  You still have the file
open, but the name is worthless.  The file still exists.  You can
copy it elsewhere, or do all sorts of things to it.  Under
Unix/Linux/Posix, but not Winders.

its hardly Jacob's fault if this bug is still present
in Unix...


:)
 
B

Bartc

Possibly because of the person who proposed it, but possibly also
or instead because the idea has been tried before and found wanting. :)

Well I can live without the feature myself, but I don't see much harm in it.
There is nothing fundamentally wrong with the idea. As at least
a few people have pointed out, it helps out in the case of files
that were opened by "bad" code ("bad" is somewhat subjective of
course) that fails to save the name even though the name is useful
later. If the implementation saves the name for you, you can work
around the bad code -- rather than fixing it -- in those places
were you want to retrieve the name by which the file was opened.

This part is, I think, straightforward enough and something on
which everyone agrees.

I think this feature is more one of convenience, especially if there is some
'distance' between the fopen() and subsequent uses.

Pointing out that the name /could/ have been saved is (a bit) like saying
ftell() is not necessary because a good programmer would have kept track of
a file position by the number of bytes read or written so far.

The objections are:

- The name may be "out of date" (due to file renames and so on).

The specification was that this is the name passed to fopen().
- The name may be unavailable, because the "fopen()" equivalent
was done in a way that did not provide a name (e.g., via fdopen()
- The "implementation question", as I will call it: how does the
The "obvious" way to implement _fname is:

[by storing the pointer]
Well, to me that is immediately obvious that is a bad way to do it. Anything
other than a constant string passed to fopen could be problematic.
The implementation can, of course, simply use something similar
to the (nonstandard) strdup() function: ....
but now the cost is less trivial: the implementor now must not
only add a pointer to each FILE object, but also allocate space
to hold the name. The implementor must also make sure to free()
the space when the FILE is fclose()d.

Because /file/ operations are involved, the performance hit is likely to be
trivial. Memory use I have a feeling is not that significant either.

Perhaps an alternative fopen(), say fopen2(), should be used that does copy
the filename. Then the regular fopen() works as normal. The only difference
is that FILE reserves space for a char* field, set to NULL for a normal
fopen().
- Lastly, there is the "utility question", which is something of
an expanded version of the previous question. Even if the C
library fopen() routine does do a strdup()-or-equivalent, so
that a non-NULL return from _fname() actually points to a
useable string, is knowing the name alone sufficient? In many
(but not all) cases, a diagnostic that prints only the name of
the file is not terribly useful.

A filename is a good starting point! But the proposed fname() may not be
helpful here:

In one example that came up in this thread: if a library function calls
fopen() on your behalf, supplying it's own name, and propagates back a NULL
return value, fname() cannot retrieve the filename from this. So fname()
would only be useful when an fopen() succeeds.
 
D

Dik T. Winter

> Honestly, people: This thread has already dragged on for
> more than ten dozen posts spread over six days, and as far as
> I can see *every* point was made within the first six hours.
> The subsequent descent into brickbattery is unedifying.

Oh, peanuts. In sci.math there is a thread with over 7000 articles spead
over many months and not only has *every* point made in the first day,
every point was already made in tht newsgroup before the first article
was posted.
 
R

Richard

CBFalconer said:
Nonsense. You open the file, saving the name you used. Another
process deletes the filename you used. You still have the file
open, but the name is worthless. The file still exists. You can
copy it elsewhere, or do all sorts of things to it. Under
Unix/Linux/Posix, but not Winders.

What is Winders? Is it like Windows which you use?
 
W

Walter Roberson

Chris Torek said:
The objections are:

There is an issue that I haven't observed being addressed in this
thread, which is that in general, what you want to present in error
messages (and the like) is not necessarily the file name that the file
was openned with, but rather some kind of user- meaningful identifying
string -- which might perhaps include the filename openned with, might
perhaps include the directory, might perhaps include a phrase such as
"configuration file", might perhaps include the phrase "temporary swap
file", might perhaps say "standard input" and so on.

If the idea is to have something to present for error/warning messages,
then an "identifying string" is more useful than just the file
name that might happened be passed to fopen -- e.g., the file name passed
might be relative to the current directory and so the name by itself
might not be very useful. [*]

Of course, passing in an identifying string for fopen to squirrel
away would require a change to the API to fopen(), which would
Not Be Cool considering the amount of code that presently uses fopen.
In theory, the API for fopen() could be extended by adding a varargs
parameter, but as there can be considerable differences in parameter
passing between non-varargs routines and varargs routines, taking
this approach would likely require a recompile / relink of lots of
existing code even though the code did not use the new facilities;
I expect that that recompile / relink step would not be considered
an acceptable cost for this functionality. There are work-arounds
to this issue, such as having fopen() record the filename
as the default identifying tag (as Jacob proposed), but also adding a
routine which could change the tag to something else after the
fopen().


([*] Note: in Unix and [I think] MS Windows with NTFS filesystems, there
can be files that you can open relatively but which you could not open
if you used the absolute path, because in Unix at least, you are given
authorization to access your home directory even if your user does not
have permission to traverse the directory tree to reach your home
directory. In Windows with NTFS Alternative Data Streams (ADS),
I believe that the effect of reading a file through one path could be
different than the effect of reading it through a different path,
since a component on the pathname could [if I understand
properly] have an ADS that could be (e.g.,) a decryptor or decompressor.)
 
K

Keith Thompson

Traditionally on unix systems the empty string refers to the current
directory, and you can, on some systems and some filesystems, fopen()
it. However, this is such a special case that simply setting the
"special string" to something like "(empty)" would handle it.

But "(empty)" is a valid file name.
 
R

Richard Tobin

Traditionally on unix systems the empty string refers to the current
directory, and you can, on some systems and some filesystems, fopen()
it. However, this is such a special case that simply setting the
"special string" to something like "(empty)" would handle it.
[/QUOTE]
But "(empty)" is a valid file name.

Look back at the thread: "special string" refers to the stuff after
the null. The normal case is

/ f o o / b a r \0

If the filename is not known, you get (for example)

\0 . . s t d i n . . \0

Twink noted this would be ambiguous if the filename really was the empty
string, so I suggested that in that case you would get

\0 ( e m p t y ) \0

to distinguish it.

So it doesn't matter that "(empty)" is a possible file name.

-- Richard
 
K

Keith Thompson

But "(empty)" is a valid file name.

Look back at the thread: "special string" refers to the stuff after
the null. The normal case is

/ f o o / b a r \0

If the filename is not known, you get (for example)

\0 . . s t d i n . . \0

Twink noted this would be ambiguous if the filename really was the empty
string, so I suggested that in that case you would get

\0 ( e m p t y ) \0

to distinguish it.

So it doesn't matter that "(empty)" is a possible file name.[/QUOTE]

You're right, I didn't make the connection.

You'd need to guarantee that, if the function returns (a pointer to)
an empty string, the '\0' is actually followed by a string.

And I agree with your earlier comment that it's ugly. (I used a
similar trick a few years ago as part of my IOCCC entry.)
 
P

Peter Nilsson

They are often in relation to modification of static duration objects.
No, it is not. Lots of fopen() implementations, for
example, obtain I/O buffers from malloc().

Some even use malloc to obtain the FILE pointers.
 
S

Serve Lau

Eric Sosman said:
Walter said:
No, it is not. Lots of fopen() implementations, for
example, obtain I/O buffers from malloc().

But when you dont want to use malloc there is a mechanism in place to
provide your own I/O buffer.

setbuf/setvbuf

Resource heavy software like commercial games do not use malloc, they often
implement their own memory manager. Adding the filename inside FILE now
introduces an obligatory malloc that wont be used. So I'm thinking if
fname() gets added to lcc then also add a setfnamebuf(FILE *, char *).

And I still didnt see an answer to my question if there will be a wide char
version for fname too. lcc provides a wfopen function and the return value
is also a FILE * so you have no way of telling with which function the file
was opened.
 
S

Serve Lau

Richard Heathfield said:
Yes, you can find a solution, but it isn't a solution that gives you
access
to the filename, whereas Mr Navia's solution could give you that access:

fp = open_config_file(42);
if(NULL == fp)
{
printf("Could not open configuration file %s.\n", _fname(fp));



Not as silly as some we've had here, not by a long way.

I meant the argument for adding filename to FILE so you can get the name
even if you have no control over some piece of code that returns FILE * is
silly. When using software that isnt under your control it is important to
write portable code and using fname is not smart then. When the software is
not under your control your code will break if that software switches
compilers.

and indeed as others pointed out, the snippet above shows another problem.

you cant do this:

fp = fopen("test", "r");
if(NULL == fp)
{
printf("Could not open configuration file %s.\n", _fname(fp));
}

So you have to remember the filename yourself at some point anyway! I see
lots of arguments against adding filename to FILE and only 1 in favor which
is a little typing convenience.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top