Bug when closing an unopened file handle

General FreeBASIC programming questions.
Post Reply
Bob the Hamster
Posts: 31
Joined: Nov 16, 2005 5:47
Contact:

Bug when closing an unopened file handle

Post by Bob the Hamster »

I recently ran into something that seems like a bug. I was closing a filehandle that had never been opened. I had been doing this for years, and it never caused a problem, until I added another file that remains opened at the same time. Here is a simple example that demonstrates the problem

Code: Select all

DIM f1 AS INTEGER = FREEFILE
OPEN "foo.bin" FOR BINARY AS #f1
PUT #f1,, "The quick brown fox"

DIM f2 AS INTEGER
CLOSE #f2 ' <--this is an error
f2 = FREEFILE
OPEN "bar.bin" FOR BINARY AS #f2
PUT #f2,, "Hello, World!" 

'This should go into foo.bin, but instead it goes into bar.bin
PUT #f1,, " jumped over the lazy fox."

CLOSE #f1
CLOSE #f2
I am not sure what the expected behavior should be. Either a runtime error on closing the non-opened file handle, or silently ignoring the attempt to close the non-open file handle...

but the actual results is that the previously opened file handle gets altered to point to the wrong file.

Now that I know about my mistake, I can work around it easily, I just though I should report it, in case anybody else wants to know about it.

EDIT: Oh, I forgot to specify my OS and version!

Code: Select all

james@rhinoctopus:~$ fbc -version
FreeBASIC Compiler - Version 0.22.0 (05-06-2011) for linux (target:linux)
Copyright (C) 2004-2011 The FreeBASIC development team.
Configured with prefix /usr
objinfo enabled using FB BFD header version 217
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

Hi Bob,
What's happening is that when you tell FB to Close #0, it closes all open files. So f1 gets closed and f2=freefile() gets its old file handle.

As far as I can tell, the reason we've done it like that is, if you do Close with no parameters, internally FB creates a dummy parameter of 0 and passes that to Close. This saved us having to create a new function in the runtime library for this behaviour.

I don't think it's documented, so perhaps we could just make the new internal Close_All function, then we could change Close #0 so it just throws an error.

Does anybody expect/rely on the current behaviour of Close #0?


EDIT: Just checked QB. CLOSE #0 doesn't close all files, but it doesn't throw an error either.
Bob the Hamster
Posts: 31
Joined: Nov 16, 2005 5:47
Contact:

Post by Bob the Hamster »

Ah! That makes sense!

I like the idea of a separate internal Close_all()

So if you did that, CLOSE 0 could be an error normally, and a non-op with -lang qb ?
fxm
Moderator
Posts: 12577
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Post by fxm »

counting_pine wrote:Does anybody expect/rely on the current behaviour of Close #0?
It would be logical that 'Close #0' induces a runtime error 1 (illegal function call). as 'Open ..... As #0' or 'Put #0, .....'.

Edit:
Of course, I also want to keep the possibility to close all files at once using the 'Close' keyword without any parameters.
Last edited by fxm on Jun 25, 2011 5:51, edited 1 time in total.
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Post by TJF »

I don't need CLOSE #0.

But I use CLOSE without parameters to close all opened handles in my code, I won't miss this feature (in #LANG "fb").
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

I also don't need CLOSE #0, and want to see CLOSE without parameters preserved as is.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

Yeah, I agree Close without params shouldn't be changing, so don't worry about that. It'd just get rerouted internally to a different function call.

Here's a tentative patch, for anyone interested (you'll probably have to Quote my post to get a working diff):

Code: Select all

Index: compiler/inc/rtl.bi
===================================================================
--- compiler/inc/rtl.bi	(revision 5577)
+++ compiler/inc/rtl.bi	(working copy)
@@ -258,6 +258,7 @@
 #define FB_RTL_FILEOPEN_COM 			"fb_FileOpenCom"
 #define FB_RTL_FILEOPEN_QB  			"fb_FileOpenQB"
 #define FB_RTL_FILECLOSE 				"fb_FileClose"
+#define FB_RTL_FILECLOSEALL 			"fb_FileCloseAll"
 
 #define FB_RTL_FILEPUT 					"fb_FilePut"
 #define FB_RTL_FILEPUTLARGE 			"fb_FilePutLarge"
@@ -648,6 +649,7 @@
 	FB_RTL_IDX_FILEOPEN_COM
 	FB_RTL_IDX_FILEOPEN_QB
 	FB_RTL_IDX_FILECLOSE
+	FB_RTL_IDX_FILECLOSEALL
 
 	FB_RTL_IDX_FILEPUT
 	FB_RTL_IDX_FILEPUTLARGE
Index: compiler/parser-quirk-file.bas
===================================================================
--- compiler/parser-quirk-file.bas	(revision 5577)
+++ compiler/parser-quirk-file.bas	(working copy)
@@ -539,7 +539,7 @@
     	filenum = cExpression( )
     	if( filenum = NULL ) then
 			if( cnt = 0 ) then
-				filenum = astNewCONSTi( 0, FB_DATATYPE_INTEGER )
+				'' pass NULL to rtlFileClose to get close-all function
 			else
 				if( errReport( FB_ERRMSG_EXPECTEDEXPRESSION ) = FALSE ) then
 					exit function
Index: compiler/rtl-file.bas
===================================================================
--- compiler/rtl-file.bas	(revision 5577)
+++ compiler/rtl-file.bas	(working copy)
@@ -27,7 +27,7 @@
 #include once "inc\lex.bi"
 #include once "inc\rtl.bi"
 
-	dim shared as FB_RTL_PROCDEF funcdata( 0 to 65 ) = _
+	dim shared as FB_RTL_PROCDEF funcdata( 0 to ... ) = _
 	{ _
 		/' fb_FileOpen( byref s as string, byval mode as integer, byval access as integer,
 				        byval lock as integer, byval filenum as integer, _
@@ -354,6 +354,18 @@
 	 			) _
 	 		} _
 		), _
+		/' fb_FileCloseAll( ) as integer '/ _
+		( _
+			@FB_RTL_FILECLOSEALL, NULL, _
+			FB_DATATYPE_INTEGER, FB_USE_FUNCMODE_FBCALL, _
+			NULL, FB_RTL_OPT_NONE, _
+			0/', _
+	 		{ _
+	 			( _
+					FB_DATATYPE_INTEGER, FB_PARAMMODE_BYVAL, FALSE _
+	 			) _
+	 		}'/ _
+		), _
 		/' fb_FilePut ( byval filenum as integer, byval offset as uinteger,
 						value as any, byval valuelen as integer ) as integer '/ _
 		( _
@@ -1509,12 +1521,16 @@
 	function = NULL
 
 	''
-    proc = astNewCALL( PROCLOOKUP( FILECLOSE ) )
+	if( filenum <> NULL ) then
+		proc = astNewCALL( PROCLOOKUP( FILECLOSE ) )
 
-    '' byval filenum as integer
-    if( astNewARG( proc, filenum ) = NULL ) then
- 		exit function
- 	end if
+		'' byval filenum as integer
+		if( astNewARG( proc, filenum ) = NULL ) then
+			exit function
+		end if
+	else
+		proc = astNewCALL( PROCLOOKUP( FILECLOSEALL) )
+	end if
 
     ''
     if( isfunc = FALSE ) then
Index: rtlib/libfb_file_close.c
===================================================================
--- rtlib/libfb_file_close.c	(revision 5577)
+++ rtlib/libfb_file_close.c	(working copy)
@@ -70,11 +70,19 @@
 /*:::::*/
 FBCALL int fb_FileClose( int fnum )
 {
-	/* QB quirk: CLOSE w/o arguments closes all files */
+	/* QB quirk: CLOSE #0 has no effect */
 	if( fnum == 0 ) {
-		fb_FileReset( );
+		/*fb_FileReset( );*/
 		return fb_ErrorSetNum( FB_RTERROR_OK );
 	}
-    return fb_FileCloseEx( FB_FILE_TO_HANDLE(fnum) );
+	return fb_FileCloseEx( FB_FILE_TO_HANDLE(fnum) );
 }
 
+/*:::::*/
+FBCALL int fb_FileCloseAll( )
+{
+	/* QB quirk: CLOSE w/o arguments closes all files */
+	fb_FileReset( );
+	return fb_ErrorSetNum( FB_RTERROR_OK );
+}
+
Now 'close' has its own function, and 'close #0' does nothing.
Although to be honest I'm thinking of changing the latter to an Illegal Function Call, so people don't just call 'close #0', thinking it's closed all files.

Regarding #lang "qb" compatibility, I'm thinking it's better to return an error than what QB does, and it's easy to circumvent by calling Close(0) as a function, e.g. 'if close(0) then end if', or even just 'if fn <> 0 then close #fn'.
I also think it's more trouble than it's worth to enable a dialect-specific quirk for something like this.
(PS. It's "jumps over the lazy dog":P)
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

Yes, Illegal Function Call is the more correct behavior here, IMO. Also, I see you used 0 to ..., maybe you should defer that and make a note in TODO about how we need to convert all those data chunks in the code to use that syntax. (Just for consistency's sake) Thoughts?
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

You're right, it would make sense to do them all at the same time. Ok, I'll just ++ it for this commit.
But I think it wouldn't be much harder to do a batch replace than to add a todo item?
I'll look into it.
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

Just make sure you run all the tests after that change... and preferably write tests for the new functionality in your current patch, pretty please? <3
jevans4949
Posts: 1188
Joined: May 08, 2006 21:58
Location: Crewe, England

Post by jevans4949 »

Could one use an "invalid" file number for the CLOSE ALL option? So existing source remains unchanged, but CLOSE #-99 is generated?

A quick look at help doesn't reveal the valid range of file numbers. Am I right in recalling that some negative file numbers are used for the default console and LPRINT?

Not that I use it myself, but just an idea.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

Well, my intention was to get rid of any special "close all" file numbers, and just create a new internal function for that. I think that's the cleanest way.

I could add #-99 or something, but people using #0 (if any) would still have to change their source code, and they might as well just change it to 'close' instead.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

OK, I've made the change in r5579:
http://fbc.svn.sf.net/viewvc/fbc?view=r ... ision=5579
Now Close closes all files as normal and Close #0 just gives an error.
And you're right cha0s, a unit test is a good idea, so I've included one.
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

I committed the var-len data array change.
Post Reply