TweetFollow Us on Twitter

HFS Transfer DA
Volume Number:4
Issue Number:10
Column Tag:Programmer's Workshop

HFS Transfer DA

By Clifford Story, Jacksonville, FL

Transfer DA

The Transfer DA is a small desk accessory that adds a Transfer menu to any application that supports desk accessories.

When Apple killed the Minifinder, my work habits were shattered. I tried Oasis, a Finder substitute, for a while but I didn’t like the way the working directory was changed by launching a program; that is, if I launched, say, Macwrite, and opened a file, the sfget dialog came up showing the directory that contained Macwrite, rather than the one I had been working in. I had to navigate through folders to return to my work files.

Transfer solves that problem. Now, I work on the desktop but when I want to switch from one application to another, I use Transfer. The working directory is unaffected, and I skip the Finder.

This DA illustrates some interesting programming techniques: it is a segmented DA; it does an HFS search of an entire hard disk; it uses the iaznotify hook; and it creates resources with TML Pascal (a subject which came up in the May Mac Tutor letters column).

How the DA Works

When the DA is launched for the first time, it starts at the root of the disk which contains the System folder, and does an HFS catalog of the entire disk. It finds each application on the disk, and stores the name and directory id in a special resource FILE called “Transfer data”, in the System folder. Henceforth, it uses this file to build the menu (the HFS catalog takes a little time - about 30 seconds on my 20 meg hard disk - so you wouldn’t want to go through it every time).

When an application is selected from the Transfer menu, the applications’s name and directory id are copied into the DA’s global data record, and the address of a special launch routine is installed in the “iaznotify” low-memory global. The transfer may be immediate or delayed; in the first case, the DA will call “ExitToShell”; in the second, it will wait for the application to quit normally.

When the application quits, the Mac will prepare to launch the next application (usually the Finder). As one of the last steps in this process, it will call “InitApplZone” to initialize the heap. This trap will, in turn, call the routine whose address is stored in “iaznotify” - the DA’s launch routine. This routine simply writes the name of the desired application to “curappname” (another low-memory global), and sets the directory to the proper directory. The Mac will then go ahead and launch the application. Sort of a cowbird launch, you might say!

DA Segmentation

This is a small DA, a bit over 6K. so why is it segmented? Am I just showing off?

Before “InitApplZone” calls the launch procedure, the Mac closes all resource files. That means that if the launch procedure is in a resource (e.g., the DRVR resource), it will be left in a free block. I don’t feel very secure when executing code in a free block! The solution is to detach the resource; the block will remain allocated until “InitApplZone” actually cleans up the heap - which happens after it calls the launch routine.

But if the launch routine is part of the DRVR resource, this means detaching the DRVR. If we do that, we’ll never know about menu selections, since the desk Manager needs a DRVR resource to pass DA events to. So the launch routine has to be in a separate segment.

The launch routine is in a PACK resource. The DRVR’s “open” routine loads and locks the PACK, and records its address. When it wants to call a routine from the PACK, it puts a routine selector into register D0 and jumps to the PACK. At the top of the PACK is a jump table that branches to the routine corresponding to the selector.

There are other ways to segment a DA; this is just the way I did this one.

Creating Resources with TML Pascal

Since it came up in the May letters column, let me say a few words on this topic. It’s really simple, once you know the (undocumented) trick. It’s all in the link control file.

For example, here is a link control file to create an LDEF:

 !fastlist
 /codetype LDEF 1001 ‘Fast List’ 32
 list
 pas$library
 macintfglue
 /end

The first line is the linker’s start point - the name of the

main routine. The second line defines the resource type. This will be an LDEF, resource id 1001, named “Fast List”, purgeable. The next three lines are the object files to link, in the order you want them to be linked. And the final line means just what it says.

The code itself should be compiled as a unit, and most resource types will require that the main routine appear at the very top of the resource.

The two resources in this program are linked a bit differently, the DRVR resource because the resource definition requires a DA header at the top, and the PACK resource because I want a jump table at the top. The fact that I left out the first line and got away with it suggests that it may not be necessary.

Using the DA

I have tested this DA with every application on my hard disk (at the time I wrote it). A number of applications require the delayed transfer; for example, any program that uses scratch files. I found only one that would not work at all. Can you guess? That’s right - Microsoft Word.

Transfer vs. Multifinder

I leave as an exercise for the reader the problem of making this DA work with the Multifinder. A more fruitful project might be converting the DA to an FKEY.

;***************************************
;
;types.asm
;--------
;
;(c) 1987, 1988 Attic Software
;
;record formats for Transfer
;
;***************************************

;***************************************
;wrecord
;***************************************

 WRthemenuequ    0; MenuHandle
 WRresfactorequ    4; integer
 WRsysdir equ    6; long
 WRpackaddr equ   10; Ptr
 WRlaunchpath  equ  14; long
 WRlaunchaddr  equ  18; long
 WRiazaddrequ   22; long
 WRtheevent equ   26
 ; EventRecord
 WRthenameequ   42; Str255
 WRtheblock equ  298; hfs block

;***************************************


(***************************************     
types.pas
--------
 
(c) 1987, 1988 Attic Software
 
Pascal declarations for Transfer
 
***************************************)

unit types;

(**************************************)

interface

(**************************************)

uses macintf, hfs;

(**************************************)

type
 
 logical= boolean;
 long   = longint;
 
 shortpointer    = ^integer;
 longpointer=  ^long;

(***************************************
 
Program constants consist of (1) DA message types (most of which are 
not used in Transfer); (2) low-memory globals; and (3) resource ids.
 
***************************************)

const
 
 accEvent = 64;
 accRun = 65;
 accCursor= 66;
 accMenu= 67;
 accUndo= 68;
 accCut = 70;
 accCopy= 71;
 accPaste = 72;
 accClear = 73;
 goodbyekiss=  -1;
 
 curappname =  $910;
 erik   = $4552494B;
 findername =  $2E0;
 iaznotify= $33C;
 sysmap = $A58;
 
 menunum= 1001;
 aboutitem= 1;
 builditem= 2;
 finderitem =  4;
 
 errordialog=  1001;
 aboutdialog=  1002;
 builddialog=  1003;
 delaydialog=  1004;
 
 packnum= 1001;
 stringnum= 1001;
 
 datastart= 1000;
 
(***************************************     
A “deskrecord” is simply an application’s name and directory.  We need 
to know both of these to transfer to that application.
An “marray” is an array of deskrecords, along with the array’s size.
Finally, a “wrecord” is the DA’s  global data.  The last three fields 
(“theEvent”, “thename” and the varient parameter block) are just scratch 
areas, used by various routines as local variables.  Putting them here 
keeps them off the stack.
 
***************************************)
 
type
 
 deskrecord =  record
 dirid  : long;
 name   : Str255;
 end;
 deskpointer=  ^deskrecord;
 deskhandle =  ^deskpointer;
 
 marray = record
 count  : integer;
 data   : array
 [0..1000] of deskhandle;
 end;
 arraypointer    = ^marray;
 arrayhandle=  ^arraypointer;
 
 wrecord= packed record
 theMenu: MenuHandle;resfactor:  integer;                sysdir
 : long;packaddr : Ptr;   
 launchpath :  long; launchaddr    : long;         
 iazaddr: long;  
 theEvent : EventRecord;
 thename: Str255;case integer of   1 : (infoblock
 : CInfoPBRec);
 2 :  (hblock
 : HParamBlockRec);
 3 :  (wdblock
 : WDPBRec);
 end;
 wpointer = ^wrecord;

(**************************************)

implementation

(**************************************)

end.

(**************************************)

;***************************************;
;drvrhead.asm
;------------
;
;(c) 1987, 1988 Attic Software
;
;header for DRVR segment of
;Transfer.
;
;***************************************

;***************************************
;includes
;***************************************

 includesysequ.d
 includetypes.asm

;***************************************
;imported routines
;***************************************

 xref open
 xref ctl

;***************************************
;exported routines
;***************************************

 xdef initglobals
 xdef setdir

;***************************************
;
;The canonical DA header.  This is a
;modified version of the DA header
;supplied with TML Pascal.
;
;***************************************

 dc.w $0400 ; flags 
 dc.w $0000 ; servicedc.w $ffff  ; mask
 dc.w $0000 ; menuID dc.w ornopen  ; open    dc.w  orndone     ; prime
 dc.w ornctl; Control
 dc.w orndone  ; Status
 dc.w ornclose ; close

;***************************************

 dc.w ‘(c) 1987, 1988 ‘
 dc.w ‘Attic Software’
 dc.w ‘All rights reserved’

;***************************************

ornopen 
 movem.lA0/A1,-(SP)
 move.l A1,-(SP) ; device
 move.l A0,-(SP) ; param  jsr open
 movem.l(SP)+,A0/A1
 move.l jiodone,-(SP)
 rts

ornctl  
 movem.lA0/A1,-(SP)
 move.l A1,-(SP) ; device
 move.l A0,-(SP) ; param  jsr ctl
 movem.l(SP)+,A0/A1
 move.l jiodone,-(SP)
 rts

ornclose
orndone
 move.l jiodone,-(SP)
 rts

;***************************************
;
;The routine loader moves a selector
;into D0, and the address of the
;PACK segment into A0.  It then
;jumps to the pack (the pack begins
;with a jump table).
;
;***************************************

initglobals
 move.w #0,D0
 bra.w  loader

setdir
 move.w #4,D0
 bra.w  loader

loader
 movea.l4(SP),A0 ; globals
 movea.lWRpackaddr(A0),A0
 jmp  (A0)

;***************************************


(***************************************
 
drvr.pas
--------
 
(c) 1987, 1988 Attic Software
 
Pascal routines for DRVR segment of Transfer
 
***************************************)

unit drvr;

(**************************************)

interface
 
(**************************************)

uses macintf, hfs, types;
 
(**************************************)

implementation
 
(**************************************)

procedure initglobals
 (globals : wpointer); external;
function setdir
 (dirid : long;
 globals : wpointer) : OSErr; external;
 
(***************************************
 
drawscreen
----------
 
This routine draws the “About” message in its rectangle.
 
***************************************)

procedure drawscreen(
 theWindow : WindowPtr;
 theitem : integer);
 
var
 theType: integer;
 theHandle: Handle;
 thebox : Rect;
 
begin
 
 SetPort(theWindow);
 
 GetFNum(‘monaco’, theType);
 TextFont(theType);
 TextSize(9);
 
 GetDItem(theWindow, theitem, theType,
 theHandle, thebox);
 
 theHandle := GetResource(‘INFO’,
 GetWRefCon(theWindow));
 HLock(theHandle);
 TextBox(theHandle^, GetHandleSize(theHandle),
 thebox, 0);
 HUnlock(theHandle);
 
end;
 
(***************************************
 
transalert
----------
 
 I want all the alerts to identify their source, so I made them dialogs, 
in titled windows, and wrote this routine to imitate “Alert”.
 
 This is not the best way to do it; for one thing, a titled window should 
be dragable.  Modal dialogs, therefore, should not have titles.  I should 
have simply added a distinctive icon to the item lists.
 
 Since this is the first use of the “resfactor” field of the global record, 
it’s as good a place as any to explain it.  This DA is given a formal 
resource id of 16, and all its owned resources are numbered accordingly. 
 The Font/DA Mover will change all these numbers when it installs the 
DA.  “resfactor”, which is computed in the “open” routine, below, is 
the correction factor that converts the hard-coded resource ids to the 
actual ids in use.
 
 Note that “resfactor” doesn’t convert the formal ids; it actually converts 
the constants defined in the “types.pas” unit, which are nice positive 
numbers, equal to the resource ids plus 16872.
 
 There’s a little bit of code to install the previous procedure if this 
is the “About” dialog.
 
***************************************)

procedure transalert
 (dialognum : integer;
 globals : wpointer);
 
var
 savedport: GrafPtr;
 theDialog: DialogPtr;
 therecord: DialogRecord;
 theType: integer;
 theHandle: Handle;
 thebox : Rect;
 choice : integer;
 
begin
 
 with globals^ do begin
 
 GetPort(savedport);
 
 theDialog := GetNewDialog(dialognum
 + resfactor, @therecord,
 pointer(-1));
 SetPort(theDialog);
 
 if dialognum = aboutdialog then
 begin
 GetDItem(theDialog, 2, theType,
 theHandle, thebox);
 SetDItem(theDialog, 2, theType,
 Handle(@drawscreen), thebox);
 SetWRefCon(theDialog, dialognum
 + resfactor);
 end;
 
 InitCursor;
 ShowWindow(theDialog);
 
 repeat
 ModalDialog(nil, choice);
 until choice = ok;
 
 CloseDialog(theDialog);
 SetPort(savedport);
 
 end;
 
end;
 
(***************************************
 
errordisplay
------------
 
 This routine displays error messages.  The texts of the messages are 
in a string list.
 
***************************************)

procedure errordisplay
 (appnum, sysnum, resnum : long;
 globals : wpointer);
 
var
 string1: Str255;
 string3: Str255;
 string4: Str255;
 
begin
 
 InitCursor;
 
 with globals^ do begin
 
 GetIndString(string1, resfactor
 + stringnum, resnum);
 if string1 = ‘’ then
 string1:= ‘An error has occurred!’;
 
 NumToString(appnum, string3);
 NumToString(sysnum, string4);
 ParamText(string1, ‘’,
 string3, string4);
 
 SysBeep(10);
 transalert(errordialog, globals);
 
 end;
 
end;
 
(***************************************
 
getapps
------
 
 Here it is - the only recursive routine I have ever been forced to use! 
 (Recursion is overrated; it is, in my opinion, better to avoid recursion, 
if you can do it in a natural fashion.  Loops are easier to read, and 
generally more efficient.)
 
 This routine is passed a directory id, the count of objects (files and 
folders) in that directory, and the volume reference number.  It indexes 
through the directory with “PBGetCatInfo”.
 
 If the object is another directory, it calls itself with that directory’s 
id and count.
 
 If the object is a file, and if the file is an application, it records 
its name and the directory id in the application array.
 
***************************************)
 
procedure getapps
 (thedir : long; thecount : integer;
 thevol : integer;
 theHandle : arrayhandle;
 globals : wpointer);
 
label
 100;
 
var
 index  : integer;
 anerror: integer;
 theDialog: DialogPtr;
 jndex  : integer;
 thelength: integer;
 thedesk: deskhandle;
 
begin
 
 with globals^ do begin
 
 for index := 1 to thecount do
 with infoblock do begin
 
 thename := ‘’;
 
 ioCompletion := nil;
 ioNamePtr := @thename;
 ioVRefNum := thevol;
 ioFDirIndex := index;
 ioDrDirID := thedir;
 
 anerror:= PBGetCatInfo(@infoblock,
 false);
 if anerror <> noErr then begin
 errordisplay(101, anerror,
 2, globals);
 goto 100;
 end;
 
 if BitAnd(ioFlAttrib, $10) = $10 then
 getapps(ioDrDirID, ioDrNmFls,
 thevol, theHandle, globals)
 else if ioFlFndrInfo.fdType
 = ‘APPL’ then begin
 
 jndex := theHandle^^.count+ 1;
 theHandle^^.count := jndex;
 SetHandleSize
 (Handle(theHandle),
 5 * (jndex + 1));
 
 HLock(Handle(theHandle));
 
 with theHandle^^ do
 while (IUCompString(thename,
 data[jndex - 1]^^.name) < 0)
 and (jndex > 1) do begin
 
 data[jndex]:= data[jndex - 1];
 jndex := jndex - 1;

 end;
 
 thelength:= 10 + length(thename);
 thelength:= 2 * (thelength div 2);
 theHandle^^.data[jndex]
 := deskhandle
 (NewHandle(thelength));
 with theHandle^^.data[jndex]^^
 do begin
 dirid := thedir;
 name := thename;
 end;
 
 HUnlock(Handle(theHandle));
 
 end;
 
100:  end;
 
 end;

end;
 
(***************************************
 
walktree
--------
 
 This routine catalogs all the applications on a given disk.
 
 It first puts up a dialog, telling the user what’s going on.
 
 It next calls “PBGetCatInfo” for the root directory (directory id = 
2), to get the number of objects in the root.  Then it calls “getapps” 
to walk the HFS tree recursively.
 
 The collected data is written to the current resource file (“Transfer 
Data”, in the System folder), and the dialog is dismissed.
 
***************************************)
 
procedure walktree
 (thevol : integer;
 theHandle : arrayhandle;
 globals : wpointer);
 
var
 savedport: GrafPtr;
 theDialog: DialogPtr;
 therecord: DialogRecord;
 index  : integer;
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 GetPort(savedport);
 
 theDialog := GetNewDialog(resfactor
 + builddialog, @therecord,
 pointer(-1));
 SetPort(theDialog);
 ShowWindow(theDialog);
 DrawDialog(theDialog);
 
 with infoblock do begin
 ioCompletion := nil;
 ioNamePtr := nil;
 ioVRefNum := thevol;
 ioFDirIndex := 0;
 ioDrDirID := 2;
 end;
 anerror := PBGetCatInfo(@infoblock,
 false);
 if anerror <> noErr then
 errordisplay(102, anerror,
 2, globals)
 else
 getapps
 (2, infoblock.ioDrNmFls,
 thevol, theHandle, globals);
 end;
 
 HLock(Handle(theHandle));
 with theHandle^^ do
 for index := 1 to count do begin

 AddResource
 (Handle(data[index]), ‘.Trn’,
 datastart + index,
 data[index]^^.name);

 SetHandleSize
 (Handle(data[index]), 4);

 end;
 HUnlock(Handle(theHandle));
 
 CloseDialog(theDialog);
 SetPort(savedport);

end;
 
(***************************************
 
buildmenu
--------
 
 This routine assembles the necessary data, and builds the Transfer menu.
 
 It finds the volume reference number of the disk with the System folder, 
sets the directory to the System folder, and opens or creates the “Transfer 
Data” file in that directory.
 
 If this file lacks a header resource (whether because it was just created, 
or because it has been corrupted), then it must be rebuilt, with “walktree”.
 
 Then the menu is built.  The menu resource is loaded, and the fourth 
item set to the name of the current Finder.  The remainder of the menu 
is copied from the resource file.
 
***************************************)
 
procedure buildmenu(globals : wpointer);
 
label
 100;
 
var
 thepointer :  shortpointer;
 thevolume: integer;
 theres : integer;
 theHandle: arrayhandle;
 index  : integer;
 jndex  : integer;
 thedesk: Handle;
 theID  : integer;
 theType: ResType;
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 thepointer := shortpointer(sysmap);
 anerror := GetVRefNum
 (thepointer^, thevolume);
 if anerror <> noErr then begin
 errordisplay(103, anerror,
 3, globals);
 goto 100;
 end;
 
 anerror := setdir(sysdir, globals);
 if anerror <> noErr then begin
 errordisplay(104, anerror,
 3, globals);
 goto 100;
 end;
 
 thename := ‘Transfer Data’;
 theres := OpenResFile(thename);
 if ResError = fnfErr then begin
 CreateResFile(thename);
 theres := OpenResFile(thename);
 end;
 if ResError <> noErr then begin
 errordisplay(105, ResError,
 3, globals);
 goto 100;
 end;
 
 theHandle
 := arrayhandle
 (Get1Resource(‘.Trn’,
 datastart));
 if theHandle = nil then begin
 theHandle := arrayhandle
 (NewHandle(6));
 theHandle^^.count := 0;
 theHandle^^.data[0]
 := deskhandle(NewHandle(16));
 AddResource(Handle(theHandle),
 ‘.Trn’, datastart, ‘’);
 walktree(thevolume,
 theHandle, globals);
 SetHandleSize
 (Handle(theHandle), 2);
 end;
 
 theMenu := GetMenu(resfactor
 + menunum);
 BlockMove(Ptr(findername),
 Ptr(@thename), 16);
 SetItem(theMenu, 4, thename);
 
 jndex := 1;
 for index := 1 to theHandle^^.count
 do begin
 thedesk := Get1Resource(‘.Trn’,
 datastart + index);
 if thedesk <> nil then begin
 AppendMenu(theMenu, ‘.Trn’);
 GetResInfo(thedesk, theID,
 theType, thename);
 SetItem(theMenu, jndex + 4,
 thename);
 jndex := jndex + 1;
 end;
 end;
 
 InsertMenu(theMenu, 0);
 DrawMenuBar;
 
 CloseResFile(theres);
 
100:

 end;
 
end;
 
(***************************************
 
systemvol
--------
 
 This routine is more or less straight out of Tech Note 77, pages 3 and 
4.  It returns a working directory reference number for the System folder, 
suitable for use in file system calls.
 
 Step one is to find the volume reference number of the volume that holds 
the System folder.  “sysmap” is the file reference number of the System 
file (an open file), so “GetVRefNum” will find the volume refence number 
of the System file and, of course, the System folder.
 
 Step two is to get the directory id, with a call to “PBHGetVInfo”.  
The directory id is returned in the “ioVFndrInfo[1]” field of the HParamBlockRec.
 
 Finally, “PBOpenWD” will return the System folder’s working directory 
reference number, which can be used as a volume reference number in file 
system calls.
 
***************************************)

function systemvol
 (globals : wpointer) : integer;
 
var
 thepointer :  shortpointer;
 thevolume: integer;
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 thepointer := shortpointer(sysmap);
 anerror := GetVRefNum(thepointer^,
 thevolume);
 
 with hblock do begin
 ioNamePtr := nil;
 ioVRefNum := thevolume;
 ioVolIndex := 0;
 end;
 anerror := PBHGetVInfo(@hblock,
 false);
 
 with wdblock do begin
 ioWDDirID
 := hblock.ioVFndrInfo[1];
 ioNamePtr := nil;
 ioVRefNum := thevolume;
 ioWDProcID := erik;
 end;
 anerror := PBOpenWD(@wdblock,
 false);
 
 systemvol := wdblock.ioVRefNum;
 
 end;
 
end;
 
(***************************************
 
rebuildmenu
----------
 
 If the “Rebuild menu” item is chosen from the menu, or an application 
is chosen which can’t be found, Transfer will rebuild the menu from scratch. 
 It does this by deleting the “Transfer Data” file, and calling “buildmenu”.
 
***************************************)

procedure rebuildmenu
 (globals : wpointer);
 
var
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 DeleteMenu(resfactor + menunum);
 ReleaseResource(Handle(theMenu));
 anerror := FSDelete
 (‘Transfer Data’,
 systemvol(globals));
 buildmenu(globals);
 
 end;
 
end;
 
(***************************************
 
dofinder
--------
 
 If the “Finder” item is chosen from the menu, then no transfer is desired, 
so restore the “iaznotify” hook to the value it held when Transfer was 
launched. (This isn’t quite right, since something besides Transfer may 
have changed it since then.  but I don’t see any way to correct for that...)
 
 If the option key is down, do nothing else.  Otherwise, do an immediate 
transfer by calling “ExitToShell”.
 
***************************************)

procedure dofinder(globals : wpointer);
 
var
 thepointer :  longpointer;
 
begin
 
 with globals^ do begin
 
 thepointer
 := longpointer(iaznotify);
 thepointer^ := iazaddr;
 
 if GetNextEvent(0, theEvent) then
 ;
 if BitAnd(theEvent.modifiers,
 optionKey) = 0 then
 ExitToShell;
 
 end;
 
end;
 
(***************************************
 
clickmenu
--------
 
 The first few menu choices are handled by routines above.
 
 If an application is chosen, we need to get (1) the application’s name, 
and (2) its directory.  The name is easy; it’s on the menu.  To get the 
directory, we have to go back to the “Transfer Data” file.
 
 Once we have the application’s directory, the next thing to do is to 
make sure it’s there.  Transferring to a non-existent application will 
cause a system bomb.
 
 If everything is ok, then if the option key is down, prepare to do a 
delayed transfer; otherwise, do an immediate transfer by calling “ExitToShell”.
 
***************************************)

procedure clickmenu
 (itemchoice : integer;
 globals : wpointer);
 
label
 100;
 
var
 theres : integer;
 thedesk: deskhandle;
 theinfo: FInfo;
 thepointer :  longpointer;
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 case itemchoice of
 aboutitem:
 transalert(aboutdialog,
 globals);
 builditem:
 rebuildmenu(globals);
 finderitem :
 dofinder(globals);
 otherwise

 thepointer := longpointer
 (iaznotify);
 thepointer^ := launchaddr;

 anerror := setdir(sysdir,
 globals);
 if anerror <> noErr then begin
 errordisplay(106, anerror,
 3, globals);
 goto 100;
 end;
 
 theres := OpenResFile
 (‘Transfer Data’);
 if ResError <> noErr then begin
 rebuildmenu(globals);
 goto 100;
 end;
 
 GetItem(theMenu,
 itemchoice, thename);
 thedesk := deskhandle
 (Get1NamedResource(‘.Trn’,
 thename));
 DetachResource(Handle(thedesk));
 CloseResFile(theres);
 
 if thedesk = nil then begin
 rebuildmenu(globals);
 goto 100;
 end;
 
 anerror := setdir
 (thedesk^^.dirid, globals);
 if anerror <> noErr then begin
 rebuildmenu(globals);
 goto 100;
 end;
 
 anerror := GetFInfo(thename,
 0, theinfo);
 if anerror <> noErr then begin
 rebuildmenu(globals);
 goto 100;
 end;
 
 launchpath := thedesk^^.dirid;
 
 if GetNextEvent(0, theEvent) then
 ;
 if BitAnd(theEvent.modifiers,
 optionKey) = 0 then
 ExitToShell
 else begin
 ParamText(thename,
 ‘’, ‘’, ‘’);
 transalert(delaydialog,
 globals);
 end;
 
 end;
 
100:  HiliteMenu(0);
 
 end;
 
end;
 
(***************************************
 
open
----
 
 This is the canonical DA open routine.  If the DA has already been opened, 
device.dCtlMenu will be nonzero; do nothing.  Otherwise, Allocate the 
globals, and fill in a few fields.  Of particular interest is the calculation 
of “resfactor” by the magic formula $BFE0 - 32 * dCtlRefNum - 1000.  
This, assuming I have given the DA the formal resource id of 16, allows 
be to refer to owned resources by ids from 1000 to 1031, adding resfactor 
to convert to the actual values.
 
 Next, load and detach the PACK segment.  And lock it; it’s created locked 
but why take chances?
 
 Finally, call “initglobals” to fill in the rest of the fields, and “buildmenu” 
to set up the menu.
 
***************************************)
 
procedure open(var device : DCtlEntry;
 var block : ParamBlockRec);
 
var
 globals: wpointer;
 packhandle :  Handle;
 
begin
 
 if device.dCtlMenu = 0 then begin
 
 globals := wpointer
 (NewPtr(sizeof(wrecord)));
 if globals <> nil then
 with globals^ do begin
 
 with device do begin
 resfactor := $BFE0
 - 32 * dCtlRefNum - 1000;
 dCtlMenu := resfactor
 + menunum;
 dctlwindow := nil;
 dCtlStorage
 := Handle(globals);
 end;
 
 packhandle := GetResource
 (‘PACK’, resfactor + packnum);
 DetachResource(packhandle);
 HLock(packhandle);
 packaddr := packhandle^;
 
 initglobals(globals);
 buildmenu(globals);
 
 end;
 
 end;
 
end;
 
(***************************************
 
ctl
--
 
 The canonical Control routine.  The only events we’re interested in 
are menu clicks; if we get one, call “clickmenu”.
 
***************************************)
 
procedure ctl(var device : DCtlEntry;
 var block : ParamBlockRec);

var
 globals: wpointer;
 
begin
 
 if (device.dCtlMenu <> 0)
 and (block.csCode = accMenu)
 then begin
 globals := wpointer
 (device.dCtlStorage);
 clickmenu
 (block.csParam[1], globals);
 end;
 
end;
 
(**************************************)
 
 end.
 
(**************************************)


/codetype DRVR 16 ‘Transfer HD’ 16
drvrhead
drvr
pas$library
macintfglue
/end


;***************************************
;
;packhead.asm
;------------
;
;(c) 1987, 1988 Attic Software
;
;header and assembly routines for
;PACK segment of Transfer.
;
;***************************************

;***************************************
;exported routines
;***************************************

 xdef setglobal
 xdef getglobal
 xdef runiaz

;***************************************
;
;The jump table begins with a jump
;into the body of the table, indexed
;by D0 (the routine selector).  Then
;it branches to the appropriate
;routine.
;
;***************************************
 
 xref initglobals
 xref setdir

 jmp  2(PC,D0.w)
 bra.w  initglobals
 bra.w  setdir

;***************************************
;
;The global access routines provide
;a way to save the address of the
;global record, which would
;otherwise be lost.  This is done by
;writing the address into a four-
;byte constant in the PACK segment.
;
;Note:  writing to code segments is
;frowned upon in some circles.  This
;objection has a legitimate basis:
;self-modifying code is a horror.
;It makes debugging difficult, and
;maintenance impossible.  (5 points:
;where in “Inside Mac” are you
;instructed to write self-modifying
;code?)  In this instance, I an not
;modifying code; I am modifying
;data, and there’s nothing wrong
;with that.
;
;procedure setglobal(value : long);
;function getglobal : long;
;
;***************************************

 module ‘access’

setglobal
 movea.l(SP)+,A0
 lea  dummy,A1
 move.l (SP)+,(A1)
 jmp  (A0)

getglobal
 movea.l(SP)+,A0
 move.l dummy,(SP)
 jmp  (A0)

dummy
 dc.w ‘xxxx’

;***************************************
;
;procedure runiaz(iazaddr : long)
;
;The runiaz routine zeros the
;“iaznotify” hook, and calls the
;routine that preceded Transfer’s
;routine, if any.
;
;***************************************

 module ‘runiaz’

runiaz
 clr.l  $33C
 movea.l4(SP),A0
 move.l (SP)+,(SP)

 cmpa.l #0,A0
 beq.w  L0001
 move.l A0,-(SP)

L0001
 rts

;***************************************


(***************************************
 
pack.pas
--------

(c) 1987, 1988 Attic Software
 
Pascal routines for PACK segment of Transfer
 
***************************************)

unit pack;

(**************************************)

interface
 
(**************************************)

uses macintf, hfs, types;
 
(**************************************)

implementation
 
(**************************************)

procedure setglobal
 (value : long); external;
function getglobal : long; external;
procedure runiaz
 (iazaddr : long); external;
 
(***************************************
 
systemdir
--------
 
 This routine is more or less straight out of Tech Note 67.  It returns 
a directory id for the System folder, suitable for use in “SetVol” calls.
 
 Step one is to find the volume reference number of the volume that holds 
the System folder.  “sysmap” is the file reference number of the System 
file (an open file), so “GetVRefNum” will find the volume refence number 
of the System file and, of course, the System folder.  (The Tech Note 
skips this step; it searches the boot drive for a System folder, and 
may not find one.)
 
 Step two is to get the directory id, with a call to “PBHGetVInfo”.  
The directory id is returned in the “ioVFndrInfo[1]” field of the HParamBlockRec.
 
***************************************)

function systemdir
 (globals : wpointer) : long;
 
var
 thepointer :  shortpointer;
 thevolume: integer;
 anerror: integer;
 
begin
 
 with globals^ do begin
 
 thepointer := shortpointer(sysmap);
 anerror := GetVRefNum(thepointer^,
 thevolume);
 
 with hblock do begin
 ioNamePtr := nil;
 ioVRefNum := thevolume;
 ioVolIndex := 0;
 end;
 anerror := PBHGetVInfo(@hblock,
 false);
 
 systemdir := hblock.ioVFndrInfo[1];
 
 end;
 
end;

(***************************************
 
setdir
------
 
 This is just a shell around “PBHSetVol”.
 
***************************************)

function setdir
 (dirid : long;
 globals : wpointer) : OSErr;
 
begin
 
 with globals^ do begin
 
 with wdblock do begin
 ioCompletion := nil;
 ioNamePtr := nil;
 ioVRefNum := 0;
 ioWDDirID := dirid;
 end;
 
 setdir := PBHSetVol(@wdblock,
 false);
 
 end;
 
end;
 
(***************************************
 
postlaunch
----------
 
 This is the routine that performs the actual launch.  It is called by 
“InitApplZone” throught the “iaznotify” hook.
 
 First, recover the global record with a call to “getglobal”.  This routine 
is called after all the resources have been released; the DRVR segment 
is no longer around, and even if it was, “postlaunch” isn’t called by 
it, so we can’t find the globals in the usual way.  “getglobal” will 
return a long word stored right in the PACK segment, which has been previously 
set to a pointer to the globals.
 
 Next, “postlaunch” will call any routines that were already installed 
in “iaznotify” when “postlaunch” was installed there, via the “runiaz” 
routine.
 
 Then it calls “setdir” to set the volume to the folder containing the 
chosen application, and copies the applications’s name to “curappname”. 
 The system will now be fooled into launching that application instead 
of the Finder.
 
***************************************)

procedure postlaunch;
 
var
 globals: wpointer;
 anerror: integer;
 
begin
 
 globals := wpointer(getglobal);
 with globals^ do begin
 runiaz(iazaddr);
 anerror := setdir(launchpath,globals);
 if anerror = noErr then
 BlockMove(@thename, Ptr(curappname), 32);
 end;
 
end;
 
(***************************************
 
initglobals
----------
 
 Initialize global data.  Note that we preserve the value at “iaznotify”; 
if the current application has installed a routine there, we will want 
to run it before we run ours.
 
 Also, we don’t want to confuse the new application with the old applications’s 
data files, so we clear the finder files.
 
***************************************)

procedure initglobals
 (globals : wpointer);
 
var
 thepointer :  longpointer;
 message: integer;
 count  : integer;
 index  : integer;
 
begin
 
 setglobal(long(globals));
 
 with globals^ do begin
 
 sysdir := systemdir(globals);
 
 thepointer
 := longpointer(iaznotify);
 iazaddr := thepointer^;
 
 launchaddr
 := BitAnd(long(@postlaunch),
 $FFFFFF);
 
 end;
 
 CountAppFiles(message, count);
 for index := 1 to count do
 ClrAppFiles(index);
 
end;
 
(**************************************)
 
end.
 
(**************************************)


/codetype PACK -15871 ‘Transfer HD’ 16
packhead
pack
pas$library
macintfglue
/end


*****************************************
*rsrc.r
*
*(c) 1987, 1988 Attic Software
*
*resources for Transfer DA
*
****************************************

Transfer 3.2 HD
DFILDMOV

include drvr
include pack

*****************************************
*
*Menu resource
*
****************************************

type MENU
 ,-15871 (4)
Transfer
 About Transfer...
 Rebuild menu
 (-
 Finder

*****************************************
*
*Error messages
*
****************************************

Type STR#
 ,-15871 (32)
 4
I’m unable to load the Transfer DA.
Error building the menu list.
Error opening the “Transfer Data” file.
Error restoring the volume.

*****************************************
*
*About information
*
****************************************

Type INFO=GNRL
 Trans,-15870 (32)
.S
Transfer 3.2 HD adds a Transfer menu ++
to any application that ++
supports desk accessories.  It is ++
intended for use with your ++
hard disk, and 
.S
expects that you are using ++
HFS.\0D\0DTransfer 3.2 HD was ++
written by Clifford Story of ++
Attic Software.\0D\0D
.S
                    Attic Software\0D++
                    P.O. Box 24695\0D++
                    Jacksonville, ++
Florida   32241

*****************************************
*
*Item lists
*
****************************************

Type DITL
     Error,-15871 (32)
2
*   1
BtnItem Enabled
116 83 136 173
OK

*   2
statText Disabled
10 10 106 246
^0^1\0D\0DTransfer HD error ^2,++
\0DSystem error ^3.

     About,-15870 (32)
2
*   1
BtnItem Enabled
252 201 272 291
OK

*   2
userItem Enabled
10 10 242 482

     Build,-15869 (32)
1
*   1
statText Disabled
10 10 90 246
Transfer is building the menu list.  ++
This will take 30 seconds ++
or so but the list will be preserved ++
and won’t have to be ++
re-built unless you re-arrange your ++
disk.

     Delay,-15868 (32)
2
*   1
BtnItem Enabled
68 83 88 173
OK

*   2
statText Disabled
10 10 58 246
“^0” will be launched when you quit ++
this application.

*****************************************
*
*Dialogs
*
****************************************

Type DLOG
 Error,-15871 (32)
Transfer HD
64 128 210 384
invisible nogoaway
0
0
-15871

 About,-15870 (32)
Transfer 3.2 HD, © 1987, 1988 ++
Attic Software
50 10 332 502
invisible nogoaway
0
0
-15870

 Build,-15869 (32)
Transfer HD
64 128 164 384
invisible nogoaway
0
0
-15869

 Delay,-15868 (32)
Transfer HD
64 128 162 384
invisible nogoaway
0
0
-15868

****************************************
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Duplicate Annihilator 5.7.5 - Find and d...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator... Read more
BusyContacts 1.0.2 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Capture One Pro 8.2.0.82 - RAW workflow...
Capture One Pro 8 is a professional RAW converter offering you ultimate image quality with accurate colors and incredible detail from more than 300 high-end cameras -- straight out of the box. It... Read more
Backblaze 4.0.0.872 - Online backup serv...
Backblaze is an online backup service designed from the ground-up for the Mac.With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Little Snitch 3.5.2 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
Monolingual 1.6.4 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. If you use your computer in only one (human) language, you... Read more
CleanApp 5.0 - Application deinstaller a...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Fantastical 2.0 - Create calendar events...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event details... Read more
Cocktail 8.2 - General maintenance and o...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
Direct Mail 4.0.4 - Create and send grea...
Direct Mail is an easy-to-use, fully-featured email marketing app purpose-built for OS X. It lets you create and send great looking email campaigns. Start your newsletter by selecting from a gallery... Read more

These are All the Apple Watch Apps and G...
The Apple Watch is less than a month from hitting store shelves, and once you get your hands on it you're probably going to want some apps and games to install. Fear not! We've compiled a list of all the Apple Watch apps and games we've been able to... | Read more »
Appy to Have Known You - Lee Hamlet Look...
Being at 148Apps these past 2 years has been an awesome experience that has taught me a great deal, and working with such a great team has been a privilege. Thank you to Rob Rich, and to both Rob LeFebvre and Jeff Scott before him, for helping me... | Read more »
Hands-On With Allstar Heroes - A Promisi...
Let’s get this out of the way quickly. Allstar Heroes looks a lot like a certain other recent action RPG release, but it turns out that while it’s not yet available here, Allstar Heroes has been around for much longer than that other title. Now that... | Read more »
Macho Man and Steve Austin Join the Rank...
WWE Immortals, by Warner Bros. Interactive Entertainment and WWE, has gotten a superstar update. You'll now have access to Macho Man Randy Savage and Steve Austin. Both characters have two different versions: Macho Man Randy Savage Renegade or Macho... | Read more »
Fearless Fantasy is Fantastic for the iF...
I actually had my first look at Fearless Fantasy last year at E3, but it was on a PC so there wasn't much for me to talk about. But now that I've been able to play with a pre-release version of the iOS build, there's quite a bit for me to talk... | Read more »
MLB Manager 2015 (Games)
MLB Manager 2015 5.0.14 Device: iOS Universal Category: Games Price: $4.99, Version: 5.0.14 (iTunes) Description: Guide your favorite MLB franchise to glory! MLB Manager 2015, officially licensed by MLB.com and based on the award-... | Read more »
Breath of Light (Games)
Breath of Light 1.0.1421 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.1421 (iTunes) Description: Hold a quiet moment. Breath of Light is a meditative and beautiful puzzle game with a hypnotic soundtrack by... | Read more »
WWE WrestleMania Tags into the App Store
Are You ready to rumble? The official WWE WrestleMania app, by World Wrestling Entertainment, is now available. Now you can get all your WrestleMania info in one place before anyone else. The app offers details on superstar signings, interactive... | Read more »
Bio Inc's New Expansion is Infectin...
Bio Inc., by DryGin Studios, is the real time strategy game where you infect a human body with the worst virus your evil brain can design. Recently, the game was updated to add a whole lot of new features. Now you can play the new “Lethal”... | Read more »
The Monocular Minion is Here! Despicable...
Despicable Me: Minion Rush, by Gameloft, is introducing a new runner to the mix in their latest update. Now you can play as Carl, the prankster minion. Carl has a few new abilities to play with, including running at a higher speed from the start.... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.... Read more
Save up to $80 on iPad Air 2s, NY tax only, f...
 B&H Photo has iPad Air 2s on sale for $80 off MSRP including free shipping plus NY sales tax only: - 16GB iPad Air 2 WiFi: $469.99 $30 off - 64GB iPad Air 2 WiFi: $549.99 $50 off - 128GB iPad... Read more
iMacs on sale for up to $205 off MSRP
B&H Photo has 21″ and 27″ iMacs on sale for up to $205 off MSRP including free shipping plus NY sales tax only: - 21″ 1.4GHz iMac: $1019 $80 off - 21″ 2.7GHz iMac: $1189 $110 off - 21″ 2.9GHz... Read more
Färbe Technik Offers iPhone Battery Charge LI...
Färbe Technik, which manufactures and markets of mobile accessories for Apple, Blackberry and Samsung mobile devices, is offering tips on how to keep your iPhone charged while in the field: •... Read more
Electronic Recyclers International CEO Urges...
Citing a recent story on CNBC about concerns some security professionals have about the forthcoming Apple Watch, John Shegerian, Chairman and CEO of Electronic Recyclers International (ERI), the... Read more
Save up to $380 with Apple refurbished iMacs
The Apple Store has Apple Certified Refurbished iMacs available for up to $380 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 27″ 3.5GHz 5K iMac – $2119 $... Read more
Mac minis on sale for up to $75 off, starting...
MacMall has Mac minis on sale for up to $75 off MSRP including free shipping. Their prices are the lowest available for these models from any reseller: - 1.4GHz Mac mini: $459.99 $40 off - 2.6GHz Mac... Read more
College Student Deals: Additional $50 off Mac...
Take an additional $50 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through April 11, 2015. Anyone with a valid .EDU email address can take advantage... Read more
Mac Pros on sale for up to $260 off MSRP
B&H Photo has Mac Pros on sale for up to $260 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: $3719.99... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more

Jobs Board

DevOps Software Engineer - *Apple* Pay, iOS...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Sr. Technical Services Consultant, *Apple*...
**Job Summary** Apple Professional Services (APS) has an opening for a senior technical position that contributes to Apple 's efforts for strategic and transactional Read more
Lead *Apple* Solutions Consultant - Retail...
**Job Summary** Job Summary The Lead ASC is an Apple employee who serves as the Apple business manager and influencer in a hyper-business critical Reseller's store Read more
*Apple* Pay - Site Reliability Engineer - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.