💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › MiscInfo › Programmin… captured on 2024-07-09 at 04:05:45.
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
On 30 May 2002 05:10:41 -0700, ludditetech@mac.com (Simon Williams) wrote: >Thanks for that very informative response... just one further >question: > >Suppose the user is a jerk and enters an invalid or non-existent file >name, the resulting SYNTAX ERROR or OUT OF DATA with all the beeping >is quite unlovely... is there a way to keep the program running and >merely ask the user to re-enter the filename? Comments ~~~~~~~~ Sorry I didn't have time to reply to this earlier. Life has gotten insane for me this past week. I see Charlie has given you information about using ONERR GOTO. Unfortunately, in this case it doesn't quite work the way you are thinking. At least, not under ProDOS. I can't remember if DOS 3.3 acts the same way or not. When you use an open command under ProDOS, it will create the file if it doesn't exist so it will not return a "file not found" error. It will return errors if you try to read data out of the empty file, however, so you could trap that type of error in your ONERR GOTO routine. PEEK(222) will return 5 (END OF DATA) if you try reading data from an empty file or try reading data past then end of the file and that is what Charlie's program looks for in the ONERR routine. Just so you have it, here is a list of ProDOS errors that BASIC.SYSTEM can return: 0 - no error occurred 2 - RANGE ERROR 3 - NO DEVICE CONNECTED 4 - WRITE PROTECTED 5 - END OF DATA 6 - PATH NOT FOUND 7 - PATH NOT FOUND 8 - I/O ERROR 9 - DISK FULL 10 - FILE LOCKED 11 - INVALID PARAMETER 12 - NO BUFFERS AVAILABLE 13 - FILE TYPE MISMATCH 14 - PROGRAM TOO LARGE 15 - NOT DIRECT COMMAND 16 - SYNTAX ERROR 17 - DIRECTORY FULL 18 - FILE NOT OPEN 19 - DUPLICATE FILE NAME 20 - FILE BUSY 21 - FILE(S) STILL OPEN 22 - DIRECT COMMAND Another way is to check the directory for the file yourself but this can be a little involved but here my reader program from before with added code to handle it this way with a few semi-useful subroutines: New Sample Code ~~~~~~~~~~~~~~~ 5 HOME: D$ = CHR$(4) 10 PRINT D$;"PREFIX": INPUT PF$: DP$ = PF$ + "DATA/" 100 PRINT "Press <RETURN> to quit.": INPUT "View what file? "; X$ 105 IF X$ = "" THEN END 110 GOSUB 1005: IF A$ = "" THEN PRINT "That file doesn't exist.": GOTO 100 115 IF A% = 0 THEN PRINT "That file is empty.": GOTO 100 120 PRINT D$;"OPEN";DP$;X$ 125 PRINT D$;"READ";DP$;X$ 130 INPUT A, B, C 135 PRINT D$;"CLOSE" 140 PRINT X$;"'s three favourite numbers are: ";A;", ";B;" and ";C 145 END 1000 REM Checked to see if pathname in X$ exists and whether it contains data 1001 REM Returns filename in A$, filetype in FT$ and file length in A% 1002 REM If A$ = "" then filename in X$ was not found 1003 REM If A% = 0 then file is empty. 1004 REM For directories, A% will return a multiple of 512 1005 GOSUB 2005: GOSUB 3005: IF A% <> 0 THEN A$ = "": FT$ = "": A% = 0: RETURN 1010 OP$ = DP$: IF LEFT$(X$, 1) = "/" THEN OP$ = "" 1015 A% = LEN(X$) 1020 A$ = MID$(X$, A%, 1): IF A$ <> "/" AND A% > 1 THEN A% = A% - 1: GOTO 1020 1025 IF A$ = "/" THEN OP$ = OP$ + LEFT$(X$, A%): GOTO 1035 1030 A% = A% - 1 1035 OF$ = RIGHT$(X$, LEN(X$) - A%) 1040 PRINT D$;"OPEN";OP$;",TDIR" 1045 PRINT D$;"READ";OP$ 1050 INPUT A$: INPUT A$: INPUT A$ 1055 INPUT A$: IF MID$(A$, 2, LEN(OF$)) = OF$ OR A$ = "" THEN 1065 1060 GOTO 1055 1065 PRINT D$;"CLOSE";OP$ 1070 IF A$ = "" THEN A% = 0: RETURN 1075 FT$ = MID$(A$, 18, 3): A% = VAL(MID$(A$, 64, 8)): A$ = OF$ 1080 RETURN 2000 REM Convert string X$ to all upper case 2005 A$ = "": FOR A = 1 TO LEN(X$) 2010 A% = ASC(MID$(X$, A, 1)) 2015 IF A% > 96 AND A% < 123 THEN A$ = A$ + CHR$(A% - 32): GOTO 2025 2020 A$ = A$ + CHR$(A%) 2025 NEXT A 2030 X$ = A$: RETURN 3000 REM Check X$ to see if it contains a valid pathname 3001 REM Returns A% as 0 meaning pathname is valid 3002 REM Returns A% as 1 meaning first character is not a letter 3003 REM Returns A% as 2 meaning there is at least one invalid character in pathname 3004 REM Returns A% as 3 meaning there is a file/directory name is longer than 15 characters 3005 A$ = X$ 3010 IF LEFT$(A$, 1) = "/" THEN A$ = RIGHT$(A$, LEN(A$) - 1) 3015 IF LEFT$(A$, 1) < "A" OR LEFT$(A$, 1) > "Z" THEN A% = 1: RETURN 3020 B% = 1 3025 C$ = MID$(A$, B%, 1): IF C$ <> "/" AND C$ <> "." AND (C$ < "A" OR C$ > "Z") AND (C$ < "0" OR C$ > "9") THEN A% = 2: RETURN 3030 IF C$ <> "/" AND B% < LEN(A$) THEN B% = B% + 1: GOTO 3025 3035 IF B% > 16 THEN A% = 3: RETURN 3040 IF LEN(A$) > B% THEN A$ = RIGHT$(A$, LEN(A$) - B%): GOTO 3020 3045 A% = 0: RETURN New Sample Code Description ~~~~~~~~~~~~~~~~~~~~~~~~~~~ What's different in this version is, to start, I've renumbered everything and use line numbers incrementing by 5 instead of 10. The first block of code clears the screen and initializes variables. D$ is the CTRL-D we discussed before. I also do a ProDOS PREFIX command from within the program and the first input command after that will get the current ProDOS prefix. I then set the DataPath (DP$) to that prefix + the word "DATA/". The trailing slash is needed in this case as we will be adding other data to the end of it. The second block of code is almost the same as my last example. I've added an option to allow the user to quit by simply pressing return instead of entering data. Then in lines 110 and 115 I've added a call to a subroutine that will ensure the data typed in by the user is all in upper case, verify that the pathname we are looking for is valid and will then make sure it exists and has data in it. The rest is pretty much the same. One note, however, is that you could add a line 106 that says X$ = DP$ + X$ and then replace the ;DP$;X$ parts of lines 120 and 125 to just ;X$. The subroutine at line 1000 can take either a full or partial pathname so either way works. The next block I will skip over for now as the first thing is does is call the other two subroutines. The subroutine at line 2000 will make sure that all alphabetic characters in X$ are uppercase and will leave all other characters as they were. It simply does a loop looking at each character in X$ to see if it is between "a" and "z" (ASCII 97-122) and if it is, change it to the same character in the range "A" to "Z" (ASCII 65-90). It builds a new copy of the string as it goes the the loop and after the loop finishes, it sets X$ to that new copy which no longer has any lower case letters in it. The subroutine at line 3000 checks to see if the full or partial pathname in X$ is a valid ProDOS path. It first sets A$ to X$ so that the modifications I make as I parse the path don't end up changing the original value of X$. Line 3010 just checks to see if the first character is a slash ("/"). If it is we are dealing with a full pathname but I'm not worried about that so I strip that character off. Line 3015 checks to make sure the first character in the string is a letter as ProDOS filenames must start with a letter and if it isn't, it sets A% to 1 and returns. 3020 initializes a counter which keeps track of where in the file/directory name I am. Line 3025 checks to make sure that all subsequent characters are either a slash, period, upper case letter or number as these are the only valid characters in ProDOS filenames. If there is an invalid character in the file/directory name, A% gets set to 2 and the subroutine returns to the calling routine. Line 3030 just checks to see if we are at the end of a directory name (C$ = "/"), the end of a filename (B% = LEN(A$)) and if not, keeps looping until we are. Line 3035 then checks to see if the file/directory name is longer than 15 characters, the maximum length of ProDOS filenames and if so, sets A% to 3 and returns. Line 3040 checks to see if we've finished processing the entire string and if we haven't, goes back to 3020 to continue with the next file/directory name. 3045 just sets A% to 0 as everything came out okay and returns. Now, going back to the subroutine at 1000, it calls the routine at 2000 to convert X$ to upper case. It then calls the routine at 3000 and checks for an error from that routine and if it got one, it sets A$ and FT$ to null strings (""") and sets A% to 0 and returns. You could actually change this slightly so that you could pass the errors that you got from the routine at 3000 back to the calling routine so that they could put up a more informative reason why the program couldn't process the filename the user gave as at present, this program will just display the "That file doesn't exist" message if the problem is actually that the filename is invalid. If the pathname is okay, then we set OurPath (OP$) to the DataPath and then check to see if we are dealing with a full pathname or a partial pathname. If it is a full pathname (path starts with a slash), then we reset OurPath to a null string. 1015 initializes a counter to work our way backwards through X$ to find out the actual filename and to pull out any the full or partial directory path to that file. 1020 does the actual loop. 1025 pulls out any directory information from X$ if there is any there and 1035 pulls out the file name and puts it in OurFile (OF$). 1030 is needed because A% is out by one if X$ holds only a file name (no slashes in X$) so we need to adjust it. This line is skipped if there were slashes in X$. Line 1040 - 1065 open OurPath which is where OurFile should be stored and searches through the directory to make sure it is there. Note that line 1040 ends in ;",TDIR". This tells ProDOS the type of file we are opening, in this case, it is a directory. Line 1050 just pulls the top three lines of the file out which contain the pathname, a line with headers for each column in the directory listing and a blank line. Line 1055 reads in a line, checks to see if it is listing OurFile or whether it is another blank line and keeps looping if it isn't. If it is either of those it just to 1065 to close OurPath and then checks to see if we got the blank line that indicated the end of the directory. If we did, it sets A$ and FT$ to null strings and A% to 0 and returns as we didn't find the file. If we did find the file, line 1075 set FT$ to the file type, A% to the ENDFILE column which I believe almost always lists the length of the file in bytes and sets A$ to OurFile to let the calling routine know that we found that file. Wrap Up ~~~~~~~ After spending the time doing up this code and while writing up the description of how it works, I realize that some of this isn't necessary and that ProDOS and an ONERR handling would probably deal with it better. Also, this version could also benefit from using an ONERR routine to at least catch FILE TYPE MISMATCH errors. One way to avoid most of them is to use the FT$ that I added at the last minute to the subroutine in line 1000. Just change line 120 to: 120 PRINT D$:"OPEN";DP$;X$;",T";FT$ and that will solve that problem but it is still possible to run into a FILE TYPE MISMATCH in the the subroutine at line 1000 as it assumes that OP$ will be a valid directory and not pointing to some other file type. There are other problems that an ONERR routine would be able to catch in this sample too, the file type one is just a sample. That being said, I really enjoyed putting this together and I'm sure there is some interesting bits of code in there that others can benefit from so I'm posting it anyways. The section where I read through the directory is always useful. The only other thing to mention about it is after the second blank line (the one that tells my program we've reached the end of the directory listing) there is another line that lists the blocks free, blocks used and total blocks of the current volume which is sometimes handy to know. Finally, I will mention one other way that you could make sure users only pick valid files and that is to give them a menu of files that they are allowed to choose from. I wrote a program launcher called Selector v1.0 which you could use to look at how I did a menu system. I can't seem to locate the final version but I did find a BINscii encoded version of the beta I released and it is identical to the final if I remember correctly. It can be downloaded from: ftp://ground.ecn.uiowa.edu/2/apple8/Pgms/selector10beta.bsq This is a multi-column menu system, though, and you might prefer a single column version but there should be source code available to do this and I'm sure I've got some lying around here somewhere too. If not, I can always right a new one. :-) By the way, there may be typos and other problems (almost certainly grammatical errors :-) in my comments but it is just a tad too long for me to spend the time to proof read it all again. My apologies in advance for any mistakes. -- Jeff Blakeney - Dean of the Apple II University in the Apple II Community on Syndicomm.com CUT the obvious from my address if you want to e-mail me