Dec 172017
Aston Tate dBase IV Tech Notes for May 91. Useful information. | |||
---|---|---|---|
File Name | File Size | Zip Size | Zip Type |
TN0591_T.TXT | 72100 | 24427 | deflated |
Download File TN9105.ZIP Here
Contents of the TN0591_T.TXT file
1 Read Me First
This article is reprinted from a recent edition of TechNotes. Due to
the limitations of this media, certain graphic elements such as screen
shots, illustrations and some tables have been omitted. Where
possible, reference to such items has been deleted. As a result,
continuity may be compromised.
TechNotes is a monthly publication from the Ashton-Tate Software
Support Center. For subscription information, call 800-545-9364.
2 WHOOPS! Gilbert Catipon
Whoops!
Gilbert Catipon
Protection from the Hazards of Accidental Erasure.
Eventually everyone erases a file unintentionally and, oh the
frustration it can cause and it's probably happened to you. At that
point, you feel like you've been driving without insurance and have
caused insurmountable damage. It would be helpful to have some way of
protecting us from our impulsive selves. After all, this is the age
of protection. We need a tool to make our files a little more
secure.
While you could buy a "C" library to acquire a .BIN file capable of
making a file read-only and hidden, that would be going a bit
overboard when all you really want is to keep us or someone else from
looking at or destroying your files. Well in fact, just a few lines
of C code, and a quick UDF, and maybe you can save the money to buy
some frivilous screen saver software instead. Here is the C code:
/* ATTRIB.C -> ATTRIB.BIN to call _chmod() */
#include "..\include\dos.h"
#include "..\include\io.h"
int _argc = 0;
char **_argv = (char **) 0L;
int errno = 0;
void far main(void)
{
_DS = _CS; _argc = _CX; _argv = MK_FP(_ES,_DI);
if(_argc == 2)
if(*_argv[0] == ' ') /* GET file attributes */
*_argv[0] = (unsigned char) _chmod(_argv[1],0,0);
else /* SET file attributes */
*_argv[1] = (unsigned char) _chmod(_argv[0],1,(int) *_argv[1]);
errno = 0;
}
/* End of file ATTRIB.C */
The .BIN itself is quite straightforward. All it does is call the
_chmod function in Borland TurboC 2.0. If you have a different C
compiler, your directives may vary. The only tricky part is passing
the necessary parameters back and forth. This method of making a .BIN
file was discussed extensively in the Febuary '91 issue of
Technotes/dBASE IV, but if you can't find that issue then here are the
compiler directives you need to turn Attrib.C to Attrib.BIN.
tcc -G -O -a -d -Z -c -mc -zPDGROUP attrib
tlink /i /s /m attrib,attrib,,\turboc\lib\cc
exe2bin attrib
How do you use Attrib.BIN? Once we've loaded the .BIN to memory we
can call the .BIN directly by passing it a filename and an attribute,
such as in the command
CALL ATTRIB WITH "CUSTOMER.DBF",CHR(2)
We send a CHR(2) because the function _chmod() will use 2 as a
parameter to set the file attributes of Customer.DBF to Hidden. The
macros that are passed as parameters to _chmod() are in the file
#include. If there is an error, the .BIN returns a CHR(255)
(a space) otherwise it returns the value we sent it.
To use the CHMOD.PRG, enter the following at the dot prompt:
? chmod("customer.dbf","P")
This will make CUSTOMER.DBF a read-only AND hidden file and return the
letter "P". Note that if the file does not exist, then the UDF
returns the string "Customer.dbf?".
To get the file attributes, enter the command
? chmod("customer.dbf","?")
and you get a "P" again. To reset the file attributes, enter the
command:
? chmod("customer.dbf","U").
The syntax for this UDF is
CHMOD("","")
The possible attributes are:
readonly"R"read-only and hidden"P"
hidden"H"read-only and hidden and system"X"
system"S"no attributes (reset) "U"
archive"A"get file attributes"?"
Function: ChMod()
FUNCTION ChMod
PARAMETER fname, attrib
attrib = AT(UPPER(attrib),
"URHPSvvXlvvvvvvvdvvvvvvvvvvvvvvvA")
IF attrib = 0 && GET file attributes
attrib = SPACE(1)
CALL ATTRIB WITH attrib, fname
ELSE && SET file attributes
attrib = CHR(attrib - 1)
CALL ATTRIB WITH fname, attrib
ENDIF
RETURN IIF(ASC(Attrib) > 32, fname + [?], ;
SUBSTR("URHPSvvXLvvvvvvvDvvvvvvvvvvvvvvvA", ASC(attrib) + 1, 1))
3 NOTEIT.PRG
NoteIt.PRG
* Program ......: NoteIt.PRG
* Notes ........: Miscellaneous Notes/Telephone book
* Syntax .......: DO Noteit
************************************************************************
* Immediately suppress screen activity.
* For other settings see PROCEDURE: Envset
SET TALK OFF
* Initialize variables.
gc_currdir = SET("DIRECTORY")
lc_deleted = .F.
* Open database.
USE NoteIt ORDER mainline
IF RECCOUNT() = 0
? "NoteIt cannot run without at least one record in
Noteit.DBF"
RETURN
ENDIF
* Set up the environment.
DO Envset
DO NoteList
* Delete any marked records.
IF lc_deleted
DEFINE WINDOW chkit FROM 7,15 TO 9,65 DOUBLE COLOR
w+/n,w+/n,r/n
ACTIVATE WINDOW chkit
@ 0,5 SAY "Checking for/Removing Deleted Records" COLOR w+*/n
PACK
RELEASE WINDOW chkit
ENDIF
* Reset the environment.
SET DIRECTORY TO &gc_currdir
DO ResetEnv
RETURN
* EOP: NoteIt.PRG
************************************************************************
PROCEDURE Envset
* Notes ........: Setup the dBASE environment
* Store environment setttings.
sys_bell= SET("BELL")
sys_dbtrap= SET("DBTRAP")
sys_clock= SET("CLOCK")
sys_cursor= SET("CURSOR")
sys_delete= SET("DELETED")
sys_dev= SET("DEVELOPMENT")
sys_escape= SET("ESCAPE")
sys_exact= SET("EXACT")
sys_path= SET("PATH")
sys_safety= SET("SAFETY")
sys_score= SET("SCOREBOARD")
sys_status= SET("STATUS")
sys_talk= SET("TALK")
sys_type= SET("TYPEAHEAD")
* Set up environment.
SET BELL ON
SET BELL TO 330,2
SET DBTRAP OFF
SET CLOCK OFF
SET CURSOR OFF
SET DELETED OFF
SET DEVELOPMENT OFF
SET ESCAPE ON
SET EXACT OFF
SET PATH TO C:\NOTEIT
SET SAFETY ON
SET SCOREBOARD OFF
SET STATUS OFF
SET TYPEAHEAD TO 100
* Set up the colors.
sys_norm = COLOR("NORMAL")
SET COLOR OF NORMAL TO w+/n
* Save the settings to file, then release the memory
variables.
SET SAFETY OFF
SAVE ALL LIKE sys_* TO sysset
SET SAFETY ON
RELEASE ALL LIKE sys_*
RETURN
* EOP: EnvSet.PRG
************************************************************************
PROCEDURE ResetEnv
* Notes .....: Reset the user's environment.
* Restore the original system settings from memory.
RESTORE FROM sysset ADDITIVE
* Reset Environment
SET BELL &sys_bell
SET DBTRAP &sys_dbtrap
SET CLOCK &sys_clock
SET CURSOR &sys_cursor
SET DELETED &sys_delete
SET DEVELOPMENT &sys_dev
SET ESCAPE &sys_escape
SET EXACT &sys_exact
SET MESSAGE TO
SET PATH TO &sys_path
SET SAFETY &sys_safety
SET SCOREBOARD &sys_score
SET STATUS &sys_status
SET TALK &sys_talk
SET TYPEAHEAD TO sys_type
SET COLOR OF NORMAL TO &sys_norm
CLEAR
ON ERROR
ON KEY
* Erase the system environment memory file.
lc_temp = gc_currdir + IIF(TRIM(RIGHT(gc_currdir, 1)) = "\",;
"SysSet.MEM",
"\SysSet.MEM")
ERASE &lc_temp..
* Close all databases.
CLOSE DATABASES
RETURN
* EOP: ResetEnv.PRG
************************************************************************
PROCEDURE NoteList
* Notes ........: View/Edit anentry.
CLEAR
* Define the varibles.
lc_disp= SET("DISPLAY")
ln_line= 3
ln_maxline = IIF(AT("43", SET("DISPLAY")) > 0, 39, 20)
ll_adv = .T.
* Define the keys.
ON KEY LABEL Alt-A DO AddEntry
ON KEY LABEL Alt-C DO ComSrch
ON KEY LABEL Alt-D DO DelEntry
ON KEY LABEL Alt-E DO IndxEdit
ON KEY LABEL Alt-M DO MainSrch
ON KEY LABEL Alt-Q DO IndxSrch
ON KEY LABEL F1 DO MyHelp
* Define the windows.
DEFINE WINDOW viewit FROM 2,35 TO ln_maxline + 1, 76 COLOR
n/g,w+/g,w+/b
DEFINE WINDOW findit FROM 7, 5 TO ln_maxline - 9, 75 DOUBLE
DEFINE WINDOW myhelp FROM 1, 5 TO 23, 75 DOUBLE COLOR
w+/g,,w+/b
* Draw the screen.
@ 2, 2 TO ln_maxline + 1, 33 COLOR n/gr
@ 3, 3 FILL TO ln_maxline, 32 COLOR n/gr
@ 1, 8 SAY "Index Line"
@ 1,50 SAY "Comments"
* Get the records.
GO TOP
ln_recno = RECNO()
DO ReDraw
GO ln_recno
ln_line = 3
DO Litebar
DO WHILE LASTKEY() # 27
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press to Edit comments"
READ
DO CASE
CASE READKEY() = 15 .OR. READKEY() = 271
&&
@ 4,5 GET comments OPEN WINDOW viewit
MESSAGE;
"Press then to
save changes, then to abort"
SET CURSOR on
KEYBOARD CHR(29) CLEAR
READ
SET CURSOR off
CASE READKEY() = 5 .OR. READKEY() = 261&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP
IF EOF()
SKIP -1
ll_adv = .F.
ENDIF
ln_recno = RECNO()
IF ln_line = 20
SKIP -17
DO ReDraw
GO ln_recno
ll_adv = .F.
ENDIF
IF ll_adv
ln_line = IIF(ln_line =
ln_maxline, 3, ln_line + 1)
ELSE
ll_adv = .T.
ENDIF
CASE READKEY() = 4 .OR. READKEY() = 260&& Arrow>
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP -1
IF BOF()
ll_adv = .F.
ENDIF
ln_recno = RECNO()
IF ln_line = 3
DO ReDraw
GO ln_recno
ENDIF
IF ll_adv
ln_line = IIF(ln_line = 3,
ln_line, ln_line - 1)
ELSE
ll_adv = .T.
ENDIF
CASE READKEY() = 34 .OR. READKEY() = 290
&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
GO TOP
ln_line = 3
DO ReDraw
GO TOP
CASE READKEY() = 35 .OR. READKEY() = 291
&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
GO BOTTOM
SKIP -17
ln_recno = RECNO()
ln_line = 20
DO ReDraw
CASE READKEY() = 6 .OR. READKEY() = 262 &&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP -17
ln_line = 3
ln_recno = RECNO()
DO ReDraw
GO ln_recno
CASE READKEY() = 7 .OR. READKEY() = 263 &&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP 17
ln_line = 3
IF EOF()
SKIP -1
ENDIF
ln_recno = RECNO()
DO ReDraw
GO ln_recno
CASE READKEY() = 12&&
EXIT
ENDCASE
IF LASTKEY() = 27 .OR. LASTKEY() = 13
KEYBOARD CHR(19)
ENDIF
IF READKEY() # 15 .OR. READKEY() # 271
DO LiteBar
ELSE
KEYBOARD CHR(19)
ENDIF
ENDDO
* Release key assignments.
ON KEY
RETURN
* EOP: NoteList.PRG
************************************************************************
PROCEDURE ReDraw
* Notes ........: Draw the entries on the screen.
PRIVATE ln_line
ln_line = 3
DO WHILE ln_line <= ln_maxline .AND. .NOT. EOF()
* If the record is marked for deletion, put an * next
to it.
IF DELETED()
@ ln_line, 1 SAY "*" COLOR w+*/n
ELSE
@ ln_line, 1 SAY " " COLOR n/n
ENDIF
@ ln_line,3 SAY mainlineCOLOR w+/gr+
ln_line = ln_line + 1
SKIP
IF EOF()
SKIP - 1
DO WHILE ln_line <= ln_maxline
@ ln_line, 1 SAY " " COLOR n/n
@ ln_line, 3 SAY SPACE(30) COLOR
w+/gr+
ln_line = ln_line + 1
ENDDO
ENDIF
ENDDO
RETURN
* EOP: ReDraw.PRG
************************************************************************
PROCEDURE LiteBar
* Notes ........: Draw the LiteBar
@ ln_line, 3 SAY mainline COLOR n/g
RETURN
* EOP: LiteBar.PRG
************************************************************************
PROCEDURE AddEntry
* Notes ........: Add an entry
lc_addme = SPACE(30)
ACTIVATE SCREEN
@ ln_line, 3 SAY mainline COLOR w+/gr+
@ ln_maxline + 3, 3 GET lc_addme MESSAGE;
"Enter the new entry, or press to abort";
COLOR ,w+/r
SET CURSOR on
READ
SET CURSOR off
IF READKEY()< 256&& All numbers < 256 are non-updated
codes.
* Abort the add.
@ ln_maxline + 3, 3 CLEAR TO ln_maxline + 3, 75
DO LiteBar
ELSE
* Add the record.
APPEND BLANK
REPLACE mainline WITH lc_addme
@ ln_maxline + 3, 3 SAY "NEW ENTRY: " + mainline COLOR
g/n
@ 4, 5 GET comments OPEN WINDOW viewit MESSAGE;
"Press then to save changes,
then to abort"
KEYBOARD CHR(29) CLEAR
SET CURSOR on
READ
SET CURSOR off
@ ln_maxline + 3, 3 CLEAR TO ln_maxline + 3, 75
GO TOP
DO ReDraw
GO TOP
ln_line = 3
DO LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press then to save changes,
then to abort"
KEYBOARD CHR(24) CLEAR
READ
ENDIF
RETURN
* EOP: AddEntry.PRG
************************************************************************
PROCEDURE DelEntry
* Notes ......: Delete or undelete an entry.
* Display an asterisk if the record is marked for deletion,
or clear the mark.
IF DELETED()
RECALL
@ ln_line, 1 SAY " " COLOR n/n
ELSE
DELETE
lc_deleted = .T.
@ ln_line, 1 SAY "*" COLOR w+*/n
ENDIF
RETURN
* EOP: DelEntry.PRG
************************************************************************
PROCEDURE IndxEdit
* Notes ........: Edit the index line.
ACTIVATE SCREEN
@ ln_line, 3 GET mainline MESSAGE "Edit the Index line (
aborts)";
COLOR ,w+/bg+
SET CURSOR on
READ
SET CURSOR off
IF READKEY() >= 256&& Field was changed
ln_recno = RECNO()
DO ReDraw
GO ln_recno
ln_line = 3
ENDIF
DO LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press to Edit comments"
KEYBOARD CHR(24) CLEAR
READ
RETURN
* EOP: IndxEdit.PRG
************************************************************************
PROCEDURE IndxSrch
* Notes .....: Search for an INDEXED entry in Mainline.
* Initialize variables.
lc_srch = SPACE(30)
ACTIVATE WINDOW findit
CLEAR
@ 0,19 SAY "Search for (Quick Search)"
@ 1,17 GET lc_srch MESSAGE;
"Enter the value to search for ( aborts)"
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
SEEK UPPER(TRIM(lc_srch))
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press to Edit comments"
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: IndxSrch.PRG
************************************************************************
PROCEDURE ComSrch
* Notes ........: Search for an entry in comments.
* Initialize variables.
lc_srch = SPACE(60)
ACTIVATE WINDOW findit
CLEAR
@ 0,17 SAY "Search for (Comments Search)"
@ 1, 2 GET lc_srch MESSAGE;
"Enter the value to search the comments for (
aborts)"
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
LOCATE FOR AT(TRIM(lc_srch), comments) > 0
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
DO Again
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press to Edit comments"
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: ComSrch.PRG
************************************************************************
PROCEDURE MainSrch
* Notes ........: Content search for an entry in Mainline.
* Initialize variables.
lc_srch = SPACE(30)
ACTIVATE WINDOW findit
CLEAR
@ 0,17 SAY "Search for (Index Line Search)"
@ 1,17 GET lc_srch MESSAGE;
"Enter the value to search the index line for (
aborts)"
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
LOCATE FOR AT(TRIM(lc_srch), mainline) > 0
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
DO LiteBar
DO Again
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press to Edit comments"
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: MainSrch.PRG
************************************************************************
PROCEDURE Again
* Notes ........: Continue a search (ComSrch and MainSrch
only).
* Disable the ON KEYs
ln_type = SET("TYPEAHEAD")
SET TYPEAHEAD TO 0
lc_again = "Yes"
DO WHILE .T.
@ ln_maxline + 3, 5 SAY "Search Again? " GET lc_again
PICTURE "@M Yes,No";
MESSAGE "Press to toggle choice";
COLOR w+/n,w+/g
READ
@ ln_maxline + 3, 0 CLEAR TO ln_maxline + 3, 70
IF lc_again = "Yes"
CONTINUE
IF FOUND()
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
@ 4,5 GET comments OPEN WINDOW viewit
MESSAGE;
"Press to Edit
comments"
CLEAR GETS
ELSE
?? CHR(7) + CHR(7)
@ ln_maxline + 3, 0 CLEAR TO
ln_maxline + 4, 77
@ ln_maxline + 3, 20 SAY "Entry Not
Found, Press any key" COLOR w+*
READ
@ ln_maxline + 3, 0 CLEAR TO
ln_maxline + 3, 70
GO ln_recno
EXIT
ENDIF
ELSE
CLEAR GETS
EXIT
ENDIF
ENDDO
SET TYPEAHEAD TO ln_type
RETURN
* EOP: Again.PRG
************************************************************************
PROCEDURE MyHelp
* Notes ........: Help system.
* If you want to add your own help pages, simply add another
procedure
* called Page (where is the next number), and
increase ln_maxpg.
ln_page= 1
ln_maxpg = 3
lc_temp= " "
ACTIVATE WINDOW myhelp
DO WHILE .T.
CLEAR
lc_page = "Page" + LTRIM(STR(ln_page))
DO &lc_page
@ 0, 0 GET lc_temp MESSAGE;
"PgUp - previous screen, PgDn - Next Screen, ESC -
Exit";
COLOR ,g/g
READ
IF READKEY() = 12
EXIT
ENDIF
ln_page = IIF(READKEY() = 7 .OR. READKEY() = 263,;
IIF(ln_page = ln_maxpg, ln_page, ln_page +
1),;
IIF(READKEY() = 6 .OR. READKEY() = 262,;
IIF(ln_page = 1, ln_page, ln_page - 1),
ln_page))
ENDDO
DEACTIVATE WINDOW myhelp
SET MESSAGE TO
RETURN
******************************************************
PROCEDURE Page1
* Help Page #1
TEXT
Available keys:
- Exit.
- Edit comments.
- Down one record.
- Up one record.
- Down one page.
- Up one page.
- Bottom of File.
- Top of File.
ENDTEXT
@ 20, 68 SAY CHR(25) COLOR w+*/r
RETURN
* EOP: Page1
****************************************************************
PROCEDURE Page2
TEXT
Available keys (cont'd):
- Add and entry.
- Search the comments field
- Mark/Unmark an entry for deletion.
(This
will put a blinking "*" next to the record
that
is marked for deletion. The record will be
physically deleted when you quit the program.
If
you press while on a record that has a
blinking "*", the record will be unmarked for
deletion.
- Edit the Index line
- Search the contents of the Index
line.
ENDTEXT
@ 0, 68 SAY CHR(24) COLOR w+*/r
@ 20, 68 SAY CHR(25) COLOR w+*/r
RETURN
* EOP: Page2
*********************************************************************
PROCEDURE Page3
TEXT
Available keys (cont'd):
- Do an index search of the index
line. This
search is the quickest search, however it will
search the beginning of the Index line. For
example, if you had a line:
Doe, John
You
could not enter "John" to find it, you would
have
to enter characters starting from the
beginning of the Index line (i.e.: "Do". To find
"John", you would need to use.
ENDTEXT
@ 0, 68 SAY CHR(24) COLOR w+*/r
RETURN
* EOP: Page3
****************************************************************************
FUNCTION Color
* Format:
* Color( )
* = NORMAL, HIGHLIGHT, MESSAGES, TITLES, BOX,
INFORMATION, FIELDS
*or a variable with all colors
stored in it.
*Ver: dBASE 1.1
*
* The Color() function either returns or sets colors returned
with the
* SET("attribute") setting.
* If is a color string then null is returned otherwise
the color
* setting is returned for one of dBASE IV's color options.
*
* See Also: SET("attribute")
*
*
PARAMETERS set_color
PRIVATE color_num, color_str, cnt
set_color = UPPER(set_color)
IF set_color = "COLOR"
*- Return standard, enhanced, border colors only
RETURN SUBSTR(SET("attr"),1, AT(" &", SET("attr")))
ENDIF
*- Declare array to parse color options from SET("attr")
PRIVATE color_
DECLARE color_[8]
*- Determine if user is restoring colors vs. saving colors
IF " &" $ set_color
color_str = ","+set_color+","
&& Restore color attributes
ELSE
color_str = ","+SET("ATTRIBUTE")+","
&& Save color attributes
ENDIF
* Stuff array with individual color setting
color_str = STUFF(color_str, AT(" &", color_str), 4, ",")
cnt = 1
DO WHILE cnt <= 8
color_str = SUBSTR(color_str, AT(",", color_str ) +1 )
color_[cnt] = SUBSTR(color_str, 1, AT(",", color_str )
- 1)
cnt = cnt + 1
ENDDO
IF " &" $ set_color
* Set color back.
SET COLOR TO
,,&color_[3]. && Border color.
SET COLOR OF NORMAL TO &color_[1].
SET COLOR OF HIGHLIGHT TO &color_[2].
SET COLOR OF MESSAGES TO &color_[4].
SET COLOR OF TITLES TO &color_[5].
SET COLOR OF BOX TO &color_[6].
SET COLOR OF INFORMATION TO &color_[7].
SET COLOR OF FIELDS TO &color_[8].
ELSE
* Return color string requested.
DO CASE
CASE set_color $ "NORMAL"
color_num =1
CASE set_color $ "HIGHLIGHT"
color_num =2
CASE set_color $ "BORDER"
color_num =3
CASE set_color $ "MESSAGES"
color_num =4
CASE set_color $ "TITLES"
color_num =5
CASE set_color $ "BOX"
color_num =6
CASE set_color $ "INFORMATION"
color_num =7
CASE set_color $ "FIELDS"
color_num =8
ENDCASE
ENDIF
RETURN IIF(" &" $ set_color, "", color_[color_num])
* EOP: Color
4 The Era of Protection Joe Stolz
The Era of Protection
Joe Stolz
Perhaps you don't want to think about it but someone could be looking
over your data files while you are out of the office! Perhaps you
don't have any data that is truly confidential but it's not hard to
imagine the sort of information that is best not seen by others. If
you are a manager, or you work in Payroll, you will need to prevent
others from seeing the private information of other individuals.
After all, if Mr. Prehensile were to discover that Miss Halcyon was
making more than he, it could cause a major downswing in office
rapport.
Perhaps you want to restrict others from entering dBASE IV
altogether. That way, all of your data files will be secure.
What if you work on a Local Area Network installation and you share
files with others? Everyone must access the same file but you alone
should see or modify certain fields in the file structure.
Does this sound like a task too big for dBASE IV? It really is simple
when you use Protect!
Protect is not a new feature of dBASE IV. It's been around since
dBASE III PLUS. However, in dBASE III PLUS, you had to have a
multi-user installation (even if it was on your single-user machine)
to get Protect to work. Further, you had to exit to DOS to run the
Protect program. It was a separate utility.
Now Protect is one of the bevy of utilities available through the
Tools menu in the Control Center. This new, easier access to Protect
makes it a good topic for review in the context of dBASE IV. There
are also some new features that were added to make Protect easier to
use.
How It Works
The dBASE IV Protect system is based on login names and passwords like
most computer security systems. The dBASE IV login screen is visible
only after you have set up a Protect system. Once you have it set up,
every time you enter dBASE IV you will first see a login screen
requesting a User name, a Password and a Group name. The User name
and Password are used to restrict access to dBASE IV to only those
that have a user profile within Protect. A Group name is extremely
important as it serves as a way to designate a group of database files
that require access by a particular subset of users. If you are not
part of the group, you will not be able to open the file.
Database files and their contained data, including their memo field
contents, are physically encrypted through Protect. Once encrypted,
the entire file is unreadable through DOS or other utility programs,
except through dBASE IV as one of the users of the Group that has been
designated to have access to it!
Protect offers a third level of security too. You can assign priority
levels to your users and restrict access to specific fields within the
database structure. This restricted access can span the range from
full reading and edit capability of all fields in the file, to the
ability to see only the data in the field but not to change its
content, to the ability to make a field appear as if it didn't even
exist in the file structure!
All this is possible through Protect. When you run Protect it creates
a file called DBSYSTEM.DB. If you are on a single user system, you
find this file in the dBASE IV directory. If you are on a LAN system,
the file is created in the DBNETCTL.300 directory which also contains
the login access files. This file contains the user profiles and is
itself encrypted using the Protect system administrator I.D. You will
want a copy of this file available in case it becomes inadvertently
deleted.
A new feature within Protect is the Reports menu which can produce a
hard copy listing of your users and their passwords, allowing for full
documentation of your Protect system.
Assigning User Groups
As the manual goes into some detail explaining the features of
Protect, it would be more useful to concentrate on some aspects of
Protect that are particularly confusing to some people.
The first thing that you will end up doing in Protect is creating user
profiles. Each user must be assigned to a group (assigning names and
passwords is pretty straight forward). As noted in Using the Menu
System, pages 14-23, a user can belong to several groups but a file
can be encrypted for only one group to access. If a user needs to
access files from another group of which he is a member, he must login
again and therefore maintain two full user profiles. There is nothing
wrong with having the same name and password for a particular user,
and to have two different group names assigned for each of the two
profiles. The uniqueness of each user profile is in the combination
of the three required key fields: Name, Password and Group name.
The fact that a file can be encrypted for only one group is crucial to
your understanding of how dBASE IV limits access to encrypted
(protected) files. If you have set up a file to be accessed by one
set of users, a new user needing to access that file must belong to
the same group.
The group name is the key to a user being able to access the contents
of a file. To each member of the correct group, the file seems as if
it contains plain English names and data. Anyone outside of the group
who attempts to access an encrypted file will be stopped by the
message, "File is encrypted" when they try to USE the file.
Attempting to view the file from DOS will lead to the same
frustration. Whereas the initial entry to dBASE IV is blocked by the
need for a user profile, even if you find a way to circumvent the
login, this second level of protection hides your data. That is to
say that even properly logged-in users can be prevented from seeing
data in a file that is encrypted for a different group.
User and File Access Levels
The next issue is one of user access levels. In dBASE IV, a level of
1 is the most powerful level. This translates to level one being the
least restrictive level. Level 8 is the most restrictive level. The
absolute values 1 through 8 have no real intrinsic meaning in and of
themselves. There may be no practical difference in your system
between level 7 and 8. Protect merely offers 8 levels of security
differences for systems whose security levels must be that complex.
It is up to the Protect system administrator to establish the
assignments of security equivalence. It is then where the eight
levels can take on a more precise security level based upon what
levels are higher or lower than any particular level.
Initially, all users are assigned a default level of 1 (highest
access) and all file privileges are assigned a level of 8 (least
restrictive). A level 1 user can perform all functions: read, update,
extend and delete since the highest level of restriction that can be
assigned to each of these operations is one. A user of level five,
however, can perform operations assigned to level five through 8 but
not those assigned to level four down to one.
Reviewing the file privileges again, they are, in order of increasing
importance, Read, Update, Extend and Delete. To give a simple
demonstration of the increasing power of these levels, let's say that
Read allows you to read the data only. No changes can be made to data
contained in the file. The Update privilege allows changes to be made
to existing data. Extend privileges provides for the addition of new
data in the form of new records added to the file. The Delete
privilege means the you can remove records from the file. This
increasingly important set of operations is what requires careful
assignment to the various levels of users in your database management
system.
Read access is the ability to simply see data in a file. This is the
lowest level of all file access privileges. You should realize that
if you have a user who must be prevented from even looking at the
contents of a file, that user really should not be included in the
group. If a user is part of the group that accesses a file, the
minimum ability he will usually have is to see the contents of the
file, though he may be prevented from altering the record contents.
Nonetheless, if you assign read access to level 7, your level 8 users
will be unable to do anything to the file.
In a different vein, assigning Read access to a number smaller (more
restrictive) than the level assigned to Update essentially nullifies
the read-only capability of your Read assignment and causes Update and
Read to be granted at the same level. This is okay if you understand
what you are doing, but for the sake of simplicity, you should assign
the four levels of file privileges in an increasing order to an ever
decreasing set of values.
The next level up is that of Update privileges. This is the ability
to change (or edit) the contents of a record. The next level is the
ability to Extend (or append to) the file. A common question raised
is whether a data entry clerk can have the ability to add new records,
but not to change the contents of pre-existing records. Since adding
records is really the same as editing a blank record (putting contents
into blank fields), the answer is no. However, to attain this kind of
restriction, data entry clerks can be given access to special data
entry files that will contain only new records and be denied access to
the main database, preventing the changing of data in that file. New
records they enter can later be appended to the master file.
Another, similar, question is whether a data entry clerk can be
restricted from viewing data but only allowed to enter new data. This
is even more impossible given what was discussed above. Remember, the
minimum privilege is to view a file (called read-only access). If a
user has a higher access right like Delete, the user would, out of
necessity, retain the rights to the lower, more fundamental access
privileges.
The highest available privilege is to Delete records. If you want to
visualize how all this fits together, let's try assigning some numbers
to the four privileges.
Say that Read is assigned level 6, Update is also assigned level 6,
Extend is assigned level 4 and Delete is assigned level 2. A level 8
user will not be able to use the file. The same is true for a user
who has a user access level of 7. A user with an access level of 6
will have both Update and Read privileges. In this example, there can
be no users that have read-only rights. Data entry must be done by
those with access rights of 4 and above. Deletion can be done only by
those with levels of 2 or 1.
Field Access Levels
The rights and restrictions discussed to this point apply on a full
file basis. That is, up to now we saw that we could totally restrict
a user from changing data in a record, or allow them the ability to
add records to a file. dBASE IV also offers the ability to apply an
even finer level of restrictions to a file, on a field by field basis.
Field privileges are not commonly assigned judging from my discussions
with users. I believe that this is due to the fact that the concept
of field privileges is three levels removed. Thinking about login
restriction, then file access privileges in relation to user access
levels, then to field level restrictions tends to boggle the mind.
It's a level of abstraction that is too much to deal with. Once
again, user access level is the basis of field privileges just as it
is with file privileges. Fields can have three levels of access
privileges, Full, Read-only, and None. "Full" translates to no
restrictions, "Read-only" restricts the user to only viewing the
contents of a field. "None" is remarkable in that fields designated
as such "don't exist" to users of that access level. It's remarkable
since not only can they not see the data, they can't even see the
field in the file's structure. DISPLAY STRUCTURE will not display
fields designated as None, as if the field didn't even exist. It's a
pretty tricky thing, a potential source of problems.
Field privileges are assigned for each User access level on a field by
field basis. That is, you establish which fields can be modified,
viewed only, or not visible at each User level. You might decide that
a user of level 8 should not see the Salary field in the Payroll
file. However, this same user might be the one who adds the basic
personal data to the other fields in the file, having then a mixture
of Full and None field privileges in the file.
Establishing field privileges this way might seem like an alternate
way to set up file level privileges since you can establish which
fields are visible and modifiable for all User access levels.
Actually, Field access privileges must work in conjunction with File
access privileges. Established File privileges will take precedence
over Field privileges. These in turn must work in conjunction with
User access levels.
Let's look again at our example of this concept. The data entry clerk
must have Extend ability (capability to APPEND to the file). As a
fine adjustment on this ability, you want to block the data entry
clerk from seeing the contents of the Salary field. This fine tuning
is what the Field access concept is all about. Note that for Human
Resource employees who do not make changes to the data at all and who
are designated as level 7 or 8 users (such as Read-only), assigning
Full field privileges to all fields WILL NOT override the Read-only
rights established on the file as a whole. That is, Field privileges
do not override File privileges.
This should help to alleviate some of the ambiguity surrounding the
use of field level restrictions. It is best used as a fine tuning
capability over the more "coarse" file level restrictions. Field
level access is usually used sparingly, don't try to over use it.
Encrypting the File
Once the File and Field levels are established and you are done, you
must exit from Protect. Of course, you will be Saving all changes
made during the Protect session.
One of the most common "problems" that new Protect users report is
that their new access scheme doesn't work at all. Without exception,
this can be attributed to a single cause. When a file is encrypted,
it is created as a new file. The original file is left untouched and
the newly encrypted file has the same name as the original file but
with a .CRP extension. Thus, when you use the .DBF, it is available
to any level of user. This is NOT the protected file. This is done
for your protection, but it certainly is confusing to the Protect
user! This simple fact is documented in Using the Menu System under
"Other Considerations" in Protect, on page 14-38. It's not really an
"other consideration," it's a crucial consideration if you ask me! At
any rate, to use your newly encrypted file you must rename it and give
it a .DBF extension. I'd suggest that you rename it to a new parent
file name and that you make a backup of both the original database and
the .CRP file to boot. Reports of the file access privileges and such
can only be made from files that have a .CRP file extension.
Another little known and little realized fact is that once a set of
privileges is established for a file and you exit Protect, the changes
are permanently recorded in the .CRP file and if you desire to modify
the access rights established you will have to start over again
establishing all rights to an unencrypted parent file! The .CRP file
cannot be modified once saved and only .dbf files can be used as the
basis of setting up new field privileges. For this reason, it is
imperative to print a report of the field and file level rights that
were established within Protect for each file. dBASE IV offers a
Reports menu for this purpose. The Report menu reads and reports on
.CRP files only. If you desire to modify any particular rights in
your file it will help to see graphically what rights were previously
established for the file. It certainly can save a lot of time and
grief in the event of a disaster.
Summary
The abilities of the dBASE IV PROTECT feature is a complicated area to
learn. Protecting your data is crucial in many database systems.
Your task is to understand what is going on in the dBASE IV Protect
system and to try to get the most out of it. Being forewarned about
the interrelations between the various aspects of Protect is your best
protection against frustration with the system. Rest assured that it
works and can be downright powerful once you get the hang of it. Give
it a whirl!
5 Sizing Up an Application Don Powells
Sizing Up an Application
Don L. Powells
What is an application? This is a very broad question. Can you
narrow it a bit? From a developer's perspective, what are the
components of a dBASE IV application? This is a fair question.
Before you go about developing an application it would seem a
reasonable prerequisite to know what an application is.
Seeing a completed application running as a whole, we sometimes forget
that there are discrete parts integrated under the surface, combined
to solve a problem or accomplish a task. This article describes the
parts or components that make up an application with the intent to
draw attention to many of those elements that go into making an
application a good one.
Setting the Environment
Most applications are affected by the environment in which they run.
It is very important to set the operating system environment so that
your application will run in it. Under DOS, for example, the
Config.SYS file must contain a large enough value for the FILES
variable to allow you to simultaneously open the maximum number of
required files. If your code depends upon the contents of the PATH or
other DOS environment variables, they must be set before program
execution.
Configuring the System
The dBASE IV environment must be configured, initially, upon
installation of your application. The dBASE IV SET commands can be
used to determine settings such as whether the clock will be on or
off, displaying 12 or 24 hour format. The SET commands also control
screen colors, the date and numeric format (especially important with
international applications), the currency format, the status of the
bell, and disk drive/directory search paths.
In conjunction with the SET commands, global variables are often used
to maintain system status information. These variables should be
saved to a .MEM file to be restored each time the application is run
so that the user does not have to reconfigure the system each time.
The SAVE TO command will store all or a portion of variables present
in your system. Unless the variables change regularly, it is not
necessary to SAVE the variables at the end of each session. The
RESTORE command will reset these variables in a new dBASE session and
will be necessary at each start-up. Neither of these processes is
automatic and must be set up in your start-up and exit routines.
To prevent the user from having to worry about what files are
necessary for the application to work, it is a good practice to
include an initialization routine that checks for the presence of the
needed files and creates them if they are not there. These files may
include data or index files, .MEM files as discussed above, memo
(.DBT) files, and any special files you may have created. The
initialization routine opens the files and sets the necessary
relations and filters, establishing your desired views of the data.
Displaying the User Interface
The user interface is the means by which the user communicates with
the application and, indirectly, the way the developer communicates
with the user. Some of the best rules for user interface design are
from the Apple User Interface Guidelines. I have paraphrased these
guidelines below.
General Principles of User Interface Design
Use concrete metaphors for computer processes that correspond
to the everyday world. A check-writing program, for example, may
display an image of a check on the screen as data is being entered.
Provide sensory feedback in response to the user's physical
actions so that they feel they are in charge. Make a noise, make a
visible change on the screen, electrically shock the user, do
something to let them know that you recognize their actions.
Allow the user to select from alternatives displayed on the
screen. Let them use recognition rather than recall. Most people
like multiple choice much better than fill-in-the-blank.
Be consistent within and across applications to ensure
ease-of-learning, and familiarity.
Make sure that what the user sees on the screen does not
differ significantly from the eventual output.
Let the user initiate and control all actions. Provide
warnings for risky activities but allow the action to proceed if
confirmed by the user.
Keep the user informed of the progress of operations with
immediate feedback which tells how long delays will last and why.
Forgive the users when they make mistakes by allowing their
actions to be reversible. Let them know when their actions are not
reversible. In retrospect, perhaps it's better not to shock the user
as was previously recommended.
Provide a conceptual sense of stability by maintaining a
number of familiar "landmarks" on the screen and avoid random changes
of environment.
Provide aesthetic integrity by making different things look
different on the screen and giving the user some control over the look
of their workplace.
A number of elements are used to build the user interface. Some of
the major user interface elements are briefly described below.
User Interface Elements
Menus are used to navigate through an application. Pull down
menus have become very popular because they allow the user to see what
functionality is available, where he is in the program, and the path
followed to get to there and back. Popup menus are great for
displaying pick-lists from which a single selection is made.
Horizontal bar menus prove useful when you need to display a limited
number of choices in a small space. Menus can be built using the
dBASE IV applications generator. Menus and pick-lists provide that
multiple-choice vs. fill-in-the-blank preference previously mentioned.
Windows allow you to collect and display related information
while separating it visually from unrelated items on the screen.
Dialog boxes are built from windows and used to ask users questions or
to pass along important information. Alert boxes are similar to
dialog boxes except they are commonly used to issue warnings. Check
boxes are commonly displayed in a window to let the user make multiple
simultaneous selections from a list. A table of data from a related
file or a body of text from a memo field may be displayed in a window
as well.
Data input forms let the user enter data on one or more full
screen pages of data entry blanks. Data input forms can be built
using the dBASE IV forms generator.
Data display forms display the data on one or more full screen
pages without the ability to edit or change the data. Data display
forms can also be built using the dBASE IV forms generator.
Progress indicators include odometers, blinking messages, and
thermometer-like bars that show the user how far a process has gone
and how far it has to go. Without progress indicators the user is
left to wonder whether the application has failed and locked up the
computer. Progress indicators help prevent inopportune rebooting
which can be a source of file loss and corruption.
Data Manipulation and Processing
Once data has been entered through the data input forms or some other
method, your application will process that data toward your desired
end. A description of some of the more common data manipulation and
processing functions follow.
Data Verification entails validating the type, format and
content of the data that is input. This process may be as simple as a
PICTURE clause or VALID expression in an @..GET statement or as
complex as a large user defined function.
Data Storage involves the transference of validated data from
memory or a temporary file to a more permanent master .DBF file. If
data storage is localized in one place, any special processing
required later can be added in one place.
Data Access functions are used to search for requested data
records by using indexed SEEKs, ad hoc LOCATEs, or sequential steps
through FILTERed .DBF files.
Data Retrieval is the opposite of data storage. It involves
the transference of data from a .DBF file to memory variables, an
array or a temporary file for display or editing.
Data Deletion allows you to remove unwanted records from your
system files. Localizing this process lets you put in safeguards
against accidental loss of important data. Some developers use the
PACK command but others just blank the record out to make their data
storage function more efficient and faster. On large files with many
indexes, PACKING may be less desirable than a blanking method.
However, the latter method requires additional programming efforts to
ignore these blank records in calculations and reports.
Unique Processing may be required for each application you
create. The processing for preventing duplication of data will most
probably occur before data storage, during a validation process.
However, it is also possible that duplicates are merely omitted from
the reporting or calculating process by filtering them out beforehand.
Data Output could be called reporting but involves outputting
the data to not only the printer, but to the screen, a file, or via
telecommunications. The output may take the form of a formatted
report, labels, invoices or other special forms, or specific file
types that other programs can read. Many of these forms of output may
be generated in dBASE IV via use of the report or label generators or
exporting commands such as COPY and EXPORT.
Context-sensitive Help
Nearly all commercial applications sold today provide
context-sensitive help and your user will probably expect no less from
you. As a minimum, the help system should provide the user with
general information about your application and aid when using the most
difficult portions of the application, however, there may be times
when you cannot predict what will be difficult for your users.
Therefore, you should make your help system flexible enough to absorb
additions, deletions and edits with relative ease. An advanced help
system allows the user to add his own help text to what you provide.
If the help is going to be substantially long, it is preferable to
include help information in a supplemental .DBF file, possibly using
memos rather than hard-coding the help into the program itself.
Maintaining the System
A group of activities fall within the realm of system maintenance. A
user may decide that one color scheme is no longer attractive and want
to change it. A different user takes over the machine and decides to
reconfigure the system totally to their desired state. Printers come
and go, replaced by newer models or substituted by inferior ones when
they malfunction. A conscientious developer should not make provision
for only one or two specific printers. You should also accommodate
customization after installation.
If the index files get corrupted by a power outage during a write, the
user should be able to recreate them from a menu option. Many times,
our technicians here will get a call from a dismayed user who can't
make a custom application work. Often times, it's only a matter of
rebuilding an index file, a function that won't normally take more
than a few seconds. But without proper instruction and a means for
recreating such files, hours of constructive time can be lost.
It makes sense for some applications to allow the user to create a
new, empty set of files. If the user wants to practice with dummy
data, new files are a great aid for helping them get started with the
real data. Only you should also provide for the erasure of these
files so that unneeded practice files do not build up in the user's
directory.
Speaking of size, if you anticipate that the size of the system files
will grow to unmanageable sizes filled with historical data, you may
want to implement an archive and restore system to allow the user to
put unneeded records in an archive file until later required.
Archiving may also speed up the processing of the application because
there are less records for your application to maintain.
Provide a means for your users to backup and restore their data as
part of a maintenance menu. This system can be a simple file copy to
a user specified drive, or using the DOS commands, or a complete
system that splits files across multiple floppies or other media if
the file is too large to fit on one. Of all the features of your
application, this one is of a critical nature. No insurance for
possible data corruption or erasure is an invitation for inevitable
disaster. As the architect of a system, you should provide the system
for easy and reliable backup and encourage your users to follow
through regularly.
Handling Errors
You should anticipate and prevent the occurrence of non-critical
errors. Make sure that only the proper data type can be placed in a
data entry field by using formatting and validation. Unfortunately,
there are some critical errors that cannot be easily prevented by
dBASE IV error trapping (such as backing up to an open disk drive or
printing to a printer that is off). In those cases you should try to
allow the user to recover or terminate gracefully under your
application's control.
An electronic error log should be automatically generated by your
error handler to help you to debug problems without having to depend
upon the user to describe them. It is easier to handle errors if you
group them into categories like disk errors, print errors, memory
errors, and so on. You can then concentrate on handling just that one
category of errors at a time instead of trying to accommodate every
possible error in one giant CASE construct. Again, if your system is
vast and the error messages numerous, consider placing the errors and
their error numbers in a database that can be called by your program
when an error occurs.
Technical and User Documentation
The comments in the source code files contain much of the technical
information needed to maintain the application, but often, more
documentation than that is needed to maintain an application. It is
helpful to have a description of the overall application and how all
the pieces fit together. A code analysis utility could prove helpful
in creating flow charts, variable usage tables, and more. Other
utilities can provide "screen-shots" of different screens that appear
in your application. The visual association can be helpful in
increasing the understanding of how to operate your application.
User documentation is a combination of what is provided on-line and in
print. As a minimum, the user should be able to understand how to get
started using your application. Ideally, the user would never have to
call you with a problem that could not be solved by using the program
or its documentation. Then again, where would our technical support
department be if that were true.
Miscellany
Additional functions may be added to an application to implement
networking or password protection. Transaction processing may become
necessary in a transaction-oriented system like accounting. If your
application requires some functionality outside of what dBASE IV can
handle, you may need to include modules written in C or Assembly
language to handle those tasks. The pages of TechNotes and the
Ashton-Tate BBS are excellent sources for such routines.
Summary
So, what is an application? It is a collection of discrete parts
integrated to solve a problem or accomplish a given task. Each part
has a purpose and the absence of even one part increases the
likelihood that your application will not reliably suit those who use
it. The parts that have been discussed here are:
Setting the Environment
Configuring the System
Displaying the User Interface
Data Manipulation and Processing
Context-sensitive Help
Maintaining the System
Handling Errors
Miscellany
Technical and User Documentation
When you conscientiously combine these parts and others you find
necessary, you get an application that provides the users with a more
efficient way of processing data and you with a minimum of headaches
(from disgruntled and perplexed users calling for technical support)
and a maximum amount of cashflow (from lots of orders and/or a raise).
6 Etc.
Etc.
Checking For Directory Existence
At some point during the course of application building, it often is
necessary to verify the existence of critical files needed for proper
program operation. This is easily accomplished through use of the
FILE() function. A program segment may look something like this:
IF .NOT. FILE("MyApp.DBF")
.
.
*Produce error message for missing file "MyApp.DBF"
.
.
ENDIF
*Continue normal processing here...
Checking for directory (or drive) existence can be accomplished in a
similar manner. Ironically, the FILE() function is still the
function to use. But since the FILE() function needs a file name
we'll have to trick it by using a DOS device name such as NUL, CON,
PRN, etc. This is possible since DOS allows you to open a device the
same way you open a file! NUL is the preferred device name to use
since it's not likely to interfere with an operation the way opening
CON or PRN may. The following example shows how to accomplish this...
IF .NOT. FILE("\MyAppDir\NUL")&& or FILE("B:\NUL") for drive
.
.
*Produce error message for missing directory "MyAppDir"
.
.
ENDIF
*Continue normal processing here...
Setting Natural Order in the Applications Generator
When using the dBASE IV Applications Generator, the Override assigned
database or view option allows you to change the active database and
its order. However, changing the order of the file to Natural order
(no index) once a particular order is in place, is not clearly
documented. This can be accomplished by entering a left and right
bracket ([]) or two single quotes ('') in the following areas:
In the Set index order option under the Item menu.
Under the ORDER option of the Override assigned database or
view option of either the Menu or Item menu.
This has the effect in the resultant dBASE code of a SET ORDER TO
command which resets the file to natural order.
Invoking Print Screen from a Program
Since the Print Screen or Shift-Print Screen key has a special use on
PC-compatible computers, it does not return a value to dBASE IV. This
makes it impossible to invoke a print screen through the use of the
KEYBOARD command. Fortunately, printing the screen can be
accomplished through use of a very small .BIN file which is relatively
easy to create. Either of the following two measures will create the
file Prntscrn.BIN.
At the DOS prompt type COPY CON Prntscrn.BIN and press Return. Then,
while holding down the Alt key, enter the numbers 205, 5 and 203 on
the numeric keypad, releasing the Alt key after each number. Lastly,
press the F6 key and then Return. This will close and save the file.
The second method for creating this .BIN file must be invoked from the
dot prompt or in a program. The dBASE code follows.
SET PRINTER OFF
SET PRINTER TO FILE Prntscrn.BIN
??? "{205}{5}{203}"
SET PRINTER TO
Having created the file, issue the command LOAD Prntscrn.
Thereafter, to print the screen, issue the command CALL Prntscrn from
your program:
Seeing Your True Colors
While it's true that there is now a way to use the SET() function to
return the values of your current color settings, it doesn't really
show you what these settings are or what they look like.
It is not hard to devise a small routine that shows the eight
different color settings in the actual colors (on a color monitor of
course). However, when the colors for foreground and background are
the same, such as N/N (or black on black), there may be no indication
on screen if your background color is that same color. t
* CSHOW.PRG
PRIVATE showcolor, cstring, comma, talkwas, cursorwas
ON ESCAPE KEYBOARD CHR(0)
IF SET("TALK") = "ON"
SET TALK OFF
talkwas = .T.
ELSE
talkwas = .F.
ENDIF
cursorwas = SET("CURSOR") = "ON"
SET CURSOR OFF
DEFINE WINDOW colorshow FROM 5, 10 TO 16, 46 220, 223, 219, 219, 220,
220, 223, 223 &&ASCII box characters
cstring = STUFF(SET("ATTR"), AT(" &" + "& ", SET("ATTR")), 4, ",")+","
ACTIVATE WINDOW colorshow
comma = 0
showcolor = ""
cntr = 1
*--note final comma in following string
pstring =
"normal,highlight,border,messages,titles,box,information,fields,"
DO WHILE cntr < 9
@ cntr, 1 SAY "Color of " +left(pstring, AT(",", pstring) - 1) +
" is"
DO Parse
@ cntr, 26 SAY showcolor COLOR &showcolor
cntr = cntr + 1
ENDDO
comma = INKEY(0)
DEACTIVATE WINDOW colorshow
RELEASE WINDOW colorshow
IF talkwas
SET TALK ON
ENDIF
IF cursorwas
SET CURSOR ON
ENDIF
ON ESCAPE
RETURN
PROCEDURE Parse
*-- parse attribute string
comma = AT(",", cstring)
showcolor = LEFT(cstring, comma - 1)
cstring = SUBSTR(cstring, comma + 1)
*-- parse pstring
pstring = SUBSTR(pstring, AT(",", pstring) + 1)
RETURN
7 Digital Notepad Mike Dean/George McMullen]
The Digital Notepad
Mike Dean and George McMullen
As we rely more and more on doing various tasks on our computer,
several utility-type programs have popped up on the software market.
For instance, wouldn't it be convenient to have a utility that would
popup a window for you to jot down notes in, record an address, a
telephone number or a reminder about some weekly activity? Well I
thought so, so I wrote one and called it NoteIt. NoteIt is based on a
database with 2 fields: an indexing field and a memo field. Use
NoteIt for a address book. Put the name of the individual or
company, and then contact names, address, phone numbers and any
comments in the memo field. Since it is a memo field, you can enter a
great deal of information.
You might be wondering how do you retrieve all of these random notes
of information? To make locating your recorded thoughts easier, there
are three searches in NoteIt:
Quick Search: This is a SEEK of the index line and is the
fastest of all of the searches. This requires that you search the
entry starting from the leftmost characters in the index field. For
example, to look up the entry "Doe, John", you would have to start
your search criteria from the last name ("D", "Do" or "Doe", and so on
...).
Main Search: This is a search of any data in the index line.
For example, using the above information you could type in "John" and
it would find "Doe, John" as well as any other records that contained
the word or partial "John".
Comments Search: Like the Main Search but it searches the
comments (memo) field. You can search for any word or set of words in
the memo field.
These last two searches are sequential and will consequently take
longer on some machines, depending upon the size of your database.
You will need to create the simple database, called NoteIt, with the
following structure.
Field NameTypeWidth
MainLineCharacter30
CommentsMemo10
For the program to function correctly, you must add at least one
record prior to using NoteIt. This initial record can be edited while
in the NoteIt program by the special editing key Alt-E.
Other special editing keys are available on-screen when using NoteIt
by pressing F1. You still press Esc to abandon a memo edit or
Ctrl-End to save the changes. One point worth noting here is that the
cursor is normally positioned on the memo field in a window. Because
of this, you will need to press Return to regain access to the popup
picklist of names to the left of the memo window.
Another point that might raise questions is the presence of the ruler
in the memo field. When not active, there is no ruler visible. Upon
entering the memo field, in edit mode, the ruler appears, shifting the
text down. The question may then be, how do you eliminate the ruler?
The only way to do so is through the Words menu (accessible by
pressing Alt-W or F10), changing the Hide ruler option to YES. This
setting is not for the dBASE session but for that particular editing
session. Consequently, the next time you enter a memo for editing,
the ruler will once again appear. There is no additional parameter
that can be added to the GET command line of a memo that provides for
elimination of the ruler.
All in all, this program provides a handy utility and the code shows
some interesting programming concepts as well.
8 UDF Library
UDF Library
"At the tone, the time will be..."
Although most of you are aware that you can do basic arithmetic
functions with date and time fields, it is not as well known that you
can add decimal values to a date field and obtain a time
differential.
A need for this usually comes when trying to determine an elapsed
period of time. Functions that accomplish this have been seen in the
pages of TechNotes/dBASE IV in the past. Here now is another
variation on how you might accomplish it.
In the function called Elapsed, the variables sdate and edate are date
type variables while stime, etime and result are character type using
the template HH:MM:SS. To start the timing sequence, enter x =
elapsed(1) at the dot prompt or in a program. To stop or mark the
time, x = elapsed(0). The 0 can be passed repetitively to provide for
numerous time measurements from a singular starting point.
Creating Temporary Files
There are times when you might want your own application to be able to
create its very own temporary files like dBASE IV occasionally does.
The TempFile() UDF below returns the name of a temporary file that can
be used in your program. For example,
Temp = TempFile()
USE TRAVEL
COPY TO &Temp
USE &Temp
REPLACE ALL Cost WITH Cost * .06
AVERAGE Cost TO avg cost
ERASE &Temp
TempFile() honors the TMP environmental variable, so that the file
that your program ends up using is created and processed along with
the rest of your dBASE IV temporary files. Be sure that your program
erases the temporary files it creates, (as shown in the example
above,) or you might end up with a directory filled to the brim with
temp files.
Fonetic Philes
One goal of a developer is to make his software foolproof. If your
application has areas of functionality that can dynamically use any
.DBF or other file type that the user pleases, then it isn't
unreasonable to assume that the user will not always remember the name
or spelling of a target file. FileHelp() is a real nifty way to
ensure that a spelling error or lapse of memory will not slow the user
down.
FileHelp() takes two parameters: the filename that the user enters,
and a file extension. A list of SOUNDEX() spellings and sound-alikes
of filenames with the specified extension pops up. The selected
spelling from the picklist is returned unless no similar spellings
exist or the user presses Esc. In this case, an asterisk (*) is
returned.
FileHelp() makes use of the TempFile() UDF shown above. You must also
create a database file called AFILES.LOD. The structure consists of a
single field definition: A character field with a length of 12 and
named "File".
Using the DOS Path
PathFind() is another way that you can make your application more
iron-clad in the event that the user has no concept of what a
sub-directory is. You could almost use PathFind() as a substitute for
the FILE() function.
FILE(), of course, returns a .T. or .F. depending on whether or not
the file specified is present in the current sub-directory.
PathFind() takes it one step further. If the specified file exists in
the current sub-directory or ANY sub-directory in the DOS PATH, the
filename with the path in front of it is returned. If the file does
not exist in the current sub-directory or anywhere in the PATH,
PathFind() returns a "". For example:
? GETENV("PATH")
C:\;C:\DBASE;C:\DBASE\SAMPLES;C:\DOS
SET DIRECTORY TO \DBASE\SAMPLES
C:\DBASE\SAMPLES
? FILE("TRAVEL.DBF")
.T.
SET DIRECTORY TO \DBASE
C:\DBASE
? FILE("TRAVEL.DBF")
.F.
? PATHFIND("TRAVEL.DBF")
C:\DBASE\SAMPLES\TRAVEL.DBF
Function: Elapsed()
FUNCTION ELAPSED
PARAMETER action
success = .t.
DO CASE
CASE action = 1&& start
PUBLIC sdate,stime
stime = TIME()
sdate = DATE()
RETURN success
CASE action = 0&& end
etime = TIME()
edate = DATE()
OTHERWISE
? "Invalid action specified"
success = .F.
ENDCASE
hour = 1 / 24
min = hour / 60
sec = min / 60
dstart = sdate + VAL(stime) * hour + VAL(SUBSTR(stime, AT(':',
stime) + 1, 2)) *;
min + VAL(RIGHT(stime, 2)) * sec
dend = edate + VAL(etime) * hour + VAL(SUBSTR(etime, AT(":",
etime) + 1, 2)) * ;
min + VAL(RIGHT(etime, 2)) * sec
timedif = ABS(dend - dstart)
result =IIF(dend < dstart , '-', '')
rhour =INT(timedif / hour)
rmin = INT((timedif - rhour * hour) /min)
rsec = ROUND(( timedif - rhour * hour - rmin * min) / sec, 0)
result = result + LTRIM(STR(rhour)) + ':' + ;
RIGHT(STR(rmin + 100, 3), 2) +':'+;
RIGHT(STR(rsec + 100, 3), 2)
? (result)
RETURN success
Function: TempFile()
FUNCTION TempFile
* Author: Dan Madoni
tf = (GETENV("tmp") + LTRIM(STR(RAND(-1)*100000000,8))
+".TMP")
DO WHILE FILE(tf)
*-- Make sure this temp file doesn't already exist
tf = (GETENV("tmp") + LTRIM(STR(RAND(-1)*100000000,8));
+ ".TMP")
ENDDO
RETURN tf
Function: FileHelp()
FUNCTION FileHelp
* Author: Dan Madoni
PARAMETERS f_infile,f_ext
IF .NOT. FILE("AFILES.LOD")
*-- UDF is no good without AFILES.LOD
RETURN "*"
ENDIF
SAVE SCREEN TO B4pop
Function: FileHelp() continued
*--Strip any existing file extension
f_infile = IIF(AT(".", f_infile) = 0,;
f_infile,SUBSTR(f_infile, 1, AT(".", f_infile)
f_ext2 = "*." + f_ext
f_tmp = TempFile()
*-- Create a text file containing all files in the current
*-- sub-directory that have the specified extension and import
*-- the text file into AFILES.LOD.
RUN DIR &f_ext2 > &f_tmp
RESTORE SCREEN FROM B4pop
SELECT 10
USE AFILES.LOD
ZAP
APPEND FROM &f_tmp TYPE SDF
*-- Strip out the DOS messages and extensions
GO TOP
DELETE NEXT 4
GO BOTTOM
DELETE
REPLACE ALL File WITH RTRIM(SUBSTR(file, 1, AT(" ", file) -
1))
*-- Get rid of all the non-sound-alikes.
DELETE ALL FOR SUBSTR(SOUNDEX(File), 2) <>;
SUBSTR(SOUNDEX(f_infile), 2)
PACK
REPLACE ALL file WITH RTRIM(File) + "." + f_ext
IF RECCOUNT() = 0
*-- No similar filenames found.
ERASE &f_tmp
ZAP
USE
SELECT 1
RESTORE SCREEN FROM B4pop
RETURN "*"
ENDIF
*-- Pop Up resulting filenames.
SET COLOR OF MESSAGES TO W+/B
SET COLOR OF BOX TO G+/B
SET COLOR OF HIGHLIGHT TO W+/R
@ 5,34 FILL TO 20,48 COLOR W/N && Shadow
DEFINE POPUP flp FROM 4,33 TO 19,47 PROMPT FIELD File
ON SELECTION POPUP flp DEACTIVATE POPUP
ACTIVATE POPUP flp
ret = PROMPT()
RELEASE POPUP flp
ERASE &f_tmp
ZAP
USE
SELECT 1
RESTORE SCREEN FROM B4pop
IF LASTKEY() = 27
*-- If the user presses Esc.
RETURN "*"
ENDIF
RETURN ret
Function: PathFind()
FUNCTION PathFind
PARAMETERS p_file
IF FILE(p_file)
*-- Return immediately if exists in current directory.
RETURN p_file
ENDIF
p_nowpath = GETENV("PATH")
p_cntr = 0
DO WHILE p_cntr < LEN(p_nowpath)
p_cntr = p_cntr + 1
p_temp = ""
*-- Extract a directory from the PATH.
DO WHILE SUBSTR(p_nowpath, p_cntr, 1) <> ";" .AND.;
p_cntr < LEN(p_nowpath)
p_temp = p_temp + SUBSTR(p_nowpath, p_cntr, 1)
p_cntr = p_cntr + 1
ENDDO
*-- Create the file name to test.
p_temp = p_temp + IIF(RIGHT(p_temp, 1) <> "\", "\",
"");
+ LTRIM(RTRIM(p_file))
IF FILE(p_temp)
*-- If the concantenated directory/file
exists, RETURN it!
RETURN p_temp
ENDIF
ENDDO
*-- If we've gotten this far without RETURNing, then no such
file
*-- exists in the PATH.
RETURN ""
This article is reprinted from a recent edition of TechNotes. Due to
the limitations of this media, certain graphic elements such as screen
shots, illustrations and some tables have been omitted. Where
possible, reference to such items has been deleted. As a result,
continuity may be compromised.
TechNotes is a monthly publication from the Ashton-Tate Software
Support Center. For subscription information, call 800-545-9364.
2 WHOOPS! Gilbert Catipon
Whoops!
Gilbert Catipon
Protection from the Hazards of Accidental Erasure.
Eventually everyone erases a file unintentionally and, oh the
frustration it can cause and it's probably happened to you. At that
point, you feel like you've been driving without insurance and have
caused insurmountable damage. It would be helpful to have some way of
protecting us from our impulsive selves. After all, this is the age
of protection. We need a tool to make our files a little more
secure.
While you could buy a "C" library to acquire a .BIN file capable of
making a file read-only and hidden, that would be going a bit
overboard when all you really want is to keep us or someone else from
looking at or destroying your files. Well in fact, just a few lines
of C code, and a quick UDF, and maybe you can save the money to buy
some frivilous screen saver software instead. Here is the C code:
/* ATTRIB.C -> ATTRIB.BIN to call _chmod() */
#include "..\include\dos.h"
#include "..\include\io.h"
int _argc = 0;
char **_argv = (char **) 0L;
int errno = 0;
void far main(void)
{
_DS = _CS; _argc = _CX; _argv = MK_FP(_ES,_DI);
if(_argc == 2)
if(*_argv[0] == ' ') /* GET file attributes */
*_argv[0] = (unsigned char) _chmod(_argv[1],0,0);
else /* SET file attributes */
*_argv[1] = (unsigned char) _chmod(_argv[0],1,(int) *_argv[1]);
errno = 0;
}
/* End of file ATTRIB.C */
The .BIN itself is quite straightforward. All it does is call the
_chmod function in Borland TurboC 2.0. If you have a different C
compiler, your directives may vary. The only tricky part is passing
the necessary parameters back and forth. This method of making a .BIN
file was discussed extensively in the Febuary '91 issue of
Technotes/dBASE IV, but if you can't find that issue then here are the
compiler directives you need to turn Attrib.C to Attrib.BIN.
tcc -G -O -a -d -Z -c -mc -zPDGROUP attrib
tlink /i /s /m attrib,attrib,,\turboc\lib\cc
exe2bin attrib
How do you use Attrib.BIN? Once we've loaded the .BIN to memory we
can call the .BIN directly by passing it a filename and an attribute,
such as in the command
CALL ATTRIB WITH "CUSTOMER.DBF",CHR(2)
We send a CHR(2) because the function _chmod() will use 2 as a
parameter to set the file attributes of Customer.DBF to Hidden. The
macros that are passed as parameters to _chmod() are in the file
#include
(a space) otherwise it returns the value we sent it.
To use the CHMOD.PRG, enter the following at the dot prompt:
? chmod("customer.dbf","P")
This will make CUSTOMER.DBF a read-only AND hidden file and return the
letter "P". Note that if the file does not exist, then the UDF
returns the string "Customer.dbf?".
To get the file attributes, enter the command
? chmod("customer.dbf","?")
and you get a "P" again. To reset the file attributes, enter the
command:
? chmod("customer.dbf","U").
The syntax for this UDF is
CHMOD("
The possible attributes are:
readonly"R"read-only and hidden"P"
hidden"H"read-only and hidden and system"X"
system"S"no attributes (reset) "U"
archive"A"get file attributes"?"
Function: ChMod()
FUNCTION ChMod
PARAMETER fname, attrib
attrib = AT(UPPER(attrib),
"URHPSvvXlvvvvvvvdvvvvvvvvvvvvvvvA")
IF attrib = 0 && GET file attributes
attrib = SPACE(1)
CALL ATTRIB WITH attrib, fname
ELSE && SET file attributes
attrib = CHR(attrib - 1)
CALL ATTRIB WITH fname, attrib
ENDIF
RETURN IIF(ASC(Attrib) > 32, fname + [?], ;
SUBSTR("URHPSvvXLvvvvvvvDvvvvvvvvvvvvvvvA", ASC(attrib) + 1, 1))
3 NOTEIT.PRG
NoteIt.PRG
* Program ......: NoteIt.PRG
* Notes ........: Miscellaneous Notes/Telephone book
* Syntax .......: DO Noteit
************************************************************************
* Immediately suppress screen activity.
* For other settings see PROCEDURE: Envset
SET TALK OFF
* Initialize variables.
gc_currdir = SET("DIRECTORY")
lc_deleted = .F.
* Open database.
USE NoteIt ORDER mainline
IF RECCOUNT() = 0
? "NoteIt cannot run without at least one record in
Noteit.DBF"
RETURN
ENDIF
* Set up the environment.
DO Envset
DO NoteList
* Delete any marked records.
IF lc_deleted
DEFINE WINDOW chkit FROM 7,15 TO 9,65 DOUBLE COLOR
w+/n,w+/n,r/n
ACTIVATE WINDOW chkit
@ 0,5 SAY "Checking for/Removing Deleted Records" COLOR w+*/n
PACK
RELEASE WINDOW chkit
ENDIF
* Reset the environment.
SET DIRECTORY TO &gc_currdir
DO ResetEnv
RETURN
* EOP: NoteIt.PRG
************************************************************************
PROCEDURE Envset
* Notes ........: Setup the dBASE environment
* Store environment setttings.
sys_bell= SET("BELL")
sys_dbtrap= SET("DBTRAP")
sys_clock= SET("CLOCK")
sys_cursor= SET("CURSOR")
sys_delete= SET("DELETED")
sys_dev= SET("DEVELOPMENT")
sys_escape= SET("ESCAPE")
sys_exact= SET("EXACT")
sys_path= SET("PATH")
sys_safety= SET("SAFETY")
sys_score= SET("SCOREBOARD")
sys_status= SET("STATUS")
sys_talk= SET("TALK")
sys_type= SET("TYPEAHEAD")
* Set up environment.
SET BELL ON
SET BELL TO 330,2
SET DBTRAP OFF
SET CLOCK OFF
SET CURSOR OFF
SET DELETED OFF
SET DEVELOPMENT OFF
SET ESCAPE ON
SET EXACT OFF
SET PATH TO C:\NOTEIT
SET SAFETY ON
SET SCOREBOARD OFF
SET STATUS OFF
SET TYPEAHEAD TO 100
* Set up the colors.
sys_norm = COLOR("NORMAL")
SET COLOR OF NORMAL TO w+/n
* Save the settings to file, then release the memory
variables.
SET SAFETY OFF
SAVE ALL LIKE sys_* TO sysset
SET SAFETY ON
RELEASE ALL LIKE sys_*
RETURN
* EOP: EnvSet.PRG
************************************************************************
PROCEDURE ResetEnv
* Notes .....: Reset the user's environment.
* Restore the original system settings from memory.
RESTORE FROM sysset ADDITIVE
* Reset Environment
SET BELL &sys_bell
SET DBTRAP &sys_dbtrap
SET CLOCK &sys_clock
SET CURSOR &sys_cursor
SET DELETED &sys_delete
SET DEVELOPMENT &sys_dev
SET ESCAPE &sys_escape
SET EXACT &sys_exact
SET MESSAGE TO
SET PATH TO &sys_path
SET SAFETY &sys_safety
SET SCOREBOARD &sys_score
SET STATUS &sys_status
SET TALK &sys_talk
SET TYPEAHEAD TO sys_type
SET COLOR OF NORMAL TO &sys_norm
CLEAR
ON ERROR
ON KEY
* Erase the system environment memory file.
lc_temp = gc_currdir + IIF(TRIM(RIGHT(gc_currdir, 1)) = "\",;
"SysSet.MEM",
"\SysSet.MEM")
ERASE &lc_temp..
* Close all databases.
CLOSE DATABASES
RETURN
* EOP: ResetEnv.PRG
************************************************************************
PROCEDURE NoteList
* Notes ........: View/Edit anentry.
CLEAR
* Define the varibles.
lc_disp= SET("DISPLAY")
ln_line= 3
ln_maxline = IIF(AT("43", SET("DISPLAY")) > 0, 39, 20)
ll_adv = .T.
* Define the keys.
ON KEY LABEL Alt-A DO AddEntry
ON KEY LABEL Alt-C DO ComSrch
ON KEY LABEL Alt-D DO DelEntry
ON KEY LABEL Alt-E DO IndxEdit
ON KEY LABEL Alt-M DO MainSrch
ON KEY LABEL Alt-Q DO IndxSrch
ON KEY LABEL F1 DO MyHelp
* Define the windows.
DEFINE WINDOW viewit FROM 2,35 TO ln_maxline + 1, 76 COLOR
n/g,w+/g,w+/b
DEFINE WINDOW findit FROM 7, 5 TO ln_maxline - 9, 75 DOUBLE
DEFINE WINDOW myhelp FROM 1, 5 TO 23, 75 DOUBLE COLOR
w+/g,,w+/b
* Draw the screen.
@ 2, 2 TO ln_maxline + 1, 33 COLOR n/gr
@ 3, 3 FILL TO ln_maxline, 32 COLOR n/gr
@ 1, 8 SAY "Index Line"
@ 1,50 SAY "Comments"
* Get the records.
GO TOP
ln_recno = RECNO()
DO ReDraw
GO ln_recno
ln_line = 3
DO Litebar
DO WHILE LASTKEY() # 27
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
READ
DO CASE
CASE READKEY() = 15 .OR. READKEY() = 271
&&
@ 4,5 GET comments OPEN WINDOW viewit
MESSAGE;
"Press
save changes,
SET CURSOR on
KEYBOARD CHR(29) CLEAR
READ
SET CURSOR off
CASE READKEY() = 5 .OR. READKEY() = 261&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP
IF EOF()
SKIP -1
ll_adv = .F.
ENDIF
ln_recno = RECNO()
IF ln_line = 20
SKIP -17
DO ReDraw
GO ln_recno
ll_adv = .F.
ENDIF
IF ll_adv
ln_line = IIF(ln_line =
ln_maxline, 3, ln_line + 1)
ELSE
ll_adv = .T.
ENDIF
CASE READKEY() = 4 .OR. READKEY() = 260&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP -1
IF BOF()
ll_adv = .F.
ENDIF
ln_recno = RECNO()
IF ln_line = 3
DO ReDraw
GO ln_recno
ENDIF
IF ll_adv
ln_line = IIF(ln_line = 3,
ln_line, ln_line - 1)
ELSE
ll_adv = .T.
ENDIF
CASE READKEY() = 34 .OR. READKEY() = 290
&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
GO TOP
ln_line = 3
DO ReDraw
GO TOP
CASE READKEY() = 35 .OR. READKEY() = 291
&&
@ ln_line, 3 SAY mainline COLOR w+/gr+
GO BOTTOM
SKIP -17
ln_recno = RECNO()
ln_line = 20
DO ReDraw
CASE READKEY() = 6 .OR. READKEY() = 262 &&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP -17
ln_line = 3
ln_recno = RECNO()
DO ReDraw
GO ln_recno
CASE READKEY() = 7 .OR. READKEY() = 263 &&
@ ln_line, 3 SAY mainline COLOR w+/gr+
SKIP 17
ln_line = 3
IF EOF()
SKIP -1
ENDIF
ln_recno = RECNO()
DO ReDraw
GO ln_recno
CASE READKEY() = 12&&
EXIT
ENDCASE
IF LASTKEY() = 27 .OR. LASTKEY() = 13
KEYBOARD CHR(19)
ENDIF
IF READKEY() # 15 .OR. READKEY() # 271
DO LiteBar
ELSE
KEYBOARD CHR(19)
ENDIF
ENDDO
* Release key assignments.
ON KEY
RETURN
* EOP: NoteList.PRG
************************************************************************
PROCEDURE ReDraw
* Notes ........: Draw the entries on the screen.
PRIVATE ln_line
ln_line = 3
DO WHILE ln_line <= ln_maxline .AND. .NOT. EOF()
* If the record is marked for deletion, put an * next
to it.
IF DELETED()
@ ln_line, 1 SAY "*" COLOR w+*/n
ELSE
@ ln_line, 1 SAY " " COLOR n/n
ENDIF
@ ln_line,3 SAY mainlineCOLOR w+/gr+
ln_line = ln_line + 1
SKIP
IF EOF()
SKIP - 1
DO WHILE ln_line <= ln_maxline
@ ln_line, 1 SAY " " COLOR n/n
@ ln_line, 3 SAY SPACE(30) COLOR
w+/gr+
ln_line = ln_line + 1
ENDDO
ENDIF
ENDDO
RETURN
* EOP: ReDraw.PRG
************************************************************************
PROCEDURE LiteBar
* Notes ........: Draw the LiteBar
@ ln_line, 3 SAY mainline COLOR n/g
RETURN
* EOP: LiteBar.PRG
************************************************************************
PROCEDURE AddEntry
* Notes ........: Add an entry
lc_addme = SPACE(30)
ACTIVATE SCREEN
@ ln_line, 3 SAY mainline COLOR w+/gr+
@ ln_maxline + 3, 3 GET lc_addme MESSAGE;
"Enter the new entry, or press
COLOR ,w+/r
SET CURSOR on
READ
SET CURSOR off
IF READKEY()< 256&& All numbers < 256 are non-updated
codes.
* Abort the add.
@ ln_maxline + 3, 3 CLEAR TO ln_maxline + 3, 75
DO LiteBar
ELSE
* Add the record.
APPEND BLANK
REPLACE mainline WITH lc_addme
@ ln_maxline + 3, 3 SAY "NEW ENTRY: " + mainline COLOR
g/n
@ 4, 5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(29) CLEAR
SET CURSOR on
READ
SET CURSOR off
@ ln_maxline + 3, 3 CLEAR TO ln_maxline + 3, 75
GO TOP
DO ReDraw
GO TOP
ln_line = 3
DO LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(24) CLEAR
READ
ENDIF
RETURN
* EOP: AddEntry.PRG
************************************************************************
PROCEDURE DelEntry
* Notes ......: Delete or undelete an entry.
* Display an asterisk if the record is marked for deletion,
or clear the mark.
IF DELETED()
RECALL
@ ln_line, 1 SAY " " COLOR n/n
ELSE
DELETE
lc_deleted = .T.
@ ln_line, 1 SAY "*" COLOR w+*/n
ENDIF
RETURN
* EOP: DelEntry.PRG
************************************************************************
PROCEDURE IndxEdit
* Notes ........: Edit the index line.
ACTIVATE SCREEN
@ ln_line, 3 GET mainline MESSAGE "Edit the Index line (
aborts)";
COLOR ,w+/bg+
SET CURSOR on
READ
SET CURSOR off
IF READKEY() >= 256&& Field was changed
ln_recno = RECNO()
DO ReDraw
GO ln_recno
ln_line = 3
ENDIF
DO LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(24) CLEAR
READ
RETURN
* EOP: IndxEdit.PRG
************************************************************************
PROCEDURE IndxSrch
* Notes .....: Search for an INDEXED entry in Mainline.
* Initialize variables.
lc_srch = SPACE(30)
ACTIVATE WINDOW findit
CLEAR
@ 0,19 SAY "Search for (Quick Search)"
@ 1,17 GET lc_srch MESSAGE;
"Enter the value to search for (
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
SEEK UPPER(TRIM(lc_srch))
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: IndxSrch.PRG
************************************************************************
PROCEDURE ComSrch
* Notes ........: Search for an entry in comments.
* Initialize variables.
lc_srch = SPACE(60)
ACTIVATE WINDOW findit
CLEAR
@ 0,17 SAY "Search for (Comments Search)"
@ 1, 2 GET lc_srch MESSAGE;
"Enter the value to search the comments for (
aborts)"
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
LOCATE FOR AT(TRIM(lc_srch), comments) > 0
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
DO Again
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: ComSrch.PRG
************************************************************************
PROCEDURE MainSrch
* Notes ........: Content search for an entry in Mainline.
* Initialize variables.
lc_srch = SPACE(30)
ACTIVATE WINDOW findit
CLEAR
@ 0,17 SAY "Search for (Index Line Search)"
@ 1,17 GET lc_srch MESSAGE;
"Enter the value to search the index line for (
aborts)"
SET CURSOR on
READ
SET CURSOR off
IF LEN(TRIM(lc_srch)) = 0
DEACTIVATE WINDOW findit
RETURN
ENDIF
ln_recno = RECNO()
LOCATE FOR AT(TRIM(lc_srch), mainline) > 0
IF FOUND()
DEACTIVATE WINDOW findit
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
DO LiteBar
DO Again
@ 4,5 GET comments OPEN WINDOW viewit MESSAGE;
"Press
KEYBOARD CHR(24) CLEAR
READ
ELSE
?? CHR(7) + CHR(7)
CLEAR
@ 1,15 SAY "Entry Not Found, Press any key" COLOR w+*
READ
DEACTIVATE WINDOW findit
GO ln_recno
ENDIF
RETURN
* EOP: MainSrch.PRG
************************************************************************
PROCEDURE Again
* Notes ........: Continue a search (ComSrch and MainSrch
only).
* Disable the ON KEYs
ln_type = SET("TYPEAHEAD")
SET TYPEAHEAD TO 0
lc_again = "Yes"
DO WHILE .T.
@ ln_maxline + 3, 5 SAY "Search Again? " GET lc_again
PICTURE "@M Yes,No";
MESSAGE "Press
COLOR w+/n,w+/g
READ
@ ln_maxline + 3, 0 CLEAR TO ln_maxline + 3, 70
IF lc_again = "Yes"
CONTINUE
IF FOUND()
ln_recno = RECNO()
ln_line = 3
DO ReDraw
GO ln_recno
Do LiteBar
@ 4,5 GET comments OPEN WINDOW viewit
MESSAGE;
"Press
comments"
CLEAR GETS
ELSE
?? CHR(7) + CHR(7)
@ ln_maxline + 3, 0 CLEAR TO
ln_maxline + 4, 77
@ ln_maxline + 3, 20 SAY "Entry Not
Found, Press any key" COLOR w+*
READ
@ ln_maxline + 3, 0 CLEAR TO
ln_maxline + 3, 70
GO ln_recno
EXIT
ENDIF
ELSE
CLEAR GETS
EXIT
ENDIF
ENDDO
SET TYPEAHEAD TO ln_type
RETURN
* EOP: Again.PRG
************************************************************************
PROCEDURE MyHelp
* Notes ........: Help system.
* If you want to add your own help pages, simply add another
procedure
* called Page
increase ln_maxpg.
ln_page= 1
ln_maxpg = 3
lc_temp= " "
ACTIVATE WINDOW myhelp
DO WHILE .T.
CLEAR
lc_page = "Page" + LTRIM(STR(ln_page))
DO &lc_page
@ 0, 0 GET lc_temp MESSAGE;
"PgUp - previous screen, PgDn - Next Screen, ESC -
Exit";
COLOR ,g/g
READ
IF READKEY() = 12
EXIT
ENDIF
ln_page = IIF(READKEY() = 7 .OR. READKEY() = 263,;
IIF(ln_page = ln_maxpg, ln_page, ln_page +
1),;
IIF(READKEY() = 6 .OR. READKEY() = 262,;
IIF(ln_page = 1, ln_page, ln_page - 1),
ln_page))
ENDDO
DEACTIVATE WINDOW myhelp
SET MESSAGE TO
RETURN
******************************************************
PROCEDURE Page1
* Help Page #1
TEXT
Available keys:
ENDTEXT
@ 20, 68 SAY CHR(25) COLOR w+*/r
RETURN
* EOP: Page1
****************************************************************
PROCEDURE Page2
TEXT
Available keys (cont'd):
(This
will put a blinking "*" next to the record
that
is marked for deletion. The record will be
physically deleted when you quit the program.
If
you press
blinking "*", the record will be unmarked for
deletion.
line.
ENDTEXT
@ 0, 68 SAY CHR(24) COLOR w+*/r
@ 20, 68 SAY CHR(25) COLOR w+*/r
RETURN
* EOP: Page2
*********************************************************************
PROCEDURE Page3
TEXT
Available keys (cont'd):
line. This
search is the quickest search, however it will
search the beginning of the Index line. For
example, if you had a line:
Doe, John
You
could not enter "John" to find it, you would
have
to enter characters starting from the
beginning of the Index line (i.e.: "Do". To find
"John", you would need to use
ENDTEXT
@ 0, 68 SAY CHR(24) COLOR w+*/r
RETURN
* EOP: Page3
****************************************************************************
FUNCTION Color
* Format:
* Color(
*
INFORMATION, FIELDS
*or a variable with all colors
stored in it.
*Ver: dBASE 1.1
*
* The Color() function either returns or sets colors returned
with the
* SET("attribute") setting.
* If
the color
* setting is returned for one of dBASE IV's color options.
*
* See Also: SET("attribute")
*
*
PARAMETERS set_color
PRIVATE color_num, color_str, cnt
set_color = UPPER(set_color)
IF set_color = "COLOR"
*- Return standard, enhanced, border colors only
RETURN SUBSTR(SET("attr"),1, AT(" &", SET("attr")))
ENDIF
*- Declare array to parse color options from SET("attr")
PRIVATE color_
DECLARE color_[8]
*- Determine if user is restoring colors vs. saving colors
IF " &" $ set_color
color_str = ","+set_color+","
&& Restore color attributes
ELSE
color_str = ","+SET("ATTRIBUTE")+","
&& Save color attributes
ENDIF
* Stuff array with individual color setting
color_str = STUFF(color_str, AT(" &", color_str), 4, ",")
cnt = 1
DO WHILE cnt <= 8
color_str = SUBSTR(color_str, AT(",", color_str ) +1 )
color_[cnt] = SUBSTR(color_str, 1, AT(",", color_str )
- 1)
cnt = cnt + 1
ENDDO
IF " &" $ set_color
* Set color back.
SET COLOR TO
,,&color_[3]. && Border color.
SET COLOR OF NORMAL TO &color_[1].
SET COLOR OF HIGHLIGHT TO &color_[2].
SET COLOR OF MESSAGES TO &color_[4].
SET COLOR OF TITLES TO &color_[5].
SET COLOR OF BOX TO &color_[6].
SET COLOR OF INFORMATION TO &color_[7].
SET COLOR OF FIELDS TO &color_[8].
ELSE
* Return color string requested.
DO CASE
CASE set_color $ "NORMAL"
color_num =1
CASE set_color $ "HIGHLIGHT"
color_num =2
CASE set_color $ "BORDER"
color_num =3
CASE set_color $ "MESSAGES"
color_num =4
CASE set_color $ "TITLES"
color_num =5
CASE set_color $ "BOX"
color_num =6
CASE set_color $ "INFORMATION"
color_num =7
CASE set_color $ "FIELDS"
color_num =8
ENDCASE
ENDIF
RETURN IIF(" &" $ set_color, "", color_[color_num])
* EOP: Color
4 The Era of Protection Joe Stolz
The Era of Protection
Joe Stolz
Perhaps you don't want to think about it but someone could be looking
over your data files while you are out of the office! Perhaps you
don't have any data that is truly confidential but it's not hard to
imagine the sort of information that is best not seen by others. If
you are a manager, or you work in Payroll, you will need to prevent
others from seeing the private information of other individuals.
After all, if Mr. Prehensile were to discover that Miss Halcyon was
making more than he, it could cause a major downswing in office
rapport.
Perhaps you want to restrict others from entering dBASE IV
altogether. That way, all of your data files will be secure.
What if you work on a Local Area Network installation and you share
files with others? Everyone must access the same file but you alone
should see or modify certain fields in the file structure.
Does this sound like a task too big for dBASE IV? It really is simple
when you use Protect!
Protect is not a new feature of dBASE IV. It's been around since
dBASE III PLUS. However, in dBASE III PLUS, you had to have a
multi-user installation (even if it was on your single-user machine)
to get Protect to work. Further, you had to exit to DOS to run the
Protect program. It was a separate utility.
Now Protect is one of the bevy of utilities available through the
Tools menu in the Control Center. This new, easier access to Protect
makes it a good topic for review in the context of dBASE IV. There
are also some new features that were added to make Protect easier to
use.
How It Works
The dBASE IV Protect system is based on login names and passwords like
most computer security systems. The dBASE IV login screen is visible
only after you have set up a Protect system. Once you have it set up,
every time you enter dBASE IV you will first see a login screen
requesting a User name, a Password and a Group name. The User name
and Password are used to restrict access to dBASE IV to only those
that have a user profile within Protect. A Group name is extremely
important as it serves as a way to designate a group of database files
that require access by a particular subset of users. If you are not
part of the group, you will not be able to open the file.
Database files and their contained data, including their memo field
contents, are physically encrypted through Protect. Once encrypted,
the entire file is unreadable through DOS or other utility programs,
except through dBASE IV as one of the users of the Group that has been
designated to have access to it!
Protect offers a third level of security too. You can assign priority
levels to your users and restrict access to specific fields within the
database structure. This restricted access can span the range from
full reading and edit capability of all fields in the file, to the
ability to see only the data in the field but not to change its
content, to the ability to make a field appear as if it didn't even
exist in the file structure!
All this is possible through Protect. When you run Protect it creates
a file called DBSYSTEM.DB. If you are on a single user system, you
find this file in the dBASE IV directory. If you are on a LAN system,
the file is created in the DBNETCTL.300 directory which also contains
the login access files. This file contains the user profiles and is
itself encrypted using the Protect system administrator I.D. You will
want a copy of this file available in case it becomes inadvertently
deleted.
A new feature within Protect is the Reports menu which can produce a
hard copy listing of your users and their passwords, allowing for full
documentation of your Protect system.
Assigning User Groups
As the manual goes into some detail explaining the features of
Protect, it would be more useful to concentrate on some aspects of
Protect that are particularly confusing to some people.
The first thing that you will end up doing in Protect is creating user
profiles. Each user must be assigned to a group (assigning names and
passwords is pretty straight forward). As noted in Using the Menu
System, pages 14-23, a user can belong to several groups but a file
can be encrypted for only one group to access. If a user needs to
access files from another group of which he is a member, he must login
again and therefore maintain two full user profiles. There is nothing
wrong with having the same name and password for a particular user,
and to have two different group names assigned for each of the two
profiles. The uniqueness of each user profile is in the combination
of the three required key fields: Name, Password and Group name.
The fact that a file can be encrypted for only one group is crucial to
your understanding of how dBASE IV limits access to encrypted
(protected) files. If you have set up a file to be accessed by one
set of users, a new user needing to access that file must belong to
the same group.
The group name is the key to a user being able to access the contents
of a file. To each member of the correct group, the file seems as if
it contains plain English names and data. Anyone outside of the group
who attempts to access an encrypted file will be stopped by the
message, "File is encrypted" when they try to USE the file.
Attempting to view the file from DOS will lead to the same
frustration. Whereas the initial entry to dBASE IV is blocked by the
need for a user profile, even if you find a way to circumvent the
login, this second level of protection hides your data. That is to
say that even properly logged-in users can be prevented from seeing
data in a file that is encrypted for a different group.
User and File Access Levels
The next issue is one of user access levels. In dBASE IV, a level of
1 is the most powerful level. This translates to level one being the
least restrictive level. Level 8 is the most restrictive level. The
absolute values 1 through 8 have no real intrinsic meaning in and of
themselves. There may be no practical difference in your system
between level 7 and 8. Protect merely offers 8 levels of security
differences for systems whose security levels must be that complex.
It is up to the Protect system administrator to establish the
assignments of security equivalence. It is then where the eight
levels can take on a more precise security level based upon what
levels are higher or lower than any particular level.
Initially, all users are assigned a default level of 1 (highest
access) and all file privileges are assigned a level of 8 (least
restrictive). A level 1 user can perform all functions: read, update,
extend and delete since the highest level of restriction that can be
assigned to each of these operations is one. A user of level five,
however, can perform operations assigned to level five through 8 but
not those assigned to level four down to one.
Reviewing the file privileges again, they are, in order of increasing
importance, Read, Update, Extend and Delete. To give a simple
demonstration of the increasing power of these levels, let's say that
Read allows you to read the data only. No changes can be made to data
contained in the file. The Update privilege allows changes to be made
to existing data. Extend privileges provides for the addition of new
data in the form of new records added to the file. The Delete
privilege means the you can remove records from the file. This
increasingly important set of operations is what requires careful
assignment to the various levels of users in your database management
system.
Read access is the ability to simply see data in a file. This is the
lowest level of all file access privileges. You should realize that
if you have a user who must be prevented from even looking at the
contents of a file, that user really should not be included in the
group. If a user is part of the group that accesses a file, the
minimum ability he will usually have is to see the contents of the
file, though he may be prevented from altering the record contents.
Nonetheless, if you assign read access to level 7, your level 8 users
will be unable to do anything to the file.
In a different vein, assigning Read access to a number smaller (more
restrictive) than the level assigned to Update essentially nullifies
the read-only capability of your Read assignment and causes Update and
Read to be granted at the same level. This is okay if you understand
what you are doing, but for the sake of simplicity, you should assign
the four levels of file privileges in an increasing order to an ever
decreasing set of values.
The next level up is that of Update privileges. This is the ability
to change (or edit) the contents of a record. The next level is the
ability to Extend (or append to) the file. A common question raised
is whether a data entry clerk can have the ability to add new records,
but not to change the contents of pre-existing records. Since adding
records is really the same as editing a blank record (putting contents
into blank fields), the answer is no. However, to attain this kind of
restriction, data entry clerks can be given access to special data
entry files that will contain only new records and be denied access to
the main database, preventing the changing of data in that file. New
records they enter can later be appended to the master file.
Another, similar, question is whether a data entry clerk can be
restricted from viewing data but only allowed to enter new data. This
is even more impossible given what was discussed above. Remember, the
minimum privilege is to view a file (called read-only access). If a
user has a higher access right like Delete, the user would, out of
necessity, retain the rights to the lower, more fundamental access
privileges.
The highest available privilege is to Delete records. If you want to
visualize how all this fits together, let's try assigning some numbers
to the four privileges.
Say that Read is assigned level 6, Update is also assigned level 6,
Extend is assigned level 4 and Delete is assigned level 2. A level 8
user will not be able to use the file. The same is true for a user
who has a user access level of 7. A user with an access level of 6
will have both Update and Read privileges. In this example, there can
be no users that have read-only rights. Data entry must be done by
those with access rights of 4 and above. Deletion can be done only by
those with levels of 2 or 1.
Field Access Levels
The rights and restrictions discussed to this point apply on a full
file basis. That is, up to now we saw that we could totally restrict
a user from changing data in a record, or allow them the ability to
add records to a file. dBASE IV also offers the ability to apply an
even finer level of restrictions to a file, on a field by field basis.
Field privileges are not commonly assigned judging from my discussions
with users. I believe that this is due to the fact that the concept
of field privileges is three levels removed. Thinking about login
restriction, then file access privileges in relation to user access
levels, then to field level restrictions tends to boggle the mind.
It's a level of abstraction that is too much to deal with. Once
again, user access level is the basis of field privileges just as it
is with file privileges. Fields can have three levels of access
privileges, Full, Read-only, and None. "Full" translates to no
restrictions, "Read-only" restricts the user to only viewing the
contents of a field. "None" is remarkable in that fields designated
as such "don't exist" to users of that access level. It's remarkable
since not only can they not see the data, they can't even see the
field in the file's structure. DISPLAY STRUCTURE will not display
fields designated as None, as if the field didn't even exist. It's a
pretty tricky thing, a potential source of problems.
Field privileges are assigned for each User access level on a field by
field basis. That is, you establish which fields can be modified,
viewed only, or not visible at each User level. You might decide that
a user of level 8 should not see the Salary field in the Payroll
file. However, this same user might be the one who adds the basic
personal data to the other fields in the file, having then a mixture
of Full and None field privileges in the file.
Establishing field privileges this way might seem like an alternate
way to set up file level privileges since you can establish which
fields are visible and modifiable for all User access levels.
Actually, Field access privileges must work in conjunction with File
access privileges. Established File privileges will take precedence
over Field privileges. These in turn must work in conjunction with
User access levels.
Let's look again at our example of this concept. The data entry clerk
must have Extend ability (capability to APPEND to the file). As a
fine adjustment on this ability, you want to block the data entry
clerk from seeing the contents of the Salary field. This fine tuning
is what the Field access concept is all about. Note that for Human
Resource employees who do not make changes to the data at all and who
are designated as level 7 or 8 users (such as Read-only), assigning
Full field privileges to all fields WILL NOT override the Read-only
rights established on the file as a whole. That is, Field privileges
do not override File privileges.
This should help to alleviate some of the ambiguity surrounding the
use of field level restrictions. It is best used as a fine tuning
capability over the more "coarse" file level restrictions. Field
level access is usually used sparingly, don't try to over use it.
Encrypting the File
Once the File and Field levels are established and you are done, you
must exit from Protect. Of course, you will be Saving all changes
made during the Protect session.
One of the most common "problems" that new Protect users report is
that their new access scheme doesn't work at all. Without exception,
this can be attributed to a single cause. When a file is encrypted,
it is created as a new file. The original file is left untouched and
the newly encrypted file has the same name as the original file but
with a .CRP extension. Thus, when you use the .DBF, it is available
to any level of user. This is NOT the protected file. This is done
for your protection, but it certainly is confusing to the Protect
user! This simple fact is documented in Using the Menu System under
"Other Considerations" in Protect, on page 14-38. It's not really an
"other consideration," it's a crucial consideration if you ask me! At
any rate, to use your newly encrypted file you must rename it and give
it a .DBF extension. I'd suggest that you rename it to a new parent
file name and that you make a backup of both the original database and
the .CRP file to boot. Reports of the file access privileges and such
can only be made from files that have a .CRP file extension.
Another little known and little realized fact is that once a set of
privileges is established for a file and you exit Protect, the changes
are permanently recorded in the .CRP file and if you desire to modify
the access rights established you will have to start over again
establishing all rights to an unencrypted parent file! The .CRP file
cannot be modified once saved and only .dbf files can be used as the
basis of setting up new field privileges. For this reason, it is
imperative to print a report of the field and file level rights that
were established within Protect for each file. dBASE IV offers a
Reports menu for this purpose. The Report menu reads and reports on
.CRP files only. If you desire to modify any particular rights in
your file it will help to see graphically what rights were previously
established for the file. It certainly can save a lot of time and
grief in the event of a disaster.
Summary
The abilities of the dBASE IV PROTECT feature is a complicated area to
learn. Protecting your data is crucial in many database systems.
Your task is to understand what is going on in the dBASE IV Protect
system and to try to get the most out of it. Being forewarned about
the interrelations between the various aspects of Protect is your best
protection against frustration with the system. Rest assured that it
works and can be downright powerful once you get the hang of it. Give
it a whirl!
5 Sizing Up an Application Don Powells
Sizing Up an Application
Don L. Powells
What is an application? This is a very broad question. Can you
narrow it a bit? From a developer's perspective, what are the
components of a dBASE IV application? This is a fair question.
Before you go about developing an application it would seem a
reasonable prerequisite to know what an application is.
Seeing a completed application running as a whole, we sometimes forget
that there are discrete parts integrated under the surface, combined
to solve a problem or accomplish a task. This article describes the
parts or components that make up an application with the intent to
draw attention to many of those elements that go into making an
application a good one.
Setting the Environment
Most applications are affected by the environment in which they run.
It is very important to set the operating system environment so that
your application will run in it. Under DOS, for example, the
Config.SYS file must contain a large enough value for the FILES
variable to allow you to simultaneously open the maximum number of
required files. If your code depends upon the contents of the PATH or
other DOS environment variables, they must be set before program
execution.
Configuring the System
The dBASE IV environment must be configured, initially, upon
installation of your application. The dBASE IV SET commands can be
used to determine settings such as whether the clock will be on or
off, displaying 12 or 24 hour format. The SET commands also control
screen colors, the date and numeric format (especially important with
international applications), the currency format, the status of the
bell, and disk drive/directory search paths.
In conjunction with the SET commands, global variables are often used
to maintain system status information. These variables should be
saved to a .MEM file to be restored each time the application is run
so that the user does not have to reconfigure the system each time.
The SAVE TO command will store all or a portion of variables present
in your system. Unless the variables change regularly, it is not
necessary to SAVE the variables at the end of each session. The
RESTORE command will reset these variables in a new dBASE session and
will be necessary at each start-up. Neither of these processes is
automatic and must be set up in your start-up and exit routines.
To prevent the user from having to worry about what files are
necessary for the application to work, it is a good practice to
include an initialization routine that checks for the presence of the
needed files and creates them if they are not there. These files may
include data or index files, .MEM files as discussed above, memo
(.DBT) files, and any special files you may have created. The
initialization routine opens the files and sets the necessary
relations and filters, establishing your desired views of the data.
Displaying the User Interface
The user interface is the means by which the user communicates with
the application and, indirectly, the way the developer communicates
with the user. Some of the best rules for user interface design are
from the Apple User Interface Guidelines. I have paraphrased these
guidelines below.
General Principles of User Interface Design
Use concrete metaphors for computer processes that correspond
to the everyday world. A check-writing program, for example, may
display an image of a check on the screen as data is being entered.
Provide sensory feedback in response to the user's physical
actions so that they feel they are in charge. Make a noise, make a
visible change on the screen, electrically shock the user, do
something to let them know that you recognize their actions.
Allow the user to select from alternatives displayed on the
screen. Let them use recognition rather than recall. Most people
like multiple choice much better than fill-in-the-blank.
Be consistent within and across applications to ensure
ease-of-learning, and familiarity.
Make sure that what the user sees on the screen does not
differ significantly from the eventual output.
Let the user initiate and control all actions. Provide
warnings for risky activities but allow the action to proceed if
confirmed by the user.
Keep the user informed of the progress of operations with
immediate feedback which tells how long delays will last and why.
Forgive the users when they make mistakes by allowing their
actions to be reversible. Let them know when their actions are not
reversible. In retrospect, perhaps it's better not to shock the user
as was previously recommended.
Provide a conceptual sense of stability by maintaining a
number of familiar "landmarks" on the screen and avoid random changes
of environment.
Provide aesthetic integrity by making different things look
different on the screen and giving the user some control over the look
of their workplace.
A number of elements are used to build the user interface. Some of
the major user interface elements are briefly described below.
User Interface Elements
Menus are used to navigate through an application. Pull down
menus have become very popular because they allow the user to see what
functionality is available, where he is in the program, and the path
followed to get to there and back. Popup menus are great for
displaying pick-lists from which a single selection is made.
Horizontal bar menus prove useful when you need to display a limited
number of choices in a small space. Menus can be built using the
dBASE IV applications generator. Menus and pick-lists provide that
multiple-choice vs. fill-in-the-blank preference previously mentioned.
Windows allow you to collect and display related information
while separating it visually from unrelated items on the screen.
Dialog boxes are built from windows and used to ask users questions or
to pass along important information. Alert boxes are similar to
dialog boxes except they are commonly used to issue warnings. Check
boxes are commonly displayed in a window to let the user make multiple
simultaneous selections from a list. A table of data from a related
file or a body of text from a memo field may be displayed in a window
as well.
Data input forms let the user enter data on one or more full
screen pages of data entry blanks. Data input forms can be built
using the dBASE IV forms generator.
Data display forms display the data on one or more full screen
pages without the ability to edit or change the data. Data display
forms can also be built using the dBASE IV forms generator.
Progress indicators include odometers, blinking messages, and
thermometer-like bars that show the user how far a process has gone
and how far it has to go. Without progress indicators the user is
left to wonder whether the application has failed and locked up the
computer. Progress indicators help prevent inopportune rebooting
which can be a source of file loss and corruption.
Data Manipulation and Processing
Once data has been entered through the data input forms or some other
method, your application will process that data toward your desired
end. A description of some of the more common data manipulation and
processing functions follow.
Data Verification entails validating the type, format and
content of the data that is input. This process may be as simple as a
PICTURE clause or VALID expression in an @..GET statement or as
complex as a large user defined function.
Data Storage involves the transference of validated data from
memory or a temporary file to a more permanent master .DBF file. If
data storage is localized in one place, any special processing
required later can be added in one place.
Data Access functions are used to search for requested data
records by using indexed SEEKs, ad hoc LOCATEs, or sequential steps
through FILTERed .DBF files.
Data Retrieval is the opposite of data storage. It involves
the transference of data from a .DBF file to memory variables, an
array or a temporary file for display or editing.
Data Deletion allows you to remove unwanted records from your
system files. Localizing this process lets you put in safeguards
against accidental loss of important data. Some developers use the
PACK command but others just blank the record out to make their data
storage function more efficient and faster. On large files with many
indexes, PACKING may be less desirable than a blanking method.
However, the latter method requires additional programming efforts to
ignore these blank records in calculations and reports.
Unique Processing may be required for each application you
create. The processing for preventing duplication of data will most
probably occur before data storage, during a validation process.
However, it is also possible that duplicates are merely omitted from
the reporting or calculating process by filtering them out beforehand.
Data Output could be called reporting but involves outputting
the data to not only the printer, but to the screen, a file, or via
telecommunications. The output may take the form of a formatted
report, labels, invoices or other special forms, or specific file
types that other programs can read. Many of these forms of output may
be generated in dBASE IV via use of the report or label generators or
exporting commands such as COPY and EXPORT.
Context-sensitive Help
Nearly all commercial applications sold today provide
context-sensitive help and your user will probably expect no less from
you. As a minimum, the help system should provide the user with
general information about your application and aid when using the most
difficult portions of the application, however, there may be times
when you cannot predict what will be difficult for your users.
Therefore, you should make your help system flexible enough to absorb
additions, deletions and edits with relative ease. An advanced help
system allows the user to add his own help text to what you provide.
If the help is going to be substantially long, it is preferable to
include help information in a supplemental .DBF file, possibly using
memos rather than hard-coding the help into the program itself.
Maintaining the System
A group of activities fall within the realm of system maintenance. A
user may decide that one color scheme is no longer attractive and want
to change it. A different user takes over the machine and decides to
reconfigure the system totally to their desired state. Printers come
and go, replaced by newer models or substituted by inferior ones when
they malfunction. A conscientious developer should not make provision
for only one or two specific printers. You should also accommodate
customization after installation.
If the index files get corrupted by a power outage during a write, the
user should be able to recreate them from a menu option. Many times,
our technicians here will get a call from a dismayed user who can't
make a custom application work. Often times, it's only a matter of
rebuilding an index file, a function that won't normally take more
than a few seconds. But without proper instruction and a means for
recreating such files, hours of constructive time can be lost.
It makes sense for some applications to allow the user to create a
new, empty set of files. If the user wants to practice with dummy
data, new files are a great aid for helping them get started with the
real data. Only you should also provide for the erasure of these
files so that unneeded practice files do not build up in the user's
directory.
Speaking of size, if you anticipate that the size of the system files
will grow to unmanageable sizes filled with historical data, you may
want to implement an archive and restore system to allow the user to
put unneeded records in an archive file until later required.
Archiving may also speed up the processing of the application because
there are less records for your application to maintain.
Provide a means for your users to backup and restore their data as
part of a maintenance menu. This system can be a simple file copy to
a user specified drive, or using the DOS commands, or a complete
system that splits files across multiple floppies or other media if
the file is too large to fit on one. Of all the features of your
application, this one is of a critical nature. No insurance for
possible data corruption or erasure is an invitation for inevitable
disaster. As the architect of a system, you should provide the system
for easy and reliable backup and encourage your users to follow
through regularly.
Handling Errors
You should anticipate and prevent the occurrence of non-critical
errors. Make sure that only the proper data type can be placed in a
data entry field by using formatting and validation. Unfortunately,
there are some critical errors that cannot be easily prevented by
dBASE IV error trapping (such as backing up to an open disk drive or
printing to a printer that is off). In those cases you should try to
allow the user to recover or terminate gracefully under your
application's control.
An electronic error log should be automatically generated by your
error handler to help you to debug problems without having to depend
upon the user to describe them. It is easier to handle errors if you
group them into categories like disk errors, print errors, memory
errors, and so on. You can then concentrate on handling just that one
category of errors at a time instead of trying to accommodate every
possible error in one giant CASE construct. Again, if your system is
vast and the error messages numerous, consider placing the errors and
their error numbers in a database that can be called by your program
when an error occurs.
Technical and User Documentation
The comments in the source code files contain much of the technical
information needed to maintain the application, but often, more
documentation than that is needed to maintain an application. It is
helpful to have a description of the overall application and how all
the pieces fit together. A code analysis utility could prove helpful
in creating flow charts, variable usage tables, and more. Other
utilities can provide "screen-shots" of different screens that appear
in your application. The visual association can be helpful in
increasing the understanding of how to operate your application.
User documentation is a combination of what is provided on-line and in
print. As a minimum, the user should be able to understand how to get
started using your application. Ideally, the user would never have to
call you with a problem that could not be solved by using the program
or its documentation. Then again, where would our technical support
department be if that were true.
Miscellany
Additional functions may be added to an application to implement
networking or password protection. Transaction processing may become
necessary in a transaction-oriented system like accounting. If your
application requires some functionality outside of what dBASE IV can
handle, you may need to include modules written in C or Assembly
language to handle those tasks. The pages of TechNotes and the
Ashton-Tate BBS are excellent sources for such routines.
Summary
So, what is an application? It is a collection of discrete parts
integrated to solve a problem or accomplish a given task. Each part
has a purpose and the absence of even one part increases the
likelihood that your application will not reliably suit those who use
it. The parts that have been discussed here are:
Setting the Environment
Configuring the System
Displaying the User Interface
Data Manipulation and Processing
Context-sensitive Help
Maintaining the System
Handling Errors
Miscellany
Technical and User Documentation
When you conscientiously combine these parts and others you find
necessary, you get an application that provides the users with a more
efficient way of processing data and you with a minimum of headaches
(from disgruntled and perplexed users calling for technical support)
and a maximum amount of cashflow (from lots of orders and/or a raise).
6 Etc.
Etc.
Checking For Directory Existence
At some point during the course of application building, it often is
necessary to verify the existence of critical files needed for proper
program operation. This is easily accomplished through use of the
FILE() function. A program segment may look something like this:
IF .NOT. FILE("MyApp.DBF")
.
.
*Produce error message for missing file "MyApp.DBF"
.
.
ENDIF
*Continue normal processing here...
Checking for directory (or drive) existence can be accomplished in a
similar manner. Ironically, the FILE() function is still the
function to use. But since the FILE() function needs a file name
we'll have to trick it by using a DOS device name such as NUL, CON,
PRN, etc. This is possible since DOS allows you to open a device the
same way you open a file! NUL is the preferred device name to use
since it's not likely to interfere with an operation the way opening
CON or PRN may. The following example shows how to accomplish this...
IF .NOT. FILE("\MyAppDir\NUL")&& or FILE("B:\NUL") for drive
.
.
*Produce error message for missing directory "MyAppDir"
.
.
ENDIF
*Continue normal processing here...
Setting Natural Order in the Applications Generator
When using the dBASE IV Applications Generator, the Override assigned
database or view option allows you to change the active database and
its order. However, changing the order of the file to Natural order
(no index) once a particular order is in place, is not clearly
documented. This can be accomplished by entering a left and right
bracket ([]) or two single quotes ('') in the following areas:
In the Set index order option under the Item menu.
Under the ORDER option of the Override assigned database or
view option of either the Menu or Item menu.
This has the effect in the resultant dBASE code of a SET ORDER TO
command which resets the file to natural order.
Invoking Print Screen from a Program
Since the Print Screen or Shift-Print Screen key has a special use on
PC-compatible computers, it does not return a value to dBASE IV. This
makes it impossible to invoke a print screen through the use of the
KEYBOARD command. Fortunately, printing the screen can be
accomplished through use of a very small .BIN file which is relatively
easy to create. Either of the following two measures will create the
file Prntscrn.BIN.
At the DOS prompt type COPY CON Prntscrn.BIN and press Return. Then,
while holding down the Alt key, enter the numbers 205, 5 and 203 on
the numeric keypad, releasing the Alt key after each number. Lastly,
press the F6 key and then Return. This will close and save the file.
The second method for creating this .BIN file must be invoked from the
dot prompt or in a program. The dBASE code follows.
SET PRINTER OFF
SET PRINTER TO FILE Prntscrn.BIN
??? "{205}{5}{203}"
SET PRINTER TO
Having created the file, issue the command LOAD Prntscrn.
Thereafter, to print the screen, issue the command CALL Prntscrn from
your program:
Seeing Your True Colors
While it's true that there is now a way to use the SET() function to
return the values of your current color settings, it doesn't really
show you what these settings are or what they look like.
It is not hard to devise a small routine that shows the eight
different color settings in the actual colors (on a color monitor of
course). However, when the colors for foreground and background are
the same, such as N/N (or black on black), there may be no indication
on screen if your background color is that same color. t
* CSHOW.PRG
PRIVATE showcolor, cstring, comma, talkwas, cursorwas
ON ESCAPE KEYBOARD CHR(0)
IF SET("TALK") = "ON"
SET TALK OFF
talkwas = .T.
ELSE
talkwas = .F.
ENDIF
cursorwas = SET("CURSOR") = "ON"
SET CURSOR OFF
DEFINE WINDOW colorshow FROM 5, 10 TO 16, 46 220, 223, 219, 219, 220,
220, 223, 223 &&ASCII box characters
cstring = STUFF(SET("ATTR"), AT(" &" + "& ", SET("ATTR")), 4, ",")+","
ACTIVATE WINDOW colorshow
comma = 0
showcolor = ""
cntr = 1
*--note final comma in following string
pstring =
"normal,highlight,border,messages,titles,box,information,fields,"
DO WHILE cntr < 9
@ cntr, 1 SAY "Color of " +left(pstring, AT(",", pstring) - 1) +
" is"
DO Parse
@ cntr, 26 SAY showcolor COLOR &showcolor
cntr = cntr + 1
ENDDO
comma = INKEY(0)
DEACTIVATE WINDOW colorshow
RELEASE WINDOW colorshow
IF talkwas
SET TALK ON
ENDIF
IF cursorwas
SET CURSOR ON
ENDIF
ON ESCAPE
RETURN
PROCEDURE Parse
*-- parse attribute string
comma = AT(",", cstring)
showcolor = LEFT(cstring, comma - 1)
cstring = SUBSTR(cstring, comma + 1)
*-- parse pstring
pstring = SUBSTR(pstring, AT(",", pstring) + 1)
RETURN
7 Digital Notepad Mike Dean/George McMullen]
The Digital Notepad
Mike Dean and George McMullen
As we rely more and more on doing various tasks on our computer,
several utility-type programs have popped up on the software market.
For instance, wouldn't it be convenient to have a utility that would
popup a window for you to jot down notes in, record an address, a
telephone number or a reminder about some weekly activity? Well I
thought so, so I wrote one and called it NoteIt. NoteIt is based on a
database with 2 fields: an indexing field and a memo field. Use
NoteIt for a address book. Put the name of the individual or
company, and then contact names, address, phone numbers and any
comments in the memo field. Since it is a memo field, you can enter a
great deal of information.
You might be wondering how do you retrieve all of these random notes
of information? To make locating your recorded thoughts easier, there
are three searches in NoteIt:
Quick Search: This is a SEEK of the index line and is the
fastest of all of the searches. This requires that you search the
entry starting from the leftmost characters in the index field. For
example, to look up the entry "Doe, John", you would have to start
your search criteria from the last name ("D", "Do" or "Doe", and so on
...).
Main Search: This is a search of any data in the index line.
For example, using the above information you could type in "John" and
it would find "Doe, John" as well as any other records that contained
the word or partial "John".
Comments Search: Like the Main Search but it searches the
comments (memo) field. You can search for any word or set of words in
the memo field.
These last two searches are sequential and will consequently take
longer on some machines, depending upon the size of your database.
You will need to create the simple database, called NoteIt, with the
following structure.
Field NameTypeWidth
MainLineCharacter30
CommentsMemo10
For the program to function correctly, you must add at least one
record prior to using NoteIt. This initial record can be edited while
in the NoteIt program by the special editing key Alt-E.
Other special editing keys are available on-screen when using NoteIt
by pressing F1. You still press Esc to abandon a memo edit or
Ctrl-End to save the changes. One point worth noting here is that the
cursor is normally positioned on the memo field in a window. Because
of this, you will need to press Return to regain access to the popup
picklist of names to the left of the memo window.
Another point that might raise questions is the presence of the ruler
in the memo field. When not active, there is no ruler visible. Upon
entering the memo field, in edit mode, the ruler appears, shifting the
text down. The question may then be, how do you eliminate the ruler?
The only way to do so is through the Words menu (accessible by
pressing Alt-W or F10), changing the Hide ruler option to YES. This
setting is not for the dBASE session but for that particular editing
session. Consequently, the next time you enter a memo for editing,
the ruler will once again appear. There is no additional parameter
that can be added to the GET command line of a memo that provides for
elimination of the ruler.
All in all, this program provides a handy utility and the code shows
some interesting programming concepts as well.
8 UDF Library
UDF Library
"At the tone, the time will be..."
Although most of you are aware that you can do basic arithmetic
functions with date and time fields, it is not as well known that you
can add decimal values to a date field and obtain a time
differential.
A need for this usually comes when trying to determine an elapsed
period of time. Functions that accomplish this have been seen in the
pages of TechNotes/dBASE IV in the past. Here now is another
variation on how you might accomplish it.
In the function called Elapsed, the variables sdate and edate are date
type variables while stime, etime and result are character type using
the template HH:MM:SS. To start the timing sequence, enter x =
elapsed(1) at the dot prompt or in a program. To stop or mark the
time, x = elapsed(0). The 0 can be passed repetitively to provide for
numerous time measurements from a singular starting point.
Creating Temporary Files
There are times when you might want your own application to be able to
create its very own temporary files like dBASE IV occasionally does.
The TempFile() UDF below returns the name of a temporary file that can
be used in your program. For example,
Temp = TempFile()
USE TRAVEL
COPY TO &Temp
USE &Temp
REPLACE ALL Cost WITH Cost * .06
AVERAGE Cost TO avg cost
ERASE &Temp
TempFile() honors the TMP environmental variable, so that the file
that your program ends up using is created and processed along with
the rest of your dBASE IV temporary files. Be sure that your program
erases the temporary files it creates, (as shown in the example
above,) or you might end up with a directory filled to the brim with
temp files.
Fonetic Philes
One goal of a developer is to make his software foolproof. If your
application has areas of functionality that can dynamically use any
.DBF or other file type that the user pleases, then it isn't
unreasonable to assume that the user will not always remember the name
or spelling of a target file. FileHelp() is a real nifty way to
ensure that a spelling error or lapse of memory will not slow the user
down.
FileHelp() takes two parameters: the filename that the user enters,
and a file extension. A list of SOUNDEX() spellings and sound-alikes
of filenames with the specified extension pops up. The selected
spelling from the picklist is returned unless no similar spellings
exist or the user presses Esc. In this case, an asterisk (*) is
returned.
FileHelp() makes use of the TempFile() UDF shown above. You must also
create a database file called AFILES.LOD. The structure consists of a
single field definition: A character field with a length of 12 and
named "File".
Using the DOS Path
PathFind() is another way that you can make your application more
iron-clad in the event that the user has no concept of what a
sub-directory is. You could almost use PathFind() as a substitute for
the FILE() function.
FILE(), of course, returns a .T. or .F. depending on whether or not
the file specified is present in the current sub-directory.
PathFind() takes it one step further. If the specified file exists in
the current sub-directory or ANY sub-directory in the DOS PATH, the
filename with the path in front of it is returned. If the file does
not exist in the current sub-directory or anywhere in the PATH,
PathFind() returns a "". For example:
? GETENV("PATH")
C:\;C:\DBASE;C:\DBASE\SAMPLES;C:\DOS
SET DIRECTORY TO \DBASE\SAMPLES
C:\DBASE\SAMPLES
? FILE("TRAVEL.DBF")
.T.
SET DIRECTORY TO \DBASE
C:\DBASE
? FILE("TRAVEL.DBF")
.F.
? PATHFIND("TRAVEL.DBF")
C:\DBASE\SAMPLES\TRAVEL.DBF
Function: Elapsed()
FUNCTION ELAPSED
PARAMETER action
success = .t.
DO CASE
CASE action = 1&& start
PUBLIC sdate,stime
stime = TIME()
sdate = DATE()
RETURN success
CASE action = 0&& end
etime = TIME()
edate = DATE()
OTHERWISE
? "Invalid action specified"
success = .F.
ENDCASE
hour = 1 / 24
min = hour / 60
sec = min / 60
dstart = sdate + VAL(stime) * hour + VAL(SUBSTR(stime, AT(':',
stime) + 1, 2)) *;
min + VAL(RIGHT(stime, 2)) * sec
dend = edate + VAL(etime) * hour + VAL(SUBSTR(etime, AT(":",
etime) + 1, 2)) * ;
min + VAL(RIGHT(etime, 2)) * sec
timedif = ABS(dend - dstart)
result =IIF(dend < dstart , '-', '')
rhour =INT(timedif / hour)
rmin = INT((timedif - rhour * hour) /min)
rsec = ROUND(( timedif - rhour * hour - rmin * min) / sec, 0)
result = result + LTRIM(STR(rhour)) + ':' + ;
RIGHT(STR(rmin + 100, 3), 2) +':'+;
RIGHT(STR(rsec + 100, 3), 2)
? (result)
RETURN success
Function: TempFile()
FUNCTION TempFile
* Author: Dan Madoni
tf = (GETENV("tmp") + LTRIM(STR(RAND(-1)*100000000,8))
+".TMP")
DO WHILE FILE(tf)
*-- Make sure this temp file doesn't already exist
tf = (GETENV("tmp") + LTRIM(STR(RAND(-1)*100000000,8));
+ ".TMP")
ENDDO
RETURN tf
Function: FileHelp()
FUNCTION FileHelp
* Author: Dan Madoni
PARAMETERS f_infile,f_ext
IF .NOT. FILE("AFILES.LOD")
*-- UDF is no good without AFILES.LOD
RETURN "*"
ENDIF
SAVE SCREEN TO B4pop
Function: FileHelp() continued
*--Strip any existing file extension
f_infile = IIF(AT(".", f_infile) = 0,;
f_infile,SUBSTR(f_infile, 1, AT(".", f_infile)
f_ext2 = "*." + f_ext
f_tmp = TempFile()
*-- Create a text file containing all files in the current
*-- sub-directory that have the specified extension and import
*-- the text file into AFILES.LOD.
RUN DIR &f_ext2 > &f_tmp
RESTORE SCREEN FROM B4pop
SELECT 10
USE AFILES.LOD
ZAP
APPEND FROM &f_tmp TYPE SDF
*-- Strip out the DOS messages and extensions
GO TOP
DELETE NEXT 4
GO BOTTOM
DELETE
REPLACE ALL File WITH RTRIM(SUBSTR(file, 1, AT(" ", file) -
1))
*-- Get rid of all the non-sound-alikes.
DELETE ALL FOR SUBSTR(SOUNDEX(File), 2) <>;
SUBSTR(SOUNDEX(f_infile), 2)
PACK
REPLACE ALL file WITH RTRIM(File) + "." + f_ext
IF RECCOUNT() = 0
*-- No similar filenames found.
ERASE &f_tmp
ZAP
USE
SELECT 1
RESTORE SCREEN FROM B4pop
RETURN "*"
ENDIF
*-- Pop Up resulting filenames.
SET COLOR OF MESSAGES TO W+/B
SET COLOR OF BOX TO G+/B
SET COLOR OF HIGHLIGHT TO W+/R
@ 5,34 FILL TO 20,48 COLOR W/N && Shadow
DEFINE POPUP flp FROM 4,33 TO 19,47 PROMPT FIELD File
ON SELECTION POPUP flp DEACTIVATE POPUP
ACTIVATE POPUP flp
ret = PROMPT()
RELEASE POPUP flp
ERASE &f_tmp
ZAP
USE
SELECT 1
RESTORE SCREEN FROM B4pop
IF LASTKEY() = 27
*-- If the user presses Esc.
RETURN "*"
ENDIF
RETURN ret
Function: PathFind()
FUNCTION PathFind
PARAMETERS p_file
IF FILE(p_file)
*-- Return immediately if exists in current directory.
RETURN p_file
ENDIF
p_nowpath = GETENV("PATH")
p_cntr = 0
DO WHILE p_cntr < LEN(p_nowpath)
p_cntr = p_cntr + 1
p_temp = ""
*-- Extract a directory from the PATH.
DO WHILE SUBSTR(p_nowpath, p_cntr, 1) <> ";" .AND.;
p_cntr < LEN(p_nowpath)
p_temp = p_temp + SUBSTR(p_nowpath, p_cntr, 1)
p_cntr = p_cntr + 1
ENDDO
*-- Create the file name to test.
p_temp = p_temp + IIF(RIGHT(p_temp, 1) <> "\", "\",
"");
+ LTRIM(RTRIM(p_file))
IF FILE(p_temp)
*-- If the concantenated directory/file
exists, RETURN it!
RETURN p_temp
ENDIF
ENDDO
*-- If we've gotten this far without RETURNing, then no such
file
*-- exists in the PATH.
RETURN ""
December 17, 2017
Add comments