IF

IF

Execute one of several alternative blocks of statements

Syntax

       IF <lCondition1>
          <statements>...
       [ELSEIF <lCondition2>]
          <statements>...
       [ELSE]
          <statements>...
       END[IF]

Arguments

<lCondition> is a logical control expression. If it evaluates to true (.T.), all following statements are executed until an ELSEIF, ELSE, or ENDIF is encountered.

ELSEIF <lCondition> identifies statements to execute if the associated condition evaluates to true (.T.) and all preceding IF or ELSEIF conditions evaluate to false (.F.). Any number of ELSEIF statements can be specified within the same IF…ENDIF control structure.

ELSE identifies statements to execute if the IF and all preceding ELSEIF conditions evaluate to false (.F.).

Description

The IF control structure works by branching execution to statements following the first true (.T.) evaluation of the IF or any ELSEIF condition. Execution then continues until the next ELSEIF, ELSE, or ENDIF is encountered whereupon execution branches to the first statement following the ENDIF.

If no condition evaluates to true (.T.), control passes to the first statement following the ELSE statement. If an ELSE statement is not specified, control branches to the first statement following the ENDIF statement.

IF…ENDIF structures may be nested within IF…ENDIF structures and other control structure commands. These structures, however, must be nested properly. The IF…ELSEIF…ENDIF form of the IF construct is identical to DO CASE…ENDCASE. There is no specific advantage of one syntax over the other.

The IF construct is also similar to the IF() function which can be used within expressions.

Examples

       .  This example evaluates a number of conditions using an
       IF...ELSEIF...ENDIF construct:
       LOCAL nNumber := 0
       //
       IF nNumber < 50
          ? "Less than 50"
       ELSEIF nNumber = 50
          ? "Is equal to 50"
       ELSE
          ? "Greater than 50"
       ENDIF

Seealso

BEGIN SEQUENCE, DO CASE, DO WHILE, FOR, IF()

FUNCTION

FUNCTION

Declare a user-defined function name and formal parameters

Syntax

       [STATIC] FUNCTION <idFunction>[(<idParam list>)]
              [LOCAL <identifier> [[:= <initializer>], ... ]]
              [STATIC <identifier> [[:= <initializer>], ... ]]
              [FIELD <identifier list> [IN <idAlias>]]
              [MEMVAR <identifier list>]
              .
              . <executable statements>
              .
              RETURN <exp>

Arguments

<idFunction> is the name of the user-defined function to be declared. User-defined function names can be any length, but only the first 10 characters are significant. Names can contain any combination of characters, numbers, or underscores, but must begin with a character or an underscore. Leading underscores are not recommended since they are reserved for internal functions.

<idParam list> is the declaration of one or more parameter variables. Variables specified in this list are declared local.

STATIC FUNCTION declares a user-defined function that can be invoked only by procedures and user-defined functions declared in the same program (.prg) file.

LOCAL declares and optionally initializes a list of variables or arrays whose visibility and lifetime is the current function.

STATIC declares and optionally initializes a list of variables or arrays whose visibility is the current user-defined function and lifetime is the duration of the program.

FIELD declares a list of identifiers to use as field names whenever encountered. If the IN clause is specified, referring to the declared name includes an implicit reference to the specified alias.

MEMVAR declares a list of identifiers to use as private or public memory variables or arrays whenever encountered.

<identifier> and <identifier list> are labels to be used as variable or array names.

<initializer> is a value to which an array or variable is originally set in an inline expression. RETURN <exp> passes control back to the calling procedure or user-defined function, returning the result of <exp> as the value of the function. Each function must have at least one

RETURN statement that returns a value. RETURN statements can occur anywhere in the body of a function.

Description

The FUNCTION statement declares a user-defined function and an optional list of local variables to receive parameters often referred to as formal parameters. A user-defined function is a subprogram comprised of a set of declarations and statements executed whenever you refer to <idFunction> followed by an open and closed parentheses pair. A function definition begins with a FUNCTION statement which is the FUNCTION declaration and ends with the next FUNCTION statement, PROCEDURE statement, or end of file.

Functions encapsulate a computational block of code and then later create expressions using the value returned. Functions and procedures increase both readability and modularity, isolate change, and help manage complexity.

A function in Harbour is the same as a procedure, except that it must return a value. The returned value can be any data type including an array, a code block, or NIL. Each function must begin with a FUNCTION statement and contain at least one RETURN statement with an argument. Function declarations cannot be nested within other function definitions. A user-defined function can be used wherever standard functions are supported, including expressions.

The visibility of function names falls into two classes. Functions that are visible anywhere in a program are referred to as public functions and declared with a FUNCTION statement. Functions that are visible only within the current program (.prg) file are referred to as static functions and declared with a STATIC FUNCTION statement.

Static functions have filewide scope. Static functions limit visibility of a function name, thereby restricting access to the function. Because of this, subsystems defined within a single program (.prg) file can provide an access protocol with a series of public functions and conceal the implementation details of the subsystem within static functions and procedures. Since the static function references are resolved at compile time, they preempt references to public functions which are resolved at link time. This ensures that within a program file, a reference to a static function executes that function if there is a name conflict with a public function.

For more information on user-defined functions, variable declarations, and parameter passing, refer to the “Basic Concepts” chapter.

Notes

. Calling a user-defined function: Use the same notation to call a user-defined function as when calling a standard Harbour function: <idFunction>([<argument list>])

You can call a user-defined function within an expression or on a line by itself. If called on a line by itself, the return value is ignored.

You can also call a user-defined function as an aliased expression by prefacing it with an alias and enclosing it in parentheses: <idAlias>->(<idFunction>(<argument list>))

When you call a user-defined function as an aliased expression, the work area associated with <idAlias> is selected, the expression is executed, and the original work area is then reselected. You can specify an aliased expression on a line by itself, as you would any other expression.

A user-defined function in Harbour may call itself recursively. This means you can make a reference to a user-defined function in statements or expressions of the same user-defined function definition.

. Parameters: User-defined functions, like procedures, can receive parameters passed from a calling procedure, user-defined function, or DOS command line. A parameter is a place holder for a value or reference. In Harbour, there are two ways to express parameters: you can declare a list of local variable names as a part of the FUNCTION declaration (referred to as formal parameters), or you can specify a list of private variables in a separate PARAMETERS statement. Note that you cannot mix a declaration of formal parameters with a PARAMETERS statement. Attempting this will result in a fatal compiler error.

Functions receive parameters in the order passed. In Harbour, the number of parameters does not have to match the number of arguments passed. You can skip arguments or omit them from the end of the argument list. A parameter not receiving a value or reference is initialized to NIL. You can skip a parameter by passing NIL. If arguments are specified, PCOUNT() returns the position of the last argument passed. (If more arguments are passed than are parameters, they are ignored.)

Parameters specified in a user-defined function can receive arguments passed by value or reference. The default method for expressions and variables is by value. This includes variables that contain references to arrays and objects. All variables except field variables, when prefaced with the pass-by-reference operator (@), are passed by reference. Field variables cannot be passed by reference and are always passed by value.

Examples

       .  This example demonstrates a user-defined function that formats
          numeric values as currency:
       ? Currency( 1000 )               // Result: Þ1,000.00
       FUNCTION Currency( nNumber )
          LOCAL cNumber
          IF nNumber < 0
             cNumber := TRANSFORM(-1 * nNumber, ;
                   "999,999,999,999.99")
             cNumber := PADL("(Þ" + LTRIM(cNumber) + ")", ;
                   LEN(cNumber))
          ELSE
             cNumber := TRANSFORM(nNumber, ;
                   "999,999,999,999.99")
             cNumber := PADL("Þ" + LTRIM(cNumber), ;
                   LEN(cNumber))
          ENDIF
          RETURN cNumber
       . This example demonstrates a user-defined function that takes a
          character string formatted as a comma-separated list and returns 
          an array with one element per item:
       aList := ListAsArray("One, Two")
       // Result: {"One", "Two"}
       FUNCTION ListAsArray( cList )
          LOCAL nPos
          // Define an empty array
          LOCAL aList := {}
          //
          DO WHILE (nPos := AT(",", cList)) != 0
             // Add a new element
             AADD(aList, SUBSTR(cList, 1, nPos - 1))
             cList := SUBSTR(cList, nPos + 1)
          ENDDO
          AADD(aList, cList)
          //
          // Return the array
          RETURN aList
       .  This example checks for a skipped argument by comparing
          the parameter to NIL:
       FUNCTION MyFunc( param1, param2, param3 )
          IF param2 == NIL
             param2 := "default value"
          ENDIF
          .
          . <statements>
          .
          RETURN NIL
       .  This example uses the user-defined function, Currency()
          (defined above), as an aliased expression:
       USE Invoices NEW
       USE Customer NEW
       ? Invoices->(Currency(Amount))

Seealso

LOCAL, PARAMETERS, PCOUNT(), PRIVATE, PROCEDURE, RETURN

FOR EACH

FOR EACH

Executes a block of statements for a data type containing multiple elements.

Syntax

      FOR EACH var1 [,var255] IN expr1 [,expr255] [DESCEND]
          [EXIT]
          [LOOP]
          ...
      NEXT

Arguments

– expr can be a string or an array – enumerator variable ‘var<n>’ stores a reference to the element of an array or a string specified by ‘expr<n>’ thus assigments to the enumerator changes the value of given array element

Description

FOR EACH is a support to enumeration / iteration loop slightly different than FOR…NEXT loop. This is a control structure that executes a block of statements for a data type containing multiple elements ( a string or an array ).

Note:

– after the loop the controlling variable(s) store the value which they had before entering the loop – the enumerator variable supports the following properties :

:__enumindex – the loop counter for variable :__enumbase – the value that is being traversed

:__enumvalue – the value of variable

Example

       a := 'A'
       b := 'B'
       FOR EACH a, b IN { 1, 2, 3, 4 }, "abcd"
          ? a, b   //prints: 1 a
                   //        2 b
                   //        3 c
                   //        4 d
       NEXT
       ? a, b   //prints: A B
       // you can use EXIT statement inside the loop
       FOR EACH a IN { 1, 2, 3, 4 }
          IF a:__enumindex == 3
             ? a
             EXIT
          ENDIF
       NEXT
       arr := { 1, 2, 3 }
       str := "abc"
       FOR EACH a, b IN arr, str
          a *= 2
          str := Upper( str )
       NEXT
       // now 'arr' stores { 2, 4, 6 }
       // howerer 'str' still stores "abc"

Notice the difference:

       FOR EACH a IN someValue 
           ? a:__enumindex   // prints current value of the index 
           ? (a):__enumindex // sends '__enumindex' message to the current value 
       NEXT

FOR…NEXT

FOR…NEXT

Execute a block of statements a specified number of times

Syntax

       FOR <idCounter> := <nStart> TO <nEnd> [STEP <nIncrement>]
           <statements>...
           [EXIT]
           <statements>...
           [LOOP]
       NEXT

Arguments

<idCounter> is the name of the loop control or counter variable. If the specified <idCounter> is not visible or does not exist, a private variable is created.

<nStart> is the initial value assigned to <idCounter>. If <nIncrement> is negative, <nStart> must be less than <nEnd>.

TO <nEnd> defines the final value of <idCounter>. If <nIncrement> is negative, <nStart> must be greater than <nEnd>; otherwise, <nStart> must be less than <nEnd>.

STEP <nIncrement> defines the amount <idCounter> is changed for each iteration of the loop. <nIncrement> can be either positive or negative. If the STEP clause is not specified, <idCounter> is incremented by one for each iteration of the loop.

EXIT unconditionally branches control from within a FOR…NEXT construct to the statement immediately following the nearest NEXT statement.

LOOP branches control to the most recently executed FOR or DO WHILE statement.

Description

FOR…NEXT is a control structure that executes a block of statements a specified number of times. The control structure loops from the initial value of <idCounter> to the boundary specified by <nEnd>, moving through the range of values of the control variable for an increment specified by <nIncrement>. All expressions in the FOR statement are reevaluated for each iteration of the loop. The <nStart> and <nEnd> expressions, therefore, can be changed as the control structure operates.

A FOR loop operates until <idCounter> is greater than <nEnd> or an EXIT statement is encountered. Control then branches to the statement following the corresponding NEXT statement. If a LOOP statement is encountered, control branches back to the current FOR statement.

If <nIncrement> is a negative value, <idCounter> is decremented rather than incremented. The FOR loop, however, continues until <idCounter> is less than <nEnd>. This means that <nEnd> must be less than <nStart> when the FOR loop begins.

FOR loops are useful for traversing arrays where <idCounter> is used as the array subscript. See the example below.

FOR…NEXT constructs may be nested within any other control structures to any depth. The only requirement is that each control structure is properly nested.

Examples

       .  This example traverses an array in ascending order:
       nLenArray := LEN(aArray)
       FOR i := 1 TO nLenArray
           <statements>...
       NEXT
       .  To traverse an array in descending order:
       nLenArray := LEN(aArray)
       FOR i := nLenArray TO 1 STEP -1
          <statements>...
       NEXT

Seealso

AEVAL(), BEGIN SEQUENCE, DO CASE, DO WHILE, IF, IF()

EXTERNAL

EXTERNAL*

Declare a list of procedure or user-defined function names to the linker

Syntax

       EXTERNAL <idProcedure list>

Arguments

<idProcedure list> is the list of procedures, user-defined functions, or format procedures to add to the list of routines that will be linked into the current executable (.EXE) file.

Description

EXTERNAL is a declaration statement that specifies uncoded references to the linker. Like all other declaration statements, an EXTERNAL statement must be specified before any executable statements in either the program file, or a procedure or user-defined function definition.

During the compilation of Harbour source code, all explicit references to procedures and user-defined functions are made to the linker. In some instances, there may be no references made to procedure or user-defined function names until runtime. EXTERNAL resolves this by forcing the named procedures or user-defined functions to be linked even if they are not explicitly referenced in the source file. This is important in several instances:

. Procedures, user-defined functions, or formats referenced with macro expressions or variables

. Procedures and user-defined functions used in REPORT and LABEL FORMs and not referenced in the source code . User-defined functions used in index keys and not referenced in the source code

. ACHOICE(), DBEDIT(), or MEMOEDIT() user functions

To group common EXTERNAL declarations together, place them in a header file and then include (#include) the header file into each program (.prg) file that might indirectly use them.

EXTERNAL is a compatibility statement and therefore not recommended. It is superseded by the REQUEST statement that defines a list of module identifiers to the linker.

Examples

       .  These examples are equivalent header files consisting of
          common EXTERNAL references for REPORT FORMs:
       // Externals.ch
       EXTERNAL HARDCR
       EXTERNAL TONE
       EXTERNAL MEMOTRAN
       EXTERNAL STRTRAN
       // Externals.ch
       EXTERNAL HARDCR, TONE, MEMOTRAN, STRTRAN

Seealso

#include, REQUEST

EXIT PROCEDURE

EXIT PROCEDURE

Declare an exit procedure

Syntax

       EXIT PROCEDURE <idProcedure>
              [FIELD <idField list> [IN <idAlias>]]
              [LOCAL <identifier> [[:= <initializer>]]]
              [MEMVAR <identifer list>]
              .
              . <executable statements>
              .
              [RETURN]

Arguments

EXIT PROCEDURE declares a procedure that will be executed on program termination.

<idProcedure> is the name of the exit procedure to declare. Exit procedure names can be any length, but only the first 10 characters are significant. Names may not begin with an underscore but can contain any combination of characters, numbers, or underscores.

FIELD declares a list of identifiers to use as field names whenever encountered. If the IN clause is specified, referring to the declared name includes an implicit reference to the specified alias.

LOCAL declares and optionally initializes a list of variables or arrays whose visibility and lifetime is the current procedure.

MEMVAR declares a list of identifiers to use as private or public memory variables or arrays whenever encountered.

RETURN passes control to the next exit procedure or to the operating system, if no other exit procedures are pending.

Description

The EXIT PROCEDURE statement declares a procedure that will be executed upon program termination. EXIT procedures are called after the last executable statement in application has completed. EXIT PROCEDUREs can be used to perform common housekeeping tasks such as saving configuration settings to a file, closing a log file, or concluding a communications session.

The visibility of exit procedures is restricted to the system; therefore, it is not possible to call an EXIT PROCEDURE from a procedure or user-defined function. Exit procedures do not receive parameters.

Once the last executable statement has completed, control passes from one EXIT PROCEDURE to the next until all procedures in the exit list have been called. Control then passes to the operating system.

The ANNOUNCE statement declares a module identifier for a source (.prg) file. Once declared, EXIT PROCEDUREs are referenced with this module identifier. An application may use any number of exit procedures by explicitly REQUESTing their module identifiers.

The EXIT PROCEDUREs requested for an application are collectively referred to as the exit list. There is no mandatory execution order of procedures in the exit list; however, if an EXIT PROCEDURE is declared in the same source (.prg) file as the application’s primary routine (root), it is guaranteed to be the first exit procedure called.

Termination of a given application can be attributed to any of the following:

       . RETURNing from the primary (root) routine 

       . the QUIT command 

       . issuing a BREAK without an enclosing BEGIN SEQUENCE...END 

       . unrecoverable error

    Execution of an EXIT PROCEDURE cannot be guaranteed when the system encounters an unrecoverable error. If         an error is raised during an exit procedure, the system returns to DOS. Pending exit procedures are not called.

Examples

       .  This example illustrates construction of a simple timing 
       mechanism using INIT and EXIT PROCEDUREs: 
       // prints the amount of time required to read, 
       // sort,  and display a list of file names. 
       ANNOUNCE MySystem 
       STATIC nStart 
       PROCEDURE Main()       AEVAL( ASORT( DIRECTORY( "*.*" ) ), ; 
               { | aFileInfo | QOUT( aFileInfo[ 1 ] ) } ) 
       RETURN 
       INIT PROCEDURE MyInit() 
          nStart := SECONDS() 
       RETURN 
       EXIT PROCEDURE MyExit() 
          ? 
          ? "Elapsed Time: " 
          ?? SECONDS() - nStart 
       RETURN

See Also

ANNOUNCE, REQUEST, INIT PROCEDURE

DO

DO*

Call a procedure

Syntax

      DO <idProcedure> [WITH <argument list>]

Arguments

<idProcedure> is the name of the procedure or user-defined function to be executed.

WITH <argument list> specifies up to 128 arguments, separated by commas, to pass to <idProcedure>. Each argument may be a single variable, field, array, array element, expression, or object. Arguments can be skipped or left off the end of the list.

Description

The DO statement calls a procedure or user-defined function, optionally passing arguments to the called routine. It performs the same action as a user-defined function or procedure specified on a line by itself with the exception that variables other than field variables are passed by reference as the default. In order to pass a field variable as an argument, enclose it in parentheses, unless you declare it with the FIELD statement or with an alias.

The number of specified arguments need not match the number of specified parameters in the called procedure. If the number of arguments is less than the number of parameters, the parameter variables with no corresponding arguments are initialized with a NIL value when the procedure is called. If the number of arguments is greater than the number of parameters, they are ignored.

Also, skipping an argument within the <argument list> by leaving an empty spot next to the comma initializes the corresponding argument to NIL. To detect the position of the last argument passed in the <argument list>, use PCOUNT(). To detect a skipped argument, compare the receiving parameter to NIL.

In addition to calling a procedure or user-defined function, DO also has an effect on compilation if you compile the current program file without the /M option. If the compiler encounters a DO statement and the specified procedure has not already been compiled, the compiler searches the current directory for a .prg file with the same name and compiles it. If the file with the same name as the procedure is not found, the called procedure is assumed to be external, and a reference is added to the object (.OBJ) file. At link time, the linker will search other object files and libraries for this external reference.

DO is a compatibility statement and therefore not recommended. Calling a procedure or function on a line by itself is the preferred method. Since this preferred calling convention normally passes parameters by value, you must preface an argument with the pass- by-reference operator (@) in order to pass by reference. If you are using DO to make a procedure call more readable, a user-defined command, specified with the #command directive, can provide greater readability without sacrificing the safety of variables passed as parameters. For more information on passing parameters refer to the Functions and Procedures section of the “Basic Concepts”.

Examples

       .  This example executes a procedure with no parameters:
          DO AcctsRpt
          AcctsRpt()                           // Preferred method
       .  This example executes a procedure passing two constants:
          DO QtrRpt WITH "2nd", "Sales Division"
          QtrRpt("2nd", "Sales Division")      // Preferred method
       .  In this example, a procedure is executed with the first
          argument passed by value and the second passed by reference:
          nNumber := 12
          DO YearRpt WITH nNumber + 12, nNumber
          YearRpt(nNumber + 12, @nNumber)      // Preferred method
       .  Here, a procedure is invoked with skipped arguments embedded
          in the list of arguments:
          DO DisplayWindow WITH ,,,,"My Window"
          DisplayWindow(,,,,"My Window")       // Preferred method

Seealso

FUNCTION, LOCAL, PARAMETERS, PRIVATE, PROCEDURE, PUBLIC

BEGIN SEQUENCE

BEGIN SEQUENCE

Define a sequence of statements for a BREAK

Syntax

      BEGIN SEQUENCE
         <statements>...
         [BREAK [<exp>]]
              <statements>...
         [RECOVER [USING <idVar>]]
              <statements>...
      END [SEQUENCE]

Arguments

BREAK <exp> branches execution to the statement immediately following the nearest RECOVER statement if one is specified or the nearest END SEQUENCE statement.

<exp> is the value returned into the <idVar> specified in the USING clause of the RECOVER statement.

RECOVER USING <idVar> defines a recover point in the SEQUENCE construct where control branches after a BREAK statement. If USING <idVar> clause is specified, <idVar> receives the value returned by the BREAK statement. In general, this is an error object.

END defines the end point of the SEQUENCE control structure. If no RECOVER statement is specified, control branches to the first statement following the END statement after a BREAK.

Description

BEGIN SEQUENCE…END is a control structure used for exception and runtime error handling. It delimits a block of statements, including invoked procedures and user-defined functions. When a BREAK is encountered anywhere in a block of statements following the BEGIN SEQUENCE statement up to the corresponding RECOVER statement, control branches to the program statement immediately following the RECOVER statement. If a RECOVER statement is not specified, control branches to the statement following the END statement, terminating the SEQUENCE. If control reaches a RECOVER statement without encountering a BREAK, it branches to the statement following the corresponding END.

The RECOVER statement optionally receives a parameter passed by a BREAK statement that is specified with a return value. This is usually an error object, generated and returned by the current error handling block defined by ERRORBLOCK(). If an error object is returned, it can be sent messages to query information about the error. With this information, a runtime error can be handled within the context of the operation rather than in the current runtime error handler. See the example below.

Within a SEQUENCE construct there are some restrictions on what statements are allowed between the BEGIN SEQUENCE and RECOVER statements. You cannot RETURN, LOOP, or EXIT between a BEGIN SEQUENCE and RECOVER statement. From within the RECOVER statement block, however, you can LOOP, EXIT, BREAK, or RETURN since the SEQUENCE is essentially completed at that point. Using LOOP from within the RECOVER statement block is useful for re-executing the SEQUENCE statement block. See the example below.

SEQUENCE constructs are quite flexible. They can be nested and more than one can be defined in the same procedure or user-defined function. If more than one SEQUENCE construct is specified, each SEQUENCE should delimit one discrete operation.

     For more information on error objects, refer to the Error class.

Examples

       .  This code fragment demonstrates a SEQUENCE construct in which
          the BREAK occurs within the current procedure:
          BEGIN SEQUENCE
             <statements>...
             IF lBreakCond
                BREAK
             ENDIF
          RECOVER
             <recovery statements>...
          END
          <recovery statements>...
       .  This example demonstrates an error handler returning an error
          object to the variable specified in the USING clause of the RECOVER
          statement:
          LOCAL objLocal, bLastHandler
          //
          // Save current and set new error handler
          bLastHandler := ERRORBLOCK({ |objErr| ;
                MyHandler(objErr, .T.) })
          //
          BEGIN SEQUENCE
             .
             . <operation that might fail>
             .
          RECOVER USING objLocal
             //
             // Send messages to objLocal and handle the error
             ? "Error: "
             IF objLocal:genCode != 0
                ?? objLocal:description
             ENDIF
             .
             .
             .
          END
          //
          // Restore previous error handler
          ERRORBLOCK( bLastHandler )
          FUNCTION MyHandler( objError, lLocalHandler )
             //
             // Handle locally returning the error object
             IF lLocalHandler
                BREAK objError
             ENDIF
             .
             . <other statements to handle the error>
             .
             RETURN NIL
       .  This example re-executes a SEQUENCE statement block by LOOPing
          from within the RECOVER statement block:
          DO WHILE .T.
             BEGIN SEQUENCE
                .
                . <operation that may fail>
                .
             RECOVER
                IF PrintRecover()
                   LOOP      // Repeat the SEQUENCE statement block
                ENDIF
             END
             EXIT            // Escape from the operation
          ENDDO

See also:

Error class, ERRORBLOCK(), RETURN

Directory and long path names

Hi,

> directory() and related function

DIRECTORY() in Harbour makes the same job as in Clipper. The only differences are platform dependent. Harbour supports wider set of attributes.

Wildcards are also platform dependent. In *nixes fnmatch() function is used internally with the following extensions: * – any number of characters

? – exactly one character. In DOS/OS2/WIN it accepts also “no character” is it’s at the end of filename part. [] – enclose group of characters, i.e. [abc] – characters “a”, “b” and “c” [a-z] – range of characters from “a” to “z” [-A-F0-9.] – characters: – A B C D E F 0 1 2 3 4 5 6 7 8 9 . please also remember that in *nixes file systems uses case sensitive names.

Mask which covers all files is “*”, function hb_osFileMask() returns mask for all files on given platform.

In DOS, OS2 and MS-Windows Mask which covers all files is “*.*” “*” is only for files without extension. In *nixes “*.*” means files which have “.” in the name. In MS-Windows (and maybe also DOS and OS2 with LFN support) there are some tricks which tries to emulate DOS behavior with fixed 8.3 format. I do not know exact tricks they implemented.

Some simple mapping like support for all files with mask like ???????.??? can be easy detected but if you made some more advances masks, i.e. with more then one dot then the behavior is really funny.

Anyhow in functions like directory() in DOS, OS2 and MS-Win file mask is interpreted by system not Harbour so the differences to expected behavior should be reported to MS not us. In these systems only one Harbour function: hb_FileMatch() makes own file mask comparison. Anyhow the differences are can appear only due to missing DOS hacks. In *nixes hb_FileMatch() uses internally fnmatch() so here OS evaluates passed file masks. BTW hb_DirScan() uses internally hb_FileMatch().

best regards,
Przemek