Clipper Tutorial: a Guide to Open Source Clipper(s)/Printable version

From Wikibooks, open books for an open world
Jump to navigation Jump to search


Clipper Tutorial: a Guide to Open Source Clipper(s)

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Clipper_Tutorial:_a_Guide_to_Open_Source_Clipper(s)

Permission is granted to copy, distribute, and/or modify this document under the terms of the Creative Commons Attribution-ShareAlike 3.0 License.


Getting Acquainted

Open Source and Licenses[edit | edit source]

Let's define Open Source. It is software distributed with the source code that anybody can study, modify for their own needs and redistribute. It is often also free (i.e. it costs nothing). It is linked with the →Copyleft concept. There is a copyright holder, and in this sense, it is different from public domain software.

A very good definition (in 10 criteria) can be found here: https://opensource.org/osd, and it is derived from the Debian Social Contract https://www.debian.org/social_contract#guidelines.

Harbour, xHarbour and Clip are released under the GNU General Public License (GPL) plus an option that allows the users to distribute the applications they develop under the license they wish (if they were to follow strictly the GPL they would have had to distribute their applications under the GPL). The GPL is the license adopted by the GNU project, making it, perhaps, the most famous Open Source license because an entire operating system is released under it - the OS that is commonly referred to by the name of its kernel, Linux, and that some people call GNU/Linux. The kernel created by the GNU Project (The Hurd) never really became successful. A modified version of the Linux kernel is the base of the operating system Android.

Another operating system (mostly experimental), is ReactOS (https://reactos.org/), which looks like Windows and is designed to work like it and use its same applications. Harbour can run smoothly under ReactOS.

These are the essential questions that we need to answer:

→Free Software Foundation: The Free Software Foundation (FSF) is a 501(c)(3) non-profit organization founded by →Richard Stallman on 4 October 1985 to support the free software movement, which promotes the universal freedom to study, distribute, create, and modify computer software,[4] with the organization's preference for software being distributed under copyleft ("share alike") terms,[5] such as with its own GNU General Public License.[6] The FSF was incorporated in Massachusetts, US, where it is also based.[7]

For more information, visit https://www.gnu.org/philosophy/floss-and-foss.html, http://www.fsf.org/licensing/licenses/gpl.html, http://www.linux.org/, https://www.gnu.org/philosophy/open-source-misses-the-point.html, https://www.toptal.com/open-source/developers-guide-to-open-source-licenses.

In addition to the GPL, Clip offers, optionally, a commercial license. Users can choose which license applies to their installation.

x2c is released under the Apache license (http://web.archive.org/web/20080819175600/http://x2c.dtop.com/LICENSE.txt). The most important software released under the Apache license is Apache, a widely used open source web server - https://w3techs.com/technologies/details/ws-apache/all/all.

X# has its own license, https://www.xsharp.info/licensing/xsharp-compiler-source-code-license

Besides the free distribution of the software source code, there is also the problem of free distribution of documentation. To complement the GPL license, the GNU project introduced the GNU Free Documentation License (https://www.gnu.org/philosophy/using-gfdl.html). Creative Commons (https://creativecommons.org/) built up six different licenses based on four conditions as described at https://creativecommons.org/share-your-work/licensing-types-examples/. This site (Wikibooks) uses the Creative Commons Attribution ShareAlike (CC BY-SA 3.0) license and the GFDL as described at https://en.wikibooks.org/wiki/Wikibooks:Copyrights ()

Software Piracy and Source Code Sharing[edit | edit source]

A letter written by Bill Gates in 1976 provoked a certain clamor. His company, Micro-Soft (which during the years changed its spelling), was selling a BASIC interpreter for the MITS Altair 8800 and Gates lamented that many were copying it without paying. You can read it here: https://genius.com/Bill-gates-an-open-letter-to-hobbyists-annotated. BASIC, however, had a great success: all the home computer vendors were developing their own BASIC interpreters or licensing one from Microsoft to store in the computer's ROM. The microcomputers, or home computers, of the time thus were simple machines with 8-bit CPUs that shipped with a BASIC interpreter in ROM. Many books, computer magazines (and some electronic magazines too) were publishing the listings of programs: back in the '80, it was normal to share the source code, at least for simple programs.

Also, because there were many different machines and therefore many BASIC dialects, many magazines had columns about this or that computer BASIC, and even existed magazines targeting specific computers: there were Sinclair magazines, Commodore magazines, Atari, Acorn, and so on.

You could, of course, have heard comments like "if it's published in a magazine it cannot be valid or they would be selling it in a store", but studying the listings was educational. A nice example is described at this URL: http://www.alfonsomartone.itb.it/fztsmo.html (I think it's a very nice read - which also give a sense of the parochialism that existed between fans of different computer brands), I am referring especially to the passage in which they talk about the copying and adaptation of the Totocalcio program listing from a Spectrum magazine. Totocalcio is the Italian system for betting on football matches (here 'football' is the sport which is called 'soccer' in the US, have a look at https://www.britannica.com/story/why-do-some-people-call-football-soccer). We will return to sport betting in a moment, as it was the... kickstart of dBASE development.

But then, after the transition to 16-bit DOS-based machines, BASIC interpreters became more rare (the free Microsoft QBasic interpreter was not automatically installed on Windows 95 or 98 - although it was provided on the CD - and no longer shipped with later versions of Windows; moreover starting with Windows Vista it was no longer supported being a 16-bit DOS application) and programming for beginners got harder and more expensive, as for a long time, compilers have been quite pricey and so more difficult to access (especially if you wished to program a Windows graphical user interfaces), while at the same time distributing interpreted programs seemed less and less 'professional', so to speak.

Meantime the increase in performance allowed the use of the "small" microcomputers (that were growing) for other applications: if the widepread use of audio tapes as mass memory prevented the use of a Commodore 64 or a ZX Spectrum to manage "databases" (and floppy discs too were also limited for these tasks), the decrease in prices and the increase in the capacity of the hard drives has allowed them to do these things.

What do we refer to when we say 'database'?[edit | edit source]

The word database is used with at least three meanings:

  1. a collection of data. In our context, these data are recorded in DBF files and, with this meaning, it can happen to hear about "a relation between two databases".
  2. the collection of data and all its ancillary components. This is the meaning of database when we talk about an "Access database": a .MDB or .ACCDB file that contains the information of the tables, relations, queries, indexes and more.
  3. a DBMS (Database Management System), that is, a software that manages data stored in database files.

Some Curiosities, Digressions, and a Little History[edit | edit source]

There is a website called foxprohistory.org which reports an interview with Wayne Ratliff: http://www.foxprohistory.org/interview_wayne_ratliff.htm (see also http://www.foxprohistory.org/people_began.htm).

The forefathers of dBase were a program called RETRIEVE and later JPLDIS (w:Jet Propulsion Laboratory Display Information System) used at the California Institute of Technology's Jet Propulsion Laboratory (https://www.jpl.nasa.gov/about/). Jeb Long wrote JPLDIS, and Cecil Wayne Ratliff in 1975 began to think of a database system with the intention of processing football game statistics to bet (according to the reports the floor of his house was completely covered with sports pages from newspapers). The following year he got himself an IMSAI 8080 computer kit (for fans of movies of the 80s it is the same computer model appearing in the movie WarGames). Ratliff later admitted that programming his DBMS for the IMSAI 8080's PTDOS operating system (which he called Vulcan - because, in Star Trek, Spock had some power of total recall) took so much time he only had had time to watch two or three football games. The work was slowed down by a couple of floppy drive failures (with corresponding waits of three months to get it back up and running), however, sales began in 1979, after the conversion of Vulcan to the new CP/M operating system. In 1980 he met George Tate, who thought it was profitable and licensed it, founding the company Ashton-Tate http://edesber.com/ashton-tate/ with Hal Lashlee (Ashton was not a person but that name was later given to a macaw which became the unofficial mascot of the company) and renamed the Vulcan DBMS as dBASE II (for marketing reasons - just like the new price tag of $695 - an increase of 1290% compared to the original Vulcan price of $50).

In 1983 Ratliff became vice-president of Ashton-Tate after discovering that the company was hiding some of the rights she owed to him. The release of dBase III for DOS (which shipped in 1984) confirmed the dominance of dBase II in the CP/M market, and so companies started to sell clones of it (in December 1984 Fox Software releases FoxBASE) or compilers for its language like Wordtech Arago Quicksilver, but we'll concentrate on Clipper Winter'84 (released by Nantucket on May 25, 1985) and its developments.

At the time the mouse existed but was not used by anyone outside research centers, and to use dBase one had to issue commands from the keyboard. Later this situation changed as dBase III Plus introduced an Assistant which provided pull-down menus and dBase IV developed the concept with the Control Center. However, several commands could have been saved in a file (which normally had a .prg extension) and then run automatically by dBase. As in all interpreted environments, there were performance issues and, if a programmer had developed a custom application, the need to have the interpreter (dBase itself) on customers' computers. For these reasons, a compiler that would allow programs to run faster and distribute them with less burden became a wish for many dBase developers.

The legend about why the compiler was named "Clipper" is reported at http://www.ghservices.com/gregh/clipper/story.htm. It says the name was chosen because their idea of a dBase compiler was conceived by Barry ReBell and Brian Russell during a lunch in a Malibu restaurant, Nantucket Lighthouse (hence the name of the company), which had pictures of Clipper ships on its napkins. Compare this with https://vivaclipper.wordpress.com/tag/barry-rebell/.

Clipper Sailboats[edit | edit source]

Clipper Ship Southern Cross Leaving Boston Harbor, Boston, 1851, painted by →Fitz Henry Lane

But then what's a clipper? A definition translated and adapted from a pair of Italian encyclopedias reads like this: «sailing vessel with three or five masts, sharp prow, large square sails, gross tonnage of 600-1200» (see https://www.brighthubengineering.com/marine-history/65188-what-is-a-clipper-ship/). (The choice of this name, and of other names such as "Harbour", "Flagship" and "Marinas-GUI", suggests that xBase programmers might have a distinctive maritime vocation - or that the people at Nantucket have a peculiar sense of humor since dBase is a navigational DBMS). Nantucket is a famous port for novels set in the seas: from there the famous whaling ship of Ahab, the Pequod, set sail in Moby Dick; as well as Gordon Pym's Grampus (not to forget that in his previous shipwreck he had already been saved by another whaler, the Penguin, not far from Nantucket) in The Narrative of Arthur Gordon Pym of Nantucket.

There are numerous (e)books which discuss these ships (i.e. "The Clipper Ship Era" by Arthur Hamilton Clark, "The Great Clippers" by Jane D. Lyon, "Cutty Sark: The Last of the Tea Clippers" by Eric Kentley, "The Book of Old Ships - From Egyptian Galleys to Clipper Ships" by Henry B. Culver).

Their history is more or less the following.

The first ships named clipper were built in Baltimore in 1746 (→Baltimore Clipper). During the War of American Independence, the Baltimore clipper had great development. Other ships later took the name of "clipper" though there was little similarity with the original Baltimore clippers (except for the width of the canopy). From 1840 onwards, clippers became especially famous for the "gold rush" and for the "tea and wool races" (→Clipper route). Although at that time regular steamships were already navigating, clippers maintained slightly higher average speeds of the fastest steamers. In 1854 the clipper →Lightning by →Donald McKay covered the distance from Boston to Liverpool in 13 days, 19 hours and a half (a record time for a vessel), the Flying Cloud covered the distance between New York and San Francisco in 89 days, and another famous clipper, the Melbourne, used to transport passengers, launched in 1875, travelled from England to Australia in 81 days. The golden age of Clippers ended around 1880 and the famous →Cutty Sark, one of the fastest sailing ships of all time, which was launched in 1869 () and could travel 363 miles in 24 hours, is in London and can be visited: http://museums.eu/museum/details/16123/cutty-sark-clipper-ship-royal-museums-greenwich (there are also some videos of it, just search YouTube). The museum sells books on it: https://shop.rmg.co.uk/products/the-cutty-sark-pocket-manual, and there are even model-building kits. A detailed history is here: http://www.bruzelius.info/Nautica/Ships/Clippers/Cutty_Sark(1869).html.

A few more links to expand on this type of Clipper: http://www.titanic-nautical.com/Clipper-Ships-Wiki.html; the page http://www.ppreservationist.com/Clippership.htm links to many interesting pages about history http://www.ppreservationist.com/Clippership_files/ClippershipHistory.htm a biography of Donald McKay http://www.ppreservationist.com/Clippership_files/Donald%20McKay.htm and lists of these ships can be found at http://www.ppreservationist.com/Clippership_files/TheLists.htm and http://www.bruzelius.info/Nautica/Ships/Clippers/American_clipper_ships.html.

According to my Italian dictionaries, "clipper" has at least two other meanings, one connected with traveling and the other with a different type of waves: "(aviation) big airplane for trans-Atlantic flights" - see http://www.flyingclippers.com/transoceanic.html and "(electronics) electronic device to reduce the amplitude of a wave" - see http://www.daenotes.com/electronics/devices-circuits/clipper-clamper, http://www.daenotes.com/electronics/digital-electronics/clipper-circuits, but but to dwell on these meanings would be digressing too much.

The dBASE IV clone by Fox Software was called FoxPro. More software houses and clones joined the market (dBFast, OnCmd). And in the meantime, large quantities of libraries for Clipper were being developed.

Ratliff quit Ashton-Tate and began working on a new DBMS, again called Vulcan and sold by a company called Migent, but Ashton-Tate sued Migent (http://corphist.computerhistory.org/corphist/documents/doc-4464b2d87d241.pdf?PHPSESSID=ccd241...) and, on the same spirit, on November 18, 1988, sued Fox Software because its database programs used a command syntax too similar to that of dBase. They also published an open letter which you can read at https://archive.computerhistory.org/resources/access/text/2017/02/102770637-05-01-acc.pdf. By the end of 1990, Judge Terrence J. Hatter invalidated Ashton-Tate copyright on dBase's interface because it is based on the public domain JPLDIS. If you have a mind for jurisprudence you can read the sentence here: https://law.justia.com/cases/federal/district-courts/FSupp/760/831/1420900/.

Quite a lot changed in the following ten years:

In the early 90s, an ANSI committee (ANSI/X3J19) was formed to identify the common language features and define a standard which was to called →xBase.

The Decline of Commercial xBase[edit | edit source]

  1. In 1992 Microsoft acquired Fox Software. Computer Associates acquired Nantucket, Borland acquired Ashton-Tate and Oracle (after an agreement with WordTech) marketed SQL-based versions of dBXL and Quicksilver.
  2. All the products of the companies above changed their names as Microsoft Windows was becoming predominant: FoxPro became Visual FoxPro, Clipper became CA-Clipper, and had an evolution called Visual Objects, and dBase became Visual dbase.

The May 1998 issue of the Italian magazine PC Professionale contained a review of Borland Visual dBase 7 which started by summarizing its "glorious" history:

«dBase III has been the workhorse of the programmers of the 80s, then gave way to other tools, especially in the transition from MS-DOS to Windows 3.1, but it never left the scene, it even had visual developments. The seventh version of the scion of that language, fiercer than ever, is thirty-two bits, supports ActiveX controls, remote SQL databases and features a renewed and productive work environment.

«It is unusual for programmers who have been in business for several years to never have heard of dBase. The first version of this product was a sort of Visual Basic of the 80s: it freed developers from the complications of accessing database files and the interface was easy to use as it could have been in DOS, thanks to the automatic instructions completion and effective online help. Since then much water has passed under the bridges and dBase programmers had access to a first compiler, graphical interfaces, and even a migration of its syntax to an object-oriented approach».

The author, Michele Costabile, noted also that:

«The difference between programmable databases (Access, FoxPro) and traditional development tools (Visual Basic, Delphi) is becoming largely a matter of nuances: the first were born as a language built around a database, and the latter have come to assimilate structurally database support».

Borland had two database products in its catalog: Visual dBase (as we just saw) and Paradox - so instead of focusing on developing a database management software and a language, it had two competing products with two different programming languages, a marketing strategy that, not surprisingly didn't pay off.. However some serious financial problems caused by the acquisition of Ashton-Tate cut off its feet: today Paradox is a Corel product and ​dBASE™ PLUS 12 (the adjective visual being dropped) is marketed by dBase LLC (https://www.dbase.com/), a company which still sells also a BORLAND dBASE 5.0 FOR DOS repackaged as dBASE CLASSIC™ for $99 (http://dbaseclassic.com/). So much for dBase successor.

Moreover, the programming experience in CA-Visual Objects was profoundly different from that of CA-Clipper and the product has never been very successful. Clipper programmers kept using Clipper as long as possible, using libraries that allowed the creation of Windows executables (the most famous of those libraries were Clip4Win and FiveWin). Visual Objects development was continued by GrafX Software and stopped in 2012 with version 2.8 sp4, it had a .NET development called Vulcan.NET, whose version 1.0 was released on November 30, 2007 and finished in August 2015 at version 4.0. Most of Vulcan.NET's developers started the X# project which came out on 3 July 2017, see https://www.xsharp.info/articles/the-end-of-grafx.

Microsoft continued the development of Visual FoxPro up to version 9 sp2 on 16 October 2007, but never considered it a flagship product, as evidenced by the inclusion in their Office suite of its competitor Access. The success of MS Windows, MS Office (and consequently) MS Access on one hand and the advent of SQL databases on the other blotted out all commercial xBase products from the market.

Meantime, according to Dev (a programmers' Italian magazine), the Harbour Project was started by Antonio Linares, author of FiveWin, about 1998 with an original name of Five and that the project started on a CVS server named harbour.joca.es. It had some forks listed at https://harbour.github.io/forks. A pair of text-based clones of dBase appeared as well (nanoBase - see its user manual at http://www.lnf.infn.it/Calcolo/doc/AppuntiLinux/a2471.html; DollyBase http://dollybase.sourceforge.net/)

More links: https://www.visualdbsee.it, http://www.alaska-software.com/products/clipper-compatibility.cxp, https://www.recitalsoftware.com/, http://www.fship.com/. WinDock (http://www.roessler-software.de/e195/e264/index_ger.html) is a clip4win-compatible Win-API for Harbour and xHarbour. Class(y), Fivewin (https://www.fivetechsoft.com/english/index.php), Clip4Win, TopClass. http://www.ntkproject.com/index.php?language=1, https://www.xailer.com/, https://www.xharbour.com/xharbour-builder.html.

I found the following Facebook groups (which are quite active) related to this topic:

  • Harbour MiniGUI
  • Harbour Project
  • CA-Clipper - Harbour - XBase - Dbase Programmers
  • Alaska XBase++ Latinos

https://medium.com/harbour-magazine

Other Sites to Visit & Files to Download[edit | edit source]

The article on Freshmeat at the address http://freshmeat.sourceforge.net/articles/non-sql-databases-for-linux, Non-SQL Databases for Linux, is a good starting point for our xBase study. See also http://www.linas.org/linux/db-non-sql.html and http://linuxfinances.info/info/xbase.html. If you discover that you like very much xBase, have a look at http://www.oocities.org/tablizer/xbasefan.htm, a page for xBase fans.

The last Harbour installs a Norton Guide file (namely in the path c:\hb32\examples\gfspell\spell.ng). Norton Guide (NG) was the format of the on-line help used by Clipper. A NG Viewer for Windows written by Dave Pearson can be downloaded at his website http://www.davep.org/norton-guides/ and the Norton Guide for Clipper 5.3 is browsable as a set of HTML pages at https://harbour.github.io/ng/c53g01c/menu.html.

I will surf to know what's in The Oasis (http://www.the-oasis.net/). They included a link to this Guide and seem to have a large archive of Clipper material.

  1. Harbour for beginners by Alexander Kresin (http://www.kresin.ru/en/hrbfaq.html) is a valuable reference.
  2. Giovanni Di Maria's wrote a nice Harbour How To - Tutorial (http://www.elektrosoft.it/tutorials/harbour-how-to/harbour-how-to.asp), which is the as far as I know the only effort done by an Italian (other than me) at providing to the community information about Harbour programming. Di Maria's work is excellent, although perhaps it would be more correct to call it a 'cookbook' and not a 'tutorial' because it lacks explanations.
  3. In Spanish is still available the Manual de Clipper (On line) by Antonio Suárez Jiménez at http://www.elguille.info/Clipper/manual.htm.

Stuart Aitken published a link to a another tutorial, Harbour and HMG Guide: http://www.flamelily.net/index.html - http://www.harbour-guide.com/ but these links are not working now, and I wonder if he will reload his job on the Net.

Viva Clipper! - Notes of a Clipper Language Student http://vivaclipper.wordpress.com/ (link provided by bpd2000), http://harbourlanguage.blogspot.com/, http://cch4clipper.blogspot.com/ and http://www.mozzarellaincarrozza.it/harbour/page/5/ are interesting blogs.

http://harbourlanguage.blogspot.it/2011/01/understanding-harbour-library.html

http://www.ghservices.com/gregh/clipper/



Getting Started

Getting Started[edit | edit source]

A simple graph showing the different ways one can use (x)Harbour, and how they correspond to BASIC interpreters and Java.

As seen above, in practice, (x)Harbour permits a great versatility, as it (they) can be used in four different ways:

  1. by running hbrun or xbscript and executing instructions interactively (much like a BASIC direct mode or immediate mode, although it is more similar to the dBase dot prompt). The main limit of this approach is that it cannot run expressions longer than a single line (but it is possible to enter more instructions on a line separating them with semicolons). However, much of my tutorial is thought to be entered, tested and understood one line at a time.
  2. by calling hbrun or xbscript specifying a .prg file as a line argument to execute it (which is again like specifying a file to run when invoking a BASIC interpreter)
  3. by compiling the file to a bytecode using the /gh option of the Harbour compiler and then running the resulting .hrb bytecode file with hbrun (this is similar to Java's workflow when you call the compiler javac and then the Java interpreter on the bytecode file)
  4. by using the Harbour compiler, C compiler and linker to get an executable file (utilities are given to get all the steps done in a single command)
  5. by using the compiler through an IDE

There are also commercial RADs, like Xailer (https://www.xailer.com/) or xHarbour Builder (https://www.xharbour.com/).

Antonino Perricone wrote an extension for Visual Studio Code, which is well documented at https://github.com/APerricone/harbourCodeExtension/wiki, https://medium.com/harbour-magazine/visual-studio-code-for-harbour-e148f9c1861a, https://harbour.wiki/index.asp?page=PublicArticles&mode=show&id=190401174818&sig=6893630672. The main problem with the precompiled version of Visual Studio Code is that it's not under the MIT license.

https://github.com/Petewg/harbour-core/wiki/Make-tools

xMate IDE: https://github.com/Petewg/MgM/tree/master/comp/ide

Packages providing syntax highlighting for various editors are available: for Sublime Text (https://www.sublimetext.com/) is available at https://github.com/asistex/Sublime-Text-harbour-Package, for SynWrite (http://www.uvviewsoft.com/synwrite/) at https://github.com/rafathefull/synwrite, for Atom Editor (https://atom.io/) at https://github.com/AtomLinter/linter-harbour, UltraEdit at http://forums.ultraedit.com/syntax-highlighting-wordfile-for-harbour-fivewin-t17880.html. I myself like Scintilla (https://www.scintilla.org/), a lightweight open source editor that supports the xBase syntax highlighting under the name Flagship - which is an implementation different from the open source ones we are considering but that does not make much difference and it's anyway highly configurable.

Using Harbour From a Windows Command Prompt[edit | edit source]

Simply open a command prompt and move to the directory where you store your sources. Issue a PATH command pointing to the bin directory of your Harbour system (this is to avoid problems if you've got different compilers in your system). I also add the path of a text editor to use from the prompt, like this:

D:\harbourcode>PATH c:\hb32\bin;D:\wscite

Getting Started With hbIDE[edit | edit source]

https://hbide.vouch.info/

  1. To create a new project, select File > New > New Project Wizard (Prototype Only). We will name this project hbidetest, enter the path C:/hb32/projects/hbidetest.hbp and click Save and Close.
  2. Select File > Open Project in the menu bar.
  3. Right click the project hbidetest in the projects dock on the right and "Set as Current" project.
  4. Select File > New > Source and create a file hbidetest.prg with the following content:
     function MAIN
     * This is an example
     clear
     ?"Hello, the weather is fine today"
     ?"(this is a test of HbIDE)"
     wait && will show "Press any key to continue..." and prevent the console window to close immediately
     return
    
  5. Double click hbidetest in the projects dock so that the and click the Select Sources button near the Project Output text box, then select Save and close. It is a button not very well indicated - I think a button clearly stating "Add source file" would have been better. "Select Sources" Button Position in hbIDE
  6. Now after selecting Build > Build and Launch our program will show up.



Basic Language Tutorial

xBase/Clipper Tutorial[edit | edit source]

Please note: if anybody will contribute, the Clipper tutorial should contain only 'standard' code that would work on every Clipper compatible compiler. Compiler-specific code should be placed somewhere else, or clearly indicated in a box.

The code here should be of simple console-mode applications using only the simplest forms of input/output and focused only on algorithms, since the methods for creating and managing user interfaces will be described in Chapter 5, Making Up a User Interface.

The helloworld application line by line[edit | edit source]

Let's try to highlight the parts of helloworld:

 function MAIN
 * This is an example
 clear
 ?"Hello, the weather is fine today"
 return nil

(in GeoCities this and all other sources were highlighted by the on-line service CodeColorizer at http://www.chami.com/colorizer/, or, alternatively by the service at http://tohtml.com/Clipper/ - they tend to look a bit better than the Wikibook highlighting).

We will comment on each line of the helloworld program.

function MAIN

The first line defines a function named MAIN. Defining such a function is not compulsory with Harbour, as if it is missed errors would occur during compilation, but I will keep it to make sure the examples work with all compilers. A function cannot be defined when typing in commands at the dot prompt (hbrun/xbscript).

Every xBase language is case insensitive, which means that all the following lines are the same:

function MAIN
FUNCTION main
FuNcTiOn mAiN

Of course, this feature is beneficial only if you use it to improve the code readability.

We will learn later how to define and use functions and procedures.

* This is an example

The second line is a comment. Commenting your programs will help you when you are to modify them later. If they aren't commenting, modifying them will be a very hard task. It will be much more difficult if you are to modify programs written by others if they didn't comment them: figuring out what a program does using only its code is very difficult and even the cleanest programming language may prove to be write-only if not properly commented.

You can write comments in many different styles: using an asterisk (*), two sweeps (//), a double ampersand (&&) or a couple sweep-asterisk (/*) and asterisk-sweep (*/), as shown below:

* This is a comment...
// ...and so is this...
&& ...and this.
/* This is an example of the fourth commenting style,
which may span over several lines.*/

The second and the fourth commenting styles are derived from the C programming language, the first and the third are peculiar of the Clipper/xBase standard. None of these comments are accepted by xbscript, but hbrun (Harbour) accepts the last three.

clear

No, the purpose of this command is not to make the source code clear (that would be too easy! It is up to us to write clear source code, and to do so we must comment it well), but instead to clean the screen. :-) You could also use clear screen or cls, although these two commands are not exactly the same of this clear (the difference will be clear later when we can GET the point - then, you could also appreciate the pun in this paragraph - but will more likely not).

What are commands?
Harbour commands are defined via the #command macro directive (http://harbour.edu.pl/clipper/en/ng122d3c.html). The first commands we encountered are defined in the file std.ch by these lines:

#command CLS                        => Scroll() ; SetPos(0,0)
#command ?  [<explist,...>]         => QOut( <explist> )
? "Hello, the weather is fine today"

The ? is a command which means print. In the BASIC programming language, it is also an abbreviation for its print command (the syntax of xBase is quite similar to that of the BASIC programming language).

In this case ? print the string "Hello, the weather is fine today". Please note that a string can be enclosed in single quotes (or apostrophes), double quotes or square brackets: all of the following are the same:

? "Hello, the weather is fine today"
? 'Hello, the weather is fine today'
? [Hello, the weather is fine today]

The last line:

return nil

Return is used to mark the end of the function. We will explain later what the return exactly does.

Today Harbour needs not an explicitly defined main function/procedure. The following version of helloworld will compile and is exactly the same as the previous one:

&& hello world without commands and main
Scroll() ; SetPos(0,0)
QOut( "Hello world" )

The Harbour Interpreter and Virtual Machine
The program above, stored in a text file named hello.prg, can be run with the command

hbrun hello.prg

hbrun is an interpreter for Harbour. This means that if we run it, we get to a screen with a "Dot prompt" on its last line, where we can enter and run instructions. For example, entering

? "Hello world"

will get Hello world printed on the screen, but only after the interpreter hbrun itself is closed. To quit the interpreter and get the greeting printed type

QUIT

All in all, hbrun permits to feel how handling databases was done in the old dBase "Dot prompt" mode - that is, like this: http://manmrk.net/tutorials/database/dbase/IntrodBASEIIIPlus.htm

Running the compiler harbour (harbour.exe on Windows) on a printA.prg containing the line

? "A"

will output a printA.c file. The bottom of this file reads:

HB_FUNC( PRINTA )
{
	static const HB_BYTE pcode[] =
	{
		36,1,0,176,1,0,106,2,65,0,20,1,7
	};

	hb_vmExecute( pcode, symbols );
}

that is, as an intermediate step the Harbour compiler compiles pcode, or bytecode for a virtual machine.

If the compilation is done with the /gh option

hbmk2 /gh printA.prg
harbour /gh printA.prg

the result will be a file named printA.hrb, a "Harbour Portable Object" file which can be executed by

hbrun printA.hrb

In this way, hbmk2 and hbrun work in couple as a perfectly equivalent tools to the Java compiler javac and java interpreter java.

As a parenthesis, virtual machines are not a new concept. If we look into the retrocomputing field, we'll see that in the late 1970s and early 1980s the University of California San Diego has a portable operating system based on pcode, the UCSD P-System which was written in UCSD Pascal and could be run on 6502, 8080, Z-80, and on PDP-11 http://www.threedee.com/jcm/psystem/index.html (it actually was a competitor of MS-DOS, PC DOS and CP/M - http://www.digitalresearch.biz/CPM.HTM and http://www.seasip.info/Cpm/index.html).

Also, in 1979 Joel Berez and Marc Blank developed the Z-machine as a virtual machine to run Infocom's text adventure games, the name not hiding they were mainly thinking about Zork. The programming language for the Z-machine was called ZIL (Zork Implementation Language). A modern compiler for ZIL to Microsoft .NET is ZILF https://bitbucket.org/jmcgrew/zilf/wiki/Home and a Z-machine.NET is available https://zmachine.codeplex.com/. More information at http://inform-fiction.org/zmachine/index.html.

Apart from the JVM (Java Virtual Machine), the Microsoft .NET CLR (Common Language Runtime), is another modern virtual machine.

A flowchart of the compilation process (kindly supplied by Wojtek Birula).

hbmk2 was created to support all shells, all compilers on all platforms, also to replace old 'bld.bat' solution, while staying compatible with existing hbmk script features and options.

Pieces of information on how to get an executable without using hbmk2 can be found at http://www.kresin.ru/en/hrbfaq.html.

Remember that the PATH Environment Variable (in Windows) must be updated to include the directory containing the compiler: use the command SET PATH=C:\hb32\bin;%PATH% locally in a Prompt Window or update it globally: "Start" → "Control Panel" → "System" → "Advanced system settings" → "Environment Variables..." → select "Path", then click "Edit..."

Data Types in General (and Their Operators)[edit | edit source]

The list of types supported by Clipper is as follows:

  • A Array
  • B (Code) Block
  • C Character
  • D Date
  • L Logical
  • M Memo
  • N Numeric
  • O Object
  • U NIL

These letters can be used as a prefix to a variable name, to indicate "at first sight" what kind of item it is. This way oBox would be the name of an object, aNames an array, dBirthday a date and so on. An alternative for logical variables is to add an is prefix as in isOk or isExisting (somewhat more readable than lOk and lExisting). This naming convention is called the Hungarian notation and applies to functions as well, and to their synopsis: ACOPY( <aSource>, <aTarget>, [<nStart>], [<nCount>], [<nTargetPos>] ) is the function to copy elements from an array to another, which takes two arrays aSource and aTarget, and optionally three numeric parameters nStart, nCount, nTargetPos; CToD(cDate) takes a character argument and converts it to a date, DBCreate() is a function to create a database.

The type Memo can only be used in a database (it is a link to a subsidiary file to a DBF table, as described in the Wikipedia DBF entry).

This is the list of results returned by the function VALTYPE(). The function TYPE() returns also:

  • U NIL, local, or static
  • UE Error syntactical
  • UI Error indeterminate

Harbour has a longer list (http://www.fivetechsoft.com/harbour-docs/harbour.html):

  • HB_ISARRAY
  • HB_ISBLOCK
  • HB_ISCHAR
  • HB_ISDATE
  • HB_ISDATETIME
  • HB_ISHASH
  • HB_ISLOGICAL
  • HB_ISMEMO
  • HB_ISNIL
  • HB_ISNULL
  • HB_ISNUMERIC
  • HB_ISOBJECT
  • HB_ISPOINTER
  • HB_ISPRINTER
  • HB_ISREGEX
  • HB_ISSTRING
  • HB_ISSYMBOL
  • HB_ISTIMESTAMP

ISPOINTER() (http://www.marinas-gui.org/projects/harbour_manual/ispointer.htm) for example is marked: Not available in CA-Cl*pper. Together with HB_IT_POINTER and HB_ISPOINTER it is used in Harbour's internals (the C-level API: http://www.fivetechsoft.com/harbour-docs/clevelapi.html#ispointer).

Let us see a little program that shows the use of the most common data types.

I wanted to show, in this example, a computation of π instead of that of √2, but xBase misses the ArcTan function. We may solve this problem by importing it from an external library, or by supplying it ourselves. (Both ways should be pursued in this tutorial).

The last two data types are a bit different from the preceding: "Memo" is not very useful when used outside of a database, and the arrays cannot be used in databases.

 && example of compilation command
 && A:\>c:\hb32\bin\hbmk2 -quiet -oc:\hbcode\test.exe test.prg -run
 
 && please note that this example has not a MAIN function
 
 && an example of string concatenation
 ? "Hello" + " " + "World!"
 
 && let us do now a few numerical computations, integer numbers are a good starting point
 ? 5+13
 ? 12*8
 
 ? 3/2
 SET DECIMALS TO 15
 sqrt2=sqrt(2) && computing the square root of 2...
 ? sqrt2 && ... and printing it
 
 && ? caporetto 
 && if the line above was not commented, the compiler would issue the two following lines
 && Error BASE/1003  Variable does not exist: CAPORETTO
 && Called from TEST(8)
 
 && as xBase is not good at history, let us revise it: http://www.historyofwar.org/articles/battles_caporetto.html,  http://www.firstworldwar.com/battles/caporetto.htm
 && we can then assign the correct date to this war
 caporetto := ctod("10-24-1917")

 a := date() && system date
 ? a + 1 && will print tomorrow's date 
 ? a - caporetto && this will tell us how many days have passed since Caporetto's battle (difference between two dates)
 
 SET DECIMALS TO 2
 ? (a - caporetto) / 365
 ?? " years have passed since Caporetto's battle"
 
 ? 1+1=3
 && it will print ".F.", that is, "FALSE" (of course...)
 
 && The following two instructions should be discussed to expand on the subject of operator precedence
 ? 1/2+2^3*sqrt(25)-3^2
 ? 1/2+2^3*(sqrt(25)-3^2)

The function SQR has been eliminated from Harbour and replaced by SQRT. Trying to use it will result in a linker error "undefined reference to 'HB_FUN_SQR'".

From the following example (which runs) we see that Harbour is a weakly typed programming language: a variable, such as a in our example can be a number, and then become a string of text.

a := 1+1
? a
a := "abc"
? a

There is a problem: if the variable type changed and the programmer did not get aware of it he may issue some instruction that will cause an error at run time. If we had another line of code

?a+1

in the program above, Harbour will attempt to add the number 1 to the string "abc", and the result will be the error

Error BASE/1081 Argument error: +.

Strings[edit | edit source]

Strings are essentially a list of characters. They are written between double quotes (eg. "Hello"). Note the difference:

123       => a number
"123"     => a string

Let's see how to use the function Stuff() to change a character within a string:

sTest:="fun"
?sTest, STUFF(sTest,2,1,"a")
&& => fun fan

Here is a list of the most commonly used functions for handling strings:

Lower()   Convert uppercase characters to lowercase
Upper()   Converts a character expression to uppercase format
Chr()     Convert an ASCII code to a character value
Asc()     Convert a character to its ASCII value
Len()     Return the length of a character string or the number of elements in an array
At()      Return the position of a substring within a character string

Nevertheless in Clipper and Clipper Tools library can also be found very curious functions such as these:

AsciiSum()   Finds the sum of the ASCII values of all the characters of a string
Soundex()    is a character function that indexes and searches for sound-alike or phonetic matches
CHARONE()    Search and remove repeating characters
CHARONLY()   Remove non-standard characters
CHAROR()     "OR" bytes in a string

Let's see what we can do with these functions.

We show the ASCII codes (http://www.asciitable.com/) corresponding to the keys pressed by the user, concluding the program when ESC is pressed.

#include "Inkey.ch"
DO WHILE .T.
WAIT "" TO cChar
?? " ", Asc( cChar )
IF ( LastKey() == K_ESC )
EXIT
ENDIF
ENDDO

inkey.ch is the eader file for Inkey() function, which serves to obtain the following key code from the keyboard buffer.

Other Considerations about the Classification of Data Types[edit | edit source]

A first classification of data types divides them into simple (or scalar) types and structured (or composite, or aggregate or compound) types.

In xBase the simple data types are numbers, logical values, strings and dates, that is, the types which can be field types in a database table. Memos are not, since they are a pointer to an external file.

Scalar data types can be composed to build up structured data types: arrays, associative arrays (hash tables), tables or databases (which are a "data type" which is stored on a disk). w:Primitive_data_type

Code blocks and objects are special data types.

Operators and Precedence[edit | edit source]

&& First we print the values of two expressions with the exponentiation operator
? 3**3**3 && is 19683
? 3**(3**3) && is 7625597484987

&& Then we let our compiler tell us if they are the same
? 3**3**3 = 3**(3**3) && result is .F.

&& We've a look at how our compiler deals with overflow and underflow...
? 9**9**9
&& ***********************
? 1/9**9**9
&& 0.00
? -(9**9**9)
&& ***********************

We see in this example that the exponentiation operator ** is left-associative (as opposed to mathematics where exponentiation is a right-associative operation).

The fact that 3**3**3 and 3**(3**3) give two different results means that parenthesis alter the operator precedence: whatever is enclosed in parenthesis is evaluated first.

Even when the operation is left-associative, the result of 9**9**9 is quite a "large" number (196627050475552913618075908526912116283103450944214766927315415537966391196809 to be precise, as you can easily check - maybe using some arbitrary precision calculator program, such as GNU bc, which is available on every Linux distribution and as a download for Windows at http://gnuwin32.sourceforge.net/packages/calc.htm - or by hand spending only some weekend time) which exceeds the resources Harbour allocates for a number (which is called overflow) and it, therefore, prints 23 times a * to indicate this error. It also gives zero (0.00) if we try to compute its reciprocal (which is called underflow). Overflow and underflow errors happen when the result of a computation is, respectively, too big or too small to be represented with the number of bits a compiler has allocated for the variable in which we're trying to store it in.

The same happens with dates. What it the result of
date()-1000000
(subtracting 1 million of days from the current date)? Let us see:
&& today my outputs are:
?date()
&& ==> 05/15/19
?date()-365000 && more or less 1000 years
&& ==> 01/13/20
set century on && we change an option...
?date()-365000 && ... and try ageing
&& ==> 01/13/1020
?date()-1000000 && more or less 2700 years (i.e. the results should be around 700 B.C.)
&& ==> 00/00/00
?ctod("31-12-9999")+1
&& ==> 00/00/00

Here, the last two outputs ("00/00/00") indicate invalid dates, just as the rows of 23 asterisks seen above indicate invalid numeric results. Acceptable dates are those that can be stored in a dBase table (ie a .dbf file) and in these files a date is a string of 8 characters in the "YYYYDDMM" format (4 characters for the year, 2 for the month and 2 for the day). Consequently, negative years (before Christ) or with more than 5 digits are not accepted.

Harbour displays the year with only two digits unless we change its behavior with a SET command and gives wrong results with these large number of years. The reason is simple: in a database application (inventory management, reservation systems) we won't have to deal with intervals of centuries and Harbour's routines for handling dates don't work correctly if we try to do that. In other fields, such as w:Archaeoastronomy this is the case and those who do deal with those problems (e.g. checking the precision of the alignment of the Winter solstice sunrise at Karnak in 2000 B.C. or the megalithic alignments) use their special routines for the computation of the dates.

There is another quandary when doing computation with computers. Look:

store 7 to n && an alternate way of an assignment (dates back to dBase II)
?sqrt(sqrt(n))^2^2=n
&& the result is .F.
set decimals to 15
?sqrt(sqrt(n))^2^2
&& 7.000000000000003

Now, if we extract the square root twice from a number and then square the result twice we should have the starting number. In fact, computers are not so precise and checking for the equality of two floating-point numbers can give unexpected results.

If you want to learn more you could have a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic at https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (which is part of a Sun Microsystems book entitled Numerical Computation Guide), and the Decimal Arithmetic FAQ at http://speleotrove.com/decimal/decifaq.html.

Fortunately, the precision of the variables used in xBase languages ​​and the fact that in common business applications square roots, logarithms and other mathematical operations that cause loss of precision are rather rare, are circumstances that mitigate the problem.


SET OPTIONS
We've seen SET DECIMALS TO n and SET CENTURY ON/OFF commands. But in fact there are many options which can be set. The SETMODE(<nRows>, <nCols>) function is particularly useful when using hbrun or xbscript: its purpose is to determine the size of the window.

Boolean Algebra & Flow Control[edit | edit source]

We can print the result of a comparison, which is a Boolean value (represented as .T. or .F.), but they are useful with special words in order to branch or repeat the execution of one or more instructions.

We have a Boolean expression when its result is true (whose xBase symbol is .T.) or false (.F.). The easiest way to get a Boolean result is to use a comparison (relational) operator: =, ==, !=, <>, #, >, <, >=, and <=. Always remember the difference between the comparison operators = (equal), == (exactly equal) and the assignment operator :=.

Actually, the "real" (mathematical) definition of a Boolean expression is different from what is stated in the last paragraph. A "real" Boolean expression would contain only the two Boolean values .T. and .F. and the Boolean operators (.AND., .OR., .NOT.).

Boolean Algebra is applied to electronic digital circuits since the work of Claude Shannon in 1937. See http://www.allaboutcircuits.com/textbook/digital/chpt-7/introduction-boolean-algebra/ (and the successive pages of that online textbook) to have an idea of what Boolean Algebra means when it's applied to bits within a circuit (including a microprocessor). Here we're using it at a much higher level.

Consider Hamlet, Act 3, Scene 1: «[…]To be, or not to be? That is the question— Whether ’tis nobler in the mind to suffer The slings and arrows of outrageous fortune…». If Prince Hamlet asked Boole (but he could not, as it is set in the 14th or 15th century whereas Boole was born in 1815), his reply would have certainly baffled him. Why?

to_be=.T. && is it true?
?to_be .OR. .NOT. to_be
&& .T.
to_be=.T. && is it false?
?to_be .OR. .NOT. to_be
&& .T.

As Harbour told us, in Boolean algebra, the answer is "true". Have a look at this page: https://vicgrout.net/2014/07/18/to-be-or-not-to-be-a-logical-perspective/.

Conditional execution[edit | edit source]

A number of graphical ways have been developed for seeing how a program's flow control is transferred: these include flowcharts (https://www.lucidchart.com/pages/what-is-a-flowchart-tutorial), data flow diagrams (https://www.lucidchart.com/pages/data-flow-diagram), Nassi-Shneiderman diagrams and the JSP and JSD diagrams by Michael A. Jackson (which is almost certainly not the first Michael Jackson you'll think of).

The following example gets a number from the keyboard and prints it. If the number is 0 it comments that value (I presented this example as a typical nihilist programming). The flowchart shows why conditional execution is sometimes called bifurcation.

Flowchart of a simple bifurcation

Function MAIN()
LOCAL number
INPUT "Key in a number: " TO number
IF number = 0
?? "Congratulations, you keyed the fabolous number "
ENDIF
? number
RETURN

This example prints two different comments, whether the condition of the remainder of the division of the number supplied by 2 is, or is not, 0.

Function MAIN()
LOCAL number
INPUT "Key in a number: " TO number
IF number % 2 = 0
   ? "You keyed in an even number"
ELSE
   ? "You keyed in an odd number"
ENDIF
RETURN

This example shows that more than a command can be executed.

Function MAIN()
LOCAL number
INPUT "Key in a number: " TO number
IF number % 2 = 0
   ? "You keyed in an even number"
   ? "I can prove it:"
   ? "the result of ",number," % 2 = 0 is ", number % 2 = 0
ELSE
   ? "You keyed in an odd number"
   ? "I can prove it:"
   ? "the result of ",number," % 2 = 0 is ", number % 2 = 0
ENDIF
RETURN

This example adds another keyword, ELSEIF, to show that the choices are not necessarily dichotomic. This is a chained conditional. Only one of the branches will be executed.

function MAIN
LOCAL nNumber := 0
//
INPUT "Key in a number: " TO nNumber
//
?? "Your number is "
IF nNumber < 50
? "less than 50"
ELSEIF nNumber = 50
? "equal to 50"
ELSE
? "greater than 50"
ENDIF

The following example checks whether the system time is earlier than 18 (6 p.m.) and prints a greeting appropriate to the time of day.

   ? Time()
   // CToN source is numconv.prg, library is libct.
   IF CToN( SubStr( Time(), 1, 2 ) ) < 18
      ? "Good day"
   ELSE
      ? "Good evening"
   ENDIF

There are three logical operators: .AND., .OR., and .NOT.

Here is an example with a rather long condition, coordinating more comparisons with .OR. operators. The example is terribly stupid, but it shows how a semicolon allows you to continue an instruction on the following line.

WAIT "Key in a letter: " TO char
IF  char = "a" .OR. char = "A" .OR. char = "e" .OR. char = "E" .OR. ;
         char = "i" .OR. char = "I" .OR. char = "o" .OR. char = "O" .OR. ;
         char = "u" .OR. char = "U"
? char, " is a vowel"
ELSE
? char, " is not a vowel"
ENDIF

This same thing is however achieved in a more concise way using the substring comparison operator $ (or regular expressions, an advanced topic that we will see later).

   WAIT "Key in a letter: " TO char
   IF  char $ [aeiouAEIOU]
      ? char, " is a vowel"
   ELSE
      ? char, " is not a vowel"
   ENDIF

Looping[edit | edit source]

DO WHILE, EXIT, LOOP, ENDDO are keywords used To repeatedly execute a series of statements (loop body) while a condition is true (i.e. its result is .T.).

Loops come in several flavours: the pre-tested loop, the post-tested loop, and the definite iteration, which is done via a count-controlled loop (usually called a for loop). In practice, we may have a middle-tested loop as well, but no specific syntax for it... we need to put an EXIT statement within an already existing loop body to get this one. The next program we will key in is of some interest for the mathematicians (Real Programmers aren't afraid of maths, do you know this famous adage?)

Here is it:

 && Function MAIN LOCAL number, sum, n 
 && An anonymous contributor renamed the variable "num" into "number", increasing this short program readability, but the line above would give
 && Error E0030  Syntax error "syntax error at 'LOCAL'"
 Function MAIN
 LOCAL number, sum, n
 CLS 
 ? "Let's sum up the first n odd numbers." 
 INPUT "How many numbers shall I sum up? " TO n 
 sum=0 
 number=1 
 DO WHILE number <= 2*n    
  sum=sum+number    
  number=number+2 
 ENDDO 
 ? "The sum requested is ", sum

As you can see, this looping statement is similar to the IF statement: both of them are ended by a END-corresponding statement, both of them contains a logical espression.

This looping statement will continue until its condition remains true (will evaluate to .T.).

The two instructions it repeats are sum=sum+num and num=num+2. The second is fundamental: if it wasn't there or was wrong (for example if you keyed in num=num/2), the condition would not evaluate to .F. and the program would not stop its execution (this is called infinite looping). When this happens to you, press the keys Ctrl and C at the same time. This should convince the computer to give his attention to you instead of running the loop.

The programs above is a nice example of how an extremely methodical calculator without any creativity would attack the problem of summing up the first n odd numbers. Notes about the creative approach to it can be found at this address: http://betterexplained.com/articles/techniques-for-adding-the-numbers-1-to-100/ (along with the usual anedocte about Gauss' show off about it in elementary school).

The WHILE looping statement is said to have the control expression in its head, opposed to the Pascal REPEAT-UNTIL looping statement (a post-test loop) which has the control of the condition in its tail (these are the Italian computer science slang for pre- and post- tested loops, aren't they funny?). How does the xBase/Clipper say REPEAT-UNTIL? It doesn't. Here is how to emulate it (copied & pasted from the Norton Guide):

LOCAL lMore := .T.
DO WHILE lMore
loopbody
lMore := condition
ENDDO

As you see, we first set a variable to be true, enter the loop, and specify the condition at the end of the loop, to make sure its body is executed at least once.

Here is an example, equivalent to the one at the pascal_repeat_until_loop page in tutorialspoint.com.

LOCAL a := 10
LOCAL lMore := .T.

   DO WHILE lMore
      ? 'value of a: ', a
      a := a + 1
      lMore := ( a != 20 )
   ENDDO
But the code above changed the condition. If we didn't want this to happen, we can simply negate the condition we'd use in the Pascal code:
repeat loopbody until condition
becomes then:
LOCAL a := 10
LOCAL lMore := .T.

   DO WHILE lMore
      ? 'value of a: ', a
      a := a + 1
      lMore := .NOT. ( a = 20 ) && or, in alternative, lMore := ! ( a = 20 )
   ENDDO

Let us now see this kind of loop:

 DO WHILE .T. 
 ? "I won't stop." 
 ENDDO

This loop prints "I won't stop." as long as 'true' is... true. It is thus called an infinite loop because the ending condition will never be satisfied. It is the first example of control flaw studied in computer science classes. By definition, you incur in an infinite loop every time you specify a tautology as the condition to be checked. It is not always obvious to detect this and other control flaws and errors - it is indeed a process that involves the use of a specific piece of software called a debugger.

Counter-controlled Loops, and Nested Loops[edit | edit source]

In this section, we will see how to use FOR loops to print multiplication tables, how to nest loops, how to format output on the console screen and the importance of indenting code to improve readability.

The FOR...NEXT construct (counter-controlled loop) is useful when we know how many times we want the loop body be executed. We shall now use it to print out and revise multiplication tables.

 CLEAR
 FOR I := 1 TO 8
   FOR J := 1 TO 8
    ?? I*J
   NEXT
 NEXT

(Please note in this example we have a loop inside the body of another loop: this is called a nested loop). Well, this works, but its output is not very nice... Moreover, it is customary to print the multiplication tables up to 10, since we're using a decimal numeration system... let us try it again!

 CLEAR
 FOR I := 1 TO 10  
   FOR J := 1 TO 10   
     @i,j*4 SAY  I*J PICTURE "999"  
   NEXT
 NEXT

this way we can print it much prettier... by the way, when nesting loops and branching statements, it becomes important to arrange the indentations in order to make the program easier to read... in The Oasis are available "source code beautifiers" such as dst314.zip. Harbour gives us the hbformat tool, https://vivaclipper.wordpress.com/tag/hbformat/.

PICTURE "999" is the instruction to specify we want the output format - as we specified three digits, the numbers in output will be printed as if they were three digits long, thus aligning them in our multiplication table.

As a second example of nested loops, the following is one of the most irritating programs I've ever written. It asks the user for an opinion and then always responds its contrary. It even quits when the user says he has more to tell him and keeps asking for input when the user says he has nothing else to add. Since it is particularly difficult for a program to answer the opposite of the user's assertions, this program simply prints the input string reversed, using the SubStr function (SubStr(<cString>, <nStart>, [<nCount>]) --> cSubstring - extract a substring from a character string cString, at index nStart with length nCount).

User input is obtained through the ACCEPT command (a COBOL reminiscence), and the control flow uses a DO WHILE loop (from FORTRAN) and a FOR ... TO ... STEP loop (from BASIC).

LOCAL cContinue := "N"
DO WHILE cContinue = "N"

ACCEPT "What's your opinion? " TO cOpinion
?""
?? "NO!! According to me, on the contrary: "
FOR i := Len( cOpinion ) TO 1 STEP -1
??SubStr( cOpinion, i, 1 )
NEXT

WAIT "Tell me, do you have any other opinions to discuss with me? (Y/N) " TO cContinue
cContinue := Upper( cContinue )
IF cContinue = "N"
? "Ok, then. "
ELSE
? "In this case let me go."
ENDIF
ENDDO

How do we deal with such an awkward customer? Just try typing "remmargorp revelc yrev a era uoy" and the solution will be clear. In Italy there is an expression to call a person who always contradicts his interlocutors: he is called a "bastian contrario". They do work like this program.

& - the Macro Evaluation Operator[edit | edit source]

Consider this short piece of code:

 i = 2
 myMacro = "i + 10"
 i = &myMacro && will execute "i+10"
 ? i          && will print "12"

The & is an operator that will evaluate (runtime compilation) an expressions and will permit text substitution within strings (see https://harbour.github.io/ng/c53g01c/ng110bc2.html). Let's make a simple calculator using it.

ACCEPT "Please enter the first number: " TO cNumber1
ACCEPT "Please enter the second number: " TO cNumber2

WAIT "Enter operation: " TO cOperation && we get a single character for the operation

? Number1+cOperation+Number2+"="       && we print the operation and an equal sign
? &(Number1+cOperation+Number2)        && and the result on the following line, evaluating the string as it appeared on the line above

Here is an example of interaction with this program:

Please enter the first number: 12
Please enter the second number: 23.5
Enter operation: -
12-23.5=
       -11.5

Arrays[edit | edit source]

An array is an ordered grouping of data, whose elements are identified by a number called an index. In many programming languages ​​the indices (or "indexes") start at 0, but in xBase they start from 1. Since in mathematics the indices are written next to the variable slightly below the line, sometimes they are called subscripts and, for this reason, arrays are sometimes called subscripted variables.

Consider this first example of arrays. We create two arrays, one with the names of the months of the year and one with their duration in days, and then we print them in an orderly manner. The same result can be obtained in a much more elegant way using associative arrays.

 && C:\hb32\bin\hbmk2 -quiet -oc:\hbcode\months.exe months.prg -run
 DECLARE months [12]
 ? LEN (months) && the function LEN() shows how many items an array has
 && We shall now load some data into our arrays
 PRIVATE month_names: = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
 PRIVATE months: = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} && for simplicity we do not consider leap years
 
 && Let us make some output (using a FOR loop - see below)
 FOR I: = 1 TO LEN (months)
 ? month_names [i], "is", months [i], "days long."
 NEXT

In the next example we show that xBase has high-level support for dynamic arrays (implementing them in a low level language like C requires pointer arithmetic and memory management functions).

Note: This example will reappear many times in these notes. Often when we will introduce a new feature of the language we will write a new program which does the same thing of this program in a different way. It is one of the best pedagogical advice I can give you. Since I don't provide you with exercises, think of different ways of redoing some program using new language features, i.e. just to give you an idea: how difficult is it to redo the program that shows the multiplication tables on the screen using code blocks?

&& compile with ''hbmk2 averaging.prg -lhbmisc''
PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   aNumbers := {}                && and an array without elements

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
      ACCEPT "next element: " TO item
      IF IsDec( item )           && the function IsDec() from the libmisc library checks if we have entered a number
         AAdd ( aNumbers, item ) && if we did, then we add the number as a new element to the array
         sum := sum + &item      && (and we update the sum of the elements in our array)...
      ELSE
         EXIT                    && ...if we did not, we quit this loop
      ENDIF
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to LEN ( aNumbers )
      ?? AVERAGE - aNumbers[n]
   NEXT
RETURN

Here is the list of functions in hbmisc: https://harbour.github.io/doc/hbmisc.html.

The program above is not very satisfactory: due to the use of the IsDec() function it can treat only positive integer numbers. What if we'd like it to accept numbers like -8 and 2.78? Three solutions come to mind offhand: write ourselves function that checks if the string represents a number (an improved version of IsDec()), test input using regular expressions, using xBase functions for error handling. We will use this last technique in the next section of the tutorial.

  FOR EACH n in aNumbers        && FOR EACH is a more modern control flow statement Harbour syntax
     ?? AVERAGE - &n
  NEXT

In addition to a function to add elements to an array we have one to delete them:

 aFruitSalad:={"peaches", "plums"}
 AAdd ( aFruitSalad, "onions" ) && we add onions
 ADel(aFruitSalad, 3) && on second thought, onions don't mix well: we remove them
 local f; for each f in aFruitSalad; ?? f," " ;  next && we print our ingredients
 && => peaches  plums  NIL
Harbour provides a function, hb_ADel, which accepts a third Boolean element and (if it's .T.) shrinks the array.

Multidimensional arrays are made by nesting arrays within other arrays.

Error Handling (the Clipper Way)[edit | edit source]

All of the programs showed up to this point will fail and show a runtime error if the user entered an unexpected input (in simple words, they crash). Programs called "robust" don't. Checking the conditions in which errors can occur and take countermeasures to prevent crashes is a job that was normally done with if-then-else.

BEGIN SEQUENCE ... END[SEQUENCE] is a control structure that executes some code and optionally some statements if the code produced an error. In our example if the conversion of the user input into a number generates an error, we will call EXIT to leave the while loop and continue our program.

PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   local bError := errorblock ( { |oError| break ( oError ) } )
   aNumbers := {}                && and an array without elements

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
      BEGIN SEQUENCE
      ACCEPT "next element: " TO item
         AAdd ( aNumbers, &item ) && if we did, then we add the number as a new element to the array
         sum := sum + &item      && (and we update the sum of the elements in our array)...
      RECOVER
         EXIT                    && ...if we did not, we quit this loop
      END SEQUENCE
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to Len ( aNumbers )
      ?? average - aNumbers[n]
   NEXT
RETURN

Clipper 5 (C5) provided four classes:

Error           Provides objects with information about runtime errors
Get             Provides objects for editing variables and database fields
TBrowse         Provides objects for browsing table-oriented data
TBColumn        Provides the column objects TBrowse objects

The Clipper 5.3 Norton Guide lists these classes:

Error class       Provides objects with information about runtime errors
CheckBox class    Provides objects for creating checkboxes
Get class         Provides objects for editing variables and database fields
ListBox class     Provides objects for creating list boxes
MenuItem class    Provides objects for creating menu items
PopUpMenu class   Provides objects for creating pop-up menus
PushButton class  Provides objects for creating push buttons
RadioButto class  Provides objects for creating radio buttons
RadioGroup class  Provides objects for creating radio button groups
Scrollbar class   Provides objects for creating scroll bars
TBColumn class    Provides objects for browsing table-oriented data
TBrowse class     Provides the column objects TBrowse objects
TopBarMenu class  Provides objects for creating top menu bars

Functions and Procedures[edit | edit source]

A function is a piece of code that returns a result every time it is called. Let us consider a function to convert Celsius into Fahrenheit (the same function is provided by libct, but we redo it for educational purposes):

FUNCTION cels2f( tempCelsius )
tempFahrenheit := (tempCelsius*1.8 + 32)
RETURN tempFahrenheit

FUNCTION MAIN()
? cels2f(100)
? cels2f(0)

But have a look at the following:

/**
 * by choosing better variable names this function becomes self-explanatory
 */
function convert_Celsius_to_Fahrenheit(Celsius_temperature)
  return Celsius_temperature * 1.8 + 32

FUNCTION MAIN()
? convert_Celsius_to_Fahrenheit(100)
? convert_Celsius_to_Fahrenheit(0)

Consider this example of use for the function HB_Random():

PROCEDURE Main
 // Random number between 0.01 and 0.99
 ? HB_Random()
 // Random number between 0.01 and 9.99
 ? HB_Random(10)
 // Random number between 8.01 and 9.99
 ? HB_Random(8,10)
 // Random integer number between -100 and 100
 ? HB_RandomInt(-100,100)
 RETURN

A good function would function as a "black box", that is you can use it without worrying about how it internally works. An example usually given at this point is that of driving a car: you can use it without worrying about how the engine works (do you know anybody which looked at the pistons of their new car before buying it?). Someone else designed the engine. But there may be problems: if we change our Celsius into Fahrenheit function as follows

FUNCTION cels2f( tempCelsius )

   tempFahrenheit := (tempCelsius*1.8 + 32)
   number := number + 1

   RETURN tempFahrenheit

FUNCTION MAIN()

   number := 0
   ? cels2f( 100 )
   ? cels2f( 0 )
   ? number

we'll get a side effect. This program output is

      212.0
       32.0
        2

That is, our new function did not only return a value, it also increased by 1 the variable number.

The Clipper Norton Guides read, "A procedure in CA-Clipper is the same as a user-defined function, with the exception that it always returns NIL" (http://www.oohg.org/cl53/ng10a778.html), thus a procedure is really just a type of function that relies on these side effects to get something done.

The utility of functions is that they improve program readability, maintainability. Splitting a program into functions is a good design approach, which lets breaking a complex problem into smaller, simpler problems (top-down).

Harbour has four different kinds of subroutines/subprograms:

  • the FUNCTION and
  • the PROCEDURE (which is a FUNCTION with no return value),
  • the method (which has no associated keyword, works like every object-oriented programming language) and
  • the code block.

Why the Main Function?[edit | edit source]

The main function is the designated start of a program written in C or C++ (Java uses a static main method). Since a procedure is a function that returns NIL, we can have a procedure main instead of a function main in our program. With Harbour it is not necessary anymore.

Another characteristic of variables: scope identifiers[edit | edit source]

Variables have a name, a type, a scope and a lifetime (http://www.dbase.com/Knowledgebase/dbulletin/bu05vari.htm) which may be explicitly declared as (list below copied verbatim from w:Harbour (software)):

  • LOCAL: Visible only within the routine which declared it. Value is lost upon exit of the routine.
  • STATIC: Visible only within the routine which declared it. Value is preserved for subsequent invocations of the routine. If a STATIC variable is declared before any Procedure/Function/Method is defined, it has a MODULE scope, and is visible within any routine defined within that same source file, it will maintain its life for the duration of the application lifetime.
  • PRIVATE: Visible within the routine which declared it, and all routines called by that routine.
  • PUBLIC: Visible by all routines in the same application.

They do not make sense before we work with functions and procedures.

/* Work on the following.
 * hbformat was run on this piece of code
 * need to provide comments, nest more functions and procedures to help figuring what goes on with scope modifiers
 */

STATIC x := 9

   ? x
   A()
   ? x
   B()
   ? x
   C()
   ? x
   D()
   ? x
   E()
   ? x

PROCEDURE A

   LOCAL x := 10

   ? "x from A=", x

   RETURN

PROCEDURE B

   PRIVATE x := 5

   ? "x from B=", x

   RETURN

PROCEDURE C

   PUBLIC x := -1

   ? "x from C=", x

   RETURN

PROCEDURE D

   ? "x from D before updating value=", x

   x := 12

   ? "x from D=", x

   RETURN

PROCEDURE E

   ? "x from E=", x

   RETURN

On running this, we get a strange output:

         9
x from A=         10
         9
x from B=          5
         9
x from C=          -1
         9
x from D before updating value=          -1
x from D=         12
         9
x from E=         12
         9

This program sets a variable x and five different procedures, A, B, C, D, E. The first three procedures define a variable x within themselves, assign it a value and print it. The fourth function assigns a new value to some variable named x without declaring it. The fifth function shows the value of some x variable, which happens to be the value of the fourth x variable. The main fact to remember here is that two variables are not the same, even if hey have the same name, provided they have different scopes and this feature is called shadowing.

x := 9

   ? x
   A()
   ? x
   B()
   ? x
   C()
   ? x
   D()
   ? x
   E()
   ? x

PROCEDURE A

   LOCAL x := 10

   ? "x from A=", x

   RETURN

PROCEDURE B

   PRIVATE x := 5

   ? "x from B=", x

   RETURN

PROCEDURE C

   PUBLIC x := -1

   ? "x from C=", x

   RETURN

PROCEDURE D

   ? "x from D before updating value=", x

   x := 12

   ? "x from D=", x

   RETURN

PROCEDURE E

   ? "x from E=", x

   RETURN
         9
x from A=         10
         9
x from B=          5
         9
x from C=         -1
        -1
x from D before updating value=         -1
x from D=         12
        12
x from E=         12
        12

As static variables seem to require further informations (they work like C static variables), here is an example:

// adapted from "An example of static local variable in C" (from Wikipedia Static_variable)
FUNCTION func()
	static x := 0
	/* x is initialized only once, the first time func() is called */
	x := x+1
	? x
RETURN

FUNCTION main() 
	func() // calls func() a first time: it prints 1
	func() // value 1 was preserved; this time it prints 2
	func() // prints 3
	func() // prints 4
	func() // prints 5

See pascal_variable_scope in tutorialspoint.com and http://aelinik.free.fr/c/ch14.htm

Variable scope is a foretaste of object oriented programming, in the sense that it anticipates some encapsulation concepts.

The search for the Ludolphine[edit | edit source]

Part 1: Using the Clipper Tools (CT) Library[edit | edit source]

If you checked the list of functions you will have noticed that the xBases have no trigonometric functions. Not that in general they miss them: these languages are targeted database applications such as "accounting systems and airline reservations systems" (see w:Database application. ) in which trigonometric functions are not really important. But we now ask ourselves how to compute an approximation to the Ludolphine number, π (more information on this "search" at http://www.mathpages.com/home/kmath457.htm), We can do it using the CT library (see http://vouch.info/harbour/index.html?hbct.htm), or importing the C standard library function atan.

 && compile with ''hbmk2 computepi.prg -lhbct''
 PROCEDURE MAIN
 SET DECIMAL TO 14
 ? 4 * ATAN (1)

Part 2: Mixing C and Harbour Code[edit | edit source]

We get the idea from Harbour for beginners by Alexander Kresin (http://www.kresin.ru/en/hrbfaq_3.html).

  #pragma BEGINDUMP
  #include <extend.h>
  #include <math.h>
   HB_FUNC( ATAN )
   {
      double x = hb_parnd(1);
      hb_retnd( atan( x ) );
   };     
 #pragma ENDDUMP
 SET DECIMAL TO 14 
 ? 4 * ATAN (1)

It must be noted that the extend.h file (* Compatibility header file for CA-Cl*pper Extend System) contains this warning:

/* DON'T USE THIS FILE FOR NEW HARBOUR C CODE */
/* This file is provided to support some level of */
/* Harbour compatibility for old Clipper C extension code */

In fact this example is here only to consider how the open source cross-compilers (Harbour in this case) extend themselves with C library functions, which gives them a lot of flexibility. Not that computing π would be useful in itself for the average xBase programmer (by the way the CT library contains a function to directly compute π). We shall not concern ourselves with a method to do that, but move to something more conventional.

The FizzBuzz interview question: critical thinking and algorithms, with an introduction to table trace and the concept of flag[edit | edit source]

FizzBuzz is a little problem that is quite often asked in job interviews. A good discussion is found at the page https://micheleriva.medium.com/about-coding-the-fizzbuzz-interview-question-9bcd08d9dfe5. As always there are many ways of solving even a simple problem like this, that can be worded as follows,

Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

The first thing one should do is to think as many ways as possible to solve the problem. Also, you may be asked to show that your solution works without having access to a computer: the way of doing so is also one of the best ways of reading a program written by someone else, and even of checking the program you yourself wrote. You take a piece of paper and a pencil and check how the program instructions change the variables and what input they produce by arranging the content of the variables in a table and behaving like the computer. I heard this being called called w:Trace table, tracing an algorithm, hand tracing, dry running. For the moment I point you to a tutorial on YouTube which should give you a taste of how the thing works: https://www.youtube.com/watch?v=UbANyxE7pGE (Trace tables tutorial GCSE Computer Science).


Take for example the following trivial solution of FizzBuzz:

FOR I = 1 TO 100
    FLAG = 0
    IF I % 5 = 0 
        FLAG = 1
        ?? "Fizz"
    ENDIF
    IF I % 3 = 0 
        FLAG = 1
        ?? "Buzz"
    ENDIF
    IF FLAG = 0 
        ?? I
    ENDIF
    ?? ", "
NEXT

This example introduces a flag variable, that is, a variable that is checked to know whether the program executed a particular instruction or set of instructions.

Sorting Algorithms[edit | edit source]

Sorting data is an important application in computer science. In fact, in the ancient times (which means in the 50s), an important component of a computer was a piece of hardware called a Card Sorter, which was used to sort punched cards, see one at http://www.columbia.edu/cu/computinghistory/sorter.html, but the Census Machine built by Hollerith in the 1880's already had its own sorter box, and the French word for computer, ordinateur, can even be literally translated to sorter.

Let's start with a piece of code

   // we'll declare and load random numbers from 1 to 100 in an array
   DECLARE arrayToSort [12]
   FOR I = 1 TO 12
      arrayToSort [I] = Int( HB_Random( 1,101 ) )
   NEXT

   // we review our array
   FOR I = 1 TO 12
      ?? arrayToSort [I]
   NEXT

   // we'll do Exchange Sort as described in http://www.ee.ryerson.ca/~courses/coe428/sorting/exchangesort.html
   // where there is also a nice animation showing how it works! :)
   for i := 2 TO Len(arrayToSort)
      for j := Len(arrayToSort) TO i step -1
         IF arrayToSort [j - 1] > arrayToSort [j] 
		TEMP := arrayToSort [j - 1]
		arrayToSort [j - 1] := arrayToSort [j]
		arrayToSort [j] := TEMP
	 ENDIF
      NEXT
   NEXT

   ?
   ? "Sorted array:"
   ?

   // we look at our sorted array
   FOR I = 1 TO 12
      ?? arrayToSort [I]
   NEXT

From the version above we can (and should) separate the sorting routine in a procedure, this way:

PROCEDURE ExchangeSort(array)
   // we'll do Exchange Sort as described in http://www.ee.ryerson.ca/~courses/coe428/sorting/exchangesort.html
   // where there is also a nice animation showing how it works! :)
   for i := 2 TO Len(array)
      for j := Len(array) TO i step -1
         IF array [j - 1] > array [j] 
		TEMP := array [j - 1]
		array [j - 1] := array [j]
		array [j] := TEMP
	 ENDIF
      NEXT
   NEXT
RETURN

FUNCTION MAIN
   // we'll declare and load random numbers from 1 to 100 in an array
   DECLARE arrayToSort [12]
   FOR I = 1 TO 12
      arrayToSort [I] = Int( HB_Random( 1,101 ) )
   NEXT

   // we review our array
   FOR I = 1 TO 12
      ?? arrayToSort [I]
   NEXT

   ExchangeSort(arrayToSort) // we call the procedure and pass to it our array called arrayToSort

   ?
   ? "Sorted array:"
   ?

   // we show the array is sorted
   FOR I = 1 TO 12
      ?? arrayToSort [I]
   NEXT

Usually the first sorting algorithm introduced (even in my high school textbook) is Bubble Sort, which is a bad choice. Here we use Exchange Sort, which is equally bad, but at least we vary a little.

Grasping Recursion[edit | edit source]

We have recursion when a function calls itself. The definition is really this simple.

Put like that, it doesn't seem like a good idea, because it suggests that a recursive function will trap the program in an infinite loop. In reality a recursive function does not just call itself, but includes a criterion to stop doing it and provide some actual result.

A first example is the factorial function, of which we take a definition from a dictionary (https://www.dictionary.com/browse/factorial): « Mathematics. the product of a given positive integer multiplied by all lesser positive integers: The quantity four factorial (4!) = 4 ⋅ 3 ⋅ 2 ⋅ 1 = 24. Symbol: n!, where n is the given integer».

Code blocks[edit | edit source]

https://github.com/vszakats/harbour-core/blob/master/doc/codebloc.txt

Sites around the Net say that Clipper 5 documentation called them unnamed assignable functions.

Let us get back at our Celsius to Fahrenheit temperature converting function:

FUNCTION cels2f( tempCelsius )
tempFahrenheit := (tempCelsius*1.8 + 32)
RETURN tempFahrenheit

and redo it as a codeblock:

Cels2F := { | celsius | Fahrenheit := (Celsius * 1.8 + 32) } // our first codeblock

? Eval(Cels2F,100)
? Eval(Cels2F,0)

In the first line we assigned a code block to a variable named Cels2F. The code block is comprised in braces. The first thing is a variable that is to be passed to the block, between vertical bars (|). More variables should be separated by commas. Then we can write the instructions (more than one should be separated by commas).

Later we use two times the Eval function giving it two arguments: the name we assigned to our codeblock and the parameter to pass it. In addition to Eval, other functions can evaluate code block: AEval() and dbEval(). Ascan() and Asort() can be given code blocks to change their behavior.

As code blocks can't contain commands, we can't put a ? command in them, but have to use the corresponding QOut() function.

Command Line Arguments[edit | edit source]

Suppose that we wish to write a clone of the Unix cat command, which means that we will have a program to which we'll pass a command line argument that is a file name and output the content of the file.

PROCEDURE MAIN(file)
    ? MEMOREAD(file)
RETURN

That's it! In fact, it is quite limited, and it is more likely a clone of the DOS type command (or even of the CP/M type command!). Procedure This file's procedure MAIN receives one argument, and gets it from the command line. Then passes it to the MEMOREAD function (description: Return the text file’s contents as a character string), and sends the result to the console output.

File Management[edit | edit source]

Accessing files can be done using low-level functions, which are: FOPEN(), FCREATE(), FWRITE(), FREAD(), FREASDSTR(), FERASE(), FERROR(), FSEEK(), FCLOSE() and FRENAME().

Harbour, however, provides functions that permit manipulating text files in a similar way to DBF files (https://vivaclipper.wordpress.com/tag/memoread/).



Working With Databases

Working with Databases[edit | edit source]

Let's return to the Wikipedia entry Database application.

Different kinds of database applications exist as well. If you did store your friend's phone numbers and addresses into a word processor, you would have what someone calls a Free-Form Database (however, a similar expression is an oxymoron in computer science) - myBase®, askSam®, Personal Knowbase®, MyInfo®, Info Select®, and GeneralKB® are a bunch of specialized free-form database application, which actually means PIM (Personal information manager). Now, a word processor lets us search the information, but other operations, such as sorting them in alphabetical or numerical order, cannot usually be done automatically by a word processor.

What about attempting to store it into a spreadsheet? We may use one column for the name, one for the surname, one for the telephone number, one for the city. This quick database, stored in a spreadsheet, may be searched and sorted: for example, we can sort it by city and person's name in alphabetical order. This is a flat database, http://www2.research.att.com/~gsf/man/man1/cql.html: a flat database is a sequence of newline terminated records of delimiter separated fields, and a spreadsheet shows its limits in data entry and reporting (if you did want to use the data in your table to print out addresses on envelopes a spreadsheet is not a good tool). An example is MyDatabase (http://www.pcmag.com/article2/0,2817,760833,00.asp).

Spreadsheets are much better to do accounting: how much harder a book-keeper's work would be if his data were stored in a wordprocessing program? The purpose here is to have our data structured in a certain way: all the costs in a place, all earnings in another.

Before 1970 complex databases were managed using Hierarchical Databases (very little information is needed about them - see for example http://www.extropia.com/tutorials/sql/hierarchical_databases.html and http://people.cs.pitt.edu/~chang/156/14hier.html). An example of a hierarchical database is IBM IMS (Information Management System, which was developed during the mid-1960s for applications in the aerospace industry). Their implementation is based on trees, a hierarchical data structure. Hierarchical Databases and Network Databases together form what today are referred to as Legacy Database Systems. Network databases were born as an extension to the programming language COBOL by the Conference on Data Systems Languages (CODASYL). The network data model is based on data structures called graphs.

Today's standard is the Relational Database (RDBMS), which is "a database with relationships between more than one table of records based on common fields". We will speak of them in some detail, but we will briefly mention the fourth approach: Object Oriented Databases. These databases store objects (in the same sense the word is used in the expression object-oriented programming). They're not much used, mostly because objects are more complex than the simple fields a relational database stores in its tables. More information on the topic at http://www.odbms.org/odmg-standard/.

The Wikipedia entry about DBase reads: «dBase is application development language and integrated navigational database management system which Ashton-Tate labeled as "relational" but it did not meet the criteria defined by Dr. Edgar F. Codd's relational model». Codd's criteria (the so-called 12 rules, which really are 13 because the rule numbered '0' actually exists) are so strict that in practice a true relational database system does not even exist, but the point is that dBase accessed databases in another way, so that it's considered a Navigational Database (which works in a way that simulates relational databases).

http://www.databasedev.co.uk/design_basics.html

DBF Files in Other Languages[edit | edit source]

Because of the great success of dBase and its, the DBF file format became an industry standard. Many other database programs have used them to store data, like Lotus Approach. We also have many little utilities to view and convert to other formats these files. Here is a bunch of URLs: https://dbfview.com/, http://www.alexnolan.net/software/dbf.htm, https://dbfviewer.com/en/, https://www.dbf2002.com/, http://www.whitetown.com/dbf2sql/ («DBF to SQL Converter allows you to convert your dbf files to SQL script. Personal license $29.95», but compare https://www.vlsoftware.net/exportizer/). And is so widely used that interfaces for working with it are available for various languages, for example:

Well, now we will see how to work with DBF files the way it was intended.

Making a first database and recording some data[edit | edit source]

A verbose way[edit | edit source]

 && it was done this way at the Dot Prompt
 && we can type this interactively in hbrun
 CREATE TMPNAMES
 USE TMPNAMES
 APPEND BLANK
 REPLACE FIELD_NAME WITH "NAME"
 REPLACE FIELD_TYPE WITH "C"
 REPLACE FIELD_LEN WITH 15
 APPEND BLANK
 REPLACE FIELD_NAME WITH "ADDRESS"
 REPLACE FIELD_TYPE WITH "C"
 REPLACE FIELD_LEN WITH 30
 CLOSE
 CREATE NAMES FROM TMPNAMES && https://www.itlnet.net/programming/program/Reference/c53g01c/ngc785e.html
 ERASE TMPNAMES.DBF && we get rid of the temporary file

The code above created a DBF file, names.dbf, to be used by the following code. It will add a record to the DBF file. It is equivalent to the "First Sample Program" of my old PC GUIDE, which missed a line that is necessary in modern xBase.

 CLEAR
 ? "First Sample Program"
 SELECT 1
 USE NAMES
 APPEND BLANK
 REPLACE NAME WITH "MIKE BROWN"
 REPLACE ADDRESS WITH "ROME STREET, 56"
 CLOSE && this line is missing in my PC GUIDE but is needed in a compiled Harbour program
 QUIT

The CLOSE command is equivalent to the dbCloseArea() function, which closes a work area: Pending updates are written, pending locks are released.

A more concise way[edit | edit source]

The short code below does the same work of the two pieces of code of the previous section (it only produces a different file name, namesdb.dbf instead of names.dbf).

 local aStruct := { { "NAME",    "C", 15, 0 }, ;
                    { "ADDRESS", "C", 30, 0 }}
 REQUEST DBFCDX
 dbCreate( "namesdb", aStruct, "DBFCDX", .t., "NAMESDB" )
 && http://www.fivetechsoft.com/harbour-docs/api.html
 USE NAMESDB
 NAMESDB->(DbAppend())
 NAMESDB->NAME := "MIKE BROWN"
 NAMESDB->ADDRESS := "ROME STREET, 56"

This example uses the alias operator, ->. http://www.ousob.com/ng/clguide/ngcf412.php

The alias->field_name notation is used to allow access to fields of databases that are loaded but not active. The alias can be specified with the work area number (e.g. 2->std_id), with the work area alias (e.g. B->std_id), or with the database name (e.g. STUDENTS->std_id).

The result of this code is a file named namesdb.dbf. Informations about how DBF files are can be find at DBF File structure, http://www.dbf2002.com/dbf-file-format.html, where we find this list of Field type:

  • C – Character
  • Y – Currency
  • N – Numeric
  • F – Float
  • D – Date
  • T – DateTime
  • B – Double
  • I – Integer
  • L – Logical
  • M – Memo
  • G – General
  • C – Character (binary)
  • M – Memo (binary)
  • P – Picture
  • + – Autoincrement (dBase Level 7)
  • O – Double (dBase Level 7)
  • @ – Timestamp (dBase Level 7)

My PC GUIDE showed how a .dbf file is made with the DataBase Utility DBU. Clones of this utility are FiveDBU (with source code) at https://code.google.com/archive/p/fivewin-contributions/downloads, DBF Viewer Plus at http://www.alexnolan.net/software/dbf.htm, CLUT at http://www.scovetta.com/archives/simtelnet/msdos/clipper. Harbour includes its own HbDBU (the source is in \hb32\addons\hbdbu) and a component IdeDBU of HbIDE (the other two components are IdeEDITOR and IdeREPORTS).

From https://code.google.com/archive/p/fivewin-contributions/downloads we can get fivedbu_20130930.zip (New FiveDBU version with enhancements on ADO fields editing). It supports ADO, 3 RDD (DBFNTX, CBFCDX and RDDADS) and 6 languages - select "Bases de datos -> Preferencias -> Lenguaje: Inglés" to have it in English.

Let us see what is in our little file so far.

 USE NAMES
 LIST DATE(), TIME(), NAME, ADDRESS

Database Design Issue: the First Normal Form (1NF)[edit | edit source]

The work done in the previous section was intended to exactly reproduce the database presented in my PC GUIDE. There are, however, drawbacks: having only one NAME field, this database cannot sort its data on the last name. Also, a careless user might insert the data of some people with the last name first, and some other data with the first name last. When designing a database precautions should be taken of these possibilities. The first normal form (http://www.1keydata.com/database-normalization/first-normal-form-1nf.php, http://www.sqa.org.uk/e-learning/SoftDevRDS02CD/page_14.htm) requires you to define fields whose information cannot be divided into smaller parts. So, instead of a NAME field, we should have a FIRST_NAME and LAST_NAME fields. Complying to the first normal form, our little database would be on the on the right track to being a normalized database.

Designing the database is an essential part of the work and it is not always obvious how it should be done. See https://www.ntu.edu.sg/home/ehchua/programming/sql/relational_database_design.html.

A graphical device used to help is database design are the Entity-Relationship Diagrams: https://www.lucidchart.com/pages/er-diagrams, https://www.guru99.com/er-diagram-tutorial-dbms.html.

Complicating our simple database[edit | edit source]

Harbour contains a file named test.dbf. Launch hbrun in its directory and type in

use test
browse()

At this point, we see that it is a 500 record table. Move around with the cursor keys and, when you're finished, punch the Esc key to quit this interactive table browser and editor. To get the record number of a person called Ted issue:

locate for first="Ted"
? recno()

Something More Elaborate[edit | edit source]

We may now try the use of hbrun interactively to solve an exercise from an ECDL book. ECDL (European Computer Driving Licence, https://www.findcourses.co.uk/inspiration/articles/why-does-ecdl-matter-17580) is a qualification to show that someone can use a computer with different degrees of proficiency. When I got it, many (many) years ago the tasks for the database module very extremely easy. It was the 5th module the one that dealt with databases. I bought a small book and rereading it I found inspiring the 2nd exercise in that module: create a table to manage a movie library. I've done this in Harbour 3.0 under ReactOS 0.4.13, using hbrun almost interactively - which gives it an enjoyable dBase feel. However it is necessary to use some auxiliary files, as some commands are quite difficult - or impossible - to type on a single line. That is not bad, we'll see how to run a piece of code stored in a file from the prompt.

Point 1. Turn on the computer and open the database manager - as absurd as it seems, the first step of every exercise in my workbook is to turn on the computer.

C:\videolib>c:\hb30\bin\hbrun

The first command to issue will set the window to 25×80 characters, so that it looks good on screen and nothing is out of sight.

.setmode(25,80)

At this point we should see more or less this (probably your screen won't be in Italian):

Hbrun from Harbour 3.0 Running Under ReactOS 0.4.13

The first line shows the last command hbrun executed, setmode(25,80), and the bottom line features the legendary Dot Prompt.

Point 2. Create a table to manage a movie library with the following fields:

  • movie code,
  • movie title,
  • genre,
  • year of production,
  • leading actor,
  • price in euro.

We can issue all the following commands at the Dot Prompt to create a database with this structure:

.aDbf := {} 
.AADD(aDbf, { "MOVIEID", "C", 6, 0 })
.AADD(aDbf, { "TITLE", "C", 30, 0 }) 
.AADD(aDbf, { "GENRE", "C", 30, 0 }) 
.AADD(aDbf, { "YEAR", "N", 4, 0 }) 
.AADD(aDbf, { "LEADACTOR", "C", 30, 0 }) 
.AADD(aDbf, { "PRICE_EUR", "N", 5, 2 })
.DBCREATE("videolib", aDbf)</syntaxhighlight>

or we can store them in a file, for example creadb.prg and invoke from the command line using:.do creadb

Now we can check what is in our working directory:

.dir

this command will show the name of all databases in the current directory. We should have something like:

 Database Files    # Records    Last Update     Size
 videolib.dbf              0    09/15/21         227

and begin using out database:

.use videolib

the first two lines should now read:

PP: use videolib                                                               

RDD: DBFNTX | Area:  1 | Dbf: VIDEOLIB   | Index:          | #       1/      0 o

Point 3. Create a form to enter data - this we will do later.

Point 4. Fill the table with at least ten records with different data.

We will get our sample data from the CSV file "movies.txt" shown below using the command

.append from movies delimited

1,"Jurassic Park","azione",1993,"Jeff Goldblum",35.99
2,"Jumanji","avventura",1995,"Robin Williams",8.49 
3,"Navigator","fantascienza",1986,"Joey Cramer",11.39 
4,"Mortal Kombat","azione",1995,"Christopher Lambert",10 
5,"Karate Kid 4","azione",1994,"Hilary Swank",4.9 
6,"Ritorno al futuro","fantascienza",1985,"Michael J. Fox",6.95 
7,"2001: Odissea nello Spazio","fantascienza",1968,"Keir Dullea",7.9 
8,"Il pianeta proibito","fantascienza",1956,"Leslie Nielsen",9.99 
9,"Interstellar","fantascienza",2014,"Matthew McConaughey",6.95
10,"Prometheus","fantascienza",2012,"Michael Fassbender",7

We may check the data currently in our database using:

.browse()

Now we can make some small changes to our sample data. For example the data of the Genre field are in Italian, but we would like to have them in English. The commands to do the substitutions are surprisingly simple:

.replace genre with "science fiction" for genre="fantascienza"
.replace genre with "action" for genre="azione"
.replace genre with "adventure" for genre="avventura"

In the same way we can translate the Italian movie titles with their English equivalents ("Il pianeta proibito" is "The Forbidden Planet", "2001: Odissea nello spazio" is "2001: A Space Odyssey" - but that was easy to guess -, "Ritorno al futuro" is "Back to the Future").

The movieid field is a simple number, and that's ok but, to spice things up, let's try to change it with this command:

.replace movieid with left(title,4)+right(str(year),2) all

.list movieid, title

At this point we can already try and extract the list of films made before the year 2000:

.cls ; ? ; ? ; ?; list title, leadactor, year for year < 2000

to see how to enter multiple commands at the Dot Prompt, just separate them with a semicolon. Here we can see also how a dBase command is built:

list - a verb
title, leadactor, year - an expression list
for year < 2000 - a condition

However, those three question marks to make some space to see all the records (the first three lines of hbrun are occupied by other information) are just ugly. Next time we will use setpos().

Now that we think about it the movie code field, Movieid, in record 7 consists only of numbers and in record 8 contains a space, which is just as ugly. We can correct it interactively by entering at the dot prompt:

.browse()

and replace those fields with "Odys68" and "Forb56".

Since we are making corrections, I just remembered that I gave away 2001: A Space Odyssey in change of a better movie, and so we may as well delete it:

.delete record 7

.list title for deleted()

.pack

Now we can browse() again and add a new record by hand: "Miss86", "Mission", "history,drama", "1986", "Jeremy Irons", "6.95".

Point 5. Sort the table by film title.

.index on title to titlesort

Again, .browse() will permit us to make sure things actually changed in the way the file appears.

Point 6. Query the database for films made before the year 2000

We’ve already done this, but this time we have the index on the field Title active:

.cls ; setpos(3,1) ; list title, leadactor, year for year < 2000

What else we can do? We can also also compute the average age of our movies (in years):

.average year(date()) - year to yearavg ; ? yearavg

(maybe i should watch more recent movies...), or see how much we spent on our movie collection:

.sum price_eur to totalprice ; ? totalprice

Point 7. Create a report named "Movie List" with the records of the films in order of genre and title and containing the fields code, genre, title, price in euro.

The DOS dBase had its own Report Generator and Clipper provided an utility, RL.EXE, to create reports and labels. Those programs create a format file used to generate the report, in those ancient times when reports were printed on continuous form paper using dot matrix printers. But how can we create a report today? How to preview it onscreen before sending it to the printer? Probably the most obvious options are to create a PDF or HTML file (which will then need a stylesheet to be watchable, otherwise it'll be too insipid). Since I'm using a vanilla Harbour 3.0 to start we will follow the simplest way: generate an HTML file. We will try a tabular report (this code needs to be corrected, as the HTML it produces although can be interpreted, cannot be validated as many closing tags are missing):

** report.prg
use videolib
index on genre+title to iReport
set printer on
set printer to 'rMovie.html'
?[<html><body>]
?[<h1>],"Movie Report",[</h1>]
?[<table>]
?[<tr><th>Movie Code<th>Genre<th>Title<th>Price in Euro</tr>]
do while .not. eof()
  ?[<tr>]
  ?[<td>],movieid
  ?[<td>],genre
  ?[<td>],title
  ?[<td>],price_eur
  ?[</tr>]
 skip
enddo

.do report

This is not much to look at, but a CSS will improve its appearance considerably. Even a messy CSS (this too needs a clean up) like this:

h1 {
  display: block;
  font-size: 2em;
  margin-top: 0.67em;
  margin-bottom: 0.67em;
  margin-left: 0;
  margin-right: 0;
  font-weight: bold;
  margin: auto;
  width: 50%;
  border: 3px solid green;
  padding: 10px;
  text-align: center;
  font-family: Arial, Helvetica, sans-serif;
  background-color: lightgreen;
}

table, th, td {
  border: 1px solid black;
  margin: auto;
  width: 75%;
  border: 3px solid green;
  padding: 10px;
}
table.center {
  margin-left: auto;
  margin-right: auto;
}
th, td {
  padding: 10px;
  width: 25%;
  text-align: center;
}
th {
  padding: 10px;
  width: 25%;
  font-family: Arial, Helvetica, sans-serif;
  border: 3px solid blue;
  background-color: lightblue;
}

can get us a colorful printed report that does not look dull - albeit unprofessional:

Report made with a Harbour script and a CSS

Point 8. Print the "Movie List" report.

Point 9. Close the database and the program. In hbrun these two things are done with these two commands, respectively:

.use

.quit

What have we left within our working directory? .dir *.* reports the following:

iReport  ntx     2048  09/15/21
movies   txt      605  09/15/21
report   prg      414  09/15/21
rMovie   htm     1290  09/15/21
styles   css      743  09/15/21
titlesor ntx     2048  09/15/21
videolib dbf     1287  09/15/21

Note 1. Using HTML for output is fun, but it is not a good idea, as often a report will be longer than one page of paper and an HTML+CSS table will not handle properly such multiple pages output. Creating a PDF of the report to print, using either libHaru (http://libharu.org/) using the bindings of contrib\hbhpdf (or extras\hbvpdf) would be a much better choice.

Note 2. This example is really basic. We could do just about everything using the Dot Prompt in less than 20 minutes. ECDL has changed a lot during the last few lustra. Now I'm giving a look at the ECDL Advanced Database Syllabus 2.0. It looks good, with some interesting exercises.

Workareas: more than one table at once[edit | edit source]

Let us for a moment think back at the old times when commonly used computers had no mouse and GUI. Nevertheless, if we wanted to use several tables for a task, we had to use some way to tell the computer which tables he should consider. Let us take for example a library. Oversimplifying, we need to use at least three tables to manage it (one for the books, one for the customers and one for the loans).

We might issue these commands:

       SELECT 1
       USE Books
       SELECT 2
       USE Customers
       SELECT 3
       USE Loans
       SELECT Books

We might visualize the result as the three windows in the picture below:

3 workareas.png

Here the command SELECT works like clicking on a window to activate it, and the workareas themselves look like windows having a number (the workarea number) and a name (the workarea alias).

Testdbf.prg[edit | edit source]

Here is the testdbf.prg source from \hb30\tests. It should be discussed in detail. It is a GPL piece of code poorly commented.

 /*
  * $Id: testdbf.prg 1792 1999-11-10 10:17:19Z bcantero $
  */

 function main()

   local nI, aStruct := { { "CHARACTER", "C", 25, 0 }, ;
                          { "NUMERIC",   "N",  8, 0 }, ;
                          { "DOUBLE",    "N",  8, 2 }, ;
                          { "DATE",      "D",  8, 0 }, ;
                          { "LOGICAL",   "L",  1, 0 }, ;
                          { "MEMO1",     "M", 10, 0 }, ;
                          { "MEMO2",     "M", 10, 0 } }

   REQUEST DBFCDX

   dbCreate( "testdbf", aStruct, "DBFCDX", .t., "MYALIAS" )

   ? "[" + MYALIAS->MEMO1 + "]"
   ? "[" + MYALIAS->MEMO2 + "]"
   ? "-"
   MYALIAS->( dbAppend() )
   MYALIAS->MEMO1 := "Hello world!"
   MYALIAS->MEMO2 := "Harbour power"
   ? "[" + MYALIAS->MEMO1 + "]"
   ? "[" + MYALIAS->MEMO2 + "]"
   MYALIAS->( dbAppend() )
   MYALIAS->MEMO1 := "This is a test for field MEMO1."
   MYALIAS->MEMO2 := "This is a test for field MEMO2."
   ? "[" + MYALIAS->MEMO1 + "]"
   ? "[" + MYALIAS->MEMO2 + "]"
   MYALIAS->NUMERIC := 90
   MYALIAS->DOUBLE := 120.138
   ? "[" + Str( MYALIAS->DOUBLE ) + "]"
   ? "[" + Str( MYALIAS->NUMERIC ) + "]"

   ? ""
   ? "Press any key..."
   InKey( 0 )

   ? ""
   ? "Append 50 records with memos..."
   for nI := 1 to 50
      MYALIAS->( dbAppend() )
      MYALIAS->MEMO1 := "This is a very long string. " + ;
                        "This may seem silly however strings like this are still " + ;
                        "used. Not by good programmers though, but I've seen " + ;
                        "stuff like this used for Copyright messages and other " + ;
                        "long text. What is the point to all of this you'd say. " + ;
                        "Well I am coming to the point right now, the constant " + ;
                        "string is limited to 256 characters and this string is " + ;
                        "a lot bigger. Do you get my drift ? If there is somebody " + ;
                        "who has read this line upto the very end: Esto es un " + ;
                        "sombrero grande rid¡culo." + Chr( 13 ) + Chr( 10 ) + ;
                        "/" + Chr( 13 ) + Chr( 10 ) + "[;-)" + Chr( 13 ) + Chr( 10 )+ ;
                        "\"
   next
   MYALIAS->( dbCommit() )

   ? "Records before ZAP:", MYALIAS->( LastRec() )
   ? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
      Directory( "testdbf.fpt" )[1][2]
   MYALIAS->( __dbZap() )
   MYALIAS->( dbCommit() )
   ? "Records after ZAP:", MYALIAS->( LastRec() )
   ? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
      Directory( "testdbf.fpt" )[1][2]
   ? "Value of fields MEMO1, MEMO2, DOUBLE and NUMERIC:"
   ? "[" + MYALIAS->MEMO1 + "]"
   ? "[" + MYALIAS->MEMO2 + "]"
   ? "[" + Str( MYALIAS->DOUBLE ) + "]"
   ? "[" + Str( MYALIAS->NUMERIC ) + "]"
   ? "Press any key..."
   InKey( 0 )
   dbCloseAll()

   dbCreate( "testdbf", aStruct,, .t., "MYALIAS" )

   for nI := 1 to 10
      MYALIAS->( dbAppend() )
      MYALIAS->NUMERIC := nI
      ? "Adding a record", nI
      if nI == 3 .or. nI == 7
         MYALIAS->( dbDelete() )
         ? "Deleting record", nI
      endif
   next
   MYALIAS->( dbCommit() )

   ? ""
   ? "With SET DELETED OFF"
   ? "Press any key..."
   InKey( 0 )

   MYALIAS->( dbGoTop() )
   do while !MYALIAS->( Eof() )
      ? MYALIAS->NUMERIC
      MYALIAS->( dbSkip() )
   enddo

   SET DELETED ON
   ? ""
   ? "With SET DELETED ON"
   ? "Press any key..."
   InKey( 0 )

   MYALIAS->( dbGoTop() )
   do while !MYALIAS->( Eof() )
      ? MYALIAS->NUMERIC
      MYALIAS->( dbSkip() )
   enddo

   ? ""
   ? "With SET DELETED ON"
   ? "and  SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
   ? "Press any key..."
   InKey( 0 )

   MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
                           "MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
   MYALIAS->( dbGoTop() )
   do while !MYALIAS->( Eof() )
      ? MYALIAS->NUMERIC
      MYALIAS->( dbSkip() )
   enddo

   SET DELETED OFF
   ? ""
   ? "With SET DELETED OFF"
   ? "and  SET FILTER TO MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8"
   ? "Press any key..."
   InKey( 0 )

   MYALIAS->( dbSetFilter( { || MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8 }, ;
                           "MYALIAS->NUMERIC > 2 .AND. MYALIAS->NUMERIC < 8" ) )
   MYALIAS->( dbGoTop() )
   do while !MYALIAS->( Eof() )
      ? MYALIAS->NUMERIC
      MYALIAS->( dbSkip() )
   enddo

   ? "dbFilter() => " + dbFilter()
   ? ""

   ? "Testing __dbPack()"
   ? "Records before PACK:", MYALIAS->( LastRec() )
   ? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
      Directory( "testdbf.dbt" )[1][2]
   SET FILTER TO
   MYALIAS->( __dbPack() )
   MYALIAS->( dbCommit() )
   ? "Records after PACK:", MYALIAS->( LastRec() )
   ? "Size of files (data and memo):", Directory( "testdbf.dbf" )[1][2], ;
      Directory( "testdbf.dbt" )[1][2]
   ? "Press any key..."
   InKey( 0 )
   ? "Value of fields:"
   MYALIAS->( dbGoTop() )
   do while !MYALIAS->( Eof() )
      ? MYALIAS->NUMERIC
      MYALIAS->( dbSkip() )
   enddo
   ? ""

   ? "Open test.dbf and LOCATE FOR TESTDBF->SALARY > 145000"
   ? "Press any key..."
   InKey( 0 )
   dbUseArea( ,, "test", "TESTDBF" )
   locate for TESTDBF->SALARY > 145000
   do while TESTDBF->( Found() )
      ? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->SALARY
      continue
   enddo
   ? ""
   ? "LOCATE FOR TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'"
   ? "Press any key..."
   InKey( 0 )
   dbUseArea( ,, "test", "TESTDBF" )
   locate for TESTDBF->MARRIED .AND. TESTDBF->FIRST > 'S'
   do while TESTDBF->( Found() )
      ? TESTDBF->FIRST, TESTDBF->LAST, TESTDBF->MARRIED
      continue
   enddo

 return nil

Input Mask[edit | edit source]

A simple data base input mask (from the Wikipedia Clipper entry):

USE Customer SHARED NEW
clear
@  1, 0 SAY "CustNum" GET Customer->CustNum PICT "999999" VALID Customer->CustNum > 0
@  3, 0 SAY "Contact" GET Customer->Contact VALID !empty(Customer->Contact)
@  4, 0 SAY "Address" GET Customer->Address
READ

RDDs: What Functions Are Available?[edit | edit source]

http://harbourlanguage.blogspot.de/2010/06/understanding-harbour-rdd.html

ADO RDD: Much Ado About Nothing[edit | edit source]

https://searchsqlserver.techtarget.com/definition/ActiveX-Data-Objects http://cch4clipper.blogspot.com/2009/10/using-adordd-with-harbourxharbour.html

Case Study: Checkbook Balancing[edit | edit source]

Deleting records[edit | edit source]

? LASTREC()
DELETE RECORD 4
PACK
? LASTREC()

In this piece of code, the command DELETE marks the fourth record for deletion. But the file is not altered, not even by a CLOSE command. The PACK command actually removes the records marked for deletion (and also makes some additional work). The RECALL command removes the deleted flags. The function DELETED() returns .T. if the current record is marked for deletion, .F. if not.

The PACK command, which does the actual deletion of data from the table, PACK requires that the current database be USEd EXCLUSIVEly. If this condition is not met when the PACK command is invoked, CA-Clipper generates a runtime error. Additional work that PACK does is to update indexes on the table it alters (if any).

The commands DELETE ALL and PACK are executed by a single command called ZAP.

     &&  This example demonstrates a typical ZAP operation in a network
     &&   environment:

        USE Sales EXCLUSIVE NEW
        IF !NETERR()
           SET INDEX TO Sales, Branch, Salesman
           ZAP
           CLOSE Sales
        ELSE
           ? "Zap operation failed"
           BREAK
        ENDIF

An Indexed Example[edit | edit source]

USE Clients NEW
INDEX ON Name TO Clients UNIQUE

Suppose a table containing these data:

FSTNAME	LSTNAME
John   	Doe
John   	Doe
John   	Doe
Jane   	Doe

We can create a little index file with this piece of code:

SELECT 1
USE ind
? FILE("ind.ntx")
INDEX ON FstName TO ind
? FILE("ind.ntx") // we verify that a NTX file has been created

Set Relation - Working with more than one table[edit | edit source]



Object Oriented Programming

Object Oriented Programming[edit | edit source]

Clipper had very limited support for OOP (Object Oriented Programming, from now on we’ll call it by its nickname). His successor CA-Visual Objects was much more advanced in this respect. However Visual Objects never had a great success, and third-party producers provided OOP libraries for Clipper, among which the most famous were Class(y), TopClass, Fivewin and Clip4Win.

Looking at Object-Oriented Programming from a Safety Distance[edit | edit source]

Let us suppose we're dealing with the distance function given the Cartesian coordinates of the points (http://mathinsight.org/cartesian_coordinates). The formulas we'll apply are:

for (Euclidean) distance on a real line, Euclidean plane and Euclidean space respectively.

In procedural programming our functions would look like this:

? distance1d(4,-3)
? distance2d(2,-3,-1,-2)
? distance3d(1,1,1,4,4,4)

        FUNCTION distance1d( x1, x2 )
           RETURN sqrt((x2-x1)^2)

        FUNCTION distance2d( x1,y1,x2,y2 )
           RETURN sqrt((x2-x1)^2+(y2-y1)^2)

        FUNCTION distance3d( x1,y1,z1,x2,y2,z2 )
           RETURN sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2)

We defined three functions, with different names, which take as arguments the coordinates. But doing so we need to pass six arguments for the distance in a three-dimensional space.

If we're doing it with object oriented programming we may get something like this:

#include "hbclass.ch"

   CREATE CLASS Point1D

   VAR Abscissa    // the abscissa of our point

METHOD New( Abscissa )    // Constructor

METHOD Distance( Point )

   ENDCLASS

   CREATE CLASS Point2D INHERIT Point1D

   VAR Ordinate    // the ordinate of our point

METHOD New( Abscissa, Ordinate )    // Constructor

METHOD Distance( Point )

   ENDCLASS

   CREATE CLASS Point3D INHERIT Point2D

   VAR Zcoord    // the Z-coordinate of our point

METHOD New( Zcoord )    // Constructor

METHOD Distance( Point )

   ENDCLASS

&& Constructors Zone
METHOD New( Abscissa ) CLASS Point1D

   ::Abscissa := Abscissa

   RETURN Self

METHOD New( Abscissa, Ordinate ) CLASS Point2D

   ::Abscissa := Abscissa
   ::Ordinate := Ordinate

   RETURN Self

METHOD New( Abscissa, Ordinate, Zcoord ) CLASS Point3D

   ::Abscissa := Abscissa
   ::Ordinate := Ordinate
   ::Zcoord := Zcoord

   RETURN Self

&&Distances Methods

METHOD Distance( Point ) CLASS Point1D

   RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 )

METHOD Distance( Point ) CLASS Point2D

   RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 )

METHOD Distance( Point ) CLASS Point3D

   RETURN Sqrt( ( Self:Abscissa - Point:Abscissa ) ^ 2 + ( Self:Ordinate - Point:Ordinate ) ^ 2 + ( Self:Zcoord - Point:Zcoord ) ^ 2 )

PROCEDURE Main()

   FirstPoint := Point1D():New( 3 )
   SecondPoint := Point1D():New( - 3 )
   ? FirstPoint:Abscissa
   ? FirstPoint:Distance( SecondPoint )
   ThirdPoint := Point2D():New( 2, - 3 )
   FourthPoint := Point2D():New( - 1, - 2 )
   ? ThirdPoint:Distance( FourthPoint )
   FifthPoint := Point3D():New( 1, 1, 1 )
   SixthPoint := Point3D():New( 4, 4, 4 )
   ? FifthPoint:Distance( SixthPoint )

   RETURN

Here we've defined three classes, their constructors, and a distance method for each of them, and showed how to use them. It is also a simple example of how inheritance works. Other concepts are encapsulation (information hiding or data hiding), abstraction, polymorphism, overloading and overriding of methods. Inheritance plays a central role in a key concept: reuse. A class can be reused in a software project if it is exactly what was needed; or if it is not exactly what was needed it can be extended by defining a subclass. Much like the design of a database, the design of object oriented class is an art with its principles, see for example http://www.oodesign.com/, http://www.codeproject.com/articles/567768/object-oriented-design-principles and pages about UML like http://www.uml-diagrams.org/uml-object-oriented-concepts.html.

The first thing to note is that we start by including the clipper header file hbclass.ch, the header file for Class commands.

Access to variables and methods of an object is done via the colon operator. A prepended double colon refers to variables with a larger scope (such as those passed to a method).

In the code above we defined three classes, each one implementing a Point. Point2D for example was defined as a class extending Point1D, that is a generalization of the concept. A method Distance was given for each of the classes.

A line such as

  ? FifthPoint:Distance( SixthPoint )

contain the output command ?, the reference to an object (FifthPoint in this case), an invocation of the Distance method :Distance, to which another point was passed ( SixthPoint ).

It is also possible to write a Distance function which takes two arguments of a Point class, that may look like this:

FUNCTION Distance ( Point1, Point2 )
   RETURN Sqrt( ( Point1:Abscissa - Point2:Abscissa ) ^ 2 + ( Point1:Ordinate - Point2:Ordinate ) ^ 2 + ( Point1:Zcoord - Point2:Zcoord ) ^ 2 )

   ? Distance( FifthPoint, SixthPoint )

This is, however, not object-oriented programming, as we could have written the same function with a not object-oriented language such as Pascal or C, passing it two structs, or records as Pascal calls them, named Point1 and Point2.

It is important the fact that some data internal to the object (set by the real programmer of the thing) can't be changed by the object user. As a real life example, we can consider a car engine. The provider of the object set a number of cylinders, and we have not many chances of changing that: we've got to regard it as a constant. There is naturally a number of interesting formulas about engines that engineers use (some to be seen at http://www.thecartech.com/subjects/engine/engine_formulas.htm). For example, the one for computing the Engine Volumetric Efficiency given the volume of air taken into a cylinder and the cylinder swept volume. Here comes the importance of data hiding: nobody needs to know those informations to get his car going. Also, when someone designes an engine they probably don't expect the user to change the volumetric efficiency by operating on the engine. The same thing is obtained in object oriented programming using visibility modifiers, or access modifiers.

   [CREATE] CLASS <cClassName> [ FROM | INHERIT <cSuperClass1> [, ... ,<cSuperClassN>] ]
             [ MODULE FRIENDLY ] [ STATIC ] [ FUNCTION <cFuncName> ]

   [HIDDEN:]
      [ CLASSDATA | CLASSVAR  | CLASS VAR <DataName1>]
      [ DATA | VAR  <DataName1> [,<DataNameN>] [ AS <type> ] [ INIT <uValue> ]
             [[EXPORTED | VISIBLE] | [PROTECTED] | [HIDDEN]] [READONLY | RO] ]
      ...
      [ METHOD <MethodName>( [<params,...>] ) [CONSTRUCTOR] ]
      [ METHOD <MethodName>( [<params,...>] ) INLINE <Code,...> ]
      [ METHOD <MethodName>( [<params,...>] ) BLOCK  <CodeBlock> ]
      [ METHOD <MethodName>( [<params,...>] ) EXTERN <funcName>([<args,...>]) ]
      [ METHOD <MethodName>( [<params,...>] ) SETGET ]
      [ METHOD <MethodName>( [<params,...>] ) VIRTUAL ]
      [ METHOD <MethodName>( [<params,...>] ) OPERATOR <op> ]
      [ ERROR HANDLER <MethodName>( [<params,...>] ) ]
      [ ON ERROR <MethodName>( [<params,...>] ) ]
      ...
   [PROTECTED:]
      ...
   [VISIBLE:]
   [EXPORTED:]
      ...

   [FRIEND CLASS <ClassName,...>]
   [FRIEND FUNCTION <FuncName,...>]

   [SYNC METHOD <cSyncMethod>]

   ENDCLASS [ LOCK | LOCKED ]

Another example[edit | edit source]

Copied verbatim from w:Harbour (software)

 #include "hbclass.ch"

 PROCEDURE Main()

    LOCAL oPerson

    CLS

    oPerson := Person():New( "Dave" )

    oPerson:Eyes := "Invalid"

    oPerson:Eyes := "Blue"

    Alert( oPerson:Describe() )

    RETURN

 CREATE CLASS Person

    VAR Name INIT ""

    METHOD New( cName )
    METHOD Describe()

    ACCESS Eyes INLINE ::pvtEyes
    ASSIGN Eyes( x ) INLINE iif( HB_ISSTRING( x ) .AND. x $ "Blue,Brown,Green", ::pvtEyes := x, Alert( "Invalid value" ) )

    PROTECTED:

    VAR pvtEyes

 ENDCLASS

 // Sample of normal Method definition
 METHOD New( cName ) CLASS Person

    ::Name := cName

    RETURN Self

 METHOD Describe() CLASS Person

    LOCAL cDescription

    IF Empty( ::Name )
       cDescription := "I have no name yet."
    ELSE
       cDescription := "My name is: " + ::Name + ";"
    ENDIF

    IF ! Empty( ::Eyes )
       cDescription += "my eyes' color is: " + ::Eyes
    ENDIF

    RETURN cDescription



Other Features (Not Present in the Old Clipper)

Other programming topics[edit | edit source]

Hash Arrays (Associative Arrays, Also Known as "Hash Tables")[edit | edit source]

In an associative array the elements are accessed using a string and not a number as in the "normal" array.

There are online "Calorie checkers" (which are also a nice idea for a web application for the last part) and I think it will be fun to make us a very small one (BEWARE: the information obtained from this program should not replace a varied, balanced diet and a healthy lifestyle).

   hFood := { => }
   hFood["Cornflakes"] := "370"
   hFood["Spaghetti"] := "101"
   hFood["Beef"] := "280"
   hFood["Ham"] := "240"
   hFood["Broccoli"] := "32"
   hFood["Grapefruit"] := "32"
   hFood["Spinach"] := "8"
   hFood["Chocolate"] := "500"
   ACCEPT "Enter a food: " TO cFood
   IF hb_HHasKey( hFood, cFood )
      ? "&cFood is " + hFood[cFood] + " calories per 100 grams"
   ELSE
      ? "&cFood: information not available"
   ENDIF

Now we will redo the example of the array of months, showing an alternative syntax for loading data into an hash array:

LOCAL month
hMonths := { "January" => 31, "February" => 28, "March" => 30,  "April" => 30, "May" => 31, "June" => 30, "July" => 31, "August" => 31, "September" => 30, "October" => 31, "November" => 30, "December" => 31 }

FOR EACH month in hMonths
   ? month:__ENUMKEY()
   ?? month:__ENUMVALUE()
NEXT

hb_HEval() is the AEval() equivalent for hash arrays.

See https://vivaclipper.wordpress.com/2013/01/18/hash-vs-table/.

Morse Code[edit | edit source]

Morse code was invented around 1840 by Samuel Finley Breese Morse and Alfred Vail to transmit messages across wires using devices called "telegraphs" they invented as well, revolutionizing long-distance communication. Initially the signals sent from one telegraph operated an electromagnet on the receiver's telegraph embossing the message on a strip of paper, later the receiver could also produce a sound. The first message transmitted was a citation of the book of Numbers from the King James Version: What hath God wrought!

Let us try to convert a text to Morse code: to begin we load the chart of Morse code in an associative array and define a code block which uses the function TONE() to sound a speaker tone for a dot or a dash - employing the IF() function - which is essentially the same function we encounter in spreadsheets - to select the right duration tone.

Then we prompts the user for a message and if none is supplied it uses the pangram "The quick brown fox jumps over a lazy dog" (a pangram is a sentence that contains all of the letters of the alphabet).

We show what message we will translate and starts two a for each loop: the first will scan the message showing which letter it will translate, one at a line (and will also use TONE() with zero frequency and appropriate lengths to make the pauses between letters and words) and then a nested for each loop will show whether it is about to sound a dot or a dash, running the code block to have the right sound.

LOCAL letter, ditdah
hMorseCode := { => }

hMorseCode[ "a" ] := ".-"
hMorseCode[ "b" ] := "-..."
hMorseCode[ "c" ] := "-.-."
hMorseCode[ "d" ] := "-.."
hMorseCode[ "e" ] := "."
hMorseCode[ "f" ] := "..-."
hMorseCode[ "g" ] := "--."
hMorseCode[ "h" ] := "...."
hMorseCode[ "i" ] := ".."
hMorseCode[ "j" ] := ".---"
hMorseCode[ "k" ] := "-.-"
hMorseCode[ "l" ] := ".-.."
hMorseCode[ "m" ] := "--"
hMorseCode[ "n" ] := "-."
hMorseCode[ "o" ] := "---"
hMorseCode[ "p" ] := ".--."
hMorseCode[ "q" ] := "--.-"
hMorseCode[ "r" ] := ".-."
hMorseCode[ "s" ] := "..."
hMorseCode[ "t" ] := "-"
hMorseCode[ "u" ] := "..-"
hMorseCode[ "v" ] := "...-"
hMorseCode[ "w" ] := ".--"
hMorseCode[ "x" ] := "-..-"
hMorseCode[ "y" ] := "-.--"
hMorseCode[ "z" ] := "--.."
hMorseCode[ "0" ] := "-----"
hMorseCode[ "1" ] := ".----"
hMorseCode[ "2" ] := "..---"
hMorseCode[ "3" ] := "...--"
hMorseCode[ "4" ] := "....-"
hMorseCode[ "5" ] := "....."
hMorseCode[ "6" ] := "-...."
hMorseCode[ "7" ] := "--..."
hMorseCode[ "8" ] := "---.."
hMorseCode[ "9" ] := "----."

nDotDuration := 4 && The dot duration is the basic unit of time measurement in Morse code transmission and we set it to 4/18 seconds
bPlayMorse := {| cCurrent| if ( cCurrent = ".", Tone( 480, nDotDuration ), Tone( 480, nDotDuration * 3 ) ) }

ACCEPT "Enter message: " TO message
IF message == "" && if no message we use a default one
  message := "The quick brown fox jumps over a lazy dog" && an English-language pangram — a sentence that contains all of the letters of the alphabet
ENDIF

? "Converting '"+Upper( message )+"' in Morse..."

FOR EACH letter in Lower( message )
  ? Upper(letter)+" "
    IF letter==" " 
      Tone( 0, nDotDuration * 7 ) && a long pause between words...
    ELSE
      FOR EACH ditdah in hMorseCode[ letter ]
        ?? ditdah
        Eval( bPlayMorse, ditdah )
      NEXT
    ENDIF
  Tone( 0, nDotDuration * 3 ) && ... and a short pause between letters
NEXT

Regular Expressions[edit | edit source]

Regular expressions can be considered an extension of wildcards. In a DOS or Windows prompt, for example, we could list all dbf files in the current directory with the command

dir *.dbf

where the asterisk, as they say, matches one or more characters. The other wildcard, the question mark, will match any character, but exactly one: the commands

dir ?.dbf
dir ??.dbf

will show, respectively, every dbf file in the current directory whose name is exactly one or two characters long.

Regular expressions are much more flexible. They were invented by the mathematician Stephen Cole Kleene (the asterisk wildcard derives from an operator he defined and which is called Kleene star). Their flexibility allows us to write an expression that can match dbf filenames one or two characters long in a single expression which looks like this:

.{1,2}\.dbf

There are different types of regular expressions: basic, extended, PCRE (Perl-compatible regular expressions, http://www.pcre.org/), and many other different implementations.

As far as we are concerned we can limit ourselves to PCRE and refer to the documentation at these URLs: https://github.com/Petewg/harbour-core/wiki/Regular-Expressions and https://github.com/zgamero/sandbox/wiki/X_RegularExpressions, https://www.pcre.org/original/doc/html/pcrepattern.html, https://www.debuggex.com/cheatsheet/regex/pcre. There are large books entirely devoted about regular expressions, such as "Mastering Regular Expressions" by Jeffrey E.F. Friedl, just to say there's so much to explore about this topic.

Here https://www.rexegg.com/regex-cookbook.html is a cookbook to see examples; and here https://www.regexpal.com/ and here https://regexr.com/ there are interactive tools to try online (and interactively) expressions.

Regular expressions are not a panacea that solves all data validation problems: for example, it is not possible to use them to validate dates, as the number of days allowed for each month depends on the month (and in the case of February also on the year).

As a first example, we will redo the program that receives a letter from the keyboard and reports if it is a vowel or not.

LOCAL cRegEx := "[aeiou]"
WAIT "Key in a letter: " TO char
? char + " is" + iif( hb_regexLike( cRegEx, char, .F. ), "", " not" ) + " a vowel."

The synopsis of hb_RegExLike is this: hb_RegExLike(<hRegEx>|<cRegEx>, <cText> [, <lCaseSensitive>] [, <lMultiLine>]).

The next example is again the program to average of an array which grows until we insert numbers as input. This time we use a regular expression to validate the numbers we enter.

PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   LOCAL reNumber := "-?[0-9]+(\.?[0-9]+)?" && and a regular expression that matches numbers (optionally with a decimal point and a sign)
   aNumbers := {}                && and an empty array

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
      ACCEPT "next element: " TO item
      IF hb_RegExLike ( reNumber, item ) && we replace IsDec() with hb_RegExLike()
         AAdd ( aNumbers, val(item) ) && if we did, then we add the number as a new element to the array
         sum := sum + val(item)      && (and we update the sum of the elements in our array)...
      ELSE
         EXIT                    && ...if we did not, we quit this loop
      ENDIF
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to LEN ( aNumbers )
      ?? AVERAGE - aNumbers[n]
   NEXT
RETURN

Error Handling With “Try-Catch-Finally” Block[edit | edit source]

This construct is analogous to those found in Java, JavaScript and C# (and similar to the C++'s one). They are actually translated into the BEGIN SEQUENCE structure and therefore perhaps they are useful only for those who are accustomed to those languages.

PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   aNumbers := {}                && and an array without elements

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
   ACCEPT "next element: " TO item
      TRY
         AAdd ( aNumbers, &item ) && if we did, then we add the number as a new element to the array
         sum := sum + &item      && (and we update the sum of the elements in our array)...
      CATCH
         EXIT                    && ...if we did not, we quit this loop
      END
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to LEN ( aNumbers )
      ?? AVERAGE - aNumbers[n]
   NEXT
RETURN

Multithreading[edit | edit source]

http://harbourlanguage.blogspot.com/2010/04/harbour-multi-thread.html

https://github.com/Petewg/harbour-core/wiki/MultiThreading

see samples in \hb30\tests\mt

INET[edit | edit source]

https://github.com/Petewg/harbour-core/wiki/Harbour-INET-API

PDF[edit | edit source]

hbhpdf: Libharu http://libharu.org/ bindings. Documentation: https://github.com/libharu/libharu/wiki/API%3A-Document

http://www.pagescript32.com/

https://github.com/MRonaldo/MR-Tools/downloads

How to create a DLL[edit | edit source]

http://www.xharbour.com/xhdn/referenceguide/index.asp?page=article&article=create_dll

Debugging[edit | edit source]

http://www.kresin.ru/en/debugger.html

You may have heard or read the legend that the term bug appeared when an actual bug was found in the Harvard's Mark II computer. According to one of my books from the high school, it was a little butterfly found in the ENIAC!

Text books less prone to folklore call this a story without foundation, for example

https://www.computerworld.com/article/2515435/moth-in-the-machine--debugging-the-origins-of--bug-.html states that Thomas Edison used the word already in 1878. The same goes with the Wikipedia entry →Software bug which even includes a picture of the moth actually found in the Harvard Mark II and describes the whole history.
A page from the →Harvard Mark II electromechanical computer's log, featuring a dead moth that was removed from the device.



Making Up a User Interface

Text (Console) Interfaces[edit | edit source]

The easiest way is to use the basic I/O commands.

Next comes the GT (Graphic Terminal) system (rough descriptions at http://harbourlanguage.blogspot.it/2010/04/harbour-gt.html and https://github.com/vszakats/harbour-core/blob/master/doc/gtapi.txt). Let us start with two simple examples.

FUNCTION One
   ? "Running function One"
   RETURN

PROCEDURE Two
   ? "Running procedure Two"
   RETURN

PROCEDURE MAIN

   // Display menu choices.

   @ 3, 25 PROMPT "First choice"
   @ 4, 25 PROMPT "Second choice"

   // Get menu key.
   MENU TO choice

   // Perform an action based on your menu choice.
   DO CASE
   CASE choice = 0
      RETURN
   CASE choice = 1
      DO One
   CASE choice = 2
      DO Two
   ENDCASE

   RETURN

Here is an example taken directly from the Harbour documentation:

// display a two line menu with status line at the bottom
// let the user select favorite day
SET MESSAGE TO 24 CENTER
@ 10, 2 PROMPT "Sunday" MESSAGE "This is the 1st item"
@ 11, 2 PROMPT "Monday" MESSAGE "Now we're on the 2nd item"
MENU TO nChoice
DO CASE
CASE nChoice == 0           // user press Esc key
QUIT
CASE nChoice == 1           // user select 1st menu item
? "Guess you don't like Mondays"
CASE nChoice == 2           // user select 2nd menu item
? "Just another day for some"
ENDCASE

This example introduces the command

SET MESSAGE TO [<nRow> [CENTER]]

Extablishes a message row for @...PROMPT command

GUI Design[edit | edit source]

Getting A Library[edit | edit source]

This section focuses on the use of some libraries for producing a Graphical User Interface.

Library URL Notes Tutorial/Documentation URL
HbQt https://sourceforge.net/projects/qtcontribs/ see \hb30\contrib\hbqt

(QtContribs includes HbQt, HbIDE & HbDBU)

http://www.elektrosoft.it/tutorials/hbqt/hbqt.asp
HMG https://sites.google.com/site/hmgweb/

http://sourceforge.net/projects/hmg

RAD https://sites.google.com/site/hmgweb/tutorial

http://www.hmgforum.com/hmgdoc/data/index.htm

Harbour MiniGUI Extended Edition http://www.hmgextended.com/

https://hmgextended.org/

RAD http://www.hmgextended.com/files/manual/index.html

http://www.hmgextended.com/files/winreport/index.html

wxHarbour http://sourceforge.net/projects/wxharbour/ bindings for wxWidgets

Last Update: 2016-08-24

ooHG https://oohg.github.io/
Marinas-GUI http://marinas-gui.org/ a wrapper for Harbour with Qt http://www.marinas-tools.com.ar/
HwGUI http://sourceforge.net/projects/hwgui/ Last Update: 2019-06-11 http://www.kresin.ru/en/hwgdoc.html
T-Gtk GUI for [x]Harbour https://sourceforge.net/projects/t-gtk/ Last Update: 2019-06-24
WoopGUI - A Win32 OOP GUI Library for Harbour http://woopgui.sourceforge.net/ Last updated: 2002/05/22
xhgtk https://sourceforge.net/projects/xhgtk/
HBUI https://github.com/RJopek/HBUI
hbgi https://github.com/tuffnatty/hbgi

Commercial software is, for instance, Fivewin for Harbour (FWH)

FiveWin is original a library for Clipper 5 which you ... More info can be found here: https://www.fivetechsoft.com/english/index.php

HwGUI[edit | edit source]

The latest HwGUI version needs the nightly build of Harbour.

HMG-IDE (by Roberto Lopez)[edit | edit source]

Download following link at https://sites.google.com/site/hmgweb/

Run the executable file C:\hmg.3.4.0\IDE\IDE.exe

The first example to get started follows.

  • Select the menu File | New Project
  • and give it a name such as "first"
  • change the Form Title with "First Test" using the Object Inspector Properties tab
  • select a Button and put it on the form
  • change the Button Caption with "Click me for a test!" using the Object Inspector Properties tab
  • select the Events tab of the Object Inspector and type in "ClickTest()" in the Value field corresponding to the Action event
  • select Modules in the Project Browser window and double click on main.prg
  • add the following code
Procedure ClickTest()
  Main.Button_1.Caption := "Test done"
Return

and save the file

  • Punch the F5 key, or select the menu Project | Run, or select the Run button on the Control Panel

The program to edit source code is defined under Tools | Preferences. I find myself at ease with SciTE (https://www.scintilla.org/SciTEDownload.html).

See http://www.elektrosoft.it/tutorials/hmg/hmg.asp

HMG-IDE (by Walter Formigoni)[edit | edit source]

http://hmgs-minigui.sourceforge.net/



Web Applications

What Is a Web Application?[edit | edit source]

A web application is an application which shows its output in a browser ("client") and which interacts with data which are managed by another program, a "web server", which is usually executed on another computer (when we test it we may have both the client and the server on the same machine).

Websites like Google, Yahoo! Mail, Facebook, Twitter, Amazon, and also CMS platforms (such as WordPress, Joomla, Drupal) and online games like "Might & Magic", "Forge of Empires" or "Seafight" all provide web applications.

In their oldest form, web applications were CGI scripts, and Harbour provides a class to use this protocol (see https://github.com/harbour/core/blob/master/contrib/hbtip/cgi.prg) and an example showing how to employ a class to (https://github.com/harbour/core/blob/master/tests/html.prg).

http://www.harbour-project.com.br/samples/testcgi.html and see the side box at http://gnosis.cx/publish/programming/harbour.html

The first six chapters of a book describing CGI programming can be downloaded from http://www.cgi101.com/book/, and a straightforward description of the thing can be read at http://www.whizkidtech.redprince.net/cgi-bin/tutorial.

There is a tentative for providing FastCGI in Harbour, see https://github.com/hernad/harbour-fastcgi.

CGI, however, is old and obsolete and the reasons are explained here: https://www.perl.com/article/perl-and-cgi/#why-not-to-use-cgi. This page is in the Perl website, as CGI and Perl have been strongly (even if improperly) associated.

The web server Apache contains needs mod_cgi or mod_perl to run CGI scripts, likewise we can use mod_harbour from https://github.com/FiveTechSoft/mod_harbour.

blog live demo: https://harbour.fourtech.es/modharbour_samples/blog/index.prg and its Full source code: https://github.com/FiveTechSoft/mod_harbour/tree/master/samples/blog

https://github.com/JoseluisSanchez/MVC_Harbour

An alternative to using Apache is downloading DBFree from http://www.dbfree.org/home.msp?XY=11421849