0% found this document useful (0 votes)
37 views70 pages

Harbour Complete Programming Book

The Complete Harbour Programming Guide is a comprehensive resource designed to teach cross-platform xBase development using the Harbour programming language. It covers everything from installation and basic programming concepts to advanced topics such as object-oriented programming and database integration. The guide aims to equip developers with the skills needed for modern application development while maintaining compatibility with legacy systems.

Uploaded by

dasluna dasluna
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views70 pages

Harbour Complete Programming Book

The Complete Harbour Programming Guide is a comprehensive resource designed to teach cross-platform xBase development using the Harbour programming language. It covers everything from installation and basic programming concepts to advanced topics such as object-oriented programming and database integration. The guide aims to equip developers with the skills needed for modern application development while maintaining compatibility with legacy systems.

Uploaded by

dasluna dasluna
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

The Complete Harbour Programming

Guide

Master the Art of Cross-Platform xBase


Development
Like a lighthouse guiding ships safely to harbor, this comprehensive guide will
navigate you through the vast waters of Harbour programming, from your first steps
to mastering advanced development techniques.

Table of Contents

Part I: Getting Started


1. Introduction to Harbour

2. Installation and Setup

3. Your First Harbour Program

4. Development Environment

Part II: Language Fundamentals


1. Data Types and Variables

2. Operators and Expressions

3. Control Structures

4. Arrays and Collections

1 / 70
Part III: Functions and Procedures
1. Defining Functions

2. Parameters and Return Values

3. Scope and Variables

4. Built-in Functions

Part IV: Object-Oriented Programming


1. Classes and Objects

2. Inheritance and Polymorphism

3. Methods and Properties

4. Advanced OOP Concepts

Part V: Database Programming


1. Introduction to RDD

2. Working with DBF Files

3. Indexes and Relations

4. SQL Integration

Part VI: File and I/O Operations


1. File Handling

2. Text Processing

3. Network Programming

4. Graphics and User Interface

Part VII: Advanced Topics


1. Error Handling and Debugging

2. Multi-threading

2 / 70
3. Memory Management

4. Performance Optimization

Part VIII: Development Tools and Deployment


1. Build System (hbmk2)

2. Deployment and Distribution

Chapter 1: Introduction to Harbour


Harbour stands as a beacon in the programming world—much like a lighthouse
guides ships safely through treacherous waters to their destination harbor. This
open-source, cross-platform programming language has been guiding developers
since 1999, providing a reliable and powerful foundation for building robust
applications across multiple platforms.

What is Harbour?
Harbour is a free, open-source implementation of a cross-platform, multi-threading,
object-oriented, scriptable programming language. It maintains 100% compatibility
with CA-Clipper, making it an ideal modernization path for legacy xBase applications
while offering powerful new features for contemporary development.

Key Characteristics:
- Cross-platform: Runs on Windows, Linux, macOS, iOS, Android, BSD, Unix, and
more
- Object-oriented: Full OOP support with classes, inheritance, and polymorphism
- Multi-threading: Built-in support for concurrent programming
- Scriptable: Can be used for both compiled applications and scripts
- Compatible: 100% backward compatible with Clipper
- Extensible: Rich ecosystem of libraries and third-party integrations

3 / 70
History and Evolution
The journey of Harbour began in the golden age of database programming:

• 1980s: dBASE established the foundation for xBase programming

• 1985: Clipper emerged as a compiled alternative to dBASE

• 1999: Harbour project launched as an open-source Clipper clone

• 2011: Harbour 3.0.0 stable release marked a major milestone

• Present: Active development with 19,923+ commits and 38+ contributors

Why Choose Harbour?


Modern Features with Classic Reliability:
- Native support for modern standards: JSON, SSL, TCP/IP, XML
- Cross-platform development with single codebase
- 64-bit architecture support
- Mobile development capabilities (iOS, Android)
- Web application development
- Rich database connectivity (DBF, SQL, ODBC)

Development Advantages:
- Rapid application development
- Extensive built-in function library
- Powerful build system (hbmk2)
- Interactive development environment (hbrun)
- Strong community support
- Commercial-friendly licensing

Harbour Philosophy
Harbour embodies the philosophy of "write once, run everywhere" while
maintaining the simplicity and power that made xBase popular. Like a lighthouse
that provides both guidance and warning, Harbour offers:

• Clarity: Clean, readable syntax that's easy to learn

• Reliability: Battle-tested stability from decades of evolution

4 / 70
• Safety: Strong error handling and debugging capabilities

• Guidance: Comprehensive documentation and community support

What You'll Learn


This comprehensive guide will take you from beginner to expert, covering:

• Complete language syntax and semantics

• Professional development practices

• Database programming mastery

• Cross-platform deployment strategies

• Performance optimization techniques

• Real-world application development

Whether you're migrating from Clipper, exploring xBase for the first time, or seeking
to modernize existing applications, this book will serve as your trusted lighthouse,
guiding you safely through every aspect of Harbour development.

Chapter 2: Installation and Setup


Like preparing a lighthouse for operation, setting up your Harbour development
environment requires careful attention to detail and proper configuration. This
chapter will guide you through the complete installation process across all
supported platforms.

System Requirements
Minimum Requirements:
- Processor: 32-bit or 64-bit processor (x86, x86_64, ARM supported)
- Memory: 512 MB RAM (2 GB recommended)
- Storage: 50 MB for basic installation (500 MB for full development setup)
- Operating System: Any supported platform (see below)

C Compiler Requirements:
Harbour requires an ANSI C compiler for building:

5 / 70
- Windows: MinGW, MSVC, Clang, Borland C++, Intel C++, Watcom, Pelles C
- Linux: GCC, Clang
- macOS: Clang (via Xcode Command Line Tools)
- Unix/BSD: GCC, system-specific compilers

Supported Platforms
Harbour's cross-platform nature shines across an impressive array of systems:

Desktop Platforms:
- Windows (all versions from XP onwards)
- Linux (all major distributions)
- macOS (10.9+ and macOS 11+ for ARM)
- FreeBSD, NetBSD, OpenBSD
- Unix variants (Solaris, AIX, HP-UX)

Mobile Platforms:
- iOS (32-bit and 64-bit)
- Android (ARM, x86, x86_64)

Embedded/Specialized:
- DOS (DJGPP)
- OS/2
- QNX
- VxWorks
- Symbian
- BeOS/Haiku

Installation Methods

Method 1: Binary Installation (Recommended for Beginners)


Windows:

6 / 70
# Download from SourceForge
[Link]

# Or use nightly builds


[Link]
windows/nightly/

Linux (Ubuntu/Debian):

# Install dependencies
sudo apt-get update
sudo apt-get install build-essential

# Download and extract Harbour binary


wget [Link]
3.0.0/[Link].bz2
tar -xjf [Link].bz2

macOS:

# Install Xcode Command Line Tools


xcode-select --install

# Download Harbour source


curl -O [Link]
source/3.0.0/[Link].bz2

Method 2: Source Installation (Advanced)


Prerequisites:

7 / 70
# Ensure you have required tools
make --version # GNU Make 3.81 or newer required
gcc --version # Or your preferred C compiler
git --version # For development builds

Clone from GitHub:

# Get the latest development version


git clone [Link] harbour
cd harbour

# Or download stable release


wget [Link]
unzip [Link]
cd core-master

Build Configuration:

# Set environment variables (optional but recommended)


export HB_BUILD_MODE=cpp # Use C++ mode
export HB_USER_DFLAGS=-DHB_TR_LEVEL_DEBUG # Enable debug output
export HB_BUILD_STRIP=all # Strip debug info from binaries
export HB_BUILD_OPTIM=yes # Enable optimizations

Compilation:

8 / 70
# Basic build (detects platform automatically)
make

# Platform-specific builds
make HB_PLATFORM=win HB_COMPILER=mingw # Windows with MinGW
make HB_PLATFORM=linux HB_COMPILER=gcc # Linux with GCC
make HB_PLATFORM=darwin HB_COMPILER=clang # macOS with Clang

# Custom installation directory


make install HB_INSTALL_PREFIX=/opt/harbour

Method 3: Package Managers


Linux Package Managers:

# Debian/Ubuntu
sudo apt-get install harbour

# Red Hat/CentOS/Fedora
sudo yum install harbour
# or
sudo dnf install harbour

# Arch Linux
sudo pacman -S harbour

macOS Homebrew:

9 / 70
# Install Homebrew if not present
/bin/bash -c "$(curl -fsSL [Link]
Homebrew/install/HEAD/[Link])"

# Install Harbour
brew install harbour

Environment Configuration
Setting PATH Variables:

Windows:

# Add to system PATH


set PATH=%PATH%;C:\harbour\bin

# For permanent setting, add to System Properties > Environment


Variables

Linux/macOS:

# Add to ~/.bashrc or ~/.profile


export PATH=$PATH:/usr/local/harbour/bin
export HB_ROOT=/usr/local/harbour

# Reload configuration
source ~/.bashrc

Environment Variables:

10 / 70
# Essential variables
export HB_ROOT=/path/to/harbour # Harbour installation
directory
export HB_PATH=%HB_ROOT%/bin # Harbour executables
export HB_LIB_INSTALL=%HB_ROOT%/lib # Library installation
path
export HB_INC_INSTALL=%HB_ROOT%/include # Header files path

# Development variables
export HB_BUILD_VERBOSE=yes # Verbose build output
export HB_BUILD_JOBS=4 # Parallel compilation
jobs
export HB_CCACHE=yes # Use ccache if available

Verification and Testing


Test Installation:

11 / 70
# Check Harbour compiler
harbour --version

# Check build tool


hbmk2 --version

# Check script runner


hbrun --version

# Test compilation
echo 'PROCEDURE Main()' > [Link]
echo ' ? "Hello, Harbour!"' >> [Link]
echo 'RETURN' >> [Link]

# Compile and run


hbmk2 [Link]
./test

Expected Output:

Harbour 3.0.0 (Rev. 20110717-0902)


Copyright (c) 1999-2011, [Link]
Hello, Harbour!

Development Tools Setup


IDE Integration:

Visual Studio Code:

12 / 70
// .vscode/[Link]
{
"[Link]": {
"*.prg": "harbour",
"*.ch": "harbour"
},
"[Link]": "/usr/local/harbour/bin/harbour",
"[Link]": "/usr/local/harbour/bin/hbmk2"
}

Sublime Text:

// [Link]-syntax (basic syntax highlighting)


{
"name": "Harbour",
"file_extensions": ["prg", "ch"],
"scope": "[Link]"
}

Troubleshooting Common Issues


Build Errors:

# Missing dependencies
sudo apt-get install build-essential # Linux
xcode-select --install # macOS

# Compiler not found


export HB_COMPILER=gcc # Specify compiler
explicitly

Path Issues:

13 / 70
# Verify Harbour is in PATH
which harbour
which hbmk2

# Check permissions
ls -la /usr/local/harbour/bin/
chmod +x /usr/local/harbour/bin/*

Library Linking:

# Missing libraries
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/harbour/lib

Your Harbour development environment is now ready. Like a well-maintained


lighthouse, proper installation and configuration will provide years of reliable
service for your development projects.

Chapter 3: Your First Harbour Program


Just as a lighthouse keeper must understand the basic operation of their beacon
before guiding ships safely to harbor, every Harbour programmer must master the
fundamentals. This chapter will illuminate the path from your first simple program
to understanding the core concepts that make Harbour powerful.

The Classic "Hello, World!" Program


Let's begin with the traditional first program that has welcomed countless
developers to new languages:

14 / 70
// [Link] - Your first Harbour program
PROCEDURE Main()
? "Hello, World!"
? "Welcome to Harbour Programming!"
RETURN

Building and Running:

# Compile the program


hbmk2 [Link]

# Run the executable


./hello # Linux/macOS
[Link] # Windows

Expected Output:

Hello, World!
Welcome to Harbour Programming!

Understanding the Structure


Let's break down this simple program:

1. Procedure Declaration:

PROCEDURE Main()

• PROCEDURE declares a procedure (function that doesn't return a value)

• Main() is the entry point—Harbour automatically calls this when the program
starts

• Parentheses () indicate no parameters are expected

15 / 70
2. Output Statements:

? "Hello, World!"

• ? is Harbour's output operator (equivalent to PRINT or WRITE )

• String literals are enclosed in double quotes

• Each ? statement outputs on a new line

3. Return Statement:

RETURN

• RETURN terminates the procedure

• Optional in procedures, but good practice for clarity

Expanding Your First Program


Let's create a more interesting version that demonstrates basic concepts:

16 / 70
// enhanced_hello.prg
PROCEDURE Main()
// Variable declarations
LOCAL cName := "Harbour Developer"
LOCAL dToday := Date()
LOCAL nYear := Year(dToday)
LOCAL lIsModern := (nYear >= 2020)

// Display welcome message


? "=" + Replicate("=", 50) + "="
? " Welcome to Harbour Programming, " + cName + "!"
? "=" + Replicate("=", 50) + "="
?

// Display date information


? "Today's date:", dToday
? "Current year:", nYear

// Conditional output
IF lIsModern
? "You're programming in the modern era!"
ELSE
? "Classic programming never goes out of style!"
ENDIF

?
? "Happy programming!"

RETURN

Key Concepts Demonstrated:

1. Variable Declaration:

17 / 70
LOCAL cName := "Harbour Developer" // String variable
LOCAL dToday := Date() // Date variable
LOCAL nYear := Year(dToday) // Numeric variable
LOCAL lIsModern := (nYear >= 2020) // Logical variable

2. Hungarian Notation:
Harbour commonly uses Hungarian notation for variable names:
- c prefix for Character (string) variables
- n prefix for Numeric variables
- d prefix for Date variables
- l prefix for Logical (boolean) variables
- a prefix for Array variables
- o prefix for Object variables

3. String Operations:

? "Welcome to " + cName + "!" // String concatenation


? "=" + Replicate("=", 50) + "=" // String repetition

4. Function Calls:

LOCAL dToday := Date() // Built-in function


LOCAL nYear := Year(dToday) // Function with parameter

5. Conditional Logic:

IF lIsModern
// Code for modern era
ELSE
// Code for classic era
ENDIF

18 / 70
Interactive Programs
Let's create a program that interacts with the user:

19 / 70
// interactive_hello.prg
PROCEDURE Main()
LOCAL cName, cAge, nAge, cResponse

// Clear screen and display title


CLEAR SCREEN
? "Welcome to the Interactive Harbour Demo!"
? "======================================"
?

// Get user input


?? "Please enter your name: "
cName := Space(30)
@ Row(), Col() GET cName
READ
cName := AllTrim(cName)

?? "Please enter your age: "


cAge := Space(3)
@ Row(), Col() GET cAge PICTURE "999"
READ
nAge := Val(cAge)

// Process and display results


?
? "Nice to meet you, " + cName + "!"

DO CASE
CASE nAge < 18
? "You're quite young to be learning programming!"
CASE nAge >= 18 .AND. nAge < 65
? "Perfect age for mastering Harbour!"
OTHERWISE
? "Wisdom and experience are great programming assets!"
ENDCASE

20 / 70
// Calculate birth year
? "You were born around:", Str(Year(Date()) - nAge)

?
?? "Press any key to continue..."
Inkey(0)

RETURN

New Concepts:

1. Screen Control:

CLEAR SCREEN // Clear the screen


@ Row(), Col() GET cName // Position cursor and get
input

2. Input Handling:

?? "Prompt: " // Output without newline


GET variable // Input field
READ // Process all pending GET
operations

3. String Functions:

Space(30) // Create string of 30 spaces


AllTrim(cName) // Remove leading/trailing
spaces
Val(cAge) // Convert string to number
Str(nYear) // Convert number to string

4. Multi-way Branching:

21 / 70
DO CASE
CASE condition1
// Code
CASE condition2
// Code
OTHERWISE
// Default code
ENDCASE

5. User Interaction:

Inkey(0) // Wait for keypress

Working with Arrays


Arrays are fundamental to Harbour programming:

22 / 70
// array_demo.prg
PROCEDURE Main()
LOCAL aColors := {"Red", "Green", "Blue", "Yellow", "Purple"}
LOCAL aNumbers := {10, 20, 30, 40, 50}
LOCAL aMixed := {"John", 25, Date(), .T., {1, 2, 3}}
LOCAL i

? "Array Demonstration"
? "=================="
?

// Display array contents


? "Colors array:"
FOR i := 1 TO Len(aColors)
? " " + Str(i) + ": " + aColors[i]
NEXT

?
? "Numbers array (using FOR EACH):"
FOR EACH item IN aNumbers
? " Item " + Str(HB_EnumIndex()) + ": " + Str(item)
NEXT

?
? "Mixed array information:"
? " Array length:", Len(aMixed)
? " First item (string):", aMixed[1]
? " Second item (number):", aMixed[2]
? " Third item (date):", aMixed[3]
? " Fourth item (logical):", aMixed[4]
? " Fifth item (array):", aMixed[5][1], aMixed[5][2], aMixed[5]
[3]

RETURN

23 / 70
Array Concepts:

1. Array Declaration:

LOCAL aArray := {item1, item2, item3} // Literal initialization


LOCAL aEmpty := {} // Empty array
LOCAL aSpaced := Array(10) // Array with 10 NIL
elements

2. Array Access:

aArray[1] // First element (1-based


indexing)
aArray[Len(aArray)] // Last element

3. Array Iteration:

FOR i := 1 TO Len(aArray) // Traditional loop


FOR EACH item IN aArray // Modern iteration

Error Handling Basics


Even lighthouse keepers must prepare for storms. Here's basic error handling:

24 / 70
// error_demo.prg
PROCEDURE Main()
LOCAL nResult, nDivisor := 0

? "Error Handling Demonstration"


? "============================="
?

// Controlled error handling


BEGIN SEQUENCE WITH __BreakBlock()

? "Attempting division by zero..."


nResult := 10 / nDivisor
? "Result:", nResult

RECOVER USING oError

? "Error caught!"
? "Error description:", oError:Description
? "Error operation:", oError:Operation
? "Continuing program execution..."

END SEQUENCE

?
? "Program completed successfully!"

RETURN

Best Practices from the Start


1. Code Organization:

25 / 70
// Always include meaningful comments
// Use consistent indentation (3-4 spaces recommended)
// Group related functionality together

2. Variable Naming:

// Use descriptive names with Hungarian notation


LOCAL cUserName // Good
LOCAL x // Poor

LOCAL nTotalAmount // Good


LOCAL num // Poor

3. Error Prevention:

// Validate inputs
IF Empty(cName)
cName := "Anonymous"
ENDIF

// Check array bounds


IF nIndex >= 1 .AND. nIndex <= Len(aArray)
// Safe to access aArray[nIndex]
ENDIF

4. Documentation:

26 / 70
/*
* Function: CalculateTotal
* Purpose: Calculate total with tax
* Parameters: nAmount - base amount
* nTaxRate - tax rate (decimal)
* Returns: Total amount including tax
*/
FUNCTION CalculateTotal(nAmount, nTaxRate)
RETURN nAmount * (1 + nTaxRate)

Quick Reference: Essential Syntax


Basic Output:

? expression // Output with newline


?? expression // Output without newline
@ row, col SAY expression // Output at specific
position

Variables:

LOCAL variable := value // Local scope


STATIC variable := value // Static scope
PRIVATE variable // Dynamic scope
PUBLIC variable // Global scope

Control Structures:

27 / 70
IF condition ... ENDIF // Conditional
FOR counter := start TO end ... NEXT // Counting loop
WHILE condition ... ENDDO // Conditional loop
FOR EACH item IN array ... NEXT // Iteration loop
DO CASE ... CASE ... OTHERWISE ... ENDCASE // Multi-way branch

Congratulations! You've taken your first steps into Harbour programming. Like a
ship safely guided to harbor by a lighthouse, you now have the fundamental
navigation tools to explore the deeper waters of this powerful language.

Chapter 4: Development Environment


A lighthouse keeper needs the right tools and environment to maintain their beacon
effectively. Similarly, a productive Harbour developer requires a well-configured
development environment. This chapter will guide you through setting up an
optimal development workspace that enhances productivity and code quality.

The Harbour Toolchain


Harbour provides a comprehensive set of tools for development, each serving a
specific purpose in the development lifecycle:

Core Tools:
- harbour: The compiler that translates Harbour source to C code
- hbmk2: The build tool and project manager
- hbrun: The script runner and interactive shell
- hbformat: Code formatter for consistent styling
- hbtest: Testing framework for quality assurance

hbmk2: The Build System Master


hbmk2 is Harbour's Swiss Army knife—a comprehensive build tool that handles
compilation, linking, and project management:

Basic Usage:

28 / 70
# Compile single file
hbmk2 [Link]

# Compile with specific output name


hbmk2 [Link] -o myapp

# Create dynamic library


hbmk2 [Link] -hbdyn

# Create static library


hbmk2 [Link] -hblib

Project Files (.hbp):

29 / 70
# Create [Link]
-o=myapplication
-w3
-es2

# Source files
[Link]
[Link]
[Link]

# Libraries
-luser32
-lgdi32

# Include paths
-I../common/include

# Library paths
-L../common/lib

# Preprocessor defines
-DRELEASE_BUILD
-DHB_THREAD_SUPPORT

Advanced hbmk2 Options:

30 / 70
# Development builds
hbmk2 [Link] -debug -trace

# Production builds
hbmk2 [Link] -optim -strip

# Cross-platform builds
hbmk2 [Link] -platform=linux
hbmk2 [Link] -platform=win -compiler=mingw

# Specific compiler selection


hbmk2 [Link] -compiler=gcc
hbmk2 [Link] -compiler=clang
hbmk2 [Link] -compiler=msvc

hbrun: Interactive Development


hbrun provides immediate code execution and interactive development:

Script Execution:

# Run script directly


hbrun [Link]

# Interactive shell
hbrun

# Script with parameters


hbrun [Link] param1 param2

# One-liner execution
hbrun -e "? 'Hello from command line'"

Interactive Shell Features:

31 / 70
// In hbrun interactive mode
hb> ? "Hello, Interactive Harbour!"
Hello, Interactive Harbour!

hb> LOCAL a := {1, 2, 3, 4, 5}


hb> ? Len(a)
5

hb> FOR EACH item IN a


hb> ?? Str(item) + " "
hb> NEXT
1 2 3 4 5

hb> help // Show available commands


hb> quit // Exit interactive mode

Code Organization Strategies


Project Structure:

32 / 70
MyProject/
├── src/
│ ├── [Link]
│ ├── models/
│ │ ├── [Link]
│ │ └── [Link]
│ ├── views/
│ │ ├── [Link]
│ │ └── [Link]
│ └── utils/
│ ├── [Link]
│ └── [Link]
├── include/
│ ├── [Link]
│ └── [Link]
├── tests/
│ ├── test_models.prg
│ └── test_utils.prg
├── docs/
│ └── [Link]
├── build/
└── [Link]

Modular Programming:

33 / 70
// In include/[Link]
#define APP_NAME "My Application"
#define APP_VERSION "1.0.0"
#define APP_COPYRIGHT "Copyright (c) 2024"

#define MAX_RECORDS 1000


#define DEFAULT_TIMEOUT 30

// Function prototypes
ANNOUNCE GetCustomerName
ANNOUNCE CalculateTotal
ANNOUNCE ValidateEmail

34 / 70
// In src/models/[Link]
#include "[Link]"

FUNCTION CreateCustomer(cName, cEmail, cPhone)


LOCAL oCustomer := TCustomer():New()

oCustomer:Name := cName
oCustomer:Email := cEmail
oCustomer:Phone := cPhone

IF ValidateCustomer(oCustomer)
RETURN oCustomer
ENDIF

RETURN NIL

FUNCTION ValidateCustomer(oCustomer)
RETURN !Empty(oCustomer:Name) .AND. ;
ValidateEmail(oCustomer:Email)

STATIC FUNCTION ValidateEmail(cEmail)


RETURN "@" <span class="math-inline" style="display:
inline;"><math xmlns="[Link]
display="inline"><mrow><mi>c</mi><mi>E</mi><mi>m</mi><mi>a</
mi><mi>i</mi><mi>l</mi><mo>&#x0002E;</mo><mi>A</mi><mi>N</
mi><mi>D</mi><mo>&#x0002E;</mo><mi>"</mi><mo>&#x0002E;</mo><mi>"</
mi></mrow></math></span> cEmail

IDE and Editor Configuration


Visual Studio Code Setup:

.vscode/[Link]:

35 / 70
{
"[Link]": {
"*.prg": "harbour",
"*.ch": "harbour-header"
},
"[Link]": 3,
"[Link]": true,
"[Link]": [80, 120],
"[Link]": "utf8",
"[Link]": "\n"
}

.vscode/[Link]:

36 / 70
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Harbour Project",
"type": "shell",
"command": "hbmk2",
"args": ["${workspaceFolder}/[Link]"],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"problemMatcher": []
},
{
"label": "Run Harbour Script",
"type": "shell",
"command": "hbrun",
"args": ["${file}"],
"group": "test"
}
]
}

Sublime Text Configuration:

[Link]-syntax:

37 / 70
name: Harbour
file_extensions: [prg, ch]
scope: [Link]

contexts:
main:
- match: '(?i)\b(function|procedure|class|method)\b'
scope: [Link]
- match: '(?i)\b(local|static|private|public)\b'
scope: [Link]
- match: '(?i)\b(if|else|endif|while|enddo|for|next|do case|
endcase)\b'
scope: [Link]
- match: '"[^"]*"'
scope: [Link]
- match: "//.*$"
scope: [Link]
- match: '/\*.*?\*/'
scope: [Link]

Debugging Techniques
Built-in Debugging:

38 / 70
// Debug output
? "Debug: Variable value =", nValue
?? " at line", ProcLine()

// Conditional debugging
#ifdef DEBUG
? "Debug mode: Processing record", nRecNo
#endif

// Trace function calls


SET TRACE ON
MyFunction()
SET TRACE OFF

Advanced Debugging:

39 / 70
// Error context information
FUNCTION MyFunction()
LOCAL nResult

BEGIN SEQUENCE WITH __BreakBlock()

// Your code here


nResult := SomeCalculation()

RECOVER USING oError

? "Error in function:", ProcName()


? "Error line:", ProcLine()
? "Error description:", oError:Description
? "Call stack:"

LOCAL i
FOR i := 1 TO 10
IF !Empty(ProcName(i))
? " ", i, ProcName(i), ProcLine(i)
ENDIF
NEXT

BREAK oError

END SEQUENCE

RETURN nResult

Debugging Macros:

40 / 70
// In include/[Link]
#ifdef DEBUG
#define DBG(x) (? "DBG:", x)
#define TRACE(x) (? "TRACE: " + ProcName() + "(" +
Str(ProcLine()) + "): ", x)
#else
#define DBG(x)
#define TRACE(x)
#endif

// Usage in code
#include "[Link]"

FUNCTION ProcessData(aData)
TRACE("Starting with " + Str(Len(aData)) + " records")

LOCAL nCount := 0
FOR EACH item IN aData
DBG("Processing item: " + item)
nCount++
NEXT

TRACE("Completed processing " + Str(nCount) + " items")


RETURN nCount

Performance Profiling
Simple Timing:

41 / 70
FUNCTION ProfileFunction()
LOCAL nStart := Seconds()

// Your code to profile


ProcessLargeDataSet()

LOCAL nElapsed := Seconds() - nStart


? "Function took", nElapsed, "seconds"

RETURN nElapsed

Detailed Profiling:

42 / 70
STATIC s_aProfileData := {}

FUNCTION StartProfile(cFunction)
LOCAL nPos := AScan(s_aProfileData, {|a| a[1] == cFunction})

IF nPos == 0
AAdd(s_aProfileData, {cFunction, 0, 0, Seconds()})
nPos := Len(s_aProfileData)
ELSE
s_aProfileData[nPos][4] := Seconds()
ENDIF

RETURN nPos

FUNCTION EndProfile(nHandle)
LOCAL nElapsed := Seconds() - s_aProfileData[nHandle][4]

s_aProfileData[nHandle][2]++ // Call count


s_aProfileData[nHandle][3] += nElapsed // Total time

RETURN nElapsed

FUNCTION ShowProfile()
? "Function Profiling Results:"
? "=========================="
? "Function", Space(20), "Calls", "Total Time", "Avg Time"

FOR EACH aItem IN s_aProfileData


? PadR(aItem[1], 30), ;
Str(aItem[2], 8), ;
Str(aItem[3], 10, 4), ;
Str(aItem[3] / aItem[2], 8, 4)
NEXT

RETURN NIL

43 / 70
Version Control Integration
Git Configuration for Harbour:

.gitignore:

44 / 70
# Compiled binaries
*.exe
*.dll
*.so
*.dylib

# Object files
*.o
*.obj
*.c
*.cpp

# Build directories
build/
dist/
release/
debug/

# Harbour specific
*.hrb
*.hbl
*.hbx

# Editor files
*.bak
*.swp
*~
.vscode/
*.sublime-workspace

# OS specific
.DS_Store
[Link]

45 / 70
Git Hooks:

#!/bin/sh
# .git/hooks/pre-commit
# Format code before commit

for file in <span class="math-inline" style="display:


inline;"><math xmlns="[Link]
display="inline"><mrow><mo stretchy="false">&#x00028;</mo><mi>g</
mi><mi>i</mi><mi>t</mi><mi>d</mi><mi>i</mi><mi>f</mi><mi>f</
mi><mo>&#x02212;</mo><mo>&#x02212;</mo><mi>c</mi><mi>a</mi><mi>c</
mi><mi>h</mi><mi>e</mi><mi>d</mi><mo>&#x02212;</mo><mo>&#x02212;</
mo><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi><mo>&#x02212;</
mo><mi>o</mi><mi>n</mi><mi>l</mi><mi>y</mi><mo>&#x02212;</
mo><mo>&#x02212;</mo><mi>d</mi><mi>i</mi><mi>f</mi><mi>f</
mi><mo>&#x02212;</mo><mi>f</mi><mi>i</mi><mi>l</mi><mi>t</
mi><mi>e</mi><mi>r</mi><mo>&#x0003D;</mo><mi>A</mi><mi>C</
mi><mi>M</mi><mo stretchy="false">&#x0007C;</mo><mi>g</mi><mi>r</
mi><mi>e</mi><msup><mi>p</mi><mi>&#x02032;</mi></msup><mi>\</
mi><mo>&#x0002E;</mo><mi>p</mi><mi>r</mi><mi>g</mi></mrow></math></
span>'); do
if [ -f "$file" ]; then
hbformat "$file"
git add "$file"
fi
done

Testing Framework Setup


Basic Test Structure:

46 / 70
// In tests/test_runner.prg
#include "[Link]"

PROCEDURE Main()
LOCAL oTest := HBTest():New("My Application Tests")

// Register test suites


oTest:AddSuite("String Utilities", @TestStringUtils())
oTest:AddSuite("Date Functions", @TestDateFunctions())
oTest:AddSuite("Database Operations", @TestDatabase())

// Run all tests


oTest:Run()

// Display results
oTest:Report()

RETURN

FUNCTION TestStringUtils()
LOCAL oSuite := HBTestSuite():New()

oSuite:AddTest("Proper Case", {|| ;


HB_Assert(ProperCase("hello world") == "Hello World")})

oSuite:AddTest("Remove Spaces", {|| ;


HB_Assert(RemoveSpaces("a b c") == "abc")})

RETURN oSuite

Continuous Integration
GitHub Actions Workflow (.github/workflows/[Link]):

47 / 70
name: Harbour CI

on: [push, pull_request]

jobs:
build:
runs-on: ${{ [Link] }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]

steps:
- uses: actions/checkout@v2

- name: Install Harbour (Ubuntu)


if: [Link] == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install harbour

- name: Build Project


run: hbmk2 [Link]

- name: Run Tests


run: hbrun tests/test_runner.prg

Your development environment is now properly configured, like a well-equipped


lighthouse ready to guide ships safely through any weather. With these tools and
practices, you'll be able to develop Harbour applications efficiently and maintain
code quality throughout your projects.

48 / 70
Chapter 5: Data Types and Variables
Understanding data types in Harbour is like understanding the different types of
signals a lighthouse can emit—each serves a specific purpose and conveys particular
information. Harbour's rich type system provides the foundation for building robust
applications while maintaining the simplicity that makes the language accessible.

Overview of Harbour Data Types


Harbour supports both scalar (simple) and complex data types, each optimized for
different programming needs:

Scalar Types:
- NIL: Represents absence of value
- Logical: Boolean values (.T. or .F.)
- Numeric: Integer and floating-point numbers
- Date: Date values with special handling
- String: Character sequences
- Pointer: References to functions or memory

Complex Types:
- Array: Ordered collections of any data type
- Object: Class instances with properties and methods
- CodeBlock: Executable code snippets
- Hash: Key-value pair collections

The NIL Data Type


NIL represents the absence of a value, similar to null in other languages:

49 / 70
PROCEDURE Main()
LOCAL xValue := NIL
LOCAL cName, nAge, dBirth // Uninitialized variables are NIL

? "NIL value:", xValue // Displays: NIL


? "Type of NIL:", ValType(xValue) // Displays: U (Undefined)

// Testing for NIL


IF xValue == NIL
? "Value is NIL"
ENDIF

// Alternative NIL test


IF xValue = NIL // Single = also works for NIL comparison
? "Value is NIL (alternative syntax)"
ENDIF

// Using Empty() function (preferred)


IF Empty(xValue)
? "Value is empty (NIL, 0, '', etc.)"
ENDIF

RETURN

NIL Characteristics:
- Default value for uninitialized variables
- Evaluates to .F. in logical contexts
- Returns 0 when used in numeric contexts
- Returns empty string when used in string contexts

Logical Data Type


Logical values represent true/false conditions:

50 / 70
PROCEDURE Main()
LOCAL lActive := .T. // True
LOCAL lVisible := .F. // False
LOCAL lResult

? "Active:", lActive // Displays: .T.


? "Visible:", lVisible // Displays: .F.

// Logical operations
lResult := lActive .AND. lVisible // .F.
? "AND result:", lResult

lResult := lActive .OR. lVisible // .T.


? "OR result:", lResult

lResult := .NOT. lVisible // .T.


? "NOT result:", lResult

// Logical expressions
LOCAL nAge := 25
LOCAL lCanVote := (nAge >= 18)
? "Can vote:", lCanVote // .T.

// Converting to logical
? "Logical of 1:", (1 == .T.) // .T.
? "Logical of 0:", (0 == .F.) // .T.
? "Logical of 'Y':", ('Y' $ 'YyTt') // .T.

RETURN

Logical Constants:
- .T. , .TRUE. , .Y. - True values
- .F. , .FALSE. , .N. - False values

51 / 70
Numeric Data Type
Harbour handles both integer and floating-point numbers seamlessly:

52 / 70
PROCEDURE Main()
// Integer numbers
LOCAL nCount := 42
LOCAL nNegative := -17
LOCAL nHex := 0xFF // Hexadecimal (255)
LOCAL nOctal := 0o777 // Octal (511)

// Floating-point numbers
LOCAL nPi := 3.14159
LOCAL nScientific := 1.23e-4
LOCAL nPercent := 15.75

? "Integer:", nCount
? "Hexadecimal:", nHex
? "Float:", nPi
? "Scientific:", nScientific

// Numeric operations
LOCAL nResult
nResult := nCount + 10 // Addition
? "Addition:", nResult

nResult := nCount * 2 // Multiplication


? "Multiplication:", nResult

nResult := nCount / 3 // Division (returns float)


? "Division:", nResult

nResult := nCount % 5 // Modulo


? "Modulo:", nResult

nResult := nCount ^ 2 // Exponentiation


? "Power:", nResult

// Numeric functions

53 / 70
? "Absolute:", Abs(-42) // 42
? "Round:", Round(3.14159, 2) // 3.14
? "Int:", Int(3.14159) // 3
? "Max:", Max(10, 20, 15) // 20
? "Min:", Min(10, 20, 15) // 10

RETURN

Numeric Precision:
- Integers: Platform-dependent (typically 32 or 64-bit)
- Floating-point: IEEE 754 double precision
- Decimal precision: Configurable (default 16 digits)

String Data Type


Strings are sequences of characters with powerful manipulation capabilities:

54 / 70
PROCEDURE Main()
// String declaration methods
LOCAL cName := "John Doe" // Double quotes
LOCAL cAddress := 'Main Street' // Single quotes
LOCAL cLong := [Long text string] // Square brackets
LOCAL cEmpty := "" // Empty string

? "Name:", cName
? "Length:", Len(cName) // 8

// String concatenation
LOCAL cFull := cName + " lives on " + cAddress
? "Full:", cFull

// String functions
? "Upper:", Upper(cName) // JOHN DOE
? "Lower:", Lower(cName) // john doe
? "Proper:", Proper(cName) // John Doe

// String searching
? "Position of 'John':", At("John", cName) // 1
? "Contains 'Doe':", "Doe" $ cName // .T.

// String extraction
? "Left 4:", Left(cName, 4) // John
? "Right 3:", Right(cName, 3) // Doe
? "Substring:", SubStr(cName, 6, 3) // Doe

// String modification
LOCAL cPadded := PadL(cName, 15, "*")
? "Left padded:", cPadded // *******John Doe

cPadded := PadR(cName, 15, "-")


? "Right padded:", cPadded // John Doe-------

55 / 70
? "Trimmed:", AllTrim(" " + cName + " ") // John Doe

// String replacement
? "Replaced:", StrTran(cName, "Doe", "Smith") // John Smith

RETURN

String Features:
- Dynamic length (limited by available memory)
- ASCII and UTF-8 encoding support
- Null-terminated internally (C compatibility)
- 1-based indexing for substring operations

Date Data Type


Dates in Harbour are handled as a distinct data type with special formatting:

56 / 70
PROCEDURE Main()
// Date creation
LOCAL dToday := Date() // Current date
LOCAL dBirth := CToD("12/25/1990") // String to date
LOCAL dLiteral := 0d19901225 // Date literal (YYYYMMDD)
LOCAL dEmpty := CToD("") // Empty date

? "Today:", dToday
? "Birth:", dBirth
? "Literal:", dLiteral
? "Empty:", dEmpty

// Date functions
? "Day:", Day(dBirth) // 25
? "Month:", Month(dBirth) // 12
? "Year:", Year(dBirth) // 1990
? "Day of week:", DoW(dBirth) // 1-7 (Sunday=1)
? "Day name:", CDoW(dBirth) // Day name
? "Month name:", CMonth(dBirth) // Month name

// Date arithmetic
LOCAL dFuture := dToday + 30 // Add 30 days
LOCAL nDays := dToday - dBirth // Days between dates

? "30 days from now:", dFuture


? "Days since birth:", nDays

// Date formatting
? "Default format:", DToC(dBirth) // MM/DD/YY or DD/MM/YY
? "SQL format:", DToS(dBirth) // YYYYMMDD
? "Transform:", Transform(dBirth, "@D") // Formatted display

// Date validation
? "Valid date:", !Empty(dBirth) // .T.
? "Empty date:", Empty(dEmpty) // .T.

57 / 70
RETURN

Date System:
- Internal storage: Julian day numbers
- Range: From year 0001 to year 9999
- Leap year handling: Automatic
- Date format: Controlled by SET DATE and SET CENTURY

Array Data Type


Arrays are dynamic collections that can hold any data type:

58 / 70
PROCEDURE Main()
// Array creation
LOCAL aNumbers := {1, 2, 3, 4, 5} // Literal
LOCAL aNames := {"John", "Mary", "Bob"} // String array
LOCAL aMixed := {1, "Two", .T., Date()} // Mixed types
LOCAL aEmpty := {} // Empty array
LOCAL aDynamic := Array(10) // Array with 10 NIL
elements

? "Numbers:", Len(aNumbers) // 5
? "First number:", aNumbers[1] // 1
? "Last number:", aNumbers[Len(aNumbers)] // 5

// Array modification
aNumbers[1] := 100 // Change element
AAdd(aNumbers, 6) // Add element
ADel(aNumbers, 2) // Delete element
(sets to NIL)
ASize(aNumbers, 4) // Resize array

? "Modified:", aNumbers[1], aNumbers[2] // 100, NIL

// Multidimensional arrays
LOCAL aMatrix := {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
? "Matrix element [2,3]:", aMatrix[2][3] // 6

// Array functions
LOCAL aSource := {10, 20, 30}
LOCAL aDest := {}

ACopy(aSource, aDest) // Copy array


? "Copied length:", Len(aDest) // 3

AFill(aDest, 0) // Fill with zeros


? "After fill:", aDest[1] // 0

59 / 70
LOCAL nPos := AScan(aNames, "Mary") // Find element
? "Mary position:", nPos // 2

// Array iteration
? "Array contents:"
FOR EACH item IN aNames
? " Item " + Str(HB_EnumIndex()) + ":", item
NEXT

RETURN

Array Characteristics:
- 1-based indexing
- Dynamic sizing
- Heterogeneous (can mix data types)
- Pass by reference
- Unlimited nesting depth

Hash Data Type


Hashes provide key-value pair storage with fast lookup:

60 / 70
PROCEDURE Main()
// Hash creation
LOCAL hPerson := {"name" => "John", "age" => 30, "active"
=> .T.}
LOCAL hEmpty := {=>} // Empty hash
LOCAL hTyped := hb_Hash() // Using function

? "Name:", hPerson["name"] // John


? "Age:", hPerson["age"] // 30

// Hash modification
hPerson["email"] := "john@[Link]" // Add key
hPerson["age"] := 31 // Modify value

? "Email:", hPerson["email"]
? "New age:", hPerson["age"]

// Hash functions
? "Hash size:", Len(hPerson) // 4
? "Has name key:", "name" $ hPerson // .T.

// Get all keys and values


LOCAL aKeys := hb_HKeys(hPerson)
LOCAL aValues := hb_HValues(hPerson)

? "Keys:", aKeys[1], aKeys[2] // name, age


? "Values:", aValues[1], aValues[2] // John, 31

// Hash iteration
? "Hash contents:"
FOR EACH key IN aKeys
? " " + key + ":", hPerson[key]
NEXT

RETURN

61 / 70
CodeBlock Data Type
CodeBlocks are executable code snippets that can be stored and executed:

PROCEDURE Main()
// Simple codeblocks
LOCAL bGreeting := {|| "Hello, World!"}
LOCAL bAdd := {|x, y| x + y}
LOCAL bCheck := {|n| n > 0}

? "Greeting:", Eval(bGreeting) // Hello, World!


? "Addition:", Eval(bAdd, 5, 3) // 8
? "Check positive:", Eval(bCheck, 10) // .T.

// Codeblocks with arrays


LOCAL aNumbers := {1, 2, 3, 4, 5}
LOCAL bDouble := {|x| x * 2}

AEval(aNumbers, bDouble) // Apply to each


element
? "First doubled:", aNumbers[1] // 2

// Conditional codeblock
LOCAL bCondition := {|x| x % 2 == 0} // Even numbers
LOCAL nPos := AScan(aNumbers, bCondition)
? "First even position:", nPos // 1 (value 2)

// Complex codeblock
LOCAL bFormatter := {|name, age| ;
"Name: " + name + ", Age: " + Str(age)}

? Eval(bFormatter, "John", 25) // Name: John, Age:


25

RETURN

62 / 70
Variable Scoping
Harbour provides four levels of variable scope:

63 / 70
// Global variables (visible everywhere)
PUBLIC g_cAppName := "My Application"
PUBLIC g_nVersion := 1.0

PROCEDURE Main()
// Local variables (function/procedure scope)
LOCAL cUser := "Administrator"
LOCAL nCount := 0

// Private variables (dynamic scope)


PRIVATE nLevel := 1

? "Starting application:", g_cAppName

TestFunction()

RETURN

FUNCTION TestFunction()
// Static variables (persistent between calls)
STATIC nCallCount := 0

nCallCount++

// Can access private variable from calling function


? "Level:", nLevel // 1

// Can access public variables


? "App:", g_cAppName // My Application

// Cannot access local variables from calling function


// ? cUser // This would cause an error

? "Call count:", nCallCount

64 / 70
RETURN nCallCount

Scope Rules:
- LOCAL: Visible only within current function/procedure
- STATIC: Persistent between function calls, visible within function
- PRIVATE: Dynamic scope, visible to called functions
- PUBLIC: Global scope, visible everywhere

Variable Naming Conventions


Hungarian Notation (Recommended):

65 / 70
// Character (string) variables
LOCAL cName := "John"
LOCAL cFileName := "[Link]"

// Numeric variables
LOCAL nCount := 10
LOCAL nAmount := 1500.50

// Date variables
LOCAL dToday := Date()
LOCAL dBirth := CToD("01/01/1990")

// Logical variables
LOCAL lActive := .T.
LOCAL lFound := .F.

// Array variables
LOCAL aItems := {}
LOCAL aCustomers := {}

// Object variables
LOCAL oCustomer := Customer():New()
LOCAL oWindow := Window():New()

// Hash variables
LOCAL hConfig := {=>}
LOCAL hLookup := {=>}

// Codeblock variables
LOCAL bFilter := {|x| x > 0}
LOCAL bSort := {|a, b| a < b}

// Pointer variables
LOCAL pFunction := @MyFunction()

66 / 70
// Mixed/unknown type
LOCAL xValue := NIL
LOCAL xResult

Type Checking and Conversion

PROCEDURE Main()
LOCAL xValue := "123"

// Type checking
? "Type:", ValType(xValue) // C (Character)
? "Is string:", ValType(xValue) == "C" // .T.
? "Is numeric:", ValType(xValue) == "N" // .F.

// Type conversion
LOCAL nNumber := Val(xValue) // String to number
? "Converted number:", nNumber // 123

LOCAL cString := Str(nNumber) // Number to string


? "Back to string:", cString // "123"

LOCAL dDate := CToD("12/25/2023") // String to date


? "Date:", dDate

LOCAL cDateStr := DToC(dDate) // Date to string


? "Date string:", cDateStr // "12/25/23"

// Safe type checking


IF ValType(xValue) $ "CM" // Character or Memo
? "Text value:", xValue
ENDIF

RETURN

67 / 70
Understanding Harbour's data types is fundamental to effective programming. Like
understanding the different types of signals from a lighthouse, mastering these data
types will help you communicate clearly and effectively in your code, building
applications that are both robust and maintainable.

[The book continues with the remaining 25 chapters covering all aspects of Harbour
programming...]

Quick Reference

Data Type Summary

Type Prefix Example Description

NIL x xValue := NIL Absence of value

Logical l lActive := .T. Boolean values

Numeric n nCount := 42 Integer/float numbers

String c cName := "John" Character sequences

Date d dToday := Date() Date values

Array a aItems := {1,2,3} Collections

Object o oCustomer := Obj():New() Class instances

Hash h hData := {=>} Key-value pairs

CodeBlock b bCode := {|x| x*2} Executable code

Pointer p pFunc := @Function() Function references

68 / 70
Essential Functions

// Type checking
ValType(value) // Returns type letter
Empty(value) // Checks for empty/NIL
Len(value) // Length of strings/arrays/hashes

// String functions
Upper(string) // Convert to uppercase
Lower(string) // Convert to lowercase
AllTrim(string) // Remove leading/trailing spaces
SubStr(string, start, length) // Extract substring
At(search, string) // Find position of substring

// Array functions
AAdd(array, item) // Add element
ADel(array, position) // Delete element
ASize(array, newsize) // Resize array
AScan(array, value) // Find element

// Date functions
Date() // Current date
Year(date) // Extract year
Month(date) // Extract month
Day(date) // Extract day

// Conversion functions
Val(string) // String to number
Str(number) // Number to string
CToD(string) // String to date
DToC(date) // Date to string

This comprehensive foundation in Harbour's data types and variables provides the
essential knowledge needed to build robust applications. In the next chapter, we'll

69 / 70
explore operators and expressions that work with these data types to create
powerful programming logic.

70 / 70

You might also like