Style Guide for FoxPro

File Names
Variables and Arithmetic
Flow of Control and Procedures
String Manipulation
Dates
Screen Drawing and Input
Database Access
File I/O
Other Conventions
What Else?

If you're lucky, the rewrite of reg and mlist will not require looking at the existing code at all and this whole page can be ignored. If you're not lucky or just curious, this page serves to give a quick overview of the elements of the language. The code is neither pretty nor elegant. As I came to FoxPro/dBase III+ as a C programmer I mostly used the low level elements which in the end made it quite labor-intensive. This may have kept the implementation easier to understand. Not sure.

FoxPro is a very simple, but adequate language for making customized database applications like reg and mlist. Here we mostly explain by example because it is assumed that the audience is composed of competent software engineers. This is a quick overview. The most authoritative documentation is, of course, the FoxPro on-line help. Invoke 'foxpro -t' at the unix shell and type 'help <topicname>' in the command window. There is a 'See Also' choice which is helpful. Ask Sahadev about anything as well. As usual, only a small subset of the language is used. Since reg and mlist were originally written in dBase III+ many/most of the FoxPro extensions and higher level tools were not used. In part, this is because the work was done as a volunteer.

File Names     

Source files end in ".prg". They are 'compiled' into ".fxp" files. This compilation happens automatically if the source is out of date or you can at any time manually issue the "comp" command in the command window.

Variables and Arithmetic     

Variable are undeclared and typeless (Perl-like) (or more accurately dynamically typed), case-insensitive, and composed of letters, digits and underscores. Only the first 10 characters matter. So these two lines have the same effect:
x123456789 = 10
x123456789012345 = 10
Scoping? Not precisely sure. I use the language in an 'empirical' way. Variables can be declared 'public' to make them globally available across files. Those declared 'private' are scoped within a proc.

Flow of Control and Procedures     

if nrec = 3
    j = 4
endif

*
* calculation of k:
*
if total > 4 .and. sum < 23
    k = 5
else
    k = 6       && not seven
endif

do case
case type <> 2 .or. ans = 'yes'
    ...
case type = 3
    ...
case type >= 4
    ...
otherwise
    ...
endcase

sum = 1 + 2 + 3 + ;
	  4 + 5 + 6 + ;
	  7

**********************
*
*
proc check_it
parameter x
return x > maxnum

**********************
*
*
proc dumpout
parameter s, i
? trim(s)
? i*3
return

done = .f.
i = 1
do while i <= 10 .and. .not. done
    ? i
    i = i + 1
    do dumpout with "hello", 34+i
    done = check_it(i)
    do case
    case k = 4
        loop        && next iteration
    case k = 5
        exit        && out of the loop
    case k = 6
        quit        && program ends
    endcase
enddo

String Manipulation     

+ is the concatenation operator. Constant strings use either a single or double quote. These functions do pretty much what their names say: trim, alltrim, len, str, at, substr, upper, lower, empty, replicate. Look them up in the online help. str and val are inverses of each other.

Dates     

Look up these date related functions: date, dtoc, ctod, year, month, day, cdow, cmonth, dmy, ymd.

Screen Drawing and Input     

Basics:
clear

@10,25 say "Hi there"

@11,20				&& clear from 11,20 to end of line

@10,10 clear to @12,20    && clear a rectangular region
Simple screen input:
ans = 'N'
@10,10 say "Are you sure?" get ans picture '!'
read
More details in the FoxPro online help topic 'help @ ... get'.

Point and pick menus:

dimension fruit(4)      && an array
fruit(1) = "banana"
fruit(2) = "apple"
fruit(3) = "orange"
fruit(4) = "plum"
@10,10 menu fruit, 4 title " Which Fruit? "
j = 3       && default to oranges
read menu to j

Database Access     

Access to the data is nicely integrated into the FoxPro language. SQL access was implmented in later releases of FoxPro but since it was not present in dBase III+ it is not used much (if at all) within reg/mlist. Also, the few times I tried to use the FoxPro SQL it unfortunately (and inexplicably) proved somewhat unreliable.

Tables (known as databases in FoxPro - kept in .dbf files) are created with a nifty full screen dialog:

create people
And modified with this:
use people
modify struct       && abbreviated 'modi stru'
Both dialogs are terminated with Control-W (write) and aborted with Escape.
Data types include: Character, Numeric, Logical, Date, Memo.

Multiple tables can be 'active' at the same time in different work areas. Set them with this:

select 1
use people
select 2
use roles
select 3
use pets
'select 0' will use the first unused work area.

Perhaps unique to dBase (and descendants) applications there is the concept of a 'current table' and within each table there is a 'current record'. People used to using SQL to access the data may find this odd or too low level or too labor intensive. Surely it is more fragile than using SQL!

In any case, continuing with the description ... The pointer to the current record can be moved around with:

skip 
if eof()    && are we at the end?
    ...
endif
skip 10
skip -1     && go backwards
go top
go bottom
Tables are ordered with the 'index' command. Once the index is defined it is maintained as records are edited, inserted and deleted.
use people
index on last+first tag name
index on age tag age
set order to name
set exact off
seek "SMITH"
if found()
    ...
endif
Filters limit which records can be seen:
select people       && makes 'people' the current table
set filter to age > 20
go top              && this actually sets the filter
Relations between tables help to give FoxPro the title of a 'relational database'. As you move through one table the current record pointer for a related table also moves. Unfortunately, this is not so easy to maintain properly by hand. SQL is a good idea!
select 0
use car
set order to id

select 0
use people
set order to name
set relation to p_car_id into car

do while .not. eof()
    ? p_name + " => " + car->c_make + ;
                  " " + car->c_model
    skip
enddo
Multi-user access to a table/record is protected by using the rlock, flock and unlock functions. If this is done consistently (and voluntarily) before any additions, replacements or deletions then all will be okay when multiple users simultaneously access the same table/record. If not, all will not be okay!
select people
if flock()
    append blank
    gather memvar
    unlock
endif

File I/O     

These are the functions to do file I/O: fcreate, fopen, fgets, fputs, feof. Read about them in the online FoxPro help. Here's an example:
F = fcreate("program")
if F = 0
    return
endif
use prog
do while .not. eof()
    =fputs(F, "name " + p_name)
    =fputs(F, "sdate " + dtoc(p_sdate))
    skip
enddo
=fclose(F)

G = fopen(fname)
do while .not. feof()
    line = fgets(G)
    ...
enddo
=fclose(G)
FoxPro can't deal with files whose names are longer than 8 characters for compatibility with DOS (remember DOS?).

Other Conventions     

I have consistently followed indentation conventions of 4 spaces (not tabs). I have aligned screen coordinates like this:
@ 9, 8 say "hi"
@10,20 say "bye"
Database (table) field names have a unique prefix (mlist => m_, reg => r_, prog => p_). I was mostly consistent in the way I implemented access to a table. Simple menus at row 23 with "Next, Back, Search, Add, Edit, Delete, Quit" menu choices. Records are shown on the screen with procs named using the above prefix: r_paint, r_show, r_get. Sometimes the x_get proc needed to have individual 'read' statements for each field to be able to include a point and pick menu. This complicated things considerably. If I had used the FoxPro screen and menu generator it wouldn't have been so labor-intensive... but I didn't ... for a variety of reasons. Getting data out of a table record and into memory variables (m.*) is done with 'scatter memvar'. Putting it back (after modification or at insert time) is done with 'gather memvar'.

What Else?     

Ask Sahadev.