How I have joined …

How I have joined HMG Family – A Story

It was in the year 1990, I had seen a computer, first time in my life.

I was a commerce student, studying in a 100+ years old school in my home town Sivakasi (famous for fireworks ), Tamilnadu, Southern part of India. I was studying +2 (12th and final year in school). Once, the exams were over, it was time to join a College. Not to waste the exam holidays, one of my friends had asked me to accompany him for a part time computer course in a near by Polytechnic College. I told him, “Ok”. We joined that course which was for six months, conducted on Saturdays and Sundays. I studied some basics about computers and languages like BASIC, Pascal, COBOL and an introduction to Lotus 1-2-3, Wordstar and dBase III Plus.

After that I had joined a college and my main subject was Commerce. After finishing my Under Graduation degree in the year 1993, I had joined Chartered Accountancy Course. And, once I had finished this course in the year 1996, I joined my brother to manage our family business.

Even though there was a computer in our office, I was not allowed to touch that.  The reason was, at that time, computers (AT 386) were costlier and one cannot take the risk of losing precious data and computers were operated only by computer professionals.

In the year 1997, I had purchased a computer on my own, and started implementing something which I had studied some seven years back.

I had studied under the DOS environment and I had got Windows ’95 in my new system as my operating environment. Even though it was easy to operate, I could not do any programming. I had so much of works before hand and I was involved in them for about 1 year. It was a Costing project in Excel with about 150 sheets, everything interlinked. It was a nice experience. I had done some macro programming in Excel for introducing thousand’s comma according to Indian tradition.

In the year 1998, the computer professional who had programmed for the accounting, invoice processing and payroll processing for our business firm had gone for a better job. We could not create new reports according to the requirements and we had to play only with the old options. Only at that time, I had realized about the importance of Database programming. Having the knowledge of DBase III+ programming, I had started to write small utilities to take self configured reports from the existing tables. I had the reference of the source codes for the existing software. It was done in Clipper Summer ’87 version.

There is a saying, ‘Necessity is the mother of invention’. Yes. Necessity had driven me to do more and more programming. Actually, I had started staring huge prg files with various do while .not. eof() loops and virtually indefinite nested if … endif conditions. Once understood, I had the confidence of creating bigger software too. So, in addition to the accounting software, I had created order processing and inventory maintenance and integrated the same with the existing project.

It was in the mid, 1998, I could get an internet connection with a dial up modem. In the mean time, I had a dream of using GUI in my programming. I had tried Visual Basic. I could not link with my existing dbf tables and dropped. In the beginning, I misunderstood about OOPS and GUI. Actually I had an allergy over this OOPS, I don’t know why even now.  So, I had abandoned my dream  of GUI because of OOPS.

In the year 1999, while searching the net, I had come across a site called Oasis, a site which had shared source code/libraries for Clipper and many utilities. From that site, I had got an excellent library called Super. It had some assembly language routines too to capture mouse gestures and enabled Clipper code to be ‘RAT’ified. Apart from this mouse functionality, the library had so many small utilities like sorting multiple arrays, finding out Day of the week like that. Actually I had realized about the advantages of Open Source on seeing that library source codes. I had studied in depth, and I had known about optimization of codes, effective memory management, different routes to a same destination in programming etc.

With the SuperLib, I had changed all my projects to be mouse enabled, there by satisfying half of my dreams. However, I could not stop dreaming about creating a full fledged Windows program eliminating the dark DOS command box. As new versions of Windows came in the market, I was afraid that, one day there won’t be this DOS Command Box and all my programs would not be useful at all.

I was, why was? even now am, very fond of Open Source. In my system I don’t use any copyright protected software except the Windows Operating System. I would list all the open source software I use in a separate thread for all my requirements.

I had found out Harbour from the Oasis site I had already mentioned. I had tested a lot but could not use it because of SuperLib, as it could not be linked with Harbour.

I was in a confused state. I liked Harbour a lot because of its open nature. I could not abandon SuperLib too, since all of my projects were linked with this library. So, I was desperately searching for a GUI library (at least mouse enabled for Harbour).

On one fine day in my life, my search was fruitful.

I could even remember the date. It was 4th of October, 2002, I had downloaded Harbour MiniGUI and could create a window with my limited xBase programming knowledge. Hurray! I could not describe my feelings in words!

Immediately on compiling my hello world prg, I had commented in the yahoo group from where I had downloaded the 0.40 version of the library with the following words,

Dear Roberto,

It is wonderful to use your Harbour Minigui Library, an open
source library for Harbour.

Thank you very much for your sincere efforts.

I would like to make a small suggestion regarding text boxes.

Shall you please make provision for right aligning the text boxes
for numeric fields?

Thank you once again.

srgiri

From that day, Harbour MiniGUI page was literally my home page.  I read all the messages from various users of the library. It helped me a lot to understand programming, inside out of the library, even about calling C API.

I had happily started to convert all of my projects to HMG. As the product matured, all my projects were fully converted to HMG and I had seen my dream come true.

Most of my programs are used in-house. However, I had created software for many of my friends. I had earned some money from my programs too. As you know, my income is not based on programming, since basically I am a Chartered Accountant and managing business.

I wish to list some of my major HMG projects,

  1. PyroERP (an ERP software for manufacturing companies with accounting, inventory, order processing,payroll)
  2. PyroBase (a License maintenance software given to Explosives department, Government of India)
  3. FACE (Financial Accounting under Computer Environment)
  4. Interest(ing) Calculator (Calculate interesting part of interest for loans and deposits) – An open source product, hosted in Sourceforge.net.
  5. FBT Reference (Fringe Benefit Tax referencer)
  6. Phataka (A Cultural Event maintenance software used by local Rotary Club)
  7. DBU
  8. GSM Calculator

There are many tiny projects too. I am not listing them to show my talents. It shows the simplicity of HMG and my craze on HMG.

It is my nature, to escape from any politics. I wish to be good for all. During July 2005, when Roberto had decided to move on MingW and introduced HMG, MiniGUI Extended product had also born. I didn’t tell a word about anything in the group. I believe in Karma and thought, “if this happens, this is also for the good”.

I had actively participated in the yahoo group up to April, 2006 and kept silence after that.

However, I used HMG in a full fledged manner and I liked MingW version as Roberto distributed them. This is mainly because of the single installation of the whole thing (ie., MiniGUI library + Harbour Compiler + C Compiler) and full utilization of Open Source Software.

All the software projects listed above where developed by me during the period from 2004 till now. I too had contributed some parts like drawing pie graph, alternative syntax (with the valuable guidance of Roberto) (now Roberto had revamped in a better way!), Grid2Print which Roberto has accepted kindly.

Can you believe that this forum had been created in just 2 days time? On 29th of July 2008, I had asked Roberto, breaking my long silence, by an email about the need for a forum exclusively for HMG. Roberto also liked and generously accepted to guide and participate in the forum. On that day itself, I registered this domain hmgforum.com and installed PHPBB forum software and on 1st of August 2008, the forum was officially opened.

I am so HAPPY and PROUD to be part of the HMG family.

Thus, this story has a happy ending.

Sri Rathinagiri
Sivakasi, India

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Courtesy of author, this article borrowed from here.

DO WHILE..ENDDO Loop

Control Structures – DO WHILE..ENDDO Loop

 

/*

Control Structures - DO WHILE..ENDDO Loop

 Second kind of looping structure is DO WHILE..ENDDO Loop. 

 When the repetion count is unknown, FOR...NEXT isn't appropriate for looping; 
 thus, DO WHILE..ENDDO Loop is used for primarily for such situations. 

 Famous 

 DO WHILE .NOT. EOF() 
    ... 
    SKIP 
    ... 
 ENDDO

 loop is probably most used kind of this loop structure. 

 This example is a simple digital watch that run until any key pressed.

 For more information refer here  
*/

PROCEDURE Main()
  CLS

  cTime := TIME()

  nKey := 0

  DO WHILE nKey = 0
    @ 0,70 SAY cTime
    DO WHILE cTime = TIME()
    ENDDO
    cTime := TIME()
    nKey := INKEY()
  ENDDO 

RETURN // Main()

UDFs_02 : Calling by reference

/*

UDFs_02 : Calling by reference

In the previous example “cCurrent” was an “argument” ( or actual parameter ), in caller side. In called (invoked / recevied ) side “cTime” is a parameter ( or “formal” parameter).

Though two variables has same value (at the beginning of called function), nothing changed in these definitions, though if two variables has the same name.

In the previous example, “cCurrent” passed by value by caller and “cTime” is a “local” variable as a (“formal”) parameter. So, after terminated the “AmPm” function, “cTime” too erased from memory and became inaccessible.

There is one another method: passing by reference. In this method, caller routine passes (sent) the argument by its reference, not value. In this case called routin uses this formal parameter by its reference, can change  its value and after termination of called routine, argument left alive for caller.

For passing an argument by reference only requirement is adding a “@” sign at the beginning of variable name: AmPm( @cCurrent ), instead of AmPm( cCurrent ).

In this case we haven’t need a return value by called routine, and so called routine haven’t being a FUNCTION.

For more information refer here:

*/

PROCEDURE Main()
cCurrent := "01:43" 

 AmPm( @cCurrent )

 ? cCurrent

 WAIT

RETURN // Main()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
PROCEDURE AmPm( cTime )
IF VAL( cTime ) < 12
 cTime += " am"
 ELSEIF VAL( cTime ) = 12
 cTime += " pm"
 ELSE 
 cTime += STR( VAL( cTime ) - 12, 2 ) +;
 SUBSTR( cTime, 3 ) + " pm"
 ENDIF

RETURN // AmPm()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

Extended Field Types

/*
 Harbour extended Field Types 

 Type Short
 Code Name Width (Bytes) Description
 ---- ------- ----------------- -------------------------------------------------------------------
 D Date 3, 4 or 8 Date
 M Memo 4 or 8 Memo
 + AutoInc 4 Auto increment
 = ModTime 8 Last modified date & time of this record
 ^ RowVers 8 Row version number; modification count of this record
 @ DayTime 8 Date & Time
 I Integer 1, 2, 3, 4 or 8 Signed Integer ( Width : )" },;
 T Time 4 or 8 Only time (if width is 4 ) or Date & Time (if width is 8 ) (?)
 V Variant 3, 4, 6 or more Variable type Field
 Y Currency 8 64 bit integer with implied 4 decimal
 B Double 8 Floating point / 64 bit binary
Program : ExFldTps.prg
 Author : Bicahi Esgici ( esgici <at> gmail.com )

 All rights reserved.

 2011.10.12

 A warn : While running, this program produce some .dbf and .txt file(s) and don't erase its upon close. 
 This is because you may want inspect its. 
 You may erase these files arbitrarily.

*/
PROCEDURE Main()
LOCAL aOperations := { { "Width", "Testing Field Widths" },;
 { "Numeric Limits", "Determining Numeric Limits" },;
 { "Integer Limits", "Determining Integer Limits" },;
 { "Set/Get", "Set & read back field values" },; 
 { "Conversion", "Convert and test signed to integer" }},; 
 a1Oper := {},;
 n1Oper := 1

 LOCAL aFldTypes := { { "D", "Date", "Date ( Width : 3, 4 or 8 )" } ,;
 { "M", "Memo", "Memo ( Width : 4 or 8 )" },;
 { "+", "AutoInc", "Auto increment ( Width : 4 )" },;
 { "=", "ModTime", "Last modified date & time of this record ( Width : 8 )" },;
 { "^", "RowVers", "Row version number; modification count of this record ( Width : 8 )" },;
 { "@", "DayTime", "Date & Time ( Width : 8 )" },;
 { "I", "Integer", "Signed Integer ( Width : 1, 2, 3, 4 or 8 )" },;
 { "T", "Time", "Only time (if width is 4 ) or Date & Time (if width is 8 )" },;
 { "V", "Variant", "Variable type (!) Field ( Width : 3, 4, 6 or more)" },;
 { "Y", "Currency", "Integer 64 bit with implied 4 decimal" },; 
 { "B", "Double", "Floating point / 64 bit binary ( Width : 8 )" } },; 
 a1Type := {},;
 n1Type := 1,; 
 n2Type := 1 

 LOCAL nMColumn := 0,; // Menu Column No
 nMRow := 0 // Menu Row No 

 SET WRAP ON
 SET MESSAGE TO 58 CENTER
 SET CENTURY ON
* In screen resolution 1440 * 900 SetMode( 60, 150 ) seem good. 
 * In this case value of SET MESSAGE TO will be 58 ( less 2 than nRow specified in SetMod() ).
SetMode( 60, 150 )

 WHILE n1Oper > 0

 CLS 

 nMSutn := 0

 FOR EACH a1Oper IN aOperations
 @ 0, nMSutn PROMPT a1Oper[ 1 ] MESSAGE a1Oper[ 2 ]
 nMSutn += LEN( a1Oper[ 1 ] ) + 1
 NEXT 

 MENU TO n1Oper

 SWITCH n1Oper 

 CASE 0
 EXIT

 CASE 1 // Testing Field Widths
 n1Type := 1
 WHILE n1Type > 0

 @ 1,0 CLEAR TO 24, 80
 nMRow := 2
 FOR EACH a1Type IN aFldTypes 
 @ nMRow++, 0 PROMPT a1Type[ 2 ] MESSAGE a1Type[ 3 ]
 NEXT a1Type

 MENU TO n1Type

 IF n1Type > 0
 @ 1,0 CLEAR TO 24, 80
 @ 1, 0 SAY aFldTypes[ n1Type, 2 ] COLOR "B/W"
 FT_Widths( aFldTypes[ n1Type ] ) 
 ENDIF

 ENDDO n1Type
 EXIT

 CASE 2 // Determining Numeric Limits
 NumLimits()
 EXIT

 CASE 3 // Determining Integer Limits
 IntLimits()
 EXIT 

 CASE 4 // Set & read back field values
 n2Type := 1
 WHILE n2Type > 0

 @ 1,0 CLEAR TO 24, 80
 nMRow := 2
 FOR EACH a1Type IN aFldTypes 
 @ nMRow++, 36 PROMPT a1Type[ 2 ] MESSAGE a1Type[ 3 ] 
 NEXT 

 MENU TO n2Type

 IF n2Type > 0
 V_SetGet( aFldTypes[ n2Type ] ) 
 ENDIF

 ENDDO n1Type
 EXIT

 CASE 5 // Convert and test signed to integer
 SignChng()
 EXIT

 END SWITCH 

 ENDDO n1Oper

RETURN // Main()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE FT_Widths( a1Type ) // Testing Field Widths
LOCAL cType := a1Type[ 1 ],;
 nFldNo := 0,;
 aStru1 := {},;
 aStru2 := {},;
 aStru3 := { { 'FldType', "C", 1, 0 },; // Type of field
 { 'WidtSpec', "N", 2, 0 },; // Specified width
 { 'Dec_Spec', "N", 2, 0 },; // Specified decimal
 { 'WidtAppl', "N", 2, 0 },; // Applied (by Harbour) width 
 { 'Dec_Max', "N", 2, 0 },; // Computed maximum dec
 { 'Result', "C", 1, 0 } }
FOR nFldNo := 1 TO 32
 AADD( aStru1, { "X" + STRZERO( nFldNo, 2 ), cType, nFldNo, 0 } )
 NEXT nFldNo
DBCREATE( "Widths", aStru1 )
 USE Widths

 aStru2 := DBSTRUCT()

 IF cType $ "IYB"
 AEVAL( aStru2, { | a1, i1 | aStru2[ i1, 4 ] := aStru2[ i1, 3 ] - 1 } )
 ENDIF

 USE
 DBCREATE( "Widths", aStru2 )
 USE Widths

 aStru2 := DBSTRUCT()
USE 

 DBCREATE( "Widths", aStru3 )

 USE Widths

 FOR nFldNo := 1 TO 32
 DBAPPEND()

 REPLACE FldType WITH aStru1[ nFldNo, 2 ],;
 WidtSpec WITH aStru1[ nFldNo, 3 ],;
 Dec_Spec WITH aStru1[ nFldNo, 4 ],;
 WidtAppl WITH aStru2[ nFldNo, 3 ],;
 Dec_Max WITH aStru2[ nFldNo, 4 ],;
 Result WITH IF( aStru1[ nFldNo, 3 ] # aStru2[ nFldNo, 3 ], "-", "+" )

 NEXT nFldNo

 DBGOTOP() 
 BROWSE()
USE

RETURN // FT_Widths()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE NumLimits() // Determining Numeric Limits
LOCAL mBayt := 0,;
 nBit := 0,;
 nExpo := 0

* nExpos := { 8, 16, 24, 32, 64 }

 SET ALTE TO N_Limits
 SET ALTE ON

 ? SPACE(9), "Unsigned ( Always + )"
 ? "-- ------ ---------------------------"
FOR nBayt := 1 TO 8
 nBit := nBayt * 8
 nExpo := 2^nBit
 ? STR( nBayt, 2 ), "2^" + PADL( nBit, 2) + " : ", TRANSFORM( nExpo, "99,999,999,999,999,999,999" )
 NEXT nBayt
?
 ? 
 ? PADC( "Signed ( - / + )", 80 )
 ? "-- ------ -----------------------------------------------------"
FOR nBayt := 1 TO 8
 nBit := nBayt * 8
 nExpo := 2^nBit
 ? STR( nBayt, 2 ), "2^" + PADL( nBit, 2) + " : ", TRANSFORM( - nExpo / 2, "99,999,999,999,999,999,999" )+;
 ".."+;
 LTRIM( TRANSFORM( nExpo / 2 - 1, "99,999,999,999,999,999,999" ) )
 NEXT nBayt 

 SET ALTE OFF
 SET ALTE TO
MEMOEDIT( MEMOREAD( "N_Limits.TXT" ) )

RETURN // NumLimits()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE IntLimits() // Determining Integer Limits
LOCAL nFldNo := 0,;
 aStru4 := { { 'MNMX', "C", 7, 0 },;
 { 'INT1', "I", 1, 0 },;
 { 'INT2', "I", 2, 0 },;
 { 'INT3', "I", 3, 0 },;
 { 'INT4', "I", 4, 0 },;
 { 'INT8', "I", 8, 0 } }

 DBCREATE( "IntLimits", aStru4 )

 USE IntLimits

 DBAPPEND()
 REPLACE MNMX WITH "Minimum" ,;
 INT1 WITH -2^7 ,;
 INT2 WITH -2^15 ,;
 INT3 WITH -2^23 ,;
 INT4 WITH -2^31 ,;
 INT8 WITH -2^63

 DBAPPEND()
 REPLACE MNMX WITH "Maximum" ,;
 INT1 WITH 2^7 - 1 ,;
 INT2 WITH 2^15 - 1 ,;
 INT3 WITH 2^23 - 1 ,;
 INT4 WITH 2^31 - 1 ,; 
 INT8 WITH 2^63 - 513 // < 513 ---> "Error DBFNTX/1021 Data width error" 

 DBGOTOP()

 @ 1,0 CLEAR TO 24, 80
SET ALTE TO IntLimits
 SET ALTE ON

 WHILE !EOF()
 ?
 ? MNMX 
 ? '--------'
 FOR nFldNo := 2 TO 6
 ? FIELDNAME( nFldNo ), ": ", LTRIM( TRANSFORM( FIELDGET( nFldNo ), "99,999,999,999,999,999,999" ) )
 NEXT nFldNo
 ?
 SKIP 
 ENDDO

 SET ALTE OFF
 SET ALTE TO
MEMOEDIT( MEMOREAD( "IntLimits.txt" ) )

 USE

RETURN // IntLimits()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_NoType() // Testing NoType ( Variant ) field type
LOCAL aStru5 := { { "Initial", "C", 19, 0 },;
 { "Internal", "V", 19, 0 },; 
 { "ReadBack", "C", 19, 0 },;
 { "ReadBackTp", "C", 1, 0 } }

 DBCREATE( "SG_NoType", aStru5 )

 USE SG_NoType

 DBAPPEND()
 REPLACE Initial WITH "String", Internal WITH "String"
 DBAPPEND()
 REPLACE Initial WITH "12345", Internal WITH 12345
 DBAPPEND()
 REPLACE Initial WITH DTOC( DATE() ), Internal WITH DATE()
 DBAPPEND()
 REPLACE Initial WITH ".T.", Internal WITH .T.

 REPLACE ALL ReadBack WITH HB_ValToStr( Internal ), ReadBackTp WITH VALTYPE( Internal )

 DBGOTOP()

 BROWSE()
 USE

RETURN // SG_NoType()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE V_SetGet( aType ) // Set & read back field values
LOCAL cType := aType[ 1 ]

 @ 1,0 CLEAR TO 24, 80

 SWITCH cType

 CASE "D" // Date 
 SG_Date()
 EXIT

 CASE "M" // Memo 
 SG_Memo()
 EXIT

 CASE "+" // AutoInc 
 Alert( "Read Only" )
 EXIT

 CASE "=" // ModTime 
 Alert( "Read Only" )
 EXIT

 CASE "^" // RowVers 
 Alert( "Read Only" )
 EXIT

 CASE "@" // DayTime 
 SG_DayTime()
 EXIT

 CASE "I" // Integer 
 SG_Integers()
 EXIT

 CASE "T" // Time 
 SG_DayTime()
 EXIT

 CASE "V" // Variant 
 SG_NoType()
 EXIT

 CASE "Y" // Currency 
 SG_Currency()
 EXIT

 CASE "B" // Double 
 SG_Double()
 EXIT

 END SWITCH 

RETURN // V_SetGet()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_Date() // Date : Compare set / get
LOCAL aStru6 := { { "Initial", "D", 8, 0 } ,;
 { "Internal3", "D", 3, 0 } ,; 
 { "Internal4", "D", 4, 0 } ,; 
 { "Internal8", "D", 8, 0 } ,; 
 { "ReadBack3", "C", 12, 0 } ,;
 { "ReadBack4", "C", 12, 0 } ,;
 { "ReadBack8", "C", 12, 0 } }

 DBCREATE( "SG_Date", aStru6 )

 USE SG_Date

 DBAPPEND()
 REPLACE Initial WITH DATE() - ( 66 * 365 + 66 / 4 ),; 
 Internal3 WITH Initial ,; 
 Internal4 WITH Initial ,; 
 Internal8 WITH Initial ,; 
 ReadBack3 WITH HB_ValToStr( Internal3 ) ,; 
 ReadBack4 WITH HB_ValToStr( Internal4 ) ,; 
 ReadBack8 WITH HB_ValToStr( Internal8 )
 DBGOTOP()

 BROWSE()
 USE
RETURN // SG_Date()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_Memo() // Set / Get test for MEMO fields
LOCAL aStru7 := { { "MEMO_4", "M", 4, 0 } ,;
 { "MEMO_10", "M", 10, 0 } }

 DBCREATE( "SG_Memo", aStru7 )

 USE SG_Memo

 DBAPPEND()
 REPLACE MEMO_4 WITH "MEMO field with width 4",; 
 MEMO_10 WITH "MEMO field with width 4"

 DBGOTOP()

 BROWSE()
 USE
RETURN // SG_Date()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_DayTime() // Set / Get test for DayTime fields
LOCAL aStru8 := { { "ModTim", "=", 8, 0 } ,;
 { "DaTime", "@", 8, 0 } ,; // 
 { "Time_8", "T", 8, 0 } ,;
 { "Time_4", "T", 4, 0 } ,;
 { "Time_C", "C", 12, 0 } }
DBCREATE( "SG_Datime", aStru8 )

 USE SG_Datime

 DBAPPEND()

* REPLACE DaTime WITH DATE() // ==> Error DBFNTX/1020 Data type error: DATIME 
* REPLACE Time_4 WITH SECO() / TIME() // ==> Error DBFNTX/1020 Data type error: DATIME
* REPLACE Time_8 WITH TIME() // ==> Error DBFNTX/1020 Data type error: TIME_8 

* DBAPPEND()

* REPLACE DaTime WITH ModTim // ==> 0000-00-00 00:00:00.000 
* REPLACE Time_4 WITH ModTim // ==> Error DBFNTX/1020 Data type error: TIME_4 
* REPLACE Time_8 WITH ModTim // ==> 0000-00-00 00:00:00.000 

 REPLACE DaTime WITH ModTim,; // ==> > 0000-00-00 00:00:00.000 
 Time_8 WITH ModTim,; // ==> > 0000-00-00 00:00:00.000 
 Time_C WITH TIME() 

 DBGOTOP()

 BROWSE()
 USE

RETURN // SG_DayTime()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_Integers() // Set / Get test for INTEGER fields
LOCAL nRecno := 0,;
 aStru9 := { { 'INT1', "I", 1, 0 },;
 { 'NUM1', "N", 4, 0 },;
 { 'EQL1', "L", 1, 0 },; 
 { 'INT11', "I", 1, 1 },;
 { 'NUM11', "N", 5, 1 },;
 { 'EQL11', "L", 1, 0 },; 
 { 'INT2', "I", 2, 0 },;
 { 'NUM2', "N", 8, 0 },;
 { 'EQL2', "L", 1, 0 },; 
 { 'INT22', "I", 8, 2 },;
 { 'NUM22', "N",12, 2 },;
 { 'EQL22', "L", 1, 0 },; 
 { 'INT3', "I", 3, 0 },;
 { 'NUM3', "N", 8, 0 },;
 { 'EQL3', "L", 1, 0 },; 
 { 'INT32', "I", 3, 2 },;
 { 'NUM32', "N",12, 2 },;
 { 'EQL32', "L", 1, 0 },; 
 { 'INT4', "I", 4, 0 },;
 { 'NUM4', "N",12, 0 },;
 { 'EQL4', "L", 1, 0 },; 
 { 'INT42', "I", 4, 2 },;
 { 'NUM42', "N",14, 2 },;
 { 'EQL42', "L", 1, 0 },; 
 { 'INT8', "I", 8, 0 },;
 { 'NUM8', "N",21, 0 },;
 { 'EQL8', "L", 1, 0 },; 
 { 'INT84', "I", 8, 4 },;
 { 'NUM84', "N",21, 4 },;
 { 'EQL84', "L", 1, 0 } } 

 DBCREATE( "SG_Integers", aStru9 )
 USE SG_Integers

 FOR nRecno := 1 TO 18
 DBAPPEND()
 NEXT nRecno

 REPL ALL INT1 WITH INT( HB_RANDOM( -2^7 , 2^7 - 1 )) ,;
 INT11 WITH INT( HB_RANDOM( -2^7 , 2^7 - 1 )) / 10 ,;
 INT2 WITH INT( HB_RANDOM( -2^15, 2^15 - 1 )) ,;
 INT22 WITH INT( HB_RANDOM( -2^15, 2^15 - 1 )) ,;
 INT3 WITH INT( HB_RANDOM( -2^23, 2^23 - 1 )) ,;
 INT32 WITH INT( HB_RANDOM( -2^23, 2^23 - 1 )) / 100 ,;
 INT4 WITH INT( HB_RANDOM( -2^31, 2^31 - 1 )) ,;
 INT42 WITH INT( HB_RANDOM( -2^31, 2^31 - 1 )) / 100 ,;
 INT8 WITH INT( HB_RANDOM( -2^63, 2^63 - 513 )) ,;
 INT84 WITH INT( HB_RANDOM( -2^63, 2^63 - 513 )) / 10000 

 REPL ALL NUM1 WITH INT1, EQL1 WITH NUM1 = INT1 ,; 
 NUM11 WITH INT11, EQL11 WITH NUM11 = INT11 ,;
 NUM2 WITH INT2, EQL2 WITH NUM2 = INT2 ,;
 NUM22 WITH INT22, EQL22 WITH NUM22 = INT22 ,;
 NUM3 WITH INT3, EQL3 WITH NUM3 = INT3 ,;
 NUM32 WITH INT32, EQL32 WITH NUM32 = INT32 ,;
 NUM4 WITH INT4, EQL4 WITH NUM4 = INT4 ,;
 NUM42 WITH INT42, EQL42 WITH NUM42 = INT42 ,;
 NUM8 WITH INT8, EQL8 WITH NUM8 = INT8 ,;
 NUM84 WITH INT84, EQL84 WITH NUM84 = INT84
DBGOTOP()

 BROWSE() 
 USE

RETURN // SG_Integers()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._

PROCEDURE SG_Currency() // Set / Get test for CURRENCY fields
LOCAL aStru10 := { { "Currenc", "Y", 8, 4 } ,;
 { "NUM2D", "N", 21, 2 } ,;
 { "NUM4D", "N", 21, 4 } ,;
 { "NUM6D", "N", 23, 6 } ,;
 { "NUM8D", "N", 25, 8 } }
DBCREATE( "SG_Curncy", aStru10 )

 USE SG_Curncy

 FOR nRecno := 1 TO 100
 DBAPPEND()
 REPLACE Currenc WITH HB_RANDOM( -2^53, 2^53 ) / 10000 ,; 
 NUM2D WITH Currenc ,;
 NUM4D WITH Currenc ,;
 NUM6D WITH Currenc ,;
 NUM8D WITH Currenc 

 NEXT nRecno

 DBGOTOP() 
 BROWSE() 
 USE
RETURN // SG_Currency()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SG_Double() // Set / Get test for DOUBLE ( BINARY ) fields
LOCAL nRecno := 0

 LOCAL aStru11 := { { "Double", "B", 8, 4 } ,;
 { "NUM2D", "N", 21, 2 } ,;
 { "NUM4D", "N", 21, 4 } ,;
 { "NUM6D", "N", 23, 6 } ,;
 { "NUM8D", "N", 25, 8 } }
DBCREATE( "SG_Double", aStru11 )

 USE SG_Double

 FOR nRecno := 1 TO 100
 DBAPPEND()
 REPLACE Double WITH HB_RANDOM( -2^53, 2^53 ) / 10000 ,; 
 NUM2D WITH Double ,;
 NUM4D WITH Double ,;
 NUM6D WITH Double ,;
 NUM8D WITH Double 

 NEXT nRecno

 DBGOTOP() 
 BROWSE() 
 USE
RETURN // SG_Double()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
PROCEDURE SignChng() // Convert and test signed to integer
LOCAL nRecno := 0
LOCAL aStru12 := { { 'NUM1', "N", 3, 0 },;
 { 'INT1', "I", 1, 0 },;
 { 'RET1', "N", 3, 0 },;
 { 'EQL1', "L", 1, 0 },;
 { 'NUM2', "N", 6, 0 },;
 { 'INT2', "I", 2, 0 },;
 { 'RET2', "N", 6, 0 },;
 { 'EQL2', "L", 1, 0 },;
 { 'NUM3', "N", 9, 0 },;
 { 'INT3', "I", 3, 0 },;
 { 'RET3', "N", 9, 0 },;
 { 'EQL3', "L", 1, 0 },;
 { 'NUM4', "N", 11, 0 },;
 { 'INT4', "I", 4, 0 },;
 { 'RET4', "N", 11, 0 },;
 { 'EQL4', "L", 1, 0 },;
 { 'NUM8', "N", 21, 0 },;
 { 'INT8', "I", 8, 0 },;
 { 'RET8', "N", 21, 0 },;
 { 'EQL8', "L", 1, 0 } }

 DBCREATE( "SignChng", aStru12 )
 USE SignChng

 FOR nRecno := 1 TO 100

 DBAPPEND()

 REPLACE NUM1 WITH HB_RANDOM( 0, 2^8 - 1 ), INT1 WITH NUM1 - 2^7 , RET1 WITH INT1 + 2^7, EQL1 WITH NUM1 = RET1 ,;
 NUM2 WITH HB_RANDOM( 0, 2^16 - 1 ), INT2 WITH NUM2 - 2^15, RET2 WITH INT2 + 2^15, EQL2 WITH NUM2 = RET2 ,;
 NUM3 WITH HB_RANDOM( 0, 2^24 - 1 ), INT3 WITH NUM3 - 2^23, RET3 WITH INT3 + 2^23, EQL3 WITH NUM3 = RET3 ,;
 NUM4 WITH HB_RANDOM( 0, 2^32 - 1 ), INT4 WITH NUM4 - 2^31, RET4 WITH INT4 + 2^31, EQL4 WITH NUM4 = RET4 ,;
 NUM8 WITH HB_RANDOM( 0, 2^64 - 1 ), INT8 WITH NUM8 - 2^63, RET8 WITH INT8 + 2^63, EQL8 WITH NUM8 = RET8 

 NEXT nRecno

 DBGOTOP() 
 BROWSE() 
 USE

RETURN // SignChng()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._






Download Source File

A typical Harbour Program

A Typical Harbour Program

// Menu.prg -- Display a menu and process a menu choice
// Manifest constant definitions
#define ONE 1
#define TWO 2
#define THREE 3
// File-wide variable declarations go here
PROCEDURE Main
// Local variable declarations

 LOCAL nChoice

 // Continous loop redisplays menu after each function return

 DO WHILE .T.

 // Function call to display main menu

 nChoice := MainMenu()

 // CASE structure to make function call 
 // based on return value of MainMenu()

 DO CASE

 // Function called in CASE structure are defined
 // in Database.prg, included at the end of this file

 CASE nChoice = ONE
 AddRecs()
CASE nChoice = TWO
 EditRecs() 

 CASE nChoice = THREE
 Reports() 

 OTHERWISE
 EXIT // Break out of continuos loop 

 ENDCASE

 ENDDO

RETURN // Main()

FUNCTION MainMenu( MenuChoice )
 CLEAR
 SET WRAP ON
 SET MESSAGE TO 23 CENTER

 @ 6, 10 PROMPT "Add" MESSAGE "New Account"
 @ 7, 10 PROMPT "Edit" MESSAGE "Change Account"
 @ 8, 10 PROMPT "Reports" MESSAGE "Print Account Reports"
 @ 10, 10 PROMPT "Quit" MESSAGE "Return to DOS"

 MENU TO MenuCoice

 CLEAR

RETURN MenuCoice // MainMenu() 

#include "Database.prg" // Contains generic database functions

* end of menu.prg

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

A Typical Harbour Program -- Screen shoot 2

// Database.prg
PROCEDURE AddRecs() 
 ALERT( "New Account process will run" )
RETURN // AddRecs()
PROCEDURE EditRecs() 
 ALERT( "Account changing process will run" )
RETURN // EditRecs()

PROCEDURE Reports() 
 ALERT( "Printing Account Reports Process will run" )
RETURN // Reports()

Note : A detailed explanation of this program is here :

Browse by IDE

BROWSE is a control a few complicated than others. However HMG-IDE successfully support it.

In this sample we will build a Browse based application in a few steps using HMG-IDE.

Step – 1 : Make a folder for this sample.

Step – 2 : Copy TEST.DBF and TEST.DBT from \hmg\SAMPLES\TUTORIAL folder to your new folder.

Step – 3 : Run HMG-IDE and chose “New Project”.

IDE will ask you location and name for the new project; denote the folder you had build in the first step and give a name, say “IDEBrowse”.

IDE will build a new main module named “Main.Prg” and a new main form named “Main.fmg”

Step – 4 :

Double click “Main.prg” in the “Modules” tab of “Project Browser” window for editing this “main” module:

IDE send this module (.prg) file to your programing editor.

You will see your main program written by IDE :

#include <hmg.ch>

Function Main
 Load Window Main
 Main.Center
 Main.Activate
Return

Step – 5 :

Since BROWSE is a table based control, we must have a table and files opening/closing routines; these aren’t duties of IDE.

So add this two little procedures to the main.prg for opening and closing table:

Procedure OpenTables()
Use Test Shared
Return
Procedure CloseTables()
Use
Return


 Step – 6 :

Now, it’s time for making some arrangements on the form:

Title :

– Click  “Object Inspector [Main.Fmg]\Form\Properties\Title” :


– Give a “Title”, say “IDE Browse Sample” to form :


On Init Procedure :

– By clicking “Object Inspector [Main.Fmg]\Form\Events\OnInit” ;


– Assign OpenTables() procedure name to this event:

On Release Procedure :

– By clicking “Object Inspector [Main.Fmg]\Form\Events\OnRelease”;

– Assign CloseTables() procedure name to this event:

Step – 7 :

Now, we can place BROWSE control in our form:

– Click BROWSE button in the Toolbox:

– Then click anywhere in the form :

– Re-size control by dragging “resize” mark at the lower rigth corner :
 

 
 – If required, change placement of control by using “replace” mark ( at the upper-left corner )

 

– By using these two marks, you can place control in the form whatever you like:


 Step – 8 :

And now, we will make some arrangements on the BROWSE control :

After activating ( by clicking on it ) BROWSE control;

By selecting “Object Inspector[Main.Fmg]\Browse_1\Properties” give these properties to Browse_1 control:

– “Fields” property :

{‘Test->Code’,’Test->First’,’Test->Last’,’Test->Birth’,’Test->Married’,’Test->Bio’}

– “Headers” property :

{ ‘Code’, ‘First Name’, ‘Last Name’, ‘Birth Date’, ‘Married’, ‘Biography’ }

– “Widths” property :

{ 150, 150, 150, 150, 150, 150 }

– “Work Area” property :

Test

Note that “array” properties requires open and close curly-braces (‘{}’),  character properties doesn’t requires quotation marks (“”).

.
If you want allow editing  abilities to user, you have change default values (.F.) of these properties to .T.

 ALLOWEDIT
 ALLOWAPPEND
 ALLOWDELETE
 

Last Step :

That is All !

Click “Run” button ( or select “Run” from Project menu or simply press F5 key) for see results :


You have now a BROWSE based HMG application generated  by IDE.

When you use “Run” command or attempt to closing   your form by clicking “X” button, IDE ask you saving it ( if any change you have made ). You may also use Project\Save Form menu command.

Since IDE is a “Two-Way Form Designer”, you can edit .fmg files by double clicking its name in the “Forms” tab   of “Project Browser” window.

And since .fmg files are pure HMG source code, you can open a .fmg file by your editor to see  form source  code generated by IDE. Inspecting this code may be useful for better understanding and learning HMG.

You can also edit this file manually, but under your own risk ! Some modifications may not suitable to the standards of Form Designer.

What is Harbour ?

Harbour is a modern computer programming language. It is a Clipper-compatible compiler which is cross-platform, running on many operating systems (DOSMicrosoft WindowsLinuxUnix variants, several BSD descendants, Mac OS XMINIX 3Windows CEPocket PCSymbianiPhoneQNXVxWorksOS/2/eComStationBeOS/HaikuAIX) using the same source code and databases.

Although it is a powerful general-purpose programming language, it was primarily used to create database/business programs. Harbour have been actively maintained looking for diversity keeping backward-compatible with Clipper style. It has undergone many changes and revisions and regain widely popularity amongst programmers in 1980s and 1990s

The open source Harbour license is similar to the GNU General Public License, with an exception supporting proprietary applications, so proprietary applications can be produced with Harbour and distributed..

Paradigm(s) multi-paradigmimperativefunctional,object-orientedreflective
Appeared in 1999
Designed by Antonio Linares
Developer Viktor Szakáts and community
Stable release 3.0.0 (17 July 2011)
Preview release 3.1.x available from SVN
Typing discipline optionally duckdynamicsafe, partially strong
Dialects Clipper, Xbase ++, Flagship, FoxPro, xHarbour
Influenced by dBase, Clipper
Influenced xHarbour
OS Cross-platform
License Open source GPL Compatible
Usual filename extensions .prg, .ch, .hbs, .dbf
Website http://www.harbour-project.org/

History

The idea of a free software Clipper compiler has been floating around for a long time and the subject has often cropped up in discussion on comp.lang.clipper. Antonio Linares founded the Harbour project and the implementation was started.

Sailing the Clipper ship to a Harbour port. Clipper is a type of ship. Harbour is a synonym to port (where ship docks) Harbour is out port to the Clipper language.

On 2009 Harbour had a huge make over on its design promoted mainly by Viktor Szakáts and Przemyslaw Czerpak

 Database support

Harbour extends the Clipper Replaceable Database Drivers (RDD) approach. It offers multiple RDDs such as DBF, DBFNTX, DBFCDX, DBFDBT and DBFFPT. In Harbour multiple RDDs can be used in a single application, and new logical RDDs can be defined from combination of other RDDs. The RDD architecture allows for inheritance, so that a given RDD may extend the functionality of other existing RDD(s). Third-party RDDs, like RDDSQL, RDDSIX, RMDBFCDX, Advantage Database Server, and Mediator exemplify some of the RDD architecture features. DBFNTX implementation has almost same functionality of DBFCDX and RDDSIX. NETIO and LetoDB provide remote access over TCP protocol Harbour also offers ODBC support by means of an OOP syntax, and ADO support by means of OLEMySQLPostgreSQLSQLiteFirebirdOracle are examples of databases which Harbour can connect.

xBase technologies often is confused with a RDBMS software. Although this is true, xBase is more than a simple database system as the same time xBase languages using purely DBF can not provide full concept of a real RDBMS

Programming philosophy

Unlike Java which is intended to be write once, run anywhere, Harbour aims to be write once, compile anywhere. As the same compiler is available for all of the above operating systems, there is no need for recoding to produce identical products for different platforms, except when operating system dependent features are used. Cross-compiling is supported with MinGW32. Under Microsoft Windows, Harbour is more stable but less well-documented than Clipper, but has multi-platform capability and is more transparent, customizable and can run from a USB flash drive.

Under Linux and Windows Mobile, Clipper source code can be compiled with Harbour with very little adaptation. Most software originally written to run on Xbase ++, Flagship, FoxPro, xHarbour and others dialects can be compiled with Harbor with some adaptation. As 2010 many efforts have been made to turn the transition from other xBase dialects easier.

Harbour can use the following C compilers, amongothers: GCCMinGWClangICCMicrosoft Visual C++ (6.0+), Borland C++Watcom CPelles C and Sun Studio.

Harbour can make use of multiple Graphic Terminal emulations, including console drivers, and Hybrid Console/GUIs, such as GTWvt, and GTWvg.

Harbour supports external GUIs, free (e.g. HWGui, MiniGUI and Qt) and commercial (e.g. FiveWin, Xailer). HBQt is a library provinding bindings to Qt. HBIDE application included in official distribution and SVN repository is a sample of HBQt potencial.

Harbour is 100% Clipper-compatible and supports many language syntax extensions including greatly extended run-time libraries such as OLE, BlatOpenSSLFreeImage,GD, TIP, Tpathy, PCRE, HbZip (zlib and bzip2), cURLCairo, its own implementation of CA-Tools and NanFor libraries and many others. Harbour has an active development community and extensive third party support.

Any xBase language provides a very productive way to build business and data intensive applications. Harbour is not an exception.

Macro Operator (runtime compiler)

One of the most powerful features of xBase languages is the Macro Operator ‘&’. Harbour’s implementation of the Macro Operator allows for runtime compilation of any valid Harbour expression. Such a compiled expression may be used as a VALUE, i.e. the right side of an assignment (rvalue), but more interestingly, such a compiled expression may be used to resolve the left side (lvalue) of an assignment, i.e. PRIVATE, or PUBLIC variables, or a database FIELD.

Additionally, the Macro Operator may compile and execute function calls, complete assignments, or even list of arguments, and the result of the macro may be used to resolve any of the above contexts in the compiled application. In other words, any Harbour application may be extended and modified at runtime to compile and execute additional code on-demand.

Latest Macro compiler can compile any valid Harbour code including code to pre-process before compile.

Syntax:

 &( ... )

The text value of the expression ‘…’ will be compiled, and the value resulting from the execution of the compiled code is the result.

 &SomeId

is the short form for &( SomeId ).

 &SomeId.postfix

is the short form of &( SomeId + “postfix” ).

Object Oriented Programming

Programming in an OOP style is a broader issue than a specific library or a specific interface, but OOP programming is something many Clipper programmers have come to expect. CA-Clipper 5.2 and especially 5.3 added a number of base classes, and a matching OOP syntax. Libraries such as Class(y), Fivewin, Clip4Win, and TopClass provide additional OOP functionality.

Harbour has OOP extensions with full support for classes including inheritance, based on Class(y) syntax. OOP syntax in Harbour is very similar to that of earlier Clipper class libraries so it should be possible to maintain legacy Clipper code with minimal changes.

Syntax and semantics

Harbour as every xBase language is case insensitive and can optionally accept keywords written just by first four characters

Built-in data types

Harbour has 6 scalar types : NilStringDateLogical, NumberPointer, and 4 complex types: ArrayObjectCodeBlock, and Hash. A scalar holds a single value, such as a string, number, or reference to any other type. Arrays are ordered lists of scalars or complex types, indexed by number, starting at 1. Hashes, or associative arrays, are unordered collections of any type values indexed by their associated key, which may be of any scalar or complex type.

Literal (static) representation of scalar types:

  • Nil: NIL
  • String: “hello”, ‘hello’, [hello]
  • Date: 0d20100405
  • Logical: .T., .F.
  • Number: 1, 1.1, −1, 0xFF

Complex Types may also be represent as literal values:

  • Array: { “String””, 1, { “Nested Array” }, .T., FunctionCall(), @FunctionPointer() }
  • CodeBlock: { |Arg1, ArgN| Arg1 := ArgN + OuterVar + FunctionCall() }
  • Hash: { “Name” => “John”, 1 => “Numeric key”, { “Nested” => “Hash” } }

Hashes may use any type including other Hashes as the Key for any element. Hashes and Arrays may contain any type as the Value of any member, including nesting arrays, and Hashes.

Codeblocks may have references to Variables of the Procedure/Function>method in which it was defined. Such Codeblocks may be returned as a value, or by means of an argument passed BY REFERENCE, in such case the Codeblock will “outlive” the routine in which it was defined, and any variables it references, will be a DETACHED variable.

Detached variables will maintain their value for as long as a Codeblock referencing them still exists. Such values will be shared with any other Codeblock which may have access to those same variables. If the Codeblock did not outlive its containing routine, and will be evaluated within the lifetime of the routine in which it is defined, changes to its Detached Variables(s) by means of its evaluation, will be reflected back at its parent routine.

Codeblocks can be evaluated any number of times, by means of the Eval( BlockExp ) function.

Variables

All types can be assigned to named variables. Named variable identifiers are 1 to 63 characters long, start with [A-Z|_] and further consist of the characters [A-Z|0–9|_] up to a maximum of 63 characters. Named variables are not case sensitive.

Variables have one of the following scopes:

  • 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.

LOCAL and STATIC are resolved at compile time, and thus are much faster than PRIVATE and PUBLIC variables which are dynamic entities accessed by means of a runtime Symbol table. For this same reason, LOCAL and STATIC variables are not exposed to the Macro compiler, and any macro code which attempts to reference them will generate a runtime error.

Due to the dynamic nature of PRIVATE and PUBLIC variables, they can be created and destroyed at runtime, can be accessed and modified by means of runtime macros, and can be accessed and modified by Codeblocks created on the fly.

Control structures

The basic control structures include all of the standard dBase, and Clipper control structures as well as additional ones inspired by the C or Java programming languages:

Loops

[DO] WHILE ConditionExp
   ...
   [LOOP]
   [EXIT]
END[DO]

FOR Var := InitExp TO EndExp [STEP StepExp]
   ...
   [LOOP]
   [EXIT]
NEXT
FOR EACH Var IN CollectionExp
   ...
   [HB_EnumIndex()]
   [LOOP]
   [EXIT]
NEXT
  • The  is a sequence of one of more Harbour statements, and square bracketes [] denote optional syntax.
  • The HB_EnumIndex() may be optionally used to retrieve the current iteration index (1 based).
  • The LOOP statement restarts the current iteration of the enclosing loop structure, and if the enclosing loop is a FOR or FOR EACH loop, it increases the iterator, moving to the next iteration of the loop.
  • The EXIT statement immediately terminates execution of the enclosing loop structure.
  • The NEXT statement closes the control structure and moves to the next iteration of loop structure.

In the FOR statement, the assignment expression is evaluated prior to the first loop iteration. The TO expression is evaluated and compared against the value of the control variable, prior to each iteration, and the loop is terminated if it evaluates to a numeric value greater than the numeric value of the control variable. The optional STEP expression is evaluated after each iteration, prior to deciding whether to perform the next iteration.

In FOR EACH, the Var variable will have the value (scalar, or complex) of the respective element in the collection value. The collection expression, may be an Array (of any type or combinations of types), an Hash Table, or an Object type.

IF statements

IF CondExp
   ...
[ELSEIF] CondExp
   ...
[ELSE]
   ...
END[IF]

 represents 0 or more statement(s). The condition expression(s) has to evaluate to a LOGICAL value.

SWITCH statements

Harbour supports a SWITCH construct inspired by the C implementation of switch().

SWITCH SwitchExp
   CASE LiteralExp
      ...
      [EXIT]
   [CASE LiteralExp]
      ...
      [EXIT]

   [DEFAULT]
      ...
END
  • The LiteralExp must be a compiled time resolvable numeric expression, and may involve operators, as long as such operators involve compile time static value.
  • The EXIT optional statement is the equivalent of the C statement break, and if present, execution of the SWITCH structure will end when the EXIT statement is reached, otherwise it will continue with the first statement below the next CASE statement (fall through).

BEGIN SEQUENCE statements

BEGIN SEQUENCE
   ...
   [BREAK]
   [Break([Exp])]
RECOVER [USING Var]
   ...
END[SEQUENCE]

or:

BEGIN SEQUENCE
   ...
   [BREAK]
   [Break()]
END[SEQUENCE]

The BEGIN SEQUENCE structure allows for a well behaved abortion of any sequence, even when crossing nested procedures/functions. This means that a called procedure/function, may issue a BREAK statement, or a Break() expression, to force unfolding of any nested procedure/functions, all the way back to the first outer BEGIN SEQUENCE structure, either after its respective END statement, or a RECOVER clause if present. The Break statement may optionally pass any type of expression, which may be accepted by the RECOVER statement to allow further recovery handing.

Additionally the Harbour Error Object supports canDefaultcanRetry and canSubstitute properties, which allows error handlers to perform some preparations, and then request a Retry Operation, a Resume, or return a Value to replace the expression triggering the error condition.

Alternatively TRY [CATCH] [FINALLY] statements are available on xHB library working like the SEQUENCE construct.

Procedures/Functions

[STATIC] PROCEDURE SomeProcedureName
[STATIC] PROCEDURE SomeProcedureName()
[STATIC] PROCEDURE SomeProcedureName( Param1' [, ParamsN] )
INIT PROCEDURE SomeProcedureName
EXIT PROCEDURE SomeProcedureName
[STATIC] FUNCTION SomeProcedureName
[STATIC] FUNCTION SomeProcedureName()
[STATIC] FUNCTION SomeProcedureName( Param1' [, ParamsN] )

Procedures/Functions in Harbour can be specified with the keywords PROCEDURE, or FUNCTION. Naming rules are same as those for Variables (up to 63 characters non case sensitive). Both Procedures and Functions may be qualified by the scope qualifier STATIC to restrict their usage to the scope of the module where defined.

The INIT or EXIT optional qualifiers, will flag the procedure to be automatically invoked just before calling the application startup procedure, or just after quitting the application, respectively. Parameters passed to a procedure/function appear in the subroutine as local variables, and may accept any type, including references.

Changes to argument variables are not reflected in respective variables passed by the calling procedure/function/method unless explicitly passed BY REFERENCE using the@ prefix.

PROCEDURE have no return value, and if used in an Expression context will produce a NIL value.

FUNCTION may return any type by means of the RETURN statement, anywhere in the body of its definition.

An example procedure definition and a function call follows:

 x := Cube( 2 )

 FUNCTION Cube( n )
 RETURN n ** 3

 Sample code The typical “hello world” program would be:

 
  ? "Hello, world!"

Or:

  QOut( "Hello, world!" )

Or:

  Alert( "Hello, world!" )

Or, enclosed in an explicit procedure:

 PROCEDURE Main()

    ? "Hello, world!"

 RETURN

 

OOP examples

 #include "hbclass.ch"

 PROCEDURE Main()

    LOCAL oPerson := Person( "Dave" )

    oPerson:Eyes := "Invalid"

    oPerson:Eyes := "Blue"

    Alert( oPerson:Describe() )
 RETURN

 CLASS Person
    DATA Name INIT ""

    METHOD New() CONSTRUCTOR

    ACCESS Eyes INLINE ::pvtEyes
    ASSIGN Eyes( x ) INLINE IIF( ValType( x ) == 'C' .AND. ;
                 x IN "Blue,Brown,Green", ::pvtEyes := x,; 
                 Alert( "Invalid value" ) )

    // Sample of IN-LINE Method definition
    INLINE METHOD Describe()
       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
    ENDMETHOD

    PRIVATE:
       DATA pvtEyes
 ENDCLASS

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

   ::Name := cName

 RETURN Self

 

Tools

  • HBMK2 – Powerful build tool like make
  • HBDoc2 and  HBExtern – Creates documentation for Harbour
  • HPPP – Pre-processor, a powerful tool which avoids typical problems found on C language pre-processor
  • HBFormat – Formats source code written on Harbour or another dialect according defined rules
  • HBi18n – Tools to localizing text on applications
  • HBRun – Shell interpreter for Harbour. Macro compiling allows to run any valid Harbour code as it’s being compiled
  • HBIDE – Integrated Development Environment to help Harbour development and various xBase dialects

All tools are multiplatform.

Development

Today Habour development is leading by Viktor Szakáts with huge collaborations and leading many components of core and contribs by Przemysław Czerpak. HBIDE and some components, specially HBQt, are developed by Pritpal Bedi. Others members send minor changes to the Sourceforge SVN repository.  As 2010 Harbour development is keeping vibrant activity

Popularity

Although there is no way to measure popularity of Harbour or xBase, the TIOBE Programming Community Index As of June 2006 ranked Microsoft Visual FoxPro, a high profile dialect of xBase, on 12th position on programming languages popularity ranking. FoxPro/xBase ranked on 25th position As of August 2010. As of September 2010, the Clipper Usenet newsgroupscomp.lang.clipper is still active. As of August 2010 Harbour figured on 16th position on weekly downloads in compiler category and 132th position on global rank.

xHarbour comparison

xHarbour is a fork of the earlier Harbour project. xHarbour takes a more aggressive approach to implementing new features in the language, while Harbour is more conservative in its approach, aiming first of all for an exact replication of Clipper behaviour and then implementing new features and extensions as a secondary consideration. It should also be noted that Harbour is supported on a wide variety of operating systems while xHarbour only really supports MS Windows and Linux 32-bit.

The Harbour developers have attempted to document all hidden behaviour in the Clipper language and test Harbour-compiled code alongside the same code compiled with Clipper to maintain compatibility.

The Harbour developers explicitly reject extensions to the language where those extensions would break Clipper compatibility. These rejections were soften recently since the new Harbour architecture allow extensions out of the core compiler.

A detailed comparison between extensions implemented in Harbour and xHarbour can be found in SVN repository of the project on SourceForge.

As of 2009–2010, Harbour has seen a huge increase in its adoption while xHarbour decline as can be seen on his mailing list

See also

References

  1. ^ Harbour license
  2. ^ LetoDB
  3. ^ Official Harbour page
  4. ^ http://sourceforge.net/projects/harbour-project/
  5. ^ TIOBE Programming Community Index
  6. ^ SourceForge
  7. ^ About xHarbour
  8. ^ xhb-diff.txt
  9. ^ Harbour developers’ mailing list statistics
  10. ^ xHarbour developers’ mailing list statistics
  11. ^ ohloh.net Activity comparison

External links

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Note : This post borrowed by curtesy of Vikipedia from here : http://en.wikipedia.org/wiki/Harbour_(software)