Another conformance question... This time fputs().

Bruce Evans bde at zeta.org.au
Wed Mar 3 01:24:14 PST 2004


On Tue, 2 Mar 2004, Jordan K. Hubbard wrote:

> On Mar 2, 2004, at 8:04 PM, Bruce Evans wrote:
>
> > One is vfprintf(), which may output to non-files.  Oops, so can
> > __svfwrite().  The underlying function isn't always write(2).  EBADF
> > is a very bogus errno if the output is not to a file.  It can be to a
> > string or anything set up by funopen()/fropen()/fwopen().  Strings are
> > writable, so they don't cause a problem here, but anything set up by
> > fropen() or funopen() without a write function is unwritable and
> > returning EBADF is wrong for it.
>
> So, what fix are you suggesting?  I'm truly open to suggestions here,
> but if all we end up doing at the end of the day is concluding that
> things are broken but we don't like any of the proposed fixes, we've
> not really accomplished anything either.   I'd more than welcome any
> diffs to supersede mine.

We should try to call the underlying function in more cases.  This is
not so easy since there are flags that may protect whether the underlying
function can even be called.  Note that cantwrite() already handles some
of the details, and could set errno to EBADF more easily than setting it
in all callers:

% /*
%  * Return true iff the given FILE cannot be written now.
%  */
% #define	cantwrite(fp) \
%  	((((fp)->_flags & __SWR) == 0 || \
%  	    ((fp)->_bf._base == NULL && ((fp)->_flags & __SSTR) == 0)) && \
% 	 __swsetup(fp))

This returns 0 (canwrite) or calls __swsetup() (or both).  So __swsetup()
always gets a chance to set errno in a context-sensitive way in the
cantwrite cases.  It doesn't seem to do much errno setting now:

% /*
%  * Various output routines call wsetup to be sure it is safe to write,
%  * because either _flags does not include __SWR, or _buf is NULL.
%  * _wsetup returns 0 if OK to write, nonzero otherwise.
%  */
% int
% __swsetup(fp)
% 	FILE *fp;
% {
% 	/* make sure stdio is set up */
% 	if (!__sdidinit)
% 		__sinit();
%
% 	/*
% 	 * If we are not writing, we had better be reading and writing.
% 	 */
% 	if ((fp->_flags & __SWR) == 0) {
% 		if ((fp->_flags & __SRW) == 0)
% 			return (EOF);

This is the only failure case.  It bogusly returns EOF instead of boolean
true.  We can set errno to EBADF here if we have no idea why __SRW is
clear.  But we should know.  I think the cases are:
(1) a normal fopen() for reading only.  Then EBADF is correct.
(2) funopen() with no writer, or fropen().  I think this can be determined
    by checking the function pointer that we write through.  Then write is
    just unsupported and an errno like ENOTSUP is better than EBADF.
(3) otherwise, we will call the underlying function and there is no problem
    here.  The underlying function just needs to set errno if it fails, and
    old ones probably don't.
So the only immediate problem with returning EBADF in all cases seems to be
in case (2) which is not very interesting.

% 		if (fp->_flags & __SRD) {
% 			/* clobber any ungetc data */
% 			if (HASUB(fp))
% 				FREEUB(fp);
% 			fp->_flags &= ~(__SRD|__SEOF);
% 			fp->_r = 0;
% 			fp->_p = fp->_bf._base;
% 		}
% 		fp->_flags |= __SWR;
% 	}
%
% 	/*
% 	 * Make a buffer if necessary, then set _w.
% 	 */
% 	if (fp->_bf._base == NULL)
% 		__smakebuf(fp);
% 	if (fp->_flags & __SLBF) {
% 		/*
% 		 * It is line buffered, so make _lbfsize be -_bufsize
% 		 * for the putc() macro.  We will change _lbfsize back
% 		 * to 0 whenever we turn off __SWR.
% 		 */
% 		fp->_w = 0;
% 		fp->_lbfsize = -fp->_bf._size;
% 	} else
% 		fp->_w = fp->_flags & __SNBF ? 0 : fp->_bf._size;
% 	return (0);
% }

Bruce


More information about the freebsd-standards mailing list