TweetFollow Us on Twitter

Writing ACGIs with MacApp

Volume Number: 14 (1998)
Issue Number: 3
Column Tag: Webtech

Writing ACGIs with MacApp

by Klaus Halfmann

It's easy and done fast using the simple class introduced in this article

Introduction

I recently was (and still am) working on a project involving a database to be used on the web. I had a short look at [Develop #29, March 1997] High Performance ACGIs in C by Ken Urquhart, but decided against it, for several reasons.

  • I don't like to leave the familiar environment of MacApp.
  • I want to use C++ not C.
  • It would have been more difficult to dig into new ground than just using MacApp.
  • Performance was not such an issue (and we will see that we still can have reasonable speed).

I thought it would take me about a week to implement the ACGI interface but found that after two days I was ready with a skeleton ACGI. Because MacApp needs more support I decided to publish this article. So, maybe some older MacApp Applications can be found on the web soon.

I use MacApp R12, since R13 was not in a state to be used at the time I started my project. I do not expect major changes in the idea of the implementation, but many details (e.g. streaming) will change. I do not know whether I will migrate our companies project to R13.

You should be familiar with the concept of cgis and ACGIs on the Macintosh in general and with AppleEvent handling in MacApp. If not, you can still use my code, but will have trouble using / modifying some parts of it.

As an example I will show how to build a simple Form where you have three fields of a formula a x b = c. The x can be chosen with a popup out of +, -, * and /.

Building the HTML-Form

Forms can be used with two Methods: Post and Get. The obvious difference is that the parameters are invisible.

Using Get you get the familiar url ".../myacgi.acgi ?operand1=17&operand2=28&operation=+&result=". Using Post the user can not see any arguments, but your ACGI gets them nonetheless. The post method allows larger argument sizes. Also, the arguments are contained in the keyPostArgs otherwise in the keyPathArgs.

My approach allows you to use both ways. Before you start a larger project you should decide which of both to use.

Using PathArgs your ACGI can be used by an url from everywhere, but the visible arguments may confuse the user and tend to become large. An other bad habit is to use passwords in pathArgs. The password is useless (imho) if the user can create a Bookmark containing it.

Using PostArgs your ACGI can only be used by forms. The user sometimes becomes confused because the url stays the same all the time, but the contents changes. The history also may look strange. Anyway, my approach does support both methods. Lets now look at the form: (The form found in the supplied archive is more complex of course.)

<FORM ACTION="myacgi.acgi" METHOD=Get>
  <INPUT NAME="operand1" SIZE=16> 
      <SELECT Name ="operation">
        <OPTION VALUE="+" SELECTED>+
        <OPTION VALUE="-">-
        <OPTION VALUE="*">*
        <OPTION VALUE="/">/
      </SELECT>
    <INPUT NAME="operand2" SIZE=16> </TD>
    <INPUT NAME="result" SIZE=16> </TD>
    <INPUT Name = "Calc" VALUE="Calc" TYPE=submit>
</FORM>

Figure 1. A simple HTML Form.

Build an ACGI with MacApp

MacApp has a class TAppleCommand that descends (surprise) from TCommand and has the two subclasses TServerCommand and TClient command. If you are not familiar with command handling in MacApp it's now time to read the Programmers Guide to MacApp.

In our case TServerCommand is our candidate, since the ACGI is a server for the webserver (which in this case is the client). See, it's easy, the server is the client and ... yes, you got it, fine.

I have written a class TACGICommand that descends from TServerCommand and has a skeleton of routines needed for an ACGI, for parameter parsing and the like. So we go and create a Subclass of TACGICommand: TMyACGICommand. The most important method (as in every other TCommand) is the DoIt() method. You must override it to get your work done.

As a framework I took the Skeleton example out of the examples supplied with MacApp. The archive does contain the complete skeleton code since I had to modify some parts of the code.

MacApp uses a resource based mechanism to dispatch apple events. The resource is the 'aedt' (AppleEventDispatchTable). In order to enhance this table we need another command number first:

  #define cACGICommand  404

We can now define our own aedt resource and can use any number for it since MacApp locates all tables automagically and builds a complete table. (Well numbers below 404 are used by MacApp)

resource 'aedt' (404) 
{  
  { 'WWWQ',         'sdoc',         cACGICommand; }
};

Now we need an object which cares about the command. This is most naturally the application. In order to create our TMyACGICommand we will have to override

TApplication::DoScriptCommand();

(With MacApp R12 this is actually TDispatcherDoScriptCommand() but that is an other story.)

Here is the interesting part of DoScriptCommand:

...
  switch (aCommandNumber)
  { 
    case cACGICommand:
      PostCommand(new TMyACGICommand(this, message, reply);
      break;
    default:
  Inherited::DoScriptCommand(aCommandNumber, message, reply);
...

Some elder MacApp Programmer may wonder what happened to IMyACGICommand. Well, it simply does not exist, since MacApp R13 will eliminate IMethods anyway. I stopped using and implementing them in all my current projects.

The last thing to be done is to introduce the new files to the makefile. If you are using the Metrowerks IDE, add them to the project. I use MPW, and am satisfied doing so.

Doing the Real Work

First lets have a look at our Constructor:

TMyACGICommand::TMyACGICommand(
  TCommandHandler* itsContext,
  TAppleEvent* message, 
  TAppleEvent* reply) :
    TACGICommand(itsContext, message, reply)
{
}

There is nothing special here. As a first approach we will create an empty ::DoIt() method. Now our program should compile and run.

Setting Up The Environment

Meanwhile we should look at our related programs. We need a webserver that supports ACGIs. I use Quid Pro Quo 1.0 (I know there is a newer version out there), but any other Macintosh Web Server should do.

During the development process a special setup is needed. In my archive I have included an alias to my webserver. This should remind you to replace it with an alias to your webserver. On the other side (in your webservers root folder) create an alias to your project folder and in your project folder create an alias to your program named "myacgi.acgi". The webserver will not recognize an ACGI until its extension is ".ACGI".

Figure 2. Setting up the aliases.

Your final setup may be different. As we will later see you will need additional helper or template files which have to be stored (as of this implementation) besides the ACGI. But you may wish to avoid to make them public. So you might use aliases in a final setup, as well.

A browser is needed, too. Keep in mind that people with other browsers and even other operating systems (you know those windows people) look at your site. So make sure that your forms look neat on different browsers.

The ACGI should be ready now, so lets set a breakpoint at the ::DoIt() Method. Look at your url http://yourmac.yourcompany.yourdomain/myacgi/multiply.html and click at the "Calc" button. As we expect we hit our breakpoint, smile happily, and continue our program.

You may find that you did not hit your breakpoint, if so check the following areas:

  • Look with ResEdit if the aedt resource is really there.
  • Set a breakpoint at TDispatcher::DoScriptCommand, maybe your override did not work.

After hitting the breakpoint and telling the application to continue, the browser shouts at us "Document contains no data". Indeed we did nothing to give him any data.

Filling the Empty Method

The building of the ACGI should have taken us about half an hour (if you are familiar with MacApp). Now lets fill our DoIt() method with something reasonable. First we should parse our arguments. The TACGICommands already has an universal weapon for parsing these nasty lines so we call:

  ParseArgs(fArgs, keySearchArgs);  
  // may use keyPostArgs in some other case

fArgs is a Member Variable we have inherited from TACGICommand. It is of type TAssociation. TAssociation is one of the not so well known, all-round classes used internally by MacApp. For example it's used in MacApp MPW-Tools or for the MAParamText/MAReplaceText mechanism. In our case TAssociation is our Swiss army knife to cut our problem.

After the call to ParseArgs fArgs is filled with name / value pairs which can easily be retrieved. If you examine the routine ParseArgs you will find that it in turn calls InsertArg. This method can be overridden, so that your ACGI can intercept some variables.

void TACGICommand::InsertArg(
  TAssociation& argList, 
  TStream* htmlStream, 
  const CPascalStr& argName)

The default implementation parses the stream (the AppleEvent arguments have mutated into a stream) up to the next & (ampersand) and inserts the name / value pair into the argList. You may, for larger data, call ExtractHandle() to extract larger parameters which do not fit into an 255 byte Pascal string.

     Handle  ExtractHandle(TStream* htmlStream);
    // Helper for InsertArg, extract Handle from Stream up to the next & 

Well now that we have the parsing done, lets extract our 3 parameters and the button:

  // Get our operands and such
  CStr255 oper1, oper2, oper, result, message;
  
  if (fArgs.EntryWithKey("\pCalc")   &&  // Did the user press "Calc" ?
    fArgs.ValueAt("\poperand1",oper1) && // Look if we have all
    fArgs.ValueAt("\poperation",oper) && // our fields
    ...

I use EntryWithKey() just to check if the user really pressed Calc, this makes sense as soon as there is more than one button. ValueAt() extracts the parameter out of fArgs and returns if the name was really there. The code after the if statement does the real work and I will skip it here. We create a result and put it back into our AppleEvent reply

CStr255 msg(oper1 + ' ' + oper + ' ' 
            + oper2 + " = " + result);
fReply->PutKeyString(keyDirectObject,msg);

Now lets compile and test it. Maybe there are some pitfalls we have not seen yet.

I made the following mistake: I used KeyAt instead of ValueAt, which works just the other way round but was not what I expected. If you find that you have no arguments at all maybe you should verify that you have got the right mix of Post / Get and keyPostArgs / keySearchArgs.

Figure 3. Result of our first approach.

Output via Template Files

Our ACGI works fine now, but you will not be able to sell this as a final solution since the result page is almost empty, there are among others no back-links. So what about showing the result at the bottom of the original page so that the user can start over with the next calculation? TACGICommand has already a build in mechanism helping you with this work. If you look into the file multiply.html you will find a line

  <!!!!result>

since "<!" starts a HTML comment it will not show up in a browser. But the TACGICommand can parse this sort of comment and replace the entire comment with a match from its second TAssociation: fMarker. Instead of putting the result directly into the reply use

    fMarker.InsertEntry("\presult",msg);
    InsertMarker("\pMultiply.html");

This way we can put any whistles and bells into our HTML-page without affecting our core ACGI. This approach has a flaw I should mention. The parser is not quite intelligent and needs some recovery after an opening "<" character. So <HR><!!!!mydata> will not work since the parser analyses "<HR><!" finds it is no "!!!!" comment and skips both tags. In practice this is not a serious limitation, but a cause of unexpected errors you should be aware of.

Lets look at the result now:

Figure 4. Final appearance of Example.

The error shows us a general problem. What happens if an exception is thrown inside our ACGI? MacApp is polite and shows us a nice alert-box, but our actual user is the user at the other side of the internet. Another problem arises when our ACGI tries to open the dialog. During this time it is blocked and will not react to further requests. So you should always wrap your DoIt() code with a failure-handler and let the real user know what has happened:

  CATCH_ALL  // oops someone has thrown an exception
  {
    CStr15 num;
    CStr255 msg = "\p<B> CGI fatal error ";
    NumToString(fi.error, num);
    msg += num;
    msg += " </B>\n";
    fReply->PutKeyString(keyDirectObject,msg);
    // do not rethrow, we have handled it
  }
  ENDTRY

I think what we did can be done in less than one hour. I spent most of the time doing the actual work (and correcting my misspellings and the like) and had almost no work with ACGI related tasks.

Speed Considerations

MacApp can queue several Commands if needed, so if your DoIt() method is short there should be no problem. If you need some more time you will have to do your work in chunks and use some more sophisticated command handling. This way you can still be responsive if you must. If your webserver does the IP communication mostly asynchronous the webserver and your ACGI can get optimal performance out of the process. As far as I can see "Quid Pro Quo" 1.0 does not use asynchronous IP transfers, but I may be wrong on that.

One not so obvious Speedhole opens in the TACGICommands Constructor:

TACGICommand::TACGICommand(
  TCommandHandler* itsContext,
  TAppleEvent* message, 
  TAppleEvent* reply)
{
  fSuspendTheEvent = true;
  IServerCommand(cACGICommand, itsContext, kCantUndo, 
    kDoesNotCauseChange, NULL, *message, *reply);
  
  fArgs.  IAssociation();
  fMarker.IAssociation();
}

If you look close you will see that the call to IServerCommand makes a copy of the message. This is necessary since we are asynchronous and answer the request at some later time. The original message will vanish and trying to access it will result in the rarely seen error errAEReplyNotArrived (if I'm not wrong on this one). The error message is somewhat misleading since it appears when you try to read the message, not the reply.

If you fear about this problem you can start parsing the command in the constructor and create a different constructor for TACGICommand, this is left as an exercise for the reader.

I use MacApps THandleStream to do all the parsing. This should be no problem for the input side of the ACGI since the arguments are usually small. The output side is more difficult. Here we cannot stream our results directly into the webserver but must pass it back in the apple event. On the other Hand we must be flexible enough to handle output of varying sizes. You can optimize this somewhat by adjusting the resize parameter I use to initialize the THandleStreams, this way you can avoid excessive calls to ResizeHandle.

Do It Yourself

If you really want to use my classes you should try out the following exercises before actually using it, you will get aware of some more pitfalls my approach has:

  1. Go and modify the example in order to reinsert the result into the form instead of displaying it in a separate part of the window. See the problem(s)?
  2. Change the <FORM> and the ACGI to use the Post method.
  3. Create a big text-input field (more than 255 characters) and parse its contents.

Conclusion

I hope I could show you that MacApp is a good foundation for writing ACGIs in a short time. My solution is not perfect but I use it in an actual project and our customer is quite happy (at least with this part of the implementation).


Klaus Halfmann is the leader of software development at the InTeCo GmbH, Hochspeyer (Germany). He has studied computer science at the university of Kaiserslautern and after his diploma has been Programming mostly on Macintosh and MacApp. He worked more than a year at the StarDivison (Hamburg) porting the StarOffice 3.1 to the Macintosh. Now at InTeCo he is working on an autonomous project: DepotChart, a Stock Database program with a lot of numerical stuff and a sophisticated Charting Engine, currently targeted at the German market. If not programming on this project he manages the In-house Network, teaches his colleagues the many aspects of computing, cares about the other projects and chats with customers on the phone. Sometimes, after the working hours he can be found playing AVARA, a real-time TIME 3D Game by Ambrosia, on the Internet.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

iDefrag 5.1.7 - Disk defragmentation and...
iDefrag helps defragment and optimize your disk for improved performance. Features include: Supports HFS and HFS+ (Mac OS Extended). Supports case sensitive and journaled filesystems. Supports... Read more
TrailRunner 3.8.832 - Route planning for...
TrailRunner is the perfect companion for runners, bikers, hikers, and all people wandering under the sky. Plan routes on a geographical map. Import GPS or workout recordings and journalize your... Read more
VOX 2.8.14 - Music player that supports...
VOX just sounds better! The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features and support for all audio formats you should ever need.... Read more
WhiteCap 6.6 - Visual plug-in for iTunes...
WhiteCap is a sleek and sophisticated music visualizer and screensaver that features futuristic, wireframe mesh visuals with dynamic backgrounds and colors. WhiteCap contains thousands of visual... Read more
VueScan 9.5.65 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Carbon Copy Cloner 4.1.13 - Easy-to-use...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
TrailRunner 3.8.831 - Route planning for...
TrailRunner is the perfect companion for runners, bikers, hikers, and all people wandering under the sky. Plan routes on a geographical map. Import GPS or workout recordings and journalize your... Read more
Quicken 4.4.2 - Complete personal financ...
Quicken makes managing your money easier than ever. Whether paying bills, upgrading from Windows, enjoying more reliable downloads, or getting expert product help, Quicken's new and improved features... Read more
Adobe Illustrator CC 2017 21.0.2 - Profe...
Illustrator CC 2017 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Illustrator customer). Adobe Illustrator CC 2017 is the industry... Read more
Paparazzi! 1.0b2 - Make user-defined siz...
Paparazzi! is a small utility for OS X that makes screenshots of webpages. This very simple tool takes screenshots of websites which do not fit on one screen. You specify the desired width, minimal... Read more

Last week on PocketGamer
Welcome to a weekly feature looking back on the past seven days of coverage on our sister website, PocketGamer. It’s taken a while for 2017 to really get going, at least when it comes to the world of portable gaming. Thank goodness, then, for... | Read more »
ROME: Total War - Barbarian Invasion set...
To the delight of mobile strategy fans, Feral Interactive released ROME: Total War just a few months ago. Now the game's expansion, Barbarian Invasion is marching onto iPads as a standalone release. [Read more] | Read more »
Yuri (Games)
Yuri 1.0 Device: iOS iPhone Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: It's night. Yuri opens his eyes. He wakes up in a strange forest.The small, courageous explorer rides on his bed on casters in this... | Read more »
Space schmup Xenoraid launches on the Ap...
10Tons Xenoraid is out today on the App Store, bringing some high-speed space action to your mobile gadgets just in time for the weekend. The company's last premium title, another sci-fi game titled Neon Chrome, did quite well for itself, so... | Read more »
Star Wars: Force Arena Beginner's G...
Star Wars: Force Arena joined the populous ranks of Star Wars games on mobile today. It's a two-lane MOBA starring many familiar faces from George Lucas's famed sci-fi franchise. As with most games of this nature, Force Arena can be a little obtuse... | Read more »
Mysterium: The Board Game (Games)
Mysterium: The Board Game 1.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.0 (iTunes) Description: The official adaptation of the famous board game Mysterium! | Read more »
Sonny (Games)
Sonny 1.0.4 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.4 (iTunes) Description: Reimagined for iOS, cult-hit RPG Sonny brings challenging turn-based combat that requires strategy and mastery of each new skill to... | Read more »
Towaga (Games)
Towaga 1.0 Device: iOS iPhone Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: "It has been foretold that a masked being would stand atop the legendary Towaga Temple, dwelling among shadows to fulfil The Black Moon... | Read more »
Bubble Witch 3 Saga Guide: How to get th...
King's bringing its fairytale bubble-popping puzzler back for its 3rd outing in Bubble Witch 3 Saga. If you're familiar with the series, not much has changed here on the surface level, though you'll likely be pleased with the improvements. If you'... | Read more »
Sunless Sea sails onto iPad this spring
Failbetter Games, the folks who brought you Fallen London, are celebrating their 7th birthday today. To commemorate the event, the team revealed that Sunless Sea is coming to iPad this spring. | Read more »

Price Scanner via MacPrices.net

Opera Announces Neon Concept Browser For Mac
Opera is inviting users to get a glimpse of what Opera for computers could become with its Opera Neon browser concept. Each Opera Neon feature is described as “an alternate reality” for the Opera... Read more
Tellini Releases TabView 3.0 Missing Tool fo...
Tellini has announced the release of TabView 3.0. TabView has been the first macOS viewer for PowerTab tablatures. PowerTab is a well-known and widely adopted tablature editor for Windows systems and... Read more
13-inch 1.6GHz/128GB MacBook Air on sale for...
Overstock.com has the 1.6GHz/128GB 13″ MacBook Air on sale for $130 off MSRP including free shipping: - 13″ 1.6GHz/128GB MacBook Air (MMGF2LL/A): $869.99 $130 off MSRP Their price is the lowest... Read more
12-inch 32GB Space Gray iPad Pro on sale for...
B&H Photo has 12″ Space Gray 32GB WiFi Apple iPad Pros on sale for $55 off MSRP including free shipping. B&H charges sales tax in NY only: - 12″ Space Gray 32GB WiFi iPad Pro: $744.44 $55 off... Read more
9-inch 32GB Space Gray iPad Pro on sale for $...
B&H Photo has the 9.7″ 32GB Space Gray Apple iPad Pro on sale for $549 for a limited time. Shipping is free, and B&H charges NY sales tax only. Read more
Apple iMacs on sale for up to $120 off MSRP,...
B&H Photo has 21″ and 27″ Apple iMacs on sale for up to $120 off MSRP, each including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2199 $100 off MSRP - 27″ 3.2GHz/1TB Fusion iMac... Read more
Apple refurbished Apple TVs available for up...
Apple has Certified Refurbished 32GB and 64GB Apple TVs available for up to $30 off the cost of new models. Apple’s standard one-year warranty is included with each model, and shipping is free: -... Read more
1.4GHz Mac mini, refurbished, available for $...
The Apple Store has Apple Certified Refurbished 1.4GHz Mac minis available for $419. Apple’s one-year warranty is included, and shipping is free. Their price is $80 off MSRP, and it’s the lowest... Read more
16GB iPad Air 2, Apple refurbished, available...
Apple has Certified Refurbished 16GB iPad Air 2s available for $319 including free shipping. A standard Apple one-year is included. Their price is $60 off original MSRP for this model. Read more
Mac Pros on sale for $200 off MSRP, refurbish...
B&H Photo has Mac Pros on sale for $200 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2799, $200 off MSRP - 3.5GHz 6-core Mac Pro: $3799, $200... Read more

Jobs Board

*Apple* Retail - Multiple Positions- Crows N...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* & PC Desktop Support Technician...
Apple & PC Desktop Support Technician job in Los Angeles, CA Introduction: We have immediate job openings for several Desktop Support Technicians with one of our Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Premier Retailer - PT Service Specia...
DescriptionSimply Mac is the largest premier retailer for Apple products and solutions. At Simply Mac we are all Apple , all the time. Same products. Same prices. Read more
*Apple* Premier Retailer - Service Manager -...
DescriptionSimply Mac is the largest premier retailer for Apple products and solutions. At Simply Mac we are all Apple , all the time. Same products. Same prices. Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.