Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

(Ebook - Pdf) Kick Ass Delphi Programming

.pdf
Скачиваний:
284
Добавлен:
17.08.2013
Размер:
5.02 Mб
Скачать

double-click on the “Console Application” icon. Delphi will prompt you for a directory and create a new project that has the options set for the console application.

NOTE:

I haven’t quite decided if it’s a good idea to store your own objects in Delphi’s Object Repository directory. Whereas it’s a handy place to put things, it’s also asking for trouble if you ever upgrade your version of Delphi. If you upgrade, it’s quite likely that Delphi’s Objrepos directory will be deleted—along with all of your cool objects. You’ll have to back up your objects before you upgrade.

You can, if you’d rather, create your own Repository directory that’s not a child of Delphi’s main directory. Either way, you’ll have to again add the projects to Delphi’s repository after you upgrade, but with a separate directory, you won’t run the risk of inadvertantly deleting your projects’ source code.

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Console Input and Output

When a console application is started, the Input and Output standard text files are automatically associated with the console window. As a result, the ReadLn and WriteLn procedures work as expected, as do Eof, Eoln, Read, Write, and the other Text file I/O functions.

There are a number of console-specific I/O functions that can come in handy from time to time. Unfortunately, these console-specific functions are defined in the Windows console interface and there’s no convenient Delphi component wrapper to insulate us from the gory details. (Now there’s a good shareware project for an enterprising programmer: a Delphi class that encapsulates the Windows console interface.) The Windows console interface is a chapter in itself, so I’m going to conveniently ignore it here. If you’re interested in learning more about PeekConsoleInput, WriteConsole, and the rest of the console API functions, check out the “Console Functions” entry in Delphi’s online help.

Because we don’t have space here to discuss the Console API, we’re going to limit our console input and output to the standard Text file I/O routines. Don’t take this the wrong way. The Console API functions are useful for some applications—just not for the kinds of applications that you’ll typically write as console apps. Yeah, I know, it’s confusing. It turns out that the Console API is much more useful for GUI programs that want to control a console window than it is for straight console applications controlling themselves.

Console apps aren’t limited to a boring old text mode interface. Since you’ve got access to the full Windows API, you can display message and dialog boxes, control other windows, and even create another console from within your application.

Filter Programs in Delphi

Now that we know how to create a console application, let’s put that knowledge to use. The rest of this chapter is concerned with writing filter programs as console apps. After an overview of how filter programs work, we’re going to discuss processing the command line and efficient file operations. We’ll be cutting a wide swath through Delphi’s standard run-time library, and won’t have time to discuss every function in detail. Remember that online help is your friend—use it early and often.

Your Basic Filter Program

As I mentioned at the beginning of this chapter, filter programs typically accept a command line that specifies options and input/output filenames, crunch the input as specified by the options, and produce the output file.

Given that general description, there’s lots of room for improvisation. A line counter program, for example, could accept multiple input file names (including wildcards), and could have options that tell it to report not only the number of text lines in a file, but also the number of words and characters, and possibly a frequency distribution of words and characters. In a more involved program, the output can be a simple transformation of a single input file, a single file that combines multiple input files, or many different files created from a single input file.

Despite the differences in complexity, filters share a large amount of common functionality. They all process the command line, read input files, and write output files. Only the intermediate processing step changes significantly from one program to another. Because of this commonality, it’s possible to build a group of functions that provide the common functionality and allows you to quickly build a custom filter program by simply defining how the command line is to be parsed and writing the code for the “processing” step. The input, output, and command line parsing portions are all there. Kind of a dehydrated filter program—just add processing.

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

Go!

Keyword

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

Processing the Command Line

Command line processing sounds so simple. Given a text string that represents the command line, we want to parse out the file names and options, and set the program’s variables accordingly. I’m continually amazed at how difficult such a simple-sounding thing can be. Fortunately, Object Pascal has two standard functions, ParamCount and ParamStr, that make things a bit easier.

ParamCount simply returns a count of the parameters on the command line. So if your command line is “MyFilter file1.txt file2.txt”, ParamCount will return 2. The program name itself isn’t counted as a parameter by this function.

ParamStr accepts an integer and returns a string that contains the command line argument that corresponds to that integer. For example, given the above command line, this statement

WriteLn (ParamStr (1));

will output ‘file1.txt’ (without the quotes).

If you pass 0 to ParamStr, the returned string will contain the full path and file name of the program that’s currently being executed.

The Params program shown in Listing 1.2 illustrates the use of ParamCount and ParamStr. To create this program, select File|New from Delphi’s main menu, select the Console Application item from the Projects page of the New Items dialog box, and then tell Delphi where to put your new application. Be sure to save the project as Params.dpr before you modify it.

Listing 1.2 The Params program

{$APPTYPE CONSOLE}

{

Params -- a simple exploration of the ParamCount and ParamStr functions.

}

program params;

uses Windows; Var

i : Integer;

begin

WriteLn ('Program: ', ParamStr (0)); WriteLn ('ParamCount = ', ParamCount); WriteLn ('Parameters');

WriteLn ('----------');

for i := 1 to ParamCount do begin

WriteLn (ParamStr (i)); end;

Write ('Press Enter...'); ReadLn;

end.

If you want to test the program from within Delphi, you need to select Run|Parameters from Delphi’s main menu and enter the command line that you want to have passed to the program. For the above example, you’d enter the string “file1.txt file2.txt” (without the quotes) in the Run parameters dialog box.

Simple, no? Unfortunately, not so simple. Back in the days of DOS and Windows 3.1, things really were simple. But then along came long file names with embedded spaces. Now we have a problem. You see, ParamCount and ParamStr assume that command line arguments are separated by spaces. This works fine as long as your files don’t have embedded spaces, but try this command line:

params c:\program files\borland\delphi 2.0\readme.txt

ParamCount returns 3, and the individual parameters it reports are

c:\program

files\borland\delphi

2.0\readme.txt

which is clearly not what we intended! (Okay, so maybe long file names aren’t all peaches and cream—beer and skittles if you’re British. Warm beer.)

I won’t go into all the possible solutions to this problem. If you want a full discussion of this problem and the possible solutions (none of which are satisfactory, by the way—thank you Microsoft), get a copy of Lou Grinzo’s Zen of Windows 95 Programming, also published by Coriolis Group Books. The book is ostensibly about C and C++ programming for Windows 95, but there’s a wealth

of good information in there for all programmers, especially in the way of writing bug-free programs. This book is among the top three programming books I’ve ever read, along with Writing Solid Code and Debugging the Development Process, both written by Steve Maguire and published by Microsoft Press.

The only workable (although not satisfactory) solution to the embedded spaces problem is to require that file names containing embedded spaces be surrounded by quotation marks. So our sample command line becomes:

params "c:\program files\borland\delphi 2.0\readme.txt"

I guess you could require that your users always pass the short version of the file name, but I suspect they’d be more upset having to enter this

params c:\progra<1\borland\delphi<1.0\readme.txt

than they would be about the quotation marks.

Command Line Options

Most (definitely not all) command line programs get their parameters via the command line. Sometimes you’ll see programs that receive their parameters from environment variables or configuration files, and some are hybrids that can accept parameters from the command line or from a configuration file whose name is specified on the command line. Since we don’t want to get too bogged down in parameter processing, we’re going to ignore the configuration files and environment variables, and concentrate solely on command line parameters.

You’ve probably used a command line tool (like DIR) that accepts options prefaced by a slash (/). For example, if you want a listing of files in the current directory and all of its subdirectories, you’d enter the command: DIR /S. Many programs also accept command line parameters prefaced by the dash (or minus sign, -). Both are common, and many programs will accept either.

File names, on the other hand, are specified in many different ways, depending on the tool. COPY, for example, lets you specify the name of the input and output files without prefacing them with option characters. So COPY FILE1 FILE2 copies FILE1 to FILE2. Borland’s MAKE program, on the other hand, requires that you preface the input file name with the -f parameter. So, to process BUILD.MAK, you’d enter this command: MAKE -fbuild.mak.

The way MAKE processes command lines is easier, because everything is an option. Every option is separated from the others by at lease one space, and file names are handled just like other options—there aren’t any special cases. This is the model we’re going to use for our filter programs.

In general, there are four kinds of command line options: switches, numbers, strings, and file names. Switches simply turn an option on or off. A text filter, for example, might have a switch option to convert all lower-case characters to upper-case. Numbers can be integers or floating point, and can be specified in any number of ways: decimal and hexadecimal being the most common. Strings and file names are similar, although file names are often validated to ensure that they’re properly formed.

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Go!

Keyword

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin

ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Go!

-----------

A Reusable Command Line Parser

If there’s anything I dislike about programming, it’s slogging through dozens (or hundreds) of lines of code to do something for the tenth (or hundredth) time. Command line parsing is like that: Every filter program has to process the command line, and command line parsing is boring code after you’ve written it once or twice. So I keep trying to come up with a generalized command line parser that will, with a minimum of effort on my part, parse a command line and fill in my program’s options structure. That way, I’ll have more time to spend on the filter (the real problem, after all) rather than on the command line parser.

A generalized command line parser is not an easy piece of code to write, and even a minimal one can be a bit involved. The one we’ll develop here is minimal, but functional for many applications.

The basic idea is to define the valid option characters, the type of each option, and the default value for each option. The structure that contains this information is passed to the command line parser, which chews up the command line and fills in the values for the individual options that it finds. If it finds an error (an invalid option or a number where it expected a switch), it spits out an error message, stops processing, and returns an error status to the calling function. Simple, right? Ahhh...but not so easy.

An individual options record takes the format of the OptionsRec structure shown in Listing 1.3. This listing contains the full source of the CmdLine unit. You should create a new file in your editor, and enter and save this code as CMDLINE.PAS.

Listing 1.3 The CmdLine unit

{

CMDLINE.PAS -- Command line parameters parsing

}

unit cmdline;

interface

type

OptionType = (otBool, otInt, otString, otFilename);

pOptionRec = ^OptionRec; OptionRec = record

OptionChar : char;

case Option : OptionType of otBool : (OnOff : Boolean); otInt : (Value : Integer);

otString : (Param : ShortString);

otFilename : (Filename : ShortString);

end;

pOptionsArray = ^OptionsArray; OptionsArray = Array [1..1] of OptionRec;

{

GetOptionRec -- return a pointer to the options record in the passed Options array that corresponds to the specified option character. Returns Nil if the option character is not in the passed Options array.

}

function GetOptionRec

(

Options : pOptionsArray; nOptions : Integer; OptionChar : char

) : pOptionRec;

{

ProcessCommandLine -- process the command line according to the parameters list passed in the Options array. Returns True if successful, or False if an error occurred in processing.

}

function ProcessCommandLine

(

Options : pOptionsArray; nOptions : Integer

) : Boolean;

implementation

uses SysUtils;

{

GetOptionRec -- return a pointer to the options record in the passed Options array that corresponds to the specified option character. Returns Nil if the option character is not in the passed Options array.

}

function GetOptionRec

(

Options : pOptionsArray; nOptions : Integer; OptionChar : char

) : pOptionRec;

var

i : Integer; begin

Result := Nil;

for i := 1 to nOptions do begin

if (Options^[i].OptionChar = OptionChar) then begin Result := @Options^[i].OptionChar;

Break;

end;

end;

end;

{

ProcessBool

Extract the on/off state for a parameter. If the passed Param is a blank string, it is assumed to be On (+). Otherwise the routine expects the string to start with + or -, and sets the OnOff variable accordingly.

}

function ProcessBool

(

Param : String; var OnOff : Boolean

) : Boolean;

begin

Result := True;

if (Length (Param) = 0) then begin OnOff := True;

Exit;

end;

case Param[1]

of

'+' : OnOff

:= True;

'-' : OnOff

:= False;

else begin

WriteLn ('Error: + or - expected'); Result := False;

end;

end;

end;

{

ProcessInt

Extract an integer from the passed command line parameter.

}

function ProcessInt

(

Param : String; var Value : Integer

) : Boolean;

begin

if (Length (Param) = 0) then begin Result := False;

WriteLn ('Error: integer expected'); Exit;

end;

Result := True; try

Value := StrToInt (Param); except

WriteLn ('Error: integer expected'); Result := False;

end;

end;

{