03 - Open Source (EN)

Harbor – First steps in development

Last Updated on 13 June 2021 by Roberto De Pedrini

Today in this article, let’s see some basic notions to start writing code with harbor and better understand the functions made available.

Simple data type available

code block (only available as an internal variable in Harbor)

The type of code block can be imagined as a box that has an input and an output as a function, but the latter is not executed directly but behaves like a value (To treat this object we use the macros EVAL (), AEVAL ( ), DBEVAL ()), its execution occurs during the compilation phase of the program. Thus avoiding the evaluation of a macro-instruction, the slowness of calling a procedure and the error of functions not associated with the executable program.

EXAMPLES of code block:

  • code block with constant in memory:
{|| "Hello"} no arguments, returns the string hello, the value remains constant 
  • block of code with constant that is printed on the screen:
? EVAL ({|| "good morning"} // video print good morning
  • block of code stored in a variable that contains one or more arguments:
LOCAL square: = {| x | x ^ 2}? (EVAL (square, 2) + EVAL (square, 3) // print on screen 13.00
  • code block that allows you to export variables:
PROCEDURE main () LOCAL radius: = 5, height: = 10? Height * EVAL (sup (), 5) // returns a block of code containing a local variable to sup (), // the variable pi which is perfectly visible within the rest of the program RETURN FUNCTION sup () LOCAL pi: = 3.14159 RETURN ({| x | pi * x * x})

Type char (C) (present both as an internal variable in Harbor and as a field type in a dbf table)

Like all existing languages, harbor also uses string or character data types in particular it follows the C language standard. To represent the value as a character we have (as on RPG, Python etc …) the use of the single superscript. While to represent the string we use the double quote (as an alternative to the double quote we can use square brackets).

EXAMPLE of characters:

string1 = "Hello" // The most common notation string2 = 'Hello' // the notation to define the data in character format as in RPG string3 =[Hola] // alternative method to define a string // ---------------------------------------- ------------------------- thanks to the use of square brackets we can delimit the strings as: string1 =[Egli domandò:"A che ora arriva?" ] // or alternatively string2 = "The bibliographic reference"[1] "

Date type (D) (present both as an internal variable in Harbor and as a field type in a dbf table)

The date type that we find on Harbor allows us to easily manage any type of date, carrying out any type of arithmetic operation. Then using the SET DATE command we can represent it in any required standard.

EXAMPLE date type:

FUNC tomorrow () RETURN date () + 1

Boolean type (present both as an internal variable in Harbor and as a field type in a dbf table)

Another standard data also available in Harbor is the logical type. To represent them in clipper they are used .T. for True value e .F. by value False.

Bool value example:

 LOCAL y: =.F.
 IF y =.T. 
      ? "Working" ELSE? "Borken" ENDIF 

MEMO data type (M) (only available as type within dbf tables)

The MEMO data type inherits the characteristics of the character type field but the difference is that its length is variable, this data type can only be found within a DBF table and not directly as a Harbor internal variable, it can be associated with the varchar type field of SQL databases.

Numeric data type (N) (present both as an internal variable in Harbor and as a field type in a dbf table)

Unlike other languages, Harbor uses only one numeric type (the double-precision floating point number). With a series of functions it can understand the type used and automatically comes in its internal format. you can define values between 1.7 * 10 ^ -308 and 1.7 * 10 ^ 308.

SET commands

Having seen the existing datatypes in Harbor, I wanted to talk about DBF file management first to explain SET commands. The latter are global commands with which we can flexibly enable and disable functions or properties. For example we can create indexes on our DBF files with the SET INDEX command or set the date standard with the SET DATE command. Below is a list of existing commands with a description of the function they perform (each command references ( CA-Clipper 5.3. Guide To CA-Clipper – Short Entry (itlnet.net) ) also this site as the clipper documentation is valid and compared to the documentation it offers separate commands by category:

SET ALTERNATE Echo console output to a text file SET BELL Toggle sounding of the bell during full-screen operations SET CENTURY Modify the date format to include or omit century digits SET COLOR * Define screen colors SET CONFIRM Toggle required exit key to terminate GETs CONSOLE SET Toggle console display to the screen SET CURSOR Toggle the screen cursor on or off SET DATE Set the date format for input and display SET DECIMALS Set the number of decimal places to be displayed SET DEFAULT Set the CA-Clipper default drive and directory SET DELETED Toggle filtering of deleted records SET DELIMITERS Toggle or define GET delimiters SET DESCENDING Change the descending flag of the controlling order SET DEVICE Direct @ ... SAYs to the screen or printer SET EPOCH Control the interpretation of dates with no century digits SET ESCAPE Toggle Esc as a READ exit key SET EVENTMASK Specify events to be returned by the INKEY () function SET EXACT * Toggle exact matches for character strings SET EXCLUSIVE * Establish shared or exclusive USE of database files SET FILTER Hide records not meeting a condition SET FIXED Toggle fixing of the number of decimal digits displayed SET FORMAT * Activate a format when READ is executed SET FUNCTION Assign a character string to a function key SET INDEX Open one or more order bags in the current work area SET INTENSITY Toggle enhanced display of GETs and PROMPTs SET KEY Assign a procedure invocation to a key SET MARGIN Set the page offset for all printed output SET MEMOBLOCK Change the block size for memo files SET MESSAGE Set the @ ... PROMPT message line row SET OPTIMIZE Change the setting that optimizes using open orders SET ORDER Select the controlling order SET PATH Specify the CA-Clipper search path for opening files SET PRINTER Toggle echo of output to printer or set the print destination SET PROCEDURE * Compile procedures and functions into the current object file SET RELATION Relate two work areas by a key value or record number SET SCOPE Change the boundaries for scoping keys in controlling order SET SCOPEBOTTOM Change bottom boundary for scoping keys in controlling order SET SCOPETOP Change top boundary for scoping keys in controlling order SET SCOREBOARD Toggle the message display from READ or MEMOEDIT () SET SOFTSEEK Toggle relative seeking SET TYPEAHEAD Set the size of the keyboard buffer SET UNIQUE * Toggle inclusion of non-unique keys into an index SET VIDEOMODE Change the current video mode of the current application SET WRAP * Toggle wrapping of the highlight in menus

What is a DBF?

The first question that comes naturally to those who know Harbor for the first time or the clipper or who vaguely remembers it is the file with the .DBF extension. Initially created by Dbase in 1983, it has become one of the standard files that is still widely used today. This extension behaves like a PF file on ibm i and compared to other languages that need external drivers to manipulate the data inside, harbor like RPG can open these files natively. We see in this article the commands mainly used.

Creating a DBF file (batch)

To create a DBF file within Harbor, go to the folder where we want to reside our DBF and open the interpreter in the terminal HBRUN at this point we use the CREATE command or the dbcreate () function to create our first DBF file. This is the command we’re going to use:

    // command to create the structure CREATE TempStru APPEND BLANK REPLACE Field_name WITH "Name" ,; Field_type WITH "C" ,; Field_len WITH 25 ,; Field_dec WITH 0 CLOSE // command to create the DBF file CREATE NewFile FROM TempStru

If we look closely at the example we can see that the command CREATE allows us to create a temporary structure with the properties of our columns, with the command APPEND BLANK let’s add a new space to that command REPLACE for each column we declare it will be stored inside the structure. Then with the CREATE command declaring the file name and with the command FROM indicating the structure, we have successfully created our DBF file. We can create the file either as an interpreter by copying every single command or we can create a file with extension .prg copy the commands into it and launch in the terminal ( HBRUN script.prg ) and at this point our file will be created in batch ready to be used.

Creating a DBF file (interactive)

And if we don’t have a ready-made structure and we want to create the .dbf interactively, we just need to launch it in the interpreter HBRUN the command CREATE with the name we want to give to our DBF and immediately after if we notice, in the folder where the terminal was opened our DBF file is created, but here we must be careful because currently the file is empty and has not been initialized even with a column inside. At this point, if we look on the terminal once the previous command has been given, the console seems to have done nothing, but in reality it is waiting for our instructions since the file was created and committed by Harbor himself. At this point it is natural for us to close the window and believe that we have completed the work. In reality we have to define the columns and to do this it is sufficient to write the command in the terminal BROWSE (), at this point we will see a screen where we can add columns and define their properties a bit like the DBU on IBM i. At this point the DBF file will contain as data the properties of the previously inserted columns. To finalize our DBF we need to run the command CREATE test FROM test , in this way Harbor will read the definitions of our fields and transform the file ready to host our data.

1) open the terminal and type hbrun
create command to create the dbf in this case the file will be called test
newly created file
Once the file has been created, launch the Browse () command
We define the fields with their properties
at this point I launch the create command to create the final table

How to open a DBF within our program and externally

Once we have created the DBF with our columns we have our dbf file that we can use, the first thing that comes to mind is how can we view the rows of our file?

USE command indispensable

The first thing to do always both in our source and in the HBRUN is to open and allocate the file, to do it as on SQL we use the command USE , this way Harbor like SQL and RPG with the F specification knows it has to take our file into consideration.

// Example of use of USE USE test // in this way we are engaging the file if we open the DBF file in a shared way as for the PF on RPG the table will not be accessible to other users in writing and editing but they have to wait for the person who commits the file has freed it.
USE test alias t // I assign an alias to the file name from here onwards to use the columns I have to consider the alias USE test NEW // in this way I commit the file and place it in memory, to be used when I have to commit more of a DBF file, I can commit up to 10 DBFs at the same time.

USE test SHARED NEW // in this way I commit the DBF file in a shared way I can have multiple users insert lines on the network.

USE test VIA "DBFCDX" // in this way we declare which database driver must be taken into consideration, we will talk about it later // Please note: // if the dbf file is not in the same folder, the entire path of the file, to pass values to commands (instructions without brackets) through constants or variables, they must be enclosed in round brackets here is an example with the USE command in this case LOCAL path DBF: = 'C:  hb32  test.dbf '// to initialize a variable always use: = to change the value only = USE (DBFpath)

Insert and edit lines in the DBF

If we are now on HBRUN or have created our own program from the examples in the tests folder and entered USE we are wondering which command should we use to view our data? To do this it is very simple, if we want to view it on the console screen and do not have to create a custom one, we can use the standard table. Both in our program and in the HBRUN we can use the command BROWSE () . In this screen we can add, remove or edit lines.

Browse () command to edit, delete or insert rows
browse () screen by default

If instead we want to integrate this basic view of our new table we can use the browse () command within our console application and we can customize it to our liking.

// BASIC EXAMPLE OF DATA DISPLAY PROVIDED DIRECTLY BY HARBOR AVAILABLE IN THE tests FOLDER // Testing Browse () #include "inkey.ch" PROCEDURE Main () LOCAL cColor: = SetColor ("W + / B") CLS USE test Browse () SetColor (cColor) CLS RETURN

To see this example in action, just go to the tests folder with the terminal and if we want to execute it in an interpretive way we launch the command hbrun browse.prg or if we want to generate an executable and have the program compiled we use the command hbmk2 browse.prg

Example browse.prg run compiled
Executable output

If we want to insert a row directly on the DBF without displaying the grid we will have to use the combination of commands APPEND BLANK is REPLACE column_name WITH our value. In this way by default at the end of the existing lines Harbor will create a new empty line and with the command REPLACE we will substitute our values for each single column.

// example of inserting new value PROCEDURE main () USE test VIA "DBFCDX" APPEND BLANK REPLACE name WITH "Mario" RETURN

If we want to replace the values of an already existing row we have to use the command directly REPLACE , the latter behaves like the command UPDATE of SQL. This command can be used to affect the change. Let’s now see a detailed example:

// SQL language UPDATE command UPDATE table SET column = value where id = value // Harbor REPLACE command local name1 name1: = 'mario' USE clients REPLACE name with name1 FOR id = 1 // I replace the value of the column name in all the rows with the column ID = 1 // beyond the FOR I can use the WHILE REPLACE name with 'mario' WHILE id = 1 // if I specify only the REPLACE without the conditions the change will be made in the row where Harbor is currently positioned COMMIT // optional command that allows forcing the line change in case of problems

THE ndici and queries on DBF files

A strength that Harbor has available is the ability to create indexes on DBF files that are useful in our program to search for data quickly. To create an index, just use the command INDEX ON column TO index_name. In this way Harbor will create a file with our index and when we want to use it within our program we just need to use the command SET INDEX TO index_name, this command is mainly used in case we have more than one open DBF and indexes created, so we tell Harbor to consider our index in a particular situation. Index files can be combined with IBM’s logical files I useful for use in an index search, for example.

// example of search using the seek command the counterpart of the chain on harbor (topic addressed in the introductory article) RPG -------------------------- ------------------------------------------ Chain klst1 FILE01; If% found (FILE01); Dsply 'Record found'; Endif; -------------------------------------------------- --------------------- HARBOR ---------------------------- ------------------------------------ USE Customer NEW INDEX ON Customer-> ID TO ID SEEK "Doe "IF FOUND ()? "Record found" ENDIF --------------------------------------------- --------------------------

As we see in this example we use the SEEK , this command allows you to randomly search for a value that we have given as input within the column. This command is very fast, the only drawback is the obligation to search on an indexed field by starting from where the cursor is positioned, unlike the CHAIN , in combination with the command SKIP the SEEK allows you to search for more than one match within a table.

// example with seek to find a value on several lines - example taken from the clipper documentation // you can find detailed references on seek in the following link: https://www.itlnet.net/programming/program/Reference/c53g01c/ ng49aff.html PROCEDURE main () USE Customer INDEX Customer NEW // can we index directly in USE SEEK "Smith" DO WHILE FOUND ()? customer-> name // with the symbol -> I select the column from the table the SQL equivalent of table SKIP column // I go to the next record until I find a match ENDDO RETURN

If we want to avoid using loops to look up values in the table and want to use a SQL-like approach, we can rely on commands LIST is DISPLAY . The latter behave like one SELECT , as an important requirement in both is to always declare the name of the columns we want to display otherwise empty rows are returned (the * character is not present to display all the columns if I have to display everything as explained before I use the BROWSE () ). These commands also include the ability to filter data through the use of conditions . These commands allow us to decide where to print our output if we specify the command TO FILE followed by a filename, Harbor will write the output directly into it. If we specify TO PRINTER the output will be printed directly by the printer. One of the characteristics that distinguishes DISPLAY from LIST is the ability to specify the number of rows that are returned to us in output. But let’s see a small example of the two commands that we can use both in our programs and also interactively:

// display command examples - for more details refer to documentation USE Sales NEW // I commit the file DISPLAY DATE (), TIME (), Branch // I show on the screen only the line where the cursor is currently positioned with the columns that I have declared DISPLAY NEXT 20 DATE (), TIME (), Branch // I show 20 lines from the cursor with the columns I have specified DISPLAY Branch, Salesman FOR Amount> 500 // The records that satisfy the specified condition are shown on the screen DISPLAY Branch, Salesman FOR Amount> 500 TO PRINTER / / display with conditions using a FOR loop // and send the results I requested directly to the printer with the command TO PRINTER DISPLAY Branch, Salesman while Amount = 500 TO FILE test // display with conditions using a while loop // and send the results I requested to an example text file with the // command TO FILE FILE_name DISPLAY ALL Branch, Salesman // show all records // list command example - for more details refer to documentation USE Sales LIST Date (), Time (), Branch // I show all the rows on the screen with these columns specified In this example, the program outputs all the rows with amount> 500, and the output is sent to a file in this case the file is called test LIST Branch, Salesman FOR Amount> 500 TO file test In this example, the program outputs all the lines with amount> 500, and the output is sent to a LIST Branch printer, Salesman FOR Amount> 500 TO PRINTER example of LIST using the WHILE condition the query in this specific example continues indefinitely until the user has pressed the Esc key: #define K_ESC 27 USE Sales INDEX Salesman NEW LIST Branch, Salesman, Amount WHILE Inkey ()! = K_ESC

The Harbor Setll

Before going on, it is necessary to explain an important commano, the GO . The latter behaves as on RPG, i.e. it allows us to move the cursor within the table in a desired way useful in all commands such as the SEEK to decide where to start a search within the table.Let’s see immediately how it is used:

Go command documentation available here: https://www.itlnet.net/programming/program/Reference/c53g01c/ngcc35b.html // command to return to the beginning of the RPG table ------------------ setll * loval; --------------------- Harbor ------------- go top ------------- -------- // command to position the cursor at the end of the RPG table ------------ setll * hival; ---------------- Harbor -------- go bottom ---------------- // positioning on a specific record go 10 // the cursor is positioned at line 10 // example reported by the documentation // in this example a search is used and go is used to return to the previous line FUNCTION KeyExists (xKeyExpr) LOCAL nSavRecord: = RECNO () // Save the current record // pointer position LOCAL lFound SEEK xKeyExpr IF (lFound: = FOUND ())? "record found" ENDIF GO nSavRecord // Restore the record pointer position RETURN (lFound)

Delete a line in the DBF file

After entering, editing and viewing our record. One wonders how I can delete a record in our file. Here you need to be careful, when Harbor deletes the record in our file it does it logically. To prepare the line for deletion, I use the command DELETE also the latter can be conditioned with the WHILE and FOR and I can mark more than one line that meets the conditions I have indicated. Once I have marked the records to confirm the deletion we use the command PACK . If we then want to retrieve a record or more than one, we must use the command RECALL before PACK . If we want to delete our table from all the rows, we use the command ZAP .

// For documentation on all the commands mentioned you can refer here https://vivaclipper.wordpress.com/tag/delete/ // example deleting record USE Sales INDEX Salesman NEW DELETE ALL FOR Inactive // all records with boolean value in the Inactive = true column are deleted PACK // I confirm the deletion of the file // example deleting records and recall USE Sales NEW DELETE RECORD 4? DELETED () // Result:.T. // mark the record at line 4 and with deleted () check if RECALL has been marked? DELETED () // Result:.F.

 // to cancel I use recall and if I re-query the deleted () function this time 'it will return me.F. false // delete the entire ZAP table

For an overview of other commands or functions you can refer to the official documentation: https://harbour.github.io/doc where this article refers.

Constructing console interfaces – example of a working mini application

For those who remember the good old clipper, the latter was famous for simplified console interface design. We know well even if obsolete, our dear 5250 screens or consoles are still very productive today. Thanks to its simplified syntax as seen in the introductory article with a few lines of code we can create a selective menu or a data entry screen (similar to the creation of display files on IBM i).But let’s see in detail a small example:

// small example of input mask with creation and insertion of values inside the database PROCEDURE Main () // command to create the structure of the table CREATE TempStru APPEND BLANK // add to the temporary table it is necessary to append blank to create the definitions of the new fields REPLACE Field_name WITH "NOME" ,; Field_type WITH "C" ,; Field_len WITH 25 ,; Field_dec WITH 0 APPEND BLANK REPLACE Field_name WITH "SURNAME" ,; Field_type WITH "C" ,; Field_len WITH 25 ,; Field_dec WITH 0 APPEND BLANK REPLACE Field_name WITH "ETA" ,; Field_type WITH "N" ,; Field_len WITH 3 ,; Field_dec WITH 0 CLOSE // command to create the DBF file CREATE registry FROM TempStru CLS cSurname = SPACE (20) cName = SPACE (20) nEta = 0 @ 07.18 TO 13.63 DOUBLE COLOR "g + / n" @ 08, 20 SAY "Enter the surname" COLOR "w + / n" get cSurname COLOR "rg + / r" @ 10,20 SAY "Enter the name" COLOR "w + / n" get cName COLOR "rg + / r" @ 12,20 SAY "Enter the age" COLOR "rg + / n" get nEta COLOR "w + / b" READ USE anagraphic ALIAS anag APPEND BLANK REPLACE anag-> SURNAME WITH cSurname REPLACE anag-> NOME WITH cNome REPLACE anag-> ETA WITH nEta RETURN For compiling our example, we just need to enter the command in the folder where we created our source: hbmk2 nomesorgente.prg if we want to launch it without compiling within the harbor interpreter we use the command hbrun nomesorgente.prg if we then inserted a line launching the program and we see inside our table that the fields have been added inside our DBF file.

Output:

As we can see in two lines we have created a ready-to-use input screen. Let’s now analyze the syntax in detail:

CLS – equivalent of the DOS command to clear terminal screens

cSurname = SPACE (20) initialize a variable to capture the input value entered by the user

@ – the @ symbol allows me to specify the position of the field I am going to insert.

SAY – the SAY command corresponds to a label type field to be used as a constant or as an output field

COLOR – following a table present in the documentation we can declare the color of each single element.

GET – The GET command corresponds to the input field (remaining in the ibm i theme it is equivalent to the FLD000x field) that we use to obtain value from the user.

READ – Command that reads the input values

@ 07.18 TO 13.63 DOUBLE COLOR “g + / n” – This compound command allows you to create a frame with a double pane.

This syntax is universal and with the evolution of Harbor we are not forced to study particular graphics libraries (unless we want to use a specific library such as QT). Once we have learned how these commands work, we can use them with their respective libraries in creating desktop or web screens, in the next chapter we will see how to create a classic desktop form using the HMG graphics library. In the meantime, for those who want to snoop on the commands for the interface, they can always refer to the official documentation.

EXTERNAL REFERENCE LINKS TAKEN FOR THIS ARTICLE:

https://vivaclipper.wordpress.com/

CA-Clipper 5.3. Guide To CA-Clipper – Short Entry (itlnet.net)

Harbor Language Wiki Home

ElektroSoft – IT and Electronics in Sicily – Caltanissetta

Harbor Reference Guide · Index

About | Fivetech Software

Harbor Language – Old Clipper Gets New Life | Open Source Electronics (emcelettronica.com)

Related Posts
DB2 for i SQL – String Manipulation – POSSTR-LOCATE-LOCATE_IN_STRING (EN)

Introduction Often, in our applications, we need to work with text strings, and DB2 SQL can come in very useful Read more

DB2 for i – FAQ & Howtos (EN)

DB2 Database and SQL ... maybe the most important things on IBM i platform: here's a collection of FAQs, tips Read more

IBM i 7.4 Announcement (En)

Comes directly with the Easter egg this IBM announcement for the news of the IBM i 7.4 version, iNext version Read more

Generated Always Columns (EN)

Introduction "Generated Always Column": are columns, table fields, filled by DB2 engine: something like columns with a default value but Read more

About author

Giorgio Caponera, IBM Fresh Face 2019, developer and IBM i passionate

Leave a Reply

Your email address will not be published. Required fields are marked *