Technical Bulletin Clipper [R] Summer '87, 2:00 am December 22, 1988 Copyright (c) 1988 Nantucket Corporation. All Rights Reserved. -------------------------------------------------------------------- Definitions: Anomalies - Items that behave in a manner other than that which was intended. Limitations/Constraints - Items that have parametric upper/lower limits. Documentation Errors - Items that were incorrectly documented in the product manual. Log Items - Items that should be noted and considered. Anomalies [ ] COPY FILE with Not Enough Disk-space COPY FILE will not issue an error message when there is not enough room on the diskette to complete the copy. Rather, the file is truncated to the number of bytes left on the disk. If there are zero bytes on the disk, no error is given and the file is copied with zero bytes. Example: * Test.prg is 10,240 bytes. * A: drive only has 5032 bytes free. COPY FILE Test.prg TO A:Test.prg * Test.prg on the A: drive will * only have 5032 bytes. Work-around: Before COPYing the file, make sure there is enough room on the disk for the file. Use DISKSPACE() to ascertain the bytes available. Use FOPEN() and FSEEK(handle,0,2) to find the length of the file. [ ] DBEDIT() and First Record Display When inside DBEDIT(), users calling user-defined functions which SET FILTER have discovered that the first displayed record remains, regardless of the new FILTER condition. The same has been found with SET DELETED. The problem relates to the way in which FILTER and DELETED work, not specifically to DBEDIT(). SET FILTER or SET DELETED are not active until the record pointer is moved. And if you GOTO RECORD, any FILTER condition is ignored. For performance reasons, DBEDIT() uses GOTO RECORD to move the first record to be displayed. Therefore, regardless of any FILTER, the first record will be displayed. Work-around: Any work-around depends on where you encounter the problem. In the case of DBEDIT(), to solve the problem you must force DBEDIT() to go backwards up the file beyond the first displayed record. This can be done by forcing + into the keyboard buffer. Remember, some records may now be filtered out, so you may not finish where you started. Also, with large files where a large number of records will be excluded by the condition, this may not be as quick as you would like. [ ] MEMOEDIT() and Ctrl-B MEMOEDIT() does not reformat properly when a Ctrl-B is entered after a word wrap occurred. Example: a = ' ' a = MEMOEDIT(a, 0, 0, 5, 9) Now enter "1234567 90". Then enter Ctrl-B and Ctrl-W. Results: Autumn '86: 1234567 90 Summer '87: 1234567i 90 (The i is a CHR(141)) [ ] MEMOEDIT() and Word Wrap After a save, the redisplay (in a wider window) of a string that traversed the full width of a smaller MEMOEDIT() window and wrapped onto the next line, shows a space break where the line automatically wrapped in the smaller window. Example: Narrow window: | | |qwertyuiopa| |sdfg | | | Wider window: | | |qwertyuiopa sdfg | | | | | CLEAR test = "" * Type: qwertyuiopasdfg * test = MEMOEDIT(test, 1, 1,; 10, 79, .T., "", 11) * * Then save. test = MEMOEDIT(test, 1, 1,; 10, 79, .T., "", 20) RETURN [ ] NDX.OBJ Performing a FIND on a numeric or date type data with NDX.OBJ returns Internal Error 21. Example: * Example 1. * Using NDX.OBJ, INDEX ON a numeric field, * then FIND a numeric. USE dbf INDEX ON numeric_field TO ndx1 FIND 0000000222 && Error 21. * Example 2. * Using NDX.OBJ, INDEX ON a * date field, then FIND * a date. INDEX ON date_field TO ndx1 FIND 19880503 && Error 21. Work-around: Use the SEEK command. [ ] TRANSFORM() with Index If TRANSFORM() is used as an INDEX expression, it has an interesting effect on other character fields. In the example, type some characters in , and as soon as you press Return, you will see the data from appear in . Example: * Dbf5.dbf: fn1,c,10 * fn2,c,10 USE dbf5 INDEX ON TRANSFORM(fn2, "999999.99") TO xxx GO 1 @ 5,0 SAY "Enter string " GET fn1 @ 6,0 SAY "Enter number " GET fn2 READ Work-around: * Point somewhere else in memory. mm1 = fn1 mm2 = fn2 @ 5,0 SAY "Enter string " GET mm1 @ 6,0 SAY "Enter number " GET mm2 READ Limitations and Constraints [ ] DEBUG The Debugger only allows a variable with a name of up to 10 characters. View and Assign also have this limitation. This inhibits the use of an array that has more than six or seven characters in its name. The brackets and element number take three to five characters of the 10 character limit. [ ] .Dbt Size Clipper's .dbt size limit is 16 megabytes. Documentation Errors [ ] ALTD(1) Invokes Debugger ALTD() with 1 as an argument is documented as enabling the debugger displaying the last screen. However, not only does it enable the debugger, it invokes the debugger. Example: ALTD(0) WAIT * Debugger is now disabled. ALTD(1) * Debugger is activated and invoked. Work-around: Amend page 6-34 of the manual. [ ] Line Length The compiler (CLIPPER.EXE) issues an error message for some command lines that are longer than 256 characters. These commands are: GET INDEX ON LOCATE FOR SET FILTER SET RELATION VALID Log Items [ ] Arrays with m-> Arrays should not be directly referenced using the m-> identifier. Otherwise, a "not an array" message results. Arrays are always variables; they can never be fields because the square brackets,[], that follow the array name can never be used in field names. The concept of m-> is only useful in preventing confusion between variables and fields with the same names. Such confusion does not generally occur, since most programers use variables which are not likely to also be used as field names. However, in systems which allow users to create their own .dbf files, it is possible that a conflict may occur. In these cases, it may be appropriate to use the m-> with variables (but never with arrays). Example: ? m->mcode[x] && Message "not an array." Work-around: ? mcode[x] && This works. [ ] Assembly Routines Some users have struggled with interfacing the Clipper Extend System functions and assembler. The following is a Microsoft convention of which you may not be aware. The convention for Assembler routines is that directives should assume that the direction flag is cleared before execution. So if you set the direction (STD), make sure you clear it (CLD) before calling any of Clipper's Extend System functions. [ ] CTOD() The CTOD() function hangs if the string is longer than 34 characters. Work-around: Reduce the size of the variable by using SUBSTR(). [ ] DO &var. In Autumn '86, DOing a macro whose contents contained a .prg extension was permissible (though it should not have been). In Summer '87, it produces an expression error. The dot terminator at the end makes no difference. Example: The following gives an expression error using Summer '87: var = "Test.prg" DO &var. [ ] IF...ELSEIF An IF...ELSEIF...ELSE structure causes a "type mismatch" error when the ELSEIF condition is omitted. This is a programing error which should be reported during compilation. The same error occurs on a CASE statement without a condition. Example: IF .F. && Type mismatch on this line. ? "False" ELSEIF && This line is missing the ** condition. ELSE ? "Oops" ENDIF Work-around: If you see a "type mismatch" error at runtime on a line containing an IF statement, check for a missing condition from the ELSEIF. [ ] INKEY() with Left Arrow Control-S and left arrow have the same INKEY() value: 19. Ctrl-S invokes the Clipper Break Handler to pause execution until another key is pressed. Therefore, if an INKEY() is issued after the KEYBOARD is stuffed with a CHR(19), the program pauses on that line until a key is pressed, and the return value is zero. Clipper checks for some keys while the program is running: Alt-D (debugger) Alt-C (cancel) Ctrl-S (pause) Ctrl-S (CHR(19)) is a left arrow in a wait state (GET, ACCEPT, etc.), but is a pause instruction in a non-wait state. INKEY() is not a wait state. There are other times when a pause may occur if CHR(19) is in the keyboard buffer. For instance, to prevent getting trapped in a continuous loop, the keyboard is checked at the top of a DO WHILE loop for an Alt-C incase you want to break out. Example: KEYBOARD CHR(19) && Left arrow. k = INKEY() && Program stops until ** key is hit. ? k && Always returns zero. [ ] KEYBOARD If you KEYBOARD a semicolon, it acts as a carriage return line feed pair. Anything after the semicolon is added to the next GET. This maintains consistency with SET FUNCTION command, provides third-party interpreter compatibility and allows programmatic GET block entry. Example: a = "This is test; of semicolon" b = " " c = " " KEYBOARD a @ 3,4 GET b @ 4,5 GET c READ ? b && "This is test" ? c && " of semicolon" [ ] MEMOEDIT() with Insert On Important: The following only occurs with a word (or string of continuous characters) that is longer than the width of the window you have defined. MEMOEDIT(), with the Insert key on, has a problem when a word wrap occurs. Every key stroke after word wrap is double spaced. However, if you enter spaces, one space and a line feed result. Examples: Example 1: a = MEMOEDIT(A,0,0,5,9) Enter 123456789 and CHR(23). Edit the memofield a second time with Insert key on. Then enter 00000. The results are: 000001234 5 6 7 8 9 Example 2: a = MEMOEDIT(A,0,0,5,9) Enter 123456789 and CHR(23). Edit the memofield a second time with the Insert key on. Then enter two spaces. The results are: 12345678 9 Enter space: 12345678 9 Enter space: 12345678 9 Work-around: Try to avoid defining a window width which is likely to be smaller than the largest single word in your text. [ ] Print_error() The Print_error() in Errorsys.prg has an asymmetrical I/O feature. The first thing Print_error() does is SET DEVICE TO SCREEN. However, it does not set it back to print. There is no way for Print_error() to know the initial DEVICE setting. After SETting DEVICE TO SCREEN to display the error message, it leaves it set to screen. If an application was printing via SET DEVICE TO PRINT, a retry sends the remaining output to the screen. The simple solution is to modify Print_error() on line 141 by including SET DEVICE TO PRINT. Another problem may arise when you try to BREAK from within Print_error(). When a BREAK is issued, control of the program will be transferred to the line after the END statement. But if you forget to redirect the output back to the screen, another Print_error() will occur, thus appearing as if a retry occurred instead of a BREAK. This is not an anomaly, but a problem in logic. Example: CLEAR SCREEN @ 2,0 SAY STR(seconds()) + " A" BEGIN SEQUENCE SET DEVICE TO PRINT SET PRINTER TO lpt1 SET PRINT on @ 1,1 SAY "Printing..." + CHR(13) END SEQUENCE QUIT FUNCTION Print_error PARAM name, line SET DEVICE TO SCREEN @ ROW()+1, 2 SAY STR(seconds()) BREAK RETURN .F. [ ] REPORT FORM with Long Heading If the last line in a report heading is filled with any characters and a report left margin is set, the report outputs a 32K block of blanks to the CRT, before displaying the actual data. (Sometimes it hangs.) Example: Use RL.EXE as your report-editor. Fill the last line in the heading (under Layout option) with some characters. Set the left margin to something other than the default of 8. Enter a field name and header in Field Description. Compile this code and run it: USE dbf && fn1,c,10 REPORT FORM Test * "Test" is the newly created Test.frm. Work-around: Enter 0 as the left margin and delete the last line in the page heading text. [ ] REPORT FORM with Semicolons When using semicolons in a report form field's contents, the first characters on the second line are omitted if they are spaces. Example: Fields contents: field1 + ";" + field2 Record One: field1 = "123456" field2 = " 23456" Results: 123456 23456 [ ] SELECT Alias If you selected a non-existent alias using Autumn '86, no error was given. You remained in the current work area. Summer '87 gives you a "type mismatch" error. Work-around: Autumn '86: SELECT test IF (ALIAS() != "TEST") ? "File is not open!" USE test ELSE ? "File is open" ENDIF Summer '87: IF SELECT("TEST") != 0 ? "File is open!" SELECT test ELSE ? "File is not open!" USE test ENDIF [ ] SELECT() The SELECT() function cannot tolerate ".dbf" at the tail of the alias if it receives one as an argument. Example: SELECT 7 USE states SELECT 3 ? SELECT("states") * Result: 7 ? SELECT("states.dbf") * Result: 0 --(End of Technical Bulletin)